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

#ifndef GrPipeline_DEFINED
#define GrPipeline_DEFINED

#include "GrColor.h"
#include "GrFragmentProcessor.h"
#include "GrNonAtomicRef.h"
#include "GrPendingProgramElement.h"
#include "GrPrimitiveProcessor.h"
#include "GrProcessorSet.h"
#include "GrProgramDesc.h"
#include "GrScissorState.h"
#include "GrUserStencilSettings.h"
#include "GrWindowRectsState.h"
#include "SkMatrix.h"
#include "SkRefCnt.h"
#include "effects/GrCoverageSetOpXP.h"
#include "effects/GrDisableColorXP.h"
#include "effects/GrPorterDuffXferProcessor.h"
#include "effects/GrSimpleTextureEffect.h"

class GrAppliedClip;
class GrDeviceCoordTexture;
class GrOp;
class GrPipelineBuilder;
class GrRenderTargetContext;

/**
 * Class that holds an optimized version of a GrPipelineBuilder. It is meant to be an immutable
 * class, and contains all data needed to set the state for a gpu draw.
 */
class GrPipeline : public GrNonAtomicRef<GrPipeline> {
public:
    ///////////////////////////////////////////////////////////////////////////
    /// @name Creation

    enum Flags {
        /**
         * Perform HW anti-aliasing. This means either HW FSAA, if supported by the render target,
         * or smooth-line rendering if a line primitive is drawn and line smoothing is supported by
         * the 3D API.
         */
        kHWAntialias_Flag = 0x1,

        /**
         * Modifies the vertex shader so that vertices will be positioned at pixel centers.
         */
        kSnapVerticesToPixelCenters_Flag = 0x2,
    };

    struct InitArgs {
        uint32_t fFlags = 0;
        GrDrawFace fDrawFace = GrDrawFace::kBoth;
        const GrProcessorSet* fProcessors = nullptr;
        const GrProcessorSet::FragmentProcessorAnalysis* fAnalysis;
        const GrUserStencilSettings* fUserStencil = &GrUserStencilSettings::kUnused;
        const GrAppliedClip* fAppliedClip = nullptr;
        GrRenderTarget* fRenderTarget = nullptr;
        const GrCaps* fCaps = nullptr;
        GrXferProcessor::DstTexture fDstTexture;
    };

    /**
     * A Default constructed pipeline is unusable until init() is called.
     **/
    GrPipeline() = default;

    /**
     * Creates a simple pipeline with default settings and no processors. The provided blend mode
     * must be "Porter Duff" (<= kLastCoeffMode). This pipeline is initialized without requiring
     * a call to init().
     **/
    GrPipeline(GrRenderTarget*, SkBlendMode);

    /** (Re)initializes a pipeline. After initialization the pipeline can be used. */
    GrPipelineOptimizations init(const InitArgs&);

    /** True if the pipeline has been initialized. */
    bool isInitialized() const { return SkToBool(fRenderTarget.get()); }

    /// @}

    ///////////////////////////////////////////////////////////////////////////
    /// @name Comparisons

    /**
     * Returns true if these pipelines are equivalent.  Coord transforms may be applied either on
     * the GPU or the CPU. When we apply them on the CPU then the matrices need not agree in order
     * to combine draws. Therefore we take a param that indicates whether coord transforms should be
     * compared."
     */
    static bool AreEqual(const GrPipeline& a, const GrPipeline& b);

    /**
     * Allows a GrOp subclass to determine whether two GrOp instances can combine. This is a
     * stricter test than isEqual because it also considers blend barriers when the two ops'
     * bounds overlap
     */
    static bool CanCombine(const GrPipeline& a, const SkRect& aBounds,
                           const GrPipeline& b, const SkRect& bBounds,
                           const GrCaps& caps)  {
        if (!AreEqual(a, b)) {
            return false;
        }
        if (a.xferBarrierType(caps)) {
            return aBounds.fRight <= bBounds.fLeft ||
                   aBounds.fBottom <= bBounds.fTop ||
                   bBounds.fRight <= aBounds.fLeft ||
                   bBounds.fBottom <= aBounds.fTop;
        }
        return true;
    }

    /// @}

    ///////////////////////////////////////////////////////////////////////////
    /// @name GrFragmentProcessors

    // Make the renderTarget's GrOpList (if it exists) be dependent on any
    // GrOpLists in this pipeline
    void addDependenciesTo(GrRenderTarget* rt) const;

    int numColorFragmentProcessors() const { return fNumColorProcessors; }
    int numCoverageFragmentProcessors() const {
        return fFragmentProcessors.count() - fNumColorProcessors;
    }
    int numFragmentProcessors() const { return fFragmentProcessors.count(); }

    const GrXferProcessor& getXferProcessor() const {
        if (fXferProcessor.get()) {
            return *fXferProcessor.get();
        } else {
            // A null xp member means the common src-over case. GrXferProcessor's ref'ing
            // mechanism is not thread safe so we do not hold a ref on this global.
            return GrPorterDuffXPFactory::SimpleSrcOverXP();
        }
    }

    const GrFragmentProcessor& getColorFragmentProcessor(int idx) const {
        SkASSERT(idx < this->numColorFragmentProcessors());
        return *fFragmentProcessors[idx].get();
    }

    const GrFragmentProcessor& getCoverageFragmentProcessor(int idx) const {
        SkASSERT(idx < this->numCoverageFragmentProcessors());
        return *fFragmentProcessors[fNumColorProcessors + idx].get();
    }

    const GrFragmentProcessor& getFragmentProcessor(int idx) const {
        return *fFragmentProcessors[idx].get();
    }

    /// @}

    /**
     * Retrieves the currently set render-target.
     *
     * @return    The currently set render target.
     */
    GrRenderTarget* getRenderTarget() const { return fRenderTarget.get(); }

    const GrUserStencilSettings* getUserStencil() const { return fUserStencilSettings; }

    const GrScissorState& getScissorState() const { return fScissorState; }

    const GrWindowRectsState& getWindowRectsState() const { return fWindowRectsState; }

    bool isHWAntialiasState() const { return SkToBool(fFlags & kHWAntialias_Flag); }
    bool snapVerticesToPixelCenters() const {
        return SkToBool(fFlags & kSnapVerticesToPixelCenters_Flag);
    }
    bool getDisableOutputConversionToSRGB() const {
        return SkToBool(fFlags & kDisableOutputConversionToSRGB_Flag);
    }
    bool getAllowSRGBInputs() const {
        return SkToBool(fFlags & kAllowSRGBInputs_Flag);
    }
    bool usesDistanceVectorField() const {
        return SkToBool(fFlags & kUsesDistanceVectorField_Flag);
    }
    bool hasStencilClip() const {
        return SkToBool(fFlags & kHasStencilClip_Flag);
    }
    bool isStencilEnabled() const {
        return SkToBool(fFlags & kStencilEnabled_Flag);
    }

    GrXferBarrierType xferBarrierType(const GrCaps& caps) const {
        return this->getXferProcessor().xferBarrierType(fRenderTarget.get(), caps);
    }

    /**
     * Gets whether the target is drawing clockwise, counterclockwise,
     * or both faces.
     * @return the current draw face(s).
     */
    GrDrawFace getDrawFace() const { return static_cast<GrDrawFace>(fDrawFace); }

private:
    /** This is a continuation of the public "Flags" enum. */
    enum PrivateFlags {
        kDisableOutputConversionToSRGB_Flag = 0x4,
        kAllowSRGBInputs_Flag = 0x8,
        kUsesDistanceVectorField_Flag = 0x10,
        kHasStencilClip_Flag = 0x20,
        kStencilEnabled_Flag = 0x40,
    };

    typedef GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> RenderTarget;
    typedef GrPendingProgramElement<const GrFragmentProcessor> PendingFragmentProcessor;
    typedef SkAutoSTArray<8, PendingFragmentProcessor> FragmentProcessorArray;
    typedef GrPendingProgramElement<const GrXferProcessor> ProgramXferProcessor;
    RenderTarget                        fRenderTarget;
    GrScissorState                      fScissorState;
    GrWindowRectsState                  fWindowRectsState;
    const GrUserStencilSettings*        fUserStencilSettings;
    uint16_t                            fDrawFace;
    uint16_t                            fFlags;
    ProgramXferProcessor                fXferProcessor;
    FragmentProcessorArray              fFragmentProcessors;

    // This value is also the index in fFragmentProcessors where coverage processors begin.
    int                                 fNumColorProcessors;

    typedef SkRefCnt INHERITED;
};

#endif