blob: cdcc741706b1b90bd4b71bef801bdf8e70161058 [file] [log] [blame]
robertphillips@google.com1e945b72012-04-16 18:03:03 +00001
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "GrClipMaskManager.h"
10#include "GrGpu.h"
11#include "GrRenderTarget.h"
12#include "GrStencilBuffer.h"
13#include "GrPathRenderer.h"
14
15void ScissoringSettings::setupScissoring(GrGpu* gpu) {
16 if (!fEnableScissoring) {
17 gpu->disableScissor();
18 return;
19 }
20
21 gpu->enableScissoring(fScissorRect);
22}
23
24// sort out what kind of clip mask needs to be created: A8/R8, stencil or scissor
25bool GrClipMaskManager::createClipMask(GrGpu* gpu,
26 const GrClip& clipIn,
27 ScissoringSettings* scissorSettings) {
28
29 GrAssert(scissorSettings);
30
31 scissorSettings->fEnableScissoring = false;
32 fClipMaskInStencil = false;
33
34 GrDrawState* drawState = gpu->drawState();
35 if (!drawState->isClipState()) {
36 return true;
37 }
38
39 GrRenderTarget* rt = drawState->getRenderTarget();
40
41 // GrDrawTarget should have filtered this for us
42 GrAssert(NULL != rt);
43
44 GrRect bounds;
45 GrRect rtRect;
46 rtRect.setLTRB(0, 0,
47 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
48 if (clipIn.hasConservativeBounds()) {
49 bounds = clipIn.getConservativeBounds();
50 if (!bounds.intersect(rtRect)) {
51 bounds.setEmpty();
52 }
53 } else {
54 bounds = rtRect;
55 }
56
57 bounds.roundOut(&scissorSettings->fScissorRect);
58 if (scissorSettings->fScissorRect.isEmpty()) {
59 scissorSettings->fScissorRect.setLTRB(0,0,0,0);
60 // TODO: I think we can do an early exit here - after refactoring try:
61 // set fEnableScissoring to true but leave fClipMaskInStencil false
62 // and return - everything is going to be scissored away anyway!
63 }
64 scissorSettings->fEnableScissoring = true;
65
66 // use the stencil clip if we can't represent the clip as a rectangle.
67 fClipMaskInStencil = !clipIn.isRect() && !clipIn.isEmpty() &&
68 !bounds.isEmpty();
69
70 if (fClipMaskInStencil) {
71 return this->createStencilClipMask(gpu, clipIn, bounds, scissorSettings);
72 }
73
74 return true;
75}
76
77#define VISUALIZE_COMPLEX_CLIP 0
78
79#if VISUALIZE_COMPLEX_CLIP
80 #include "GrRandom.h"
81 GrRandom gRandom;
82 #define SET_RANDOM_COLOR drawState->setColor(0xff000000 | gRandom.nextU());
83#else
84 #define SET_RANDOM_COLOR
85#endif
86
87namespace {
88// determines how many elements at the head of the clip can be skipped and
89// whether the initial clear should be to the inside- or outside-the-clip value,
90// and what op should be used to draw the first element that isn't skipped.
91int process_initial_clip_elements(const GrClip& clip,
92 const GrRect& bounds,
93 bool* clearToInside,
94 GrSetOp* startOp) {
95
96 // logically before the first element of the clip stack is
97 // processed the clip is entirely open. However, depending on the
98 // first set op we may prefer to clear to 0 for performance. We may
99 // also be able to skip the initial clip paths/rects. We loop until
100 // we cannot skip an element.
101 int curr;
102 bool done = false;
103 *clearToInside = true;
104 int count = clip.getElementCount();
105
106 for (curr = 0; curr < count && !done; ++curr) {
107 switch (clip.getOp(curr)) {
108 case kReplace_SetOp:
109 // replace ignores everything previous
110 *startOp = kReplace_SetOp;
111 *clearToInside = false;
112 done = true;
113 break;
114 case kIntersect_SetOp:
115 // if this element contains the entire bounds then we
116 // can skip it.
117 if (kRect_ClipType == clip.getElementType(curr)
118 && clip.getRect(curr).contains(bounds)) {
119 break;
120 }
121 // if everything is initially clearToInside then intersect is
122 // same as clear to 0 and treat as a replace. Otherwise,
123 // set stays empty.
124 if (*clearToInside) {
125 *startOp = kReplace_SetOp;
126 *clearToInside = false;
127 done = true;
128 }
129 break;
130 // we can skip a leading union.
131 case kUnion_SetOp:
132 // if everything is initially outside then union is
133 // same as replace. Otherwise, every pixel is still
134 // clearToInside
135 if (!*clearToInside) {
136 *startOp = kReplace_SetOp;
137 done = true;
138 }
139 break;
140 case kXor_SetOp:
141 // xor is same as difference or replace both of which
142 // can be 1-pass instead of 2 for xor.
143 if (*clearToInside) {
144 *startOp = kDifference_SetOp;
145 } else {
146 *startOp = kReplace_SetOp;
147 }
148 done = true;
149 break;
150 case kDifference_SetOp:
151 // if all pixels are clearToInside then we have to process the
152 // difference, otherwise it has no effect and all pixels
153 // remain outside.
154 if (*clearToInside) {
155 *startOp = kDifference_SetOp;
156 done = true;
157 }
158 break;
159 case kReverseDifference_SetOp:
160 // if all pixels are clearToInside then reverse difference
161 // produces empty set. Otherise it is same as replace
162 if (*clearToInside) {
163 *clearToInside = false;
164 } else {
165 *startOp = kReplace_SetOp;
166 done = true;
167 }
168 break;
169 default:
170 GrCrash("Unknown set op.");
171 }
172 }
173 return done ? curr-1 : count;
174}
175}
176
177// Create a 1-bit clip mask in the stencil buffer
178bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
179 const GrClip& clipIn,
180 const GrRect& bounds,
181 ScissoringSettings* scissorSettings) {
182
183 GrAssert(fClipMaskInStencil);
184
185 GrDrawState* drawState = gpu->drawState();
186 GrAssert(drawState->isClipState());
187
188 GrRenderTarget* rt = drawState->getRenderTarget();
189 GrAssert(NULL != rt);
190
191 // TODO: dynamically attach a SB when needed.
192 GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
193 if (NULL == stencilBuffer) {
194 return false;
195 }
196
197 if (stencilBuffer->mustRenderClip(clipIn, rt->width(), rt->height())) {
198
199 stencilBuffer->setLastClip(clipIn, rt->width(), rt->height());
200
201 // we set the current clip to the bounds so that our recursive
202 // draws are scissored to them. We use the copy of the complex clip
203 // we just stashed on the SB to render from. We set it back after
204 // we finish drawing it into the stencil.
205 const GrClip& clipCopy = stencilBuffer->getLastClip();
206 gpu->setClip(GrClip(bounds));
207
208 GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
209 drawState = gpu->drawState();
210 drawState->setRenderTarget(rt);
211 GrDrawTarget::AutoGeometryPush agp(gpu);
212
213 gpu->disableScissor();
214#if !VISUALIZE_COMPLEX_CLIP
215 drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
216#endif
217
218 int count = clipCopy.getElementCount();
219 int clipBit = stencilBuffer->bits();
220 SkASSERT((clipBit <= 16) &&
221 "Ganesh only handles 16b or smaller stencil buffers");
222 clipBit = (1 << (clipBit-1));
223
224 GrRect rtRect;
225 rtRect.setLTRB(0, 0,
226 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
227
228 bool clearToInside;
229 GrSetOp startOp = kReplace_SetOp; // suppress warning
230 int start = process_initial_clip_elements(clipCopy,
231 rtRect,
232 &clearToInside,
233 &startOp);
234
235 gpu->clearStencilClip(scissorSettings->fScissorRect, clearToInside);
236
237 // walk through each clip element and perform its set op
238 // with the existing clip.
239 for (int c = start; c < count; ++c) {
240 GrPathFill fill;
241 bool fillInverted;
242 // enabled at bottom of loop
243 drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
244
245 bool canRenderDirectToStencil; // can the clip element be drawn
246 // directly to the stencil buffer
247 // with a non-inverted fill rule
248 // without extra passes to
249 // resolve in/out status.
250
251 GrPathRenderer* pr = NULL;
252 const GrPath* clipPath = NULL;
253 if (kRect_ClipType == clipCopy.getElementType(c)) {
254 canRenderDirectToStencil = true;
255 fill = kEvenOdd_PathFill;
256 fillInverted = false;
257 // there is no point in intersecting a screen filling
258 // rectangle.
259 if (kIntersect_SetOp == clipCopy.getOp(c) &&
260 clipCopy.getRect(c).contains(rtRect)) {
261 continue;
262 }
263 } else {
264 fill = clipCopy.getPathFill(c);
265 fillInverted = GrIsFillInverted(fill);
266 fill = GrNonInvertedFill(fill);
267 clipPath = &clipCopy.getPath(c);
268 pr = this->getClipPathRenderer(gpu, *clipPath, fill);
269 if (NULL == pr) {
270 fClipMaskInStencil = false;
271 gpu->setClip(clipCopy); // restore to the original
272 return false;
273 }
274 canRenderDirectToStencil =
275 !pr->requiresStencilPass(*clipPath, fill, gpu);
276 }
277
278 GrSetOp op = (c == start) ? startOp : clipCopy.getOp(c);
279 int passes;
280 GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
281
282 bool canDrawDirectToClip; // Given the renderer, the element,
283 // fill rule, and set operation can
284 // we render the element directly to
285 // stencil bit used for clipping.
286 canDrawDirectToClip =
287 GrStencilSettings::GetClipPasses(op,
288 canRenderDirectToStencil,
289 clipBit,
290 fillInverted,
291 &passes, stencilSettings);
292
293 // draw the element to the client stencil bits if necessary
294 if (!canDrawDirectToClip) {
295 GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
296 kIncClamp_StencilOp,
297 kIncClamp_StencilOp,
298 kAlways_StencilFunc,
299 0xffff,
300 0x0000,
301 0xffff);
302 SET_RANDOM_COLOR
303 if (kRect_ClipType == clipCopy.getElementType(c)) {
304 *drawState->stencil() = gDrawToStencil;
305 gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
306 } else {
307 if (canRenderDirectToStencil) {
308 *drawState->stencil() = gDrawToStencil;
309 pr->drawPath(*clipPath, fill, NULL, gpu, 0, false);
310 } else {
311 pr->drawPathToStencil(*clipPath, fill, gpu);
312 }
313 }
314 }
315
316 // now we modify the clip bit by rendering either the clip
317 // element directly or a bounding rect of the entire clip.
318 drawState->enableState(GrGpu::kModifyStencilClip_StateBit);
319 for (int p = 0; p < passes; ++p) {
320 *drawState->stencil() = stencilSettings[p];
321 if (canDrawDirectToClip) {
322 if (kRect_ClipType == clipCopy.getElementType(c)) {
323 SET_RANDOM_COLOR
324 gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
325 } else {
326 SET_RANDOM_COLOR
327 pr->drawPath(*clipPath, fill, NULL, gpu, 0, false);
328 }
329 } else {
330 SET_RANDOM_COLOR
331 gpu->drawSimpleRect(bounds, NULL, 0);
332 }
333 }
334 }
335 // restore clip
336 gpu->setClip(clipCopy);
337 // recusive draws would have disabled this since they drew with
338 // the clip bounds as clip.
339 fClipMaskInStencil = true;
340 }
341
342 return true;
343}
344
345GrPathRenderer* GrClipMaskManager::getClipPathRenderer(GrGpu* gpu,
346 const GrPath& path,
347 GrPathFill fill) {
348 if (NULL == fPathRendererChain) {
349 fPathRendererChain =
350 new GrPathRendererChain(gpu->getContext(),
351 GrPathRendererChain::kNonAAOnly_UsageFlag);
352 }
353 return fPathRendererChain->getPathRenderer(path, fill, gpu, false);
354}
355
356void GrClipMaskManager::freeResources() {
357 // in case path renderer has any GrResources, start from scratch
358 GrSafeSetNull(fPathRendererChain);
359}