xref: /aosp_15_r20/external/skia/bench/ColorSpaceBench.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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