1/*
2 * Copyright 2012 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 GrClipMaskCache_DEFINED
9#define GrClipMaskCache_DEFINED
10
11#include "GrContext.h"
12#include "SkClipStack.h"
13#include "SkTypes.h"
14
15class GrTexture;
16
17/**
18 * The stencil buffer stores the last clip path - providing a single entry
19 * "cache". This class provides similar functionality for AA clip paths
20 */
21class GrClipMaskCache : SkNoncopyable {
22public:
23    GrClipMaskCache();
24
25    ~GrClipMaskCache() {
26
27        while (!fStack.empty()) {
28            GrClipStackFrame* temp = (GrClipStackFrame*) fStack.back();
29            temp->~GrClipStackFrame();
30            fStack.pop_back();
31        }
32    }
33
34    bool canReuse(int32_t clipGenID, const SkIRect& bounds) {
35
36        SkASSERT(clipGenID != SkClipStack::kWideOpenGenID);
37        SkASSERT(clipGenID != SkClipStack::kEmptyGenID);
38
39        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
40
41        // We could reuse the mask if bounds is a subset of last bounds. We'd have to communicate
42        // an offset to the caller.
43        if (back->fLastMask.texture() &&
44            !back->fLastMask.texture()->wasDestroyed() &&
45            back->fLastBound == bounds &&
46            back->fLastClipGenID == clipGenID) {
47            return true;
48        }
49
50        return false;
51    }
52
53    void reset() {
54        if (fStack.empty()) {
55//            SkASSERT(false);
56            return;
57        }
58
59        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
60
61        back->reset();
62    }
63
64    /**
65     * After a "push" the clip state is entirely open. Currently, the
66     * entire clip stack will be re-rendered into a new clip mask.
67     * TODO: can we take advantage of the nested nature of the clips to
68     * reduce the mask creation cost?
69     */
70    void push();
71
72    void pop() {
73        //SkASSERT(!fStack.empty());
74
75        if (!fStack.empty()) {
76            GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
77
78            back->~GrClipStackFrame();
79            fStack.pop_back();
80        }
81    }
82
83    int32_t getLastClipGenID() const {
84
85        if (fStack.empty()) {
86            return SkClipStack::kInvalidGenID;
87        }
88
89        return ((GrClipStackFrame*) fStack.back())->fLastClipGenID;
90    }
91
92    GrTexture* getLastMask() {
93
94        if (fStack.empty()) {
95            SkASSERT(false);
96            return NULL;
97        }
98
99        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
100
101        return back->fLastMask.texture();
102    }
103
104    const GrTexture* getLastMask() const {
105
106        if (fStack.empty()) {
107            SkASSERT(false);
108            return NULL;
109        }
110
111        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
112
113        return back->fLastMask.texture();
114    }
115
116    void acquireMask(int32_t clipGenID,
117                     const GrTextureDesc& desc,
118                     const SkIRect& bound) {
119
120        if (fStack.empty()) {
121            SkASSERT(false);
122            return;
123        }
124
125        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
126
127        back->acquireMask(fContext, clipGenID, desc, bound);
128    }
129
130    int getLastMaskWidth() const {
131
132        if (fStack.empty()) {
133            SkASSERT(false);
134            return -1;
135        }
136
137        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
138
139        if (NULL == back->fLastMask.texture()) {
140            return -1;
141        }
142
143        return back->fLastMask.texture()->width();
144    }
145
146    int getLastMaskHeight() const {
147
148        if (fStack.empty()) {
149            SkASSERT(false);
150            return -1;
151        }
152
153        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
154
155        if (NULL == back->fLastMask.texture()) {
156            return -1;
157        }
158
159        return back->fLastMask.texture()->height();
160    }
161
162    void getLastBound(SkIRect* bound) const {
163
164        if (fStack.empty()) {
165            SkASSERT(false);
166            bound->setEmpty();
167            return;
168        }
169
170        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
171
172        *bound = back->fLastBound;
173    }
174
175    void setContext(GrContext* context) {
176        fContext = context;
177    }
178
179    GrContext* getContext() {
180        return fContext;
181    }
182
183    //  TODO: Remove this when we hold cache keys instead of refs to textures.
184    void purgeResources() {
185        SkDeque::F2BIter iter(fStack);
186        for (GrClipStackFrame* frame = (GrClipStackFrame*) iter.next();
187                frame != NULL;
188                frame = (GrClipStackFrame*) iter.next()) {
189            frame->reset();
190        }
191    }
192
193private:
194    struct GrClipStackFrame {
195
196        GrClipStackFrame() {
197            this->reset();
198        }
199
200        void acquireMask(GrContext* context,
201                         int32_t clipGenID,
202                         const GrTextureDesc& desc,
203                         const SkIRect& bound) {
204
205            fLastClipGenID = clipGenID;
206
207            fLastMask.set(context, desc);
208
209            fLastBound = bound;
210        }
211
212        void reset () {
213            fLastClipGenID = SkClipStack::kInvalidGenID;
214
215            GrTextureDesc desc;
216
217            fLastMask.set(NULL, desc);
218            fLastBound.setEmpty();
219        }
220
221        int32_t                 fLastClipGenID;
222        // The mask's width & height values are used by GrClipMaskManager to correctly scale the
223        // texture coords for the geometry drawn with this mask. TODO: This should be a cache key
224        // and not a hard ref to a texture.
225        GrAutoScratchTexture    fLastMask;
226        // fLastBound stores the bounding box of the clip mask in clip-stack space. This rect is
227        // used by GrClipMaskManager to position a rect and compute texture coords for the mask.
228        SkIRect                 fLastBound;
229    };
230
231    GrContext*   fContext;
232    SkDeque      fStack;
233
234    typedef SkNoncopyable INHERITED;
235};
236
237#endif // GrClipMaskCache_DEFINED
238