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

#ifndef GrRenderTarget_DEFINED
#define GrRenderTarget_DEFINED

#include "GrSurface.h"
#include "SkRect.h"

class GrCaps;
class GrDrawTarget;
class GrStencilAttachment;
class GrRenderTargetPriv;

/**
 * GrRenderTarget represents a 2D buffer of pixels that can be rendered to.
 * A context's render target is set by setRenderTarget(). Render targets are
 * created by a createTexture with the kRenderTarget_SurfaceFlag flag.
 * Additionally, GrContext provides methods for creating GrRenderTargets
 * that wrap externally created render targets.
 */
class GrRenderTarget : virtual public GrSurface {
public:
    // GrSurface overrides
    GrRenderTarget* asRenderTarget() override { return this; }
    const GrRenderTarget* asRenderTarget() const  override { return this; }

    // GrRenderTarget
    /**
     * On some hardware it is possible for a render target to have multisampling
     * only in certain buffers.
     * Enforce only two legal sample configs.
     * kUnified_SampleConfig signifies multisampling in both color and stencil
     * buffers and is available across all hardware.
     * kStencil_SampleConfig means multisampling is present in stencil buffer
     * only; this config requires hardware support of
     * NV_framebuffer_mixed_samples.
    */
    enum SampleConfig {
        kUnified_SampleConfig = 0,
        kStencil_SampleConfig = 1
    };

    /**
     * @return true if the surface is multisampled in all buffers,
     *         false otherwise
     */
    bool isUnifiedMultisampled() const {
        if (fSampleConfig != kUnified_SampleConfig) {
            return false;
        }
        return 0 != fDesc.fSampleCnt;
    }

    /**
     * @return true if the surface is multisampled in the stencil buffer,
     *         false otherwise
     */
    bool isStencilBufferMultisampled() const {
        return 0 != fDesc.fSampleCnt;
    }

    /**
     * @return the number of color samples-per-pixel, or zero if non-MSAA or
     *         multisampled in the stencil buffer only.
     */
    int numColorSamples() const {
        if (fSampleConfig == kUnified_SampleConfig) {
            return fDesc.fSampleCnt;
        }
        return 0;
    }

    /**
     * @return the number of stencil samples-per-pixel, or zero if non-MSAA.
     */
    int numStencilSamples() const {
        return fDesc.fSampleCnt;
    }

    /**
     * @return true if the surface is mixed sampled, false otherwise.
     */
    bool hasMixedSamples() const {
        SkASSERT(kStencil_SampleConfig != fSampleConfig ||
                 this->isStencilBufferMultisampled());
        return kStencil_SampleConfig == fSampleConfig;
    }

    /**
     * Call to indicate the multisample contents were modified such that the
     * render target needs to be resolved before it can be used as texture. Gr
     * tracks this for its own drawing and thus this only needs to be called
     * when the render target has been modified outside of Gr. This has no
     * effect on wrapped backend render targets.
     *
     * @param rect  a rect bounding the area needing resolve. NULL indicates
     *              the whole RT needs resolving.
     */
    void flagAsNeedingResolve(const SkIRect* rect = NULL);

    /**
     * Call to override the region that needs to be resolved.
     */
    void overrideResolveRect(const SkIRect rect);

    /**
     * Call to indicate that GrRenderTarget was externally resolved. This may
     * allow Gr to skip a redundant resolve step.
     */
    void flagAsResolved() { fResolveRect.setLargestInverted(); }

    /**
     * @return true if the GrRenderTarget requires MSAA resolving
     */
    bool needsResolve() const { return !fResolveRect.isEmpty(); }

    /**
     * Returns a rect bounding the region needing resolving.
     */
    const SkIRect& getResolveRect() const { return fResolveRect; }

    /**
     * Provide a performance hint that the render target's contents are allowed
     * to become undefined.
     */
    void discard();

    // a MSAA RT may require explicit resolving , it may auto-resolve (e.g. FBO
    // 0 in GL), or be unresolvable because the client didn't give us the
    // resolve destination.
    enum ResolveType {
        kCanResolve_ResolveType,
        kAutoResolves_ResolveType,
        kCantResolve_ResolveType,
    };
    virtual ResolveType getResolveType() const = 0;

    /**
     *  Return the native ID or handle to the rendertarget, depending on the
     *  platform. e.g. on OpenGL, return the FBO ID.
     */
    virtual GrBackendObject getRenderTargetHandle() const = 0;

    // Checked when this object is asked to attach a stencil buffer.
    virtual bool canAttemptStencilAttachment() const = 0;

    // Provides access to functions that aren't part of the public API.
    GrRenderTargetPriv renderTargetPriv();
    const GrRenderTargetPriv renderTargetPriv() const;

    void setLastDrawTarget(GrDrawTarget* dt);
    GrDrawTarget* getLastDrawTarget() { return fLastDrawTarget; }

    static SampleConfig ComputeSampleConfig(const GrCaps& caps, int sampleCnt);

protected:
    GrRenderTarget(GrGpu* gpu, const GrSurfaceDesc& desc,
                   SampleConfig sampleConfig, GrStencilAttachment* stencil = nullptr)
        : INHERITED(gpu, desc)
        , fStencilAttachment(stencil)
        , fSampleConfig(sampleConfig)
        , fLastDrawTarget(nullptr) {
        fResolveRect.setLargestInverted();
    }

    ~GrRenderTarget() override;

    // override of GrResource
    void onAbandon() override;
    void onRelease() override;

private:
    // Allows the backends to perform any additional work that is required for attaching a
    // GrStencilAttachment. When this is called, the GrStencilAttachment has already been put onto
    // the GrRenderTarget. This function must return false if any failures occur when completing the
    // stencil attachment.
    virtual bool completeStencilAttachment() = 0;

    friend class GrRenderTargetPriv;

    GrStencilAttachment*  fStencilAttachment;
    SampleConfig          fSampleConfig;

    SkIRect               fResolveRect;

    // The last drawTarget that wrote to or is currently going to write to this renderTarget
    // The drawTarget can be closed (e.g., no draw context is currently bound
    // to this renderTarget).
    // This back-pointer is required so that we can add a dependancy between
    // the drawTarget used to create the current contents of this renderTarget
    // and the drawTarget of a destination renderTarget to which this one is being drawn.
    GrDrawTarget* fLastDrawTarget;

    typedef GrSurface INHERITED;
};


#endif
