| /* | |
| * Copyright 2023 Google LLC | |
| * | |
| * 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 = SkRasterPipelineContexts::MemoryCtx{fSrcPixels, kWidth}; | |
| fDstCtx = SkRasterPipelineContexts::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; | |
| SkRasterPipelineContexts::MemoryCtx fSrcCtx; | |
| SkRasterPipelineContexts::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);) |