/*
 * 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);)

