| /* | |
| * Copyright 2026 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/SkString.h" | |
| #include "include/private/SkAlign.h" | |
| #include "src/core/SkRasterPipeline.h" | |
| #include "src/core/SkRasterPipelineOpContexts.h" | |
| #include "src/core/SkRasterPipelineOpList.h" | |
| #include <memory> | |
| template <typename T> | |
| static T* allocate_and_align(std::unique_ptr<char[]>& storage, size_t sizeInBytes) { | |
| // Align to 64 bytes (critical for AVX-512 in SkRasterPipeline). | |
| // The initial allocation might not be 64-byte aligned, so we allocate extra padding | |
| // and find the next address in our buffer that is a multiple of 64. | |
| storage.reset(new char[sizeInBytes + 64]); | |
| return reinterpret_cast<T*>( | |
| SkAlignTo(reinterpret_cast<uintptr_t>(storage.get()), (uintptr_t)64)); | |
| } | |
| class SkRPDivBench : public Benchmark { | |
| public: | |
| enum class Type { kInt, kUint }; | |
| SkRPDivBench(Type type, int slots) : fType(type), fSlots(slots) { | |
| fName.printf("skrp_div_%s_%d", fType == Type::kInt ? "int" : "uint", fSlots); | |
| fData = allocate_and_align<int32_t>(fStorage, 1024 * sizeof(int32_t)); | |
| // Initialize the data we are dividing by so it's non-zero (avoiding any special casing). | |
| for (int i = 0; i < 1024; i++) { | |
| fData[i] = i + 1; | |
| } | |
| } | |
| protected: | |
| const char* onGetName() override { return fName.c_str(); } | |
| bool isSuitableFor(Backend backend) override { return backend == Backend::kNonRendering; } | |
| void onDraw(int loops, SkCanvas*) override { | |
| SkArenaAlloc alloc(1024); | |
| SkRasterPipeline p(&alloc); | |
| SkRasterPipelineOp op; | |
| if (fType == Type::kInt) { | |
| switch (fSlots) { | |
| case 1: op = SkRasterPipelineOp::div_int; break; | |
| case 4: op = SkRasterPipelineOp::div_4_ints; break; | |
| default: SkUNREACHABLE; | |
| } | |
| } else { | |
| switch (fSlots) { | |
| case 1: op = SkRasterPipelineOp::div_uint; break; | |
| case 4: op = SkRasterPipelineOp::div_4_uints; break; | |
| default: SkUNREACHABLE; | |
| } | |
| } | |
| p.append(op, fData); | |
| for (int i = 0; i < loops; i++) { | |
| p.run(0, 0, 128, 1); | |
| } | |
| } | |
| private: | |
| Type fType; | |
| int fSlots; | |
| SkString fName; | |
| std::unique_ptr<char[]> fStorage; | |
| int32_t* fData = nullptr; | |
| }; | |
| class SkRPGatherBench : public Benchmark { | |
| public: | |
| enum class Format { k8888, k565, kRG88 }; | |
| SkRPGatherBench(Format format) : fFormat(format) { | |
| switch (fFormat) { | |
| case Format::k8888: fName.printf("skrp_gather_8888"); break; | |
| case Format::k565: fName.printf("skrp_gather_565"); break; | |
| case Format::kRG88: fName.printf("skrp_gather_rg88"); break; | |
| } | |
| // Allocate and initialize pixels | |
| fPixels.reset(new uint32_t[128 * 128]); | |
| for (int i = 0; i < 128 * 128; i++) { | |
| fPixels[i] = 0xFFFFFFFF; | |
| } | |
| fCtx = allocate_and_align<SkRasterPipelineContexts::GatherCtx>( | |
| fStorage, sizeof(SkRasterPipelineContexts::GatherCtx)); | |
| fCtx->pixels = fPixels.get(); | |
| fCtx->stride = 128; | |
| fCtx->width = 128.0f; | |
| fCtx->height = 128.0f; | |
| fCtx->roundDownAtInteger = false; | |
| } | |
| protected: | |
| const char* onGetName() override { return fName.c_str(); } | |
| bool isSuitableFor(Backend backend) override { return backend == Backend::kNonRendering; } | |
| void onDraw(int loops, SkCanvas*) override { | |
| SkArenaAlloc alloc(1024); | |
| SkRasterPipeline p(&alloc); | |
| p.append(SkRasterPipelineOp::seed_shader); | |
| SkRasterPipelineOp op; | |
| switch (fFormat) { | |
| case Format::k8888: op = SkRasterPipelineOp::gather_8888; break; | |
| case Format::k565: op = SkRasterPipelineOp::gather_565; break; | |
| case Format::kRG88: op = SkRasterPipelineOp::gather_rg88; break; | |
| } | |
| p.append(op, fCtx); | |
| for (int i = 0; i < loops; i++) { | |
| p.run(0, 0, 128, 1); | |
| } | |
| } | |
| private: | |
| Format fFormat; | |
| SkString fName; | |
| std::unique_ptr<uint32_t[]> fPixels; | |
| std::unique_ptr<char[]> fStorage; | |
| SkRasterPipelineContexts::GatherCtx* fCtx = nullptr; | |
| }; | |
| DEF_BENCH(return new SkRPDivBench(SkRPDivBench::Type::kInt, 1);) | |
| DEF_BENCH(return new SkRPDivBench(SkRPDivBench::Type::kInt, 4);) | |
| DEF_BENCH(return new SkRPDivBench(SkRPDivBench::Type::kUint, 1);) | |
| DEF_BENCH(return new SkRPDivBench(SkRPDivBench::Type::kUint, 4);) | |
| DEF_BENCH(return new SkRPGatherBench(SkRPGatherBench::Format::k8888);) | |
| DEF_BENCH(return new SkRPGatherBench(SkRPGatherBench::Format::k565);) | |
| DEF_BENCH(return new SkRPGatherBench(SkRPGatherBench::Format::kRG88);) | |