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

#ifndef GrPrimitiveProcessor_DEFINED
#define GrPrimitiveProcessor_DEFINED

#include "GrColor.h"
#include "GrProcessor.h"
#include "GrShaderVar.h"

/*
 * The GrPrimitiveProcessor represents some kind of geometric primitive.  This includes the shape
 * of the primitive and the inherent color of the primitive.  The GrPrimitiveProcessor is
 * responsible for providing a color and coverage input into the Ganesh rendering pipeline.  Through
 * optimization, Ganesh may decide a different color, no color, and / or no coverage are required
 * from the GrPrimitiveProcessor, so the GrPrimitiveProcessor must be able to support this
 * functionality.
 *
 * There are two feedback loops between the GrFragmentProcessors, the GrXferProcessor, and the
 * GrPrimitiveProcessor. These loops run on the CPU and to determine known properties of the final
 * color and coverage inputs to the GrXferProcessor in order to perform optimizations that preserve
 * correctness. The GrDrawOp seeds these loops with initial color and coverage, in its
 * getProcessorAnalysisInputs implementation. These seed values are processed by the
 * subsequent
 * stages of the rendering pipeline and the output is then fed back into the GrDrawOp in
 * the applyPipelineOptimizations call, where the op can use the information to inform decisions
 * about GrPrimitiveProcessor creation.
 */

class GrGLSLPrimitiveProcessor;

/*
 * GrPrimitiveProcessor defines an interface which all subclasses must implement.  All
 * GrPrimitiveProcessors must proivide seed color and coverage for the Ganesh color / coverage
 * pipelines, and they must provide some notion of equality
 */
class GrPrimitiveProcessor : public GrResourceIOProcessor, public GrProgramElement {
public:
    struct Attribute {
        enum class InputRate : bool {
            kPerVertex,
            kPerInstance
        };
        GrShaderVar asShaderVar() const {
            return GrShaderVar(fName, GrVertexAttribTypeToSLType(fType),
                               GrShaderVar::kIn_TypeModifier);
        }
        bool isInitialized() const { return SkToBool(fName); }
        Attribute() = default;
        Attribute(const char* name, GrVertexAttribType type, int offset, InputRate rate)
                : fName(name), fType(type), fOffsetInRecord(offset), fInputRate(rate) {}
        const char*          fName = nullptr;
        GrVertexAttribType   fType;
        int                  fOffsetInRecord;
        InputRate            fInputRate;
    };

    GrPrimitiveProcessor(ClassID classID)
    : GrResourceIOProcessor(classID) {}

    int numAttribs() const { return fAttribs.count(); }
    const Attribute& getAttrib(int index) const { return fAttribs[index]; }

    bool hasVertexAttribs() const { return SkToBool(fVertexStride); }
    bool hasInstanceAttribs() const { return SkToBool(fInstanceStride); }

    /**
     * These return the strides of the vertex and instance buffers. Attributes are expected to be
     * laid out interleaved in their corresponding buffer (vertex or instance). fOffsetInRecord
     * indicates an attribute's location in bytes relative to the first attribute. (These are padded
     * to the nearest 4 bytes for performance reasons.)
     *
     * A common practice is to populate the buffer's memory using an implicit array of structs. In
     * this case, it is best to assert:
     *
     *     stride == sizeof(struct) and
     *     offsetof(struct, field[i]) == attrib[i].fOffsetInRecord
     *
     * NOTE: for instanced draws the vertex buffer has a single record that each instance reuses.
     */
    int getVertexStride() const { return fVertexStride; }
    int getInstanceStride() const { return fInstanceStride; }

    // Only the GrGeometryProcessor subclass actually has a geo shader or vertex attributes, but
    // we put these calls on the base class to prevent having to cast
    virtual bool willUseGeoShader() const = 0;

    bool willUsePrimitiveRestart() const { return fWillUsePrimitiveRestart; }

    /**
     * Computes a transformKey from an array of coord transforms. Will only look at the first
     * <numCoords> transforms in the array.
     *
     * TODO: A better name for this function  would be "compute" instead of "get".
     */
    uint32_t getTransformKey(const SkTArray<const GrCoordTransform*, true>& coords,
                             int numCoords) const;

    /**
     * Sets a unique key on the GrProcessorKeyBuilder that is directly associated with this geometry
     * processor's GL backend implementation.
     *
     * TODO: A better name for this function  would be "compute" instead of "get".
     */
    virtual void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const = 0;


    /** Returns a new instance of the appropriate *GL* implementation class
        for the given GrProcessor; caller is responsible for deleting
        the object. */
    virtual GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const = 0;

    virtual bool isPathRendering() const { return false; }

    /**
     * If non-null, overrides the dest color returned by GrGLSLFragmentShaderBuilder::dstColor().
     */
    virtual const char* getDestColorOverride() const { return nullptr; }

    virtual float getSampleShading() const {
        return 0.0;
    }

protected:
    /**
     * Subclasses call these from their constructor to register vertex and instance attributes.
     */
    const Attribute& addVertexAttrib(const char* name, GrVertexAttribType type) {
        fAttribs.push_back() = {name, type, fVertexStride, Attribute::InputRate::kPerVertex};
        fVertexStride += static_cast<int>(SkAlign4(GrVertexAttribTypeSize(type)));
        return fAttribs.back();
    }
    const Attribute& addInstanceAttrib(const char* name, GrVertexAttribType type) {
        fAttribs.push_back() = {name, type, fInstanceStride, Attribute::InputRate::kPerInstance};
        fInstanceStride += static_cast<int>(SkAlign4(GrVertexAttribTypeSize(type)));
        return fAttribs.back();
    }

    void setWillUsePrimitiveRestart() { fWillUsePrimitiveRestart = true; }

private:
    void addPendingIOs() const override { GrResourceIOProcessor::addPendingIOs(); }
    void removeRefs() const override { GrResourceIOProcessor::removeRefs(); }
    void pendingIOComplete() const override { GrResourceIOProcessor::pendingIOComplete(); }
    void notifyRefCntIsZero() const final {}
    virtual bool hasExplicitLocalCoords() const = 0;

    SkSTArray<8, Attribute> fAttribs;
    int fVertexStride = 0;
    int fInstanceStride = 0;
    bool fWillUsePrimitiveRestart = false;

    typedef GrProcessor INHERITED;
};

#endif