blob: 5da884d0ba47edf7a6f69c2700f1beff0e3b4360 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <utility>
#include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "components/viz/common/quads/tile_draw_quad.h"
#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "gpu/ipc/common/mailbox_holder_mojom_traits.h"
#include "gpu/ipc/common/mailbox_mojom_traits.h"
#include "mojo/public/cpp/bindings/message.h"
#include "services/viz/public/cpp/compositing/compositor_frame_mojom_traits.h"
#include "services/viz/public/cpp/compositing/selection_mojom_traits.h"
#include "services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h"
#include "services/viz/public/cpp/compositing/surface_id_mojom_traits.h"
#include "services/viz/public/mojom/compositing/compositor_frame.mojom.h"
#include "testing/perf/perf_result_reporter.h"
#include "ui/gfx/geometry/mojom/geometry_mojom_traits.h"
#include "ui/gfx/mojom/selection_bound_mojom_traits.h"
#include "ui/latency/mojom/latency_info_mojom_traits.h"
namespace viz {
namespace {
static const auto kTimeLimit = base::TimeDelta::FromSeconds(2);
static const int kNumWarmupRuns = 20;
static const int kNumRunsPerTimeRecord = 10;
enum class UseSingleSharedQuadState { YES, NO };
constexpr char kMetricPrefixVizSerialization[] = "VizSerialization.";
constexpr char kMetricStructDeserializationTimeUs[] =
"StructTraits_min_frame_deserialization_time";
constexpr char kMetricStructDeserializationThroughputRunsPerS[] =
"StructTraits_deserialization_throughput";
constexpr char kMetricStructSerializationTimeUs[] =
"StructTraits_min_frame_serialization_time";
constexpr char kMetricStructSerializationThroughputRunsPerS[] =
"StructTraits_serialization_throughput";
perf_test::PerfResultReporter SetUpReporter(
const std::string& story,
UseSingleSharedQuadState single_sqs) {
std::string story_suffix = single_sqs == UseSingleSharedQuadState::YES
? "_per_render_pass_shared_quad_state"
: "_per_quad_shared_quad_state";
perf_test::PerfResultReporter reporter(kMetricPrefixVizSerialization,
story + story_suffix);
reporter.RegisterImportantMetric(kMetricStructDeserializationTimeUs, "us");
reporter.RegisterImportantMetric(
kMetricStructDeserializationThroughputRunsPerS, "runs/s");
reporter.RegisterImportantMetric(kMetricStructSerializationTimeUs, "us");
reporter.RegisterImportantMetric(kMetricStructSerializationThroughputRunsPerS,
"runs/s");
return reporter;
}
class VizSerializationPerfTest : public testing::Test {
protected:
static void RunDeserializationTestStructTraits(
const std::string& story,
const CompositorFrame& frame,
UseSingleSharedQuadState single_sqs) {
mojo::Message message = mojom::CompositorFrame::SerializeAsMessage(&frame);
for (int i = 0; i < kNumWarmupRuns; ++i) {
CompositorFrame compositor_frame;
mojom::CompositorFrame::Deserialize(
message.payload(), message.payload_num_bytes(), &compositor_frame);
}
base::TimeTicks now = base::TimeTicks::Now();
base::TimeTicks end = now + kTimeLimit;
base::TimeDelta min_time;
size_t count = 0;
for (base::TimeTicks start = now; start < end; start = now) {
for (int i = 0; i < kNumRunsPerTimeRecord; ++i) {
CompositorFrame compositor_frame;
mojom::CompositorFrame::Deserialize(
message.payload(), message.payload_num_bytes(), &compositor_frame);
now = base::TimeTicks::Now();
// We don't count iterations after the end time.
if (now < end)
++count;
}
if (now - start < min_time || min_time.is_zero())
min_time = now - start;
}
auto reporter = SetUpReporter(story, single_sqs);
reporter.AddResult(kMetricStructDeserializationTimeUs,
min_time.InMicrosecondsF() / kNumRunsPerTimeRecord);
reporter.AddResult(kMetricStructDeserializationThroughputRunsPerS,
count * kTimeLimit.ToHz());
}
static void RunSerializationTestStructTraits(
const std::string& story,
const CompositorFrame& frame,
UseSingleSharedQuadState single_sqs) {
for (int i = 0; i < kNumWarmupRuns; ++i) {
mojo::Message message =
mojom::CompositorFrame::SerializeAsMessage(&frame);
}
base::TimeTicks now = base::TimeTicks::Now();
base::TimeTicks end = now + kTimeLimit;
base::TimeDelta min_time;
size_t count = 0;
for (base::TimeTicks start = now; start < end; start = now) {
for (int i = 0; i < kNumRunsPerTimeRecord; ++i) {
mojo::Message message =
mojom::CompositorFrame::SerializeAsMessage(&frame);
now = base::TimeTicks::Now();
// We don't count iterations after the end time.
if (now < end)
++count;
}
if (now - start < min_time || min_time.is_zero())
min_time = now - start;
}
auto reporter = SetUpReporter(story, single_sqs);
reporter.AddResult(kMetricStructSerializationTimeUs,
min_time.InMicrosecondsF() / kNumRunsPerTimeRecord);
reporter.AddResult(kMetricStructSerializationThroughputRunsPerS,
count / kTimeLimit.InSecondsF());
}
static void RunComplexCompositorFrameTest(const std::string& story) {
CompositorFrame frame = MakeEmptyCompositorFrame();
frame.metadata.begin_frame_ack = BeginFrameAck(0, 1, true);
std::vector<TransferableResource>& resource_list = frame.resource_list;
for (uint32_t i = 0; i < 80; ++i) {
TransferableResource arbitrary_resource;
resource_list.push_back(arbitrary_resource);
}
auto& render_pass_list = frame.render_pass_list;
gfx::Transform arbitrary_matrix1;
arbitrary_matrix1.Scale(3, 3);
arbitrary_matrix1.Translate(-5, 20);
arbitrary_matrix1.Rotate(15);
gfx::Transform arbitrary_matrix2;
arbitrary_matrix2.Scale(10, -1);
arbitrary_matrix2.Translate(20, 3);
arbitrary_matrix2.Rotate(24);
gfx::Rect arbitrary_rect1(-5, 9, 3, 15);
gfx::Rect arbitrary_rect2(40, 23, 11, 7);
gfx::Rect arbitrary_rect1_inside_rect2(44, 23, 4, 2);
gfx::Rect arbitrary_rect3(7, -53, 22, 19);
gfx::Rect arbitrary_rect2_inside_rect3(12, -51, 5, 12);
gfx::Size arbitrary_size1(15, 19);
gfx::Size arbitrary_size2(3, 99);
gfx::RectF arbitrary_rectf1(4.2f, -922.1f, 15.6f, 29.5f);
gfx::RRectF arbitrary_rrectf1(4.2f, -922.1f, 15.6f, 29.5f, 1.2f, 2.3f, 3.4f,
4.5f, 5.6f, 6.7f, 7.8f, 8.9f);
gfx::RRectF arbitrary_rrectf2(gfx::RectF(1.f, 2.f, 30.f, 45.f), 5.f);
gfx::RRectF arbitrary_rrectf3(gfx::RectF(5.f, 6.f, 20.f, 35.f), 2.f, 3.f);
gfx::PointF arbitrary_pointf1(31.4f, 15.9f);
gfx::PointF arbitrary_pointf2(26.5f, -35.8f);
float arbitrary_float1 = 0.7f;
float arbitrary_float2 = 0.3f;
float arbitrary_float3 = 0.9f;
float arbitrary_float_array[4] = {3.5f, 6.2f, 9.3f, 12.3f};
bool arbitrary_bool1 = true;
bool arbitrary_bool2 = false;
bool arbitrary_bool3 = true;
bool arbitrary_bool4 = true;
bool arbitrary_bool5 = false;
bool arbitrary_bool6 = true;
gfx::ProtectedVideoType arbitrary_protected_video_type =
gfx::ProtectedVideoType::kClear;
int arbitrary_context_id1 = 12;
int arbitrary_context_id2 = 57;
int arbitrary_context_id3 = -503;
SkColor arbitrary_color = SkColorSetARGB(25, 36, 47, 58);
SkBlendMode arbitrary_blend_mode1 = SkBlendMode::kScreen;
SkBlendMode arbitrary_blend_mode2 = SkBlendMode::kLighten;
SkBlendMode arbitrary_blend_mode3 = SkBlendMode::kOverlay;
ResourceId arbitrary_resourceid1 = 55;
ResourceId arbitrary_resourceid2 = 47;
ResourceId arbitrary_resourceid3 = 23;
ResourceId arbitrary_resourceid4 = 16;
SkScalar arbitrary_sigma = SkFloatToScalar(2.0f);
CompositorRenderPassId root_id{14};
cc::FilterOperations arbitrary_filters1;
arbitrary_filters1.Append(
cc::FilterOperation::CreateGrayscaleFilter(arbitrary_float1));
arbitrary_filters1.Append(cc::FilterOperation::CreateReferenceFilter(
sk_make_sp<cc::BlurPaintFilter>(arbitrary_sigma, arbitrary_sigma,
SkTileMode::kDecal, nullptr)));
cc::FilterOperations arbitrary_filters2;
arbitrary_filters2.Append(
cc::FilterOperation::CreateBrightnessFilter(arbitrary_float2));
auto pass_in = CompositorRenderPass::Create();
pass_in->SetAll(root_id, arbitrary_rect1, arbitrary_rect2,
arbitrary_matrix1, arbitrary_filters2, arbitrary_filters1,
arbitrary_rrectf1, SubtreeCaptureId(), arbitrary_bool1,
arbitrary_bool1, arbitrary_bool1, arbitrary_bool1);
// Texture quads
for (uint32_t i = 0; i < 10; ++i) {
SharedQuadState* shared_state1_in =
pass_in->CreateAndAppendSharedQuadState();
shared_state1_in->SetAll(
arbitrary_matrix1, arbitrary_rect1, arbitrary_rect1,
gfx::MaskFilterInfo(arbitrary_rrectf1), arbitrary_rect2,
arbitrary_bool1, arbitrary_bool1, arbitrary_float1,
arbitrary_blend_mode1, arbitrary_context_id1);
auto* texture_in = pass_in->CreateAndAppendDrawQuad<TextureDrawQuad>();
texture_in->SetAll(
shared_state1_in, arbitrary_rect2, arbitrary_rect1_inside_rect2,
arbitrary_bool1, arbitrary_resourceid1, arbitrary_size1,
arbitrary_bool1, arbitrary_pointf1, arbitrary_pointf2,
arbitrary_color, arbitrary_float_array, arbitrary_bool4,
arbitrary_bool5, arbitrary_bool6, arbitrary_protected_video_type);
auto* texture_in2 = pass_in->CreateAndAppendDrawQuad<TextureDrawQuad>();
texture_in2->SetAll(
shared_state1_in, arbitrary_rect2, arbitrary_rect1_inside_rect2,
arbitrary_bool1, arbitrary_resourceid2, arbitrary_size1,
arbitrary_bool3, arbitrary_pointf1, arbitrary_pointf2,
arbitrary_color, arbitrary_float_array, arbitrary_bool4,
arbitrary_bool5, arbitrary_bool6, arbitrary_protected_video_type);
auto* texture_in3 = pass_in->CreateAndAppendDrawQuad<TextureDrawQuad>();
texture_in3->SetAll(
shared_state1_in, arbitrary_rect2, arbitrary_rect1_inside_rect2,
arbitrary_bool1, arbitrary_resourceid3, arbitrary_size1,
arbitrary_bool2, arbitrary_pointf1, arbitrary_pointf2,
arbitrary_color, arbitrary_float_array, arbitrary_bool4,
arbitrary_bool6, arbitrary_bool6, arbitrary_protected_video_type);
auto* texture_in4 = pass_in->CreateAndAppendDrawQuad<TextureDrawQuad>();
texture_in4->SetAll(
shared_state1_in, arbitrary_rect2, arbitrary_rect1_inside_rect2,
arbitrary_bool1, arbitrary_resourceid4, arbitrary_size2,
arbitrary_bool4, arbitrary_pointf1, arbitrary_pointf2,
arbitrary_color, arbitrary_float_array, arbitrary_bool4,
arbitrary_bool5, arbitrary_bool6, arbitrary_protected_video_type);
}
// Tiled quads
for (uint32_t i = 0; i < 10; ++i) {
SharedQuadState* shared_state2_in =
pass_in->CreateAndAppendSharedQuadState();
shared_state2_in->SetAll(
arbitrary_matrix2, arbitrary_rect2, arbitrary_rect2,
gfx::MaskFilterInfo(arbitrary_rrectf2), arbitrary_rect3,
arbitrary_bool1, arbitrary_bool1, arbitrary_float2,
arbitrary_blend_mode2, arbitrary_context_id2);
for (uint32_t j = 0; j < 6; ++j) {
auto* tile_in = pass_in->CreateAndAppendDrawQuad<TileDrawQuad>();
tile_in->SetAll(
shared_state2_in, arbitrary_rect2, arbitrary_rect1_inside_rect2,
arbitrary_bool1, arbitrary_resourceid3, arbitrary_rectf1,
arbitrary_size1, arbitrary_bool2, arbitrary_bool3, arbitrary_bool4);
}
}
// Solid color quads
for (uint32_t i = 0; i < 5; ++i) {
SharedQuadState* shared_state3_in =
pass_in->CreateAndAppendSharedQuadState();
shared_state3_in->SetAll(
arbitrary_matrix1, arbitrary_rect3, arbitrary_rect3,
gfx::MaskFilterInfo(arbitrary_rrectf3), arbitrary_rect1,
arbitrary_bool1, arbitrary_bool1, arbitrary_float3,
arbitrary_blend_mode3, arbitrary_context_id3);
for (uint32_t j = 0; j < 5; ++j) {
auto* solidcolor_in =
pass_in->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
solidcolor_in->SetAll(shared_state3_in, arbitrary_rect3,
arbitrary_rect2_inside_rect3, arbitrary_bool1,
arbitrary_color, arbitrary_bool2);
}
}
render_pass_list.push_back(std::move(pass_in));
RunTest(story, std::move(frame), UseSingleSharedQuadState::NO);
}
static void RunCompositorFrameTest(const std::string& story,
uint32_t num_quads,
uint32_t num_passes,
UseSingleSharedQuadState single_sqs) {
CompositorFrame frame = MakeEmptyCompositorFrame();
for (uint32_t i = 0; i < num_passes; ++i) {
auto render_pass = CompositorRenderPass::Create();
render_pass->SetNew(CompositorRenderPassId{1}, gfx::Rect(20, 20),
gfx::Rect(), gfx::Transform());
for (uint32_t j = 0; j < num_quads; ++j) {
if (j == 0 || single_sqs == UseSingleSharedQuadState::NO)
render_pass->CreateAndAppendSharedQuadState();
const gfx::Rect bounds(100, 100, 100, 100);
const bool kForceAntiAliasingOff = true;
auto* quad = render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
quad->SetNew(render_pass->shared_quad_state_list.back(), bounds, bounds,
SK_ColorRED, kForceAntiAliasingOff);
}
frame.render_pass_list.push_back(std::move(render_pass));
}
RunTest(story, std::move(frame), single_sqs);
}
static void RunTest(const std::string& story,
CompositorFrame frame,
UseSingleSharedQuadState single_sqs) {
RunSerializationTestStructTraits(story, frame, single_sqs);
RunDeserializationTestStructTraits(story, frame, single_sqs);
}
};
// Test for compositor frames with one render pass, 80 resources in resource
// list, 10 shared quad states with 4 texture quads each, 10 shared quad states
// with 6 tiled quads each, and 5 shared quad states with 5 solid color quads
// each.
TEST_F(VizSerializationPerfTest, DelegatedFrame_Complex) {
RunComplexCompositorFrameTest("DelegatedFrame_Complex");
}
// Test for compositor frames with one render pass and 4000 quads.
TEST_F(VizSerializationPerfTest, DelegatedFrame_ManyQuads_1_4000) {
// Case 1: One shared quad state for all quads in one render pass.
RunCompositorFrameTest("DelegatedFrame_ManyQuads_1_4000", 4000, 1,
UseSingleSharedQuadState::YES);
// Case 2: One shared quad state for each quad.
RunCompositorFrameTest("DelegatedFrame_ManyQuads_1_4000", 4000, 1,
UseSingleSharedQuadState::NO);
}
// Test for compositor frames with 5 render pass and each with 100 quads.
TEST_F(VizSerializationPerfTest, DelegatedFrame_ManyRenderPasses_5_100) {
// Case 1: One shared quad state for all quads in one render pass.
RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_5_100", 100, 5,
UseSingleSharedQuadState::YES);
// Case 2: One shared quad state for each quad.
RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_5_100", 100, 5,
UseSingleSharedQuadState::NO);
}
// Test for compositor frames with 10 render pass and each with 500 quads.
TEST_F(VizSerializationPerfTest, DelegatedFrame_ManyRenderPasses_10_500) {
// Case 1: One shared quad state for all quads in one render pass.
RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_10_500", 500, 10,
UseSingleSharedQuadState::YES);
// Case 2: One shared quad state for each quad.
RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_10_500", 500, 10,
UseSingleSharedQuadState::NO);
}
} // namespace
} // namespace viz