| // 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 "cc/base/lap_timer.h" |
| #include "cc/paint/paint_op_buffer.h" |
| #include "cc/paint/paint_op_buffer_serializer.h" |
| #include "cc/test/test_options_provider.h" |
| #include "testing/perf/perf_test.h" |
| #include "third_party/skia/include/core/SkMaskFilter.h" |
| #include "third_party/skia/include/effects/SkColorMatrixFilter.h" |
| #include "third_party/skia/include/effects/SkDashPathEffect.h" |
| #include "third_party/skia/include/effects/SkLayerDrawLooper.h" |
| #include "third_party/skia/include/effects/SkOffsetImageFilter.h" |
| |
| namespace cc { |
| namespace { |
| |
| static const int kTimeLimitMillis = 2000; |
| static const int kNumWarmupRuns = 20; |
| static const int kTimeCheckInterval = 1; |
| |
| static const size_t kMaxSerializedBufferBytes = 100000; |
| |
| class PaintOpPerfTest : public testing::Test { |
| public: |
| PaintOpPerfTest() |
| : timer_(kNumWarmupRuns, |
| base::TimeDelta::FromMilliseconds(kTimeLimitMillis), |
| kTimeCheckInterval), |
| serialized_data_(static_cast<char*>( |
| base::AlignedAlloc(kMaxSerializedBufferBytes, |
| PaintOpBuffer::PaintOpAlign))), |
| deserialized_data_(static_cast<char*>( |
| base::AlignedAlloc(sizeof(LargestPaintOp), |
| PaintOpBuffer::PaintOpAlign))) {} |
| |
| void RunTest(const std::string& name, const PaintOpBuffer& buffer) { |
| TestOptionsProvider test_options_provider; |
| |
| size_t bytes_written = 0u; |
| PaintOpBufferSerializer::Preamble preamble; |
| |
| timer_.Reset(); |
| do { |
| SimpleBufferSerializer serializer( |
| serialized_data_.get(), kMaxSerializedBufferBytes, |
| test_options_provider.image_provider(), |
| test_options_provider.transfer_cache_helper(), |
| test_options_provider.strike_server(), |
| test_options_provider.color_space(), |
| test_options_provider.can_use_lcd_text()); |
| serializer.Serialize(&buffer, nullptr, preamble); |
| bytes_written = serializer.written(); |
| timer_.NextLap(); |
| } while (!timer_.HasTimeLimitExpired()); |
| CHECK_GT(bytes_written, 0u); |
| |
| perf_test::PrintResult(name.c_str(), "", " serialize", |
| buffer.size() * timer_.LapsPerSecond(), "ops/s", |
| true); |
| |
| size_t bytes_read = 0; |
| timer_.Reset(); |
| test_options_provider.PushFonts(); |
| |
| do { |
| size_t remaining_read_bytes = bytes_written; |
| char* to_read = serialized_data_.get(); |
| |
| while (true) { |
| PaintOp* deserialized_op = PaintOp::Deserialize( |
| to_read, remaining_read_bytes, deserialized_data_.get(), |
| sizeof(LargestPaintOp), &bytes_read, |
| test_options_provider.deserialize_options()); |
| deserialized_op->DestroyThis(); |
| |
| DCHECK_GE(remaining_read_bytes, bytes_read); |
| if (remaining_read_bytes == bytes_read) |
| break; |
| |
| remaining_read_bytes -= bytes_read; |
| to_read += bytes_read; |
| } |
| |
| timer_.NextLap(); |
| } while (!timer_.HasTimeLimitExpired()); |
| |
| perf_test::PrintResult(name.c_str(), "", "deserialize", |
| buffer.size() * timer_.LapsPerSecond(), "ops/s", |
| true); |
| } |
| |
| protected: |
| LapTimer timer_; |
| std::unique_ptr<char, base::AlignedFreeDeleter> serialized_data_; |
| std::unique_ptr<char, base::AlignedFreeDeleter> deserialized_data_; |
| }; |
| |
| // Ops that can be memcopied both when serializing and deserializing. |
| TEST_F(PaintOpPerfTest, SimpleOps) { |
| PaintOpBuffer buffer; |
| for (size_t i = 0; i < 100; ++i) |
| buffer.push<ConcatOp>(SkMatrix::I()); |
| RunTest("simple", buffer); |
| } |
| |
| // Drawing ops with flags that don't have nested objects. |
| TEST_F(PaintOpPerfTest, DrawOps) { |
| PaintOpBuffer buffer; |
| PaintFlags flags; |
| for (size_t i = 0; i < 100; ++i) |
| buffer.push<DrawRectOp>(SkRect::MakeXYWH(1, 1, 1, 1), flags); |
| RunTest("draw", buffer); |
| } |
| |
| // Ops with worst case flags. |
| TEST_F(PaintOpPerfTest, ManyFlagsOps) { |
| PaintOpBuffer buffer; |
| |
| PaintFlags flags; |
| SkScalar intervals[] = {1.f, 1.f}; |
| flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); |
| flags.setMaskFilter(SkMaskFilter::MakeBlur( |
| SkBlurStyle::kOuter_SkBlurStyle, 4.3f, SkRect::MakeXYWH(1, 1, 1, 1))); |
| flags.setColorFilter( |
| SkColorMatrixFilter::MakeLightingFilter(SK_ColorYELLOW, SK_ColorGREEN)); |
| |
| SkLayerDrawLooper::Builder looper_builder; |
| looper_builder.addLayer(); |
| looper_builder.addLayer(2.3f, 4.5f); |
| SkLayerDrawLooper::LayerInfo layer_info; |
| looper_builder.addLayer(layer_info); |
| flags.setLooper(looper_builder.detach()); |
| |
| sk_sp<PaintShader> shader = PaintShader::MakeColor(SK_ColorTRANSPARENT); |
| flags.setShader(std::move(shader)); |
| |
| SkPath path; |
| path.addCircle(2, 2, 5); |
| path.addCircle(3, 4, 2); |
| path.addArc(SkRect::MakeXYWH(1, 2, 3, 4), 5, 6); |
| |
| for (size_t i = 0; i < 100; ++i) |
| buffer.push<DrawPathOp>(path, flags); |
| RunTest("flags", buffer); |
| } |
| |
| // DrawTextBlobOps, |
| TEST_F(PaintOpPerfTest, TextOps) { |
| PaintOpBuffer buffer; |
| |
| auto typeface = PaintTypeface::TestTypeface(); |
| |
| SkPaint font; |
| font.setTextEncoding(SkPaint::kGlyphID_TextEncoding); |
| font.setTypeface(typeface.ToSkTypeface()); |
| |
| SkTextBlobBuilder builder; |
| int glyph_count = 5; |
| SkRect rect = SkRect::MakeXYWH(1, 1, 1, 1); |
| const auto& run = builder.allocRun(font, glyph_count, 1.2f, 2.3f, &rect); |
| std::fill(run.glyphs, run.glyphs + glyph_count, 0); |
| std::vector<PaintTypeface> typefaces = {typeface}; |
| auto blob = base::MakeRefCounted<PaintTextBlob>(builder.make(), typefaces); |
| |
| PaintFlags flags; |
| for (size_t i = 0; i < 100; ++i) |
| buffer.push<DrawTextBlobOp>(blob, 0.f, 0.f, flags); |
| |
| RunTest("text", buffer); |
| } |
| |
| } // namespace |
| } // namespace cc |