| /* |
| * Copyright 2023 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "bench/Benchmark.h" |
| #include "include/core/SkColorSpace.h" |
| #include "modules/skcms/skcms.h" |
| #include "src/core/SkColorSpaceXformSteps.h" |
| #include "src/core/SkRasterPipeline.h" |
| #include "src/core/SkRasterPipelineOpContexts.h" |
| #include "src/core/SkRasterPipelineOpList.h" |
| |
| #include <functional> |
| |
| enum class Mode { |
| kMultipleImage, // Transforms multiple images via one pipeline. (skcms doesn't support this.) |
| kSingleImage, // Transforms a single image at a time. |
| kSingleScanline, // Transforms an image scanline-by-scanline. |
| }; |
| |
| static const char* mode_name(Mode m) { |
| switch (m) { |
| case Mode::kMultipleImage: return "MultipleImage"; |
| case Mode::kSingleImage: return "SingleImage"; |
| case Mode::kSingleScanline: return "SingleScanline"; |
| default: SkUNREACHABLE; |
| } |
| } |
| |
| class ColorSpaceTransformBench : public Benchmark { |
| public: |
| ColorSpaceTransformBench(Mode mode) : fMode(mode) { |
| fName = std::string("ColorSpace") + mode_name(fMode) + "Transform"; |
| } |
| |
| protected: |
| const char* onGetName() override { |
| return fName.c_str(); |
| } |
| |
| bool isSuitableFor(Backend backend) override { |
| return backend == Backend::kNonRendering; |
| } |
| |
| // Call before draw, allows the benchmark to do setup work outside of the |
| // timer. When a benchmark is repeatedly drawn, this should be called once |
| // before the initial draw. |
| void onDelayedSetup() override { |
| // Set the image buffer to solid white. |
| std::fill(std::begin(fSrcPixels), std::end(fSrcPixels), 0xFF); |
| |
| // Create a conversion from sRGB to Adobe. |
| fSRGBCS = SkColorSpace::MakeSRGB(); |
| fAdobeCS = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB); |
| |
| fXformSteps = SkColorSpaceXformSteps(fSRGBCS.get(), kPremul_SkAlphaType, |
| fAdobeCS.get(), kPremul_SkAlphaType); |
| |
| // Build up a pipeline. |
| fSrcCtx = SkRasterPipeline_MemoryCtx{fSrcPixels, kWidth}; |
| fDstCtx = SkRasterPipeline_MemoryCtx{fDstPixels, kWidth}; |
| |
| fPipeline.append(SkRasterPipelineOp::load_8888, &fSrcCtx); |
| fXformSteps.apply(&fPipeline); |
| fPipeline.append(SkRasterPipelineOp::store_8888, &fDstCtx); |
| |
| fConversionPipeline = fPipeline.compile(); |
| } |
| |
| void onDraw(int loops, SkCanvas* canvas) override { |
| switch (fMode) { |
| case Mode::kMultipleImage: |
| for (int i = 0; i < loops; i++) { |
| fConversionPipeline(0, 0, kWidth, kHeight); |
| } |
| break; |
| |
| case Mode::kSingleImage: |
| for (int i = 0; i < loops; i++) { |
| fPipeline.run(0, 0, kWidth, kHeight); |
| } |
| break; |
| |
| case Mode::kSingleScanline: |
| for (int i = 0; i < loops; i++) { |
| for (int y = 0; y < kHeight; ++y) { |
| fPipeline.run(0, y, kWidth, 1); |
| } |
| } |
| break; |
| } |
| } |
| |
| private: |
| using INHERITED = Benchmark; |
| |
| Mode fMode; |
| std::string fName; |
| |
| sk_sp<SkColorSpace> fAdobeCS; |
| sk_sp<SkColorSpace> fSRGBCS; |
| |
| SkRasterPipeline_<256> fPipeline; |
| SkRasterPipeline_MemoryCtx fSrcCtx; |
| SkRasterPipeline_MemoryCtx fDstCtx; |
| SkColorSpaceXformSteps fXformSteps; |
| |
| static constexpr int kWidth = 512; |
| static constexpr int kHeight = 512; |
| static constexpr int kBytesPerPixel = 4; |
| uint8_t fSrcPixels[kWidth * kHeight * kBytesPerPixel]; |
| uint8_t fDstPixels[kWidth * kHeight * kBytesPerPixel]; |
| |
| std::function<void(size_t, size_t, size_t, size_t)> fConversionPipeline; |
| }; |
| |
| class SkcmsTransformBench : public Benchmark { |
| public: |
| SkcmsTransformBench(Mode mode) : fMode(mode) { |
| fName = std::string("Skcms") + mode_name(fMode) + "Transform"; |
| } |
| |
| protected: |
| const char* onGetName() override { |
| return fName.c_str(); |
| } |
| |
| bool isSuitableFor(Backend backend) override { |
| return backend == Backend::kNonRendering; |
| } |
| |
| // Call before draw, allows the benchmark to do setup work outside of the |
| // timer. When a benchmark is repeatedly drawn, this should be called once |
| // before the initial draw. |
| void onDelayedSetup() override { |
| // Set the image buffer to solid white. NANs/denorms might affect timing, but otherwise it |
| // doesn't really matter. |
| std::fill(std::begin(fSrcPixels), std::end(fSrcPixels), 0xFF); |
| |
| // Create profiles for sRGB to Adobe. |
| SkColorSpace::MakeSRGB()->toProfile(&fSRGBProfile); |
| SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, |
| SkNamedGamut::kAdobeRGB)->toProfile(&fAdobeProfile); |
| } |
| |
| void onDraw(int loops, SkCanvas* canvas) override { |
| switch (fMode) { |
| case Mode::kMultipleImage: |
| SkUNREACHABLE; |
| |
| case Mode::kSingleImage: |
| for (int i = 0; i < loops; i++) { |
| skcms_Transform(fSrcPixels, |
| skcms_PixelFormat_RGBA_8888, |
| skcms_AlphaFormat_PremulAsEncoded, |
| &fSRGBProfile, |
| fDstPixels, |
| skcms_PixelFormat_RGBA_8888, |
| skcms_AlphaFormat_PremulAsEncoded, |
| &fAdobeProfile, |
| kWidth * kHeight); |
| } |
| break; |
| |
| case Mode::kSingleScanline: |
| for (int i = 0; i < loops; i++) { |
| uint8_t* src = fSrcPixels; |
| uint8_t* dst = fDstPixels; |
| for (int y = 0; y < kHeight; ++y) { |
| skcms_Transform(src, |
| skcms_PixelFormat_RGBA_8888, |
| skcms_AlphaFormat_PremulAsEncoded, |
| &fSRGBProfile, |
| dst, |
| skcms_PixelFormat_RGBA_8888, |
| skcms_AlphaFormat_PremulAsEncoded, |
| &fAdobeProfile, |
| kWidth); |
| src += kWidth * kBytesPerPixel; |
| dst += kWidth * kBytesPerPixel; |
| } |
| } |
| break; |
| } |
| } |
| |
| private: |
| using INHERITED = Benchmark; |
| |
| Mode fMode; |
| std::string fName; |
| |
| skcms_ICCProfile fAdobeProfile; |
| skcms_ICCProfile fSRGBProfile; |
| |
| static constexpr int kWidth = 512; |
| static constexpr int kHeight = 512; |
| static constexpr int kBytesPerPixel = 4; |
| uint8_t fSrcPixels[kWidth * kHeight * kBytesPerPixel]; |
| uint8_t fDstPixels[kWidth * kHeight * kBytesPerPixel]; |
| }; |
| |
| DEF_BENCH(return new ColorSpaceTransformBench(Mode::kMultipleImage);) |
| DEF_BENCH(return new ColorSpaceTransformBench(Mode::kSingleImage);) |
| DEF_BENCH(return new ColorSpaceTransformBench(Mode::kSingleScanline);) |
| DEF_BENCH(return new SkcmsTransformBench(Mode::kSingleImage);) |
| DEF_BENCH(return new SkcmsTransformBench(Mode::kSingleScanline);) |