1/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef GrBatch_DEFINED
9#define GrBatch_DEFINED
10
11#include <new>
12#include "GrBatchTarget.h"
13#include "GrGeometryProcessor.h"
14#include "GrVertices.h"
15#include "SkRefCnt.h"
16#include "SkThread.h"
17#include "SkTypes.h"
18
19class GrGpu;
20class GrPipeline;
21
22struct GrInitInvariantOutput;
23
24/*
25 * GrBatch is the base class for all Ganesh deferred geometry generators.  To facilitate
26 * reorderable batching, Ganesh does not generate geometry inline with draw calls.  Instead, it
27 * captures the arguments to the draw and then generates the geometry on demand.  This gives GrBatch
28 * subclasses complete freedom to decide how / what they can batch.
29 *
30 * Batches are created when GrContext processes a draw call. Batches of the same  subclass may be
31 * merged using combineIfPossible. When two batches merge, one takes on the union of the data
32 * and the other is left empty. The merged batch becomes responsible for drawing the data from both
33 * the original batches.
34 *
35 * If there are any possible optimizations which might require knowing more about the full state of
36 * the draw, ie whether or not the GrBatch is allowed to tweak alpha for coverage, then this
37 * information will be communicated to the GrBatch prior to geometry generation.
38 */
39
40class GrBatch : public SkRefCnt {
41public:
42    SK_DECLARE_INST_COUNT(GrBatch)
43    GrBatch() : fClassID(kIllegalBatchClassID), fNumberOfDraws(0) { SkDEBUGCODE(fUsed = false;) }
44    virtual ~GrBatch() {}
45
46    virtual const char* name() const = 0;
47    virtual void getInvariantOutputColor(GrInitInvariantOutput* out) const = 0;
48    virtual void getInvariantOutputCoverage(GrInitInvariantOutput* out) const = 0;
49
50    /*
51     * initBatchTracker is a hook for the some additional overrides / optimization possibilities
52     * from the GrXferProcessor.
53     */
54    virtual void initBatchTracker(const GrPipelineInfo& init) = 0;
55
56    bool combineIfPossible(GrBatch* that) {
57        if (this->classID() != that->classID()) {
58            return false;
59        }
60
61        return this->onCombineIfPossible(that);
62    }
63
64    virtual bool onCombineIfPossible(GrBatch*) = 0;
65
66    virtual void generateGeometry(GrBatchTarget*, const GrPipeline*) = 0;
67
68    const SkRect& bounds() const { return fBounds; }
69
70    // TODO this goes away when batches are everywhere
71    void setNumberOfDraws(int numberOfDraws) { fNumberOfDraws = numberOfDraws; }
72    int numberOfDraws() const { return fNumberOfDraws; }
73
74    void* operator new(size_t size);
75    void operator delete(void* target);
76
77    void* operator new(size_t size, void* placement) {
78        return ::operator new(size, placement);
79    }
80    void operator delete(void* target, void* placement) {
81        ::operator delete(target, placement);
82    }
83
84    /**
85      * Helper for down-casting to a GrBatch subclass
86      */
87    template <typename T> const T& cast() const { return *static_cast<const T*>(this); }
88    template <typename T> T* cast() { return static_cast<T*>(this); }
89
90    uint32_t classID() const { SkASSERT(kIllegalBatchClassID != fClassID); return fClassID; }
91
92    // TODO no GrPrimitiveProcessors yet read fragment position
93    bool willReadFragmentPosition() const { return false; }
94
95    SkDEBUGCODE(bool isUsed() const { return fUsed; })
96
97protected:
98    template <typename PROC_SUBCLASS> void initClassID() {
99         static uint32_t kClassID = GenClassID();
100         fClassID = kClassID;
101    }
102
103    uint32_t fClassID;
104
105    // NOTE, compute some bounds, even if extremely conservative.  Do *NOT* setLargest on the bounds
106    // rect because we outset it for dst copy textures
107    void setBounds(const SkRect& newBounds) { fBounds = newBounds; }
108
109    void joinBounds(const SkRect& otherBounds) {
110        return fBounds.joinPossiblyEmptyRect(otherBounds);
111    }
112
113    /** Helper for rendering instances using an instanced index index buffer. This class creates the
114        space for the vertices and flushes the draws to the batch target.*/
115   class InstancedHelper {
116   public:
117        InstancedHelper() {}
118        /** Returns the allocated storage for the vertices. The caller should populate the before
119            vertices before calling issueDraws(). */
120        void* init(GrBatchTarget* batchTarget, GrPrimitiveType, size_t vertexStride,
121                   const GrIndexBuffer*, int verticesPerInstance, int indicesPerInstance,
122                   int instancesToDraw);
123
124        /** Call after init() to issue draws to the batch target.*/
125        void issueDraw(GrBatchTarget* batchTarget) {
126            SkASSERT(fVertices.instanceCount());
127            batchTarget->draw(fVertices);
128        }
129    private:
130        GrVertices  fVertices;
131    };
132
133    static const int kVerticesPerQuad = 4;
134    static const int kIndicesPerQuad = 6;
135
136    /** A specialization of InstanceHelper for quad rendering. */
137    class QuadHelper : private InstancedHelper {
138    public:
139        QuadHelper() : INHERITED() {}
140        /** Finds the cached quad index buffer and reserves vertex space. Returns NULL on failure
141            and on sucess a pointer to the vertex data that the caller should populate before
142            calling issueDraws(). */
143        void* init(GrBatchTarget* batchTarget, size_t vertexStride, int quadsToDraw);
144
145        using InstancedHelper::issueDraw;
146
147    private:
148        typedef InstancedHelper INHERITED;
149    };
150
151    SkRect fBounds;
152
153private:
154    static uint32_t GenClassID() {
155        // fCurrProcessorClassID has been initialized to kIllegalProcessorClassID. The
156        // atomic inc returns the old value not the incremented value. So we add
157        // 1 to the returned value.
158        uint32_t id = static_cast<uint32_t>(sk_atomic_inc(&gCurrBatchClassID)) + 1;
159        if (!id) {
160            SkFAIL("This should never wrap as it should only be called once for each GrBatch "
161                   "subclass.");
162        }
163        return id;
164    }
165
166    enum {
167        kIllegalBatchClassID = 0,
168    };
169    static int32_t gCurrBatchClassID;
170
171    SkDEBUGCODE(bool fUsed;)
172
173    int fNumberOfDraws;
174
175    typedef SkRefCnt INHERITED;
176};
177
178#endif
179