/*
 * Copyright 2024 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef skgpu_graphite_ShaderInfo_DEFINED
#define skgpu_graphite_ShaderInfo_DEFINED

#include "include/private/base/SkTArray.h"
#include "src/base/SkArenaAlloc.h"
#include "src/gpu/Blend.h"
#include "src/gpu/Swizzle.h"
#include "src/gpu/graphite/Caps.h"
#include "src/gpu/graphite/ResourceTypes.h"
#include "src/gpu/graphite/UniquePaintParamsID.h"

namespace skgpu::graphite {

class RenderStep;
class RuntimeEffectDictionary;
class ShaderCodeDictionary;
class ShaderNode;
enum class TextureFormat : uint8_t;

// ShaderInfo holds all root ShaderNodes defined for a PaintParams as well as the extracted fixed
// function blending parameters and other aggregate requirements for the effect trees that have
// been linked into a single fragment program (sans any RenderStep fragment work and fixed SkSL
// logic required for all rendering in Graphite).
class ShaderInfo {
public:
    // Accepts a real or, by default, an invalid/nullptr pointer to a container of SamplerDescs.
    // Backend implementations which may utilize static / immutable samplers should pass in a real
    // pointer to indicate that shader node data must be analyzed to determine whether
    // immutable samplers are used, and if so, ascertain SamplerDescs for them.
    // TODO(b/366220690): Actually perform this analysis.
    //
    // If provided a valid container ptr, this function will delegate the addition of SamplerDescs
    // for each sampler the nodes utilize (dynamic and immutable). This way, a SamplerDesc's index
    // within the container can inform its binding order. Each SamplerDesc will be either:
    // 1) a default-constructed SamplerDesc, indicating the use of a "regular" dynamic sampler which
    //    requires no special handling OR
    // 2) a real SamplerDesc describing an immutable sampler. Backend pipelines can then use the
    //    desc to obtain a real immutable sampler pointer (which typically must be included in
    //    pipeline layouts)
    static std::unique_ptr<ShaderInfo> Make(const Caps*,
                                            const ShaderCodeDictionary*,
                                            const RuntimeEffectDictionary*,
                                            const RenderPassDesc& rpDesc,
                                            const RenderStep*,
                                            UniquePaintParamsID,
                                            skia_private::TArray<SamplerDesc>* outDescs = nullptr);

    const ShaderCodeDictionary* shaderCodeDictionary() const {
        return fShaderCodeDictionary;
    }
    const RuntimeEffectDictionary* runtimeEffectDictionary() const {
        return fRuntimeEffectDictionary;
    }

    const char* uniformSsboIndex() const { return fUniformSsboIndex; }

    DstReadStrategy dstReadStrategy() const { return fDstReadStrategy; }
    const skgpu::BlendInfo& blendInfo() const { return fBlendInfo; }

    const std::string& vertexSkSL() const { return fVertexSkSL; }
    const std::string& fragmentSkSL() const { return fFragmentSkSL; }

    const std::string& vsLabel() const { return fVSLabel; }
    const std::string& fsLabel() const { return fFSLabel; }
    // Matches ContextUtils::GetPipelineLabel() for the same args that were passed to Make()
    const std::string& pipelineLabel() const { return fPipelineLabel; }

    int numFragmentTexturesAndSamplers() const { return fNumFragmentTexturesAndSamplers; }
    bool hasCombinedUniforms() const { return fHasCombinedUniforms; }
    bool hasGradientBuffer() const { return fHasGradientBuffer; }

    // Name used in-shader for gradient buffer uniform.
    static constexpr char kGradientBufferName[] = "fsGradientBuffer";

private:
    struct SharedGeneratorData;

    ShaderInfo(const ShaderCodeDictionary*,
               const RuntimeEffectDictionary*,
               const char* uniformSsboIndex,
               DstReadStrategy);

    // Determines fNumFragmentTexturesAndSamplers, fHasPaintUniforms, fHasGradientBuffer,
    // fHasSsboIndexVarying, and if a valid SamplerDesc ptr is passed in, any immutable
    // sampler SamplerDescs.
    void generateFragmentSkSL(const Caps*,
                              const ShaderCodeDictionary*,
                              const char* label,
                              const RenderStep*,
                              UniquePaintParamsID,
                              TextureFormat targetFormat,
                              skgpu::Swizzle writeSwizzle,
                              skia_private::TArray<SamplerDesc>* outDescs,
                              const SharedGeneratorData&);

    void generateVertexSkSL(const Caps*,
                            const RenderStep*,
                            const SharedGeneratorData&);

    const ShaderCodeDictionary* fShaderCodeDictionary;
    const RuntimeEffectDictionary* fRuntimeEffectDictionary;
    const char* fUniformSsboIndex;

    // The blendInfo represents the actual GPU blend operations, which may or may not completely
    // implement the paint and coverage blending defined by the root nodes.
    skgpu::BlendInfo fBlendInfo;
    DstReadStrategy fDstReadStrategy = DstReadStrategy::kNoneRequired;

    std::string fVertexSkSL;
    std::string fFragmentSkSL;
    std::string fVSLabel;
    std::string fFSLabel;
    std::string fPipelineLabel;

    int fNumFragmentTexturesAndSamplers = 0;
    bool fHasCombinedUniforms = false;
    bool fHasGradientBuffer = false;
};

}  // namespace skgpu::graphite

#endif  // skgpu_graphite_ShaderInfo_DEFINED
