1 /*
2 * Copyright 2023 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "bench/Benchmark.h"
9 #include "include/core/SkColorSpace.h"
10 #include "modules/skcms/skcms.h"
11 #include "src/core/SkColorSpaceXformSteps.h"
12 #include "src/core/SkRasterPipeline.h"
13 #include "src/core/SkRasterPipelineOpContexts.h"
14 #include "src/core/SkRasterPipelineOpList.h"
15
16 #include <functional>
17
18 enum class Mode {
19 kMultipleImage, // Transforms multiple images via one pipeline. (skcms doesn't support this.)
20 kSingleImage, // Transforms a single image at a time.
21 kSingleScanline, // Transforms an image scanline-by-scanline.
22 };
23
mode_name(Mode m)24 static const char* mode_name(Mode m) {
25 switch (m) {
26 case Mode::kMultipleImage: return "MultipleImage";
27 case Mode::kSingleImage: return "SingleImage";
28 case Mode::kSingleScanline: return "SingleScanline";
29 default: SkUNREACHABLE;
30 }
31 }
32
33 class ColorSpaceTransformBench : public Benchmark {
34 public:
ColorSpaceTransformBench(Mode mode)35 ColorSpaceTransformBench(Mode mode) : fMode(mode) {
36 fName = std::string("ColorSpace") + mode_name(fMode) + "Transform";
37 }
38
39 protected:
onGetName()40 const char* onGetName() override {
41 return fName.c_str();
42 }
43
isSuitableFor(Backend backend)44 bool isSuitableFor(Backend backend) override {
45 return backend == Backend::kNonRendering;
46 }
47
48 // Call before draw, allows the benchmark to do setup work outside of the
49 // timer. When a benchmark is repeatedly drawn, this should be called once
50 // before the initial draw.
onDelayedSetup()51 void onDelayedSetup() override {
52 // Set the image buffer to solid white.
53 std::fill(std::begin(fSrcPixels), std::end(fSrcPixels), 0xFF);
54
55 // Create a conversion from sRGB to Adobe.
56 fSRGBCS = SkColorSpace::MakeSRGB();
57 fAdobeCS = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB);
58
59 fXformSteps = SkColorSpaceXformSteps(fSRGBCS.get(), kPremul_SkAlphaType,
60 fAdobeCS.get(), kPremul_SkAlphaType);
61
62 // Build up a pipeline.
63 fSrcCtx = SkRasterPipeline_MemoryCtx{fSrcPixels, kWidth};
64 fDstCtx = SkRasterPipeline_MemoryCtx{fDstPixels, kWidth};
65
66 fPipeline.append(SkRasterPipelineOp::load_8888, &fSrcCtx);
67 fXformSteps.apply(&fPipeline);
68 fPipeline.append(SkRasterPipelineOp::store_8888, &fDstCtx);
69
70 fConversionPipeline = fPipeline.compile();
71 }
72
onDraw(int loops,SkCanvas * canvas)73 void onDraw(int loops, SkCanvas* canvas) override {
74 switch (fMode) {
75 case Mode::kMultipleImage:
76 for (int i = 0; i < loops; i++) {
77 fConversionPipeline(0, 0, kWidth, kHeight);
78 }
79 break;
80
81 case Mode::kSingleImage:
82 for (int i = 0; i < loops; i++) {
83 fPipeline.run(0, 0, kWidth, kHeight);
84 }
85 break;
86
87 case Mode::kSingleScanline:
88 for (int i = 0; i < loops; i++) {
89 for (int y = 0; y < kHeight; ++y) {
90 fPipeline.run(0, y, kWidth, 1);
91 }
92 }
93 break;
94 }
95 }
96
97 private:
98 using INHERITED = Benchmark;
99
100 Mode fMode;
101 std::string fName;
102
103 sk_sp<SkColorSpace> fAdobeCS;
104 sk_sp<SkColorSpace> fSRGBCS;
105
106 SkRasterPipeline_<256> fPipeline;
107 SkRasterPipeline_MemoryCtx fSrcCtx;
108 SkRasterPipeline_MemoryCtx fDstCtx;
109 SkColorSpaceXformSteps fXformSteps;
110
111 static constexpr int kWidth = 512;
112 static constexpr int kHeight = 512;
113 static constexpr int kBytesPerPixel = 4;
114 uint8_t fSrcPixels[kWidth * kHeight * kBytesPerPixel];
115 uint8_t fDstPixels[kWidth * kHeight * kBytesPerPixel];
116
117 std::function<void(size_t, size_t, size_t, size_t)> fConversionPipeline;
118 };
119
120 class SkcmsTransformBench : public Benchmark {
121 public:
SkcmsTransformBench(Mode mode)122 SkcmsTransformBench(Mode mode) : fMode(mode) {
123 fName = std::string("Skcms") + mode_name(fMode) + "Transform";
124 }
125
126 protected:
onGetName()127 const char* onGetName() override {
128 return fName.c_str();
129 }
130
isSuitableFor(Backend backend)131 bool isSuitableFor(Backend backend) override {
132 return backend == Backend::kNonRendering;
133 }
134
135 // Call before draw, allows the benchmark to do setup work outside of the
136 // timer. When a benchmark is repeatedly drawn, this should be called once
137 // before the initial draw.
onDelayedSetup()138 void onDelayedSetup() override {
139 // Set the image buffer to solid white. NANs/denorms might affect timing, but otherwise it
140 // doesn't really matter.
141 std::fill(std::begin(fSrcPixels), std::end(fSrcPixels), 0xFF);
142
143 // Create profiles for sRGB to Adobe.
144 SkColorSpace::MakeSRGB()->toProfile(&fSRGBProfile);
145 SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2,
146 SkNamedGamut::kAdobeRGB)->toProfile(&fAdobeProfile);
147 }
148
onDraw(int loops,SkCanvas * canvas)149 void onDraw(int loops, SkCanvas* canvas) override {
150 switch (fMode) {
151 case Mode::kMultipleImage:
152 SkUNREACHABLE;
153
154 case Mode::kSingleImage:
155 for (int i = 0; i < loops; i++) {
156 skcms_Transform(fSrcPixels,
157 skcms_PixelFormat_RGBA_8888,
158 skcms_AlphaFormat_PremulAsEncoded,
159 &fSRGBProfile,
160 fDstPixels,
161 skcms_PixelFormat_RGBA_8888,
162 skcms_AlphaFormat_PremulAsEncoded,
163 &fAdobeProfile,
164 kWidth * kHeight);
165 }
166 break;
167
168 case Mode::kSingleScanline:
169 for (int i = 0; i < loops; i++) {
170 uint8_t* src = fSrcPixels;
171 uint8_t* dst = fDstPixels;
172 for (int y = 0; y < kHeight; ++y) {
173 skcms_Transform(src,
174 skcms_PixelFormat_RGBA_8888,
175 skcms_AlphaFormat_PremulAsEncoded,
176 &fSRGBProfile,
177 dst,
178 skcms_PixelFormat_RGBA_8888,
179 skcms_AlphaFormat_PremulAsEncoded,
180 &fAdobeProfile,
181 kWidth);
182 src += kWidth * kBytesPerPixel;
183 dst += kWidth * kBytesPerPixel;
184 }
185 }
186 break;
187 }
188 }
189
190 private:
191 using INHERITED = Benchmark;
192
193 Mode fMode;
194 std::string fName;
195
196 skcms_ICCProfile fAdobeProfile;
197 skcms_ICCProfile fSRGBProfile;
198
199 static constexpr int kWidth = 512;
200 static constexpr int kHeight = 512;
201 static constexpr int kBytesPerPixel = 4;
202 uint8_t fSrcPixels[kWidth * kHeight * kBytesPerPixel];
203 uint8_t fDstPixels[kWidth * kHeight * kBytesPerPixel];
204 };
205
206 DEF_BENCH(return new ColorSpaceTransformBench(Mode::kMultipleImage);)
207 DEF_BENCH(return new ColorSpaceTransformBench(Mode::kSingleImage);)
208 DEF_BENCH(return new ColorSpaceTransformBench(Mode::kSingleScanline);)
209 DEF_BENCH(return new SkcmsTransformBench(Mode::kSingleImage);)
210 DEF_BENCH(return new SkcmsTransformBench(Mode::kSingleScanline);)
211