OLD | NEW |
| (Empty) |
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 #include "GrClipMaskManager.h" | |
9 #include "GrCaps.h" | |
10 #include "GrDrawingManager.h" | |
11 #include "GrDrawContextPriv.h" | |
12 #include "GrGpuResourcePriv.h" | |
13 #include "GrPaint.h" | |
14 #include "GrPathRenderer.h" | |
15 #include "GrRenderTarget.h" | |
16 #include "GrRenderTargetPriv.h" | |
17 #include "GrResourceProvider.h" | |
18 #include "GrStencilAttachment.h" | |
19 #include "GrSWMaskHelper.h" | |
20 #include "SkRasterClip.h" | |
21 #include "SkTLazy.h" | |
22 #include "batches/GrRectBatchFactory.h" | |
23 #include "effects/GrConvexPolyEffect.h" | |
24 #include "effects/GrPorterDuffXferProcessor.h" | |
25 #include "effects/GrRRectEffect.h" | |
26 #include "effects/GrTextureDomain.h" | |
27 | |
28 typedef SkClipStack::Element Element; | |
29 | |
30 static const int kMaxAnalyticElements = 4; | |
31 | |
32 //////////////////////////////////////////////////////////////////////////////// | |
33 // set up the draw state to enable the aa clipping mask. Besides setting up the | |
34 // stage matrix this also alters the vertex layout | |
35 static sk_sp<GrFragmentProcessor> create_fp_for_mask(GrTexture* result, | |
36 const SkIRect &devBound) { | |
37 SkMatrix mat; | |
38 // We use device coords to compute the texture coordinates. We set our matri
x to be a | |
39 // translation to the devBound, and then a scaling matrix to normalized coor
ds. | |
40 mat.setIDiv(result->width(), result->height()); | |
41 mat.preTranslate(SkIntToScalar(-devBound.fLeft), | |
42 SkIntToScalar(-devBound.fTop)); | |
43 | |
44 SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height()); | |
45 return sk_sp<GrFragmentProcessor>(GrTextureDomainEffect::Make( | |
46 result, | |
47 mat, | |
48 GrTextureDomain::MakeTexelDomain(result
, domainTexels), | |
49 GrTextureDomain::kDecal_Mode, | |
50 GrTextureParams::kNone_FilterMode, | |
51 kDevice_GrCoordSet)); | |
52 } | |
53 | |
54 // Does the path in 'element' require SW rendering? If so, return true (and, | |
55 // optionally, set 'prOut' to NULL. If not, return false (and, optionally, set | |
56 // 'prOut' to the non-SW path renderer that will do the job). | |
57 bool GrClipMaskManager::PathNeedsSWRenderer(GrContext* context, | |
58 bool hasUserStencilSettings, | |
59 const GrDrawContext* drawContext, | |
60 const SkMatrix& viewMatrix, | |
61 const Element* element, | |
62 GrPathRenderer** prOut, | |
63 bool needsStencil) { | |
64 if (Element::kRect_Type == element->getType()) { | |
65 // rects can always be drawn directly w/o using the software path | |
66 // TODO: skip rrects once we're drawing them directly. | |
67 if (prOut) { | |
68 *prOut = nullptr; | |
69 } | |
70 return false; | |
71 } else { | |
72 // We shouldn't get here with an empty clip element. | |
73 SkASSERT(Element::kEmpty_Type != element->getType()); | |
74 | |
75 // the gpu alpha mask will draw the inverse paths as non-inverse to a te
mp buffer | |
76 SkPath path; | |
77 element->asPath(&path); | |
78 if (path.isInverseFillType()) { | |
79 path.toggleInverseFillType(); | |
80 } | |
81 | |
82 GrPathRendererChain::DrawType type; | |
83 | |
84 if (needsStencil) { | |
85 type = element->isAA() | |
86 ? GrPathRendererChain::kStencilAndColorAntiAlias_Dra
wType | |
87 : GrPathRendererChain::kStencilAndColor_DrawType; | |
88 } else { | |
89 type = element->isAA() | |
90 ? GrPathRendererChain::kColorAntiAlias_DrawType | |
91 : GrPathRendererChain::kColor_DrawType; | |
92 } | |
93 | |
94 GrShape shape(path, GrStyle::SimpleFill()); | |
95 GrPathRenderer::CanDrawPathArgs canDrawArgs; | |
96 canDrawArgs.fShaderCaps = context->caps()->shaderCaps(); | |
97 canDrawArgs.fViewMatrix = &viewMatrix; | |
98 canDrawArgs.fShape = &shape; | |
99 canDrawArgs.fAntiAlias = element->isAA(); | |
100 canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings; | |
101 canDrawArgs.fIsStencilBufferMSAA = drawContext->isStencilBufferMultisamp
led(); | |
102 | |
103 // the 'false' parameter disallows use of the SW path renderer | |
104 GrPathRenderer* pr = context->drawingManager()->getPathRenderer(canDrawA
rgs, false, type); | |
105 if (prOut) { | |
106 *prOut = pr; | |
107 } | |
108 return SkToBool(!pr); | |
109 } | |
110 } | |
111 | |
112 /* | |
113 * This method traverses the clip stack to see if the GrSoftwarePathRenderer | |
114 * will be used on any element. If so, it returns true to indicate that the | |
115 * entire clip should be rendered in SW and then uploaded en masse to the gpu. | |
116 */ | |
117 bool GrClipMaskManager::UseSWOnlyPath(GrContext* context, | |
118 const GrPipelineBuilder& pipelineBuilder, | |
119 const GrDrawContext* drawContext, | |
120 const SkVector& clipToMaskOffset, | |
121 const GrReducedClip::ElementList& elements
) { | |
122 // TODO: generalize this function so that when | |
123 // a clip gets complex enough it can just be done in SW regardless | |
124 // of whether it would invoke the GrSoftwarePathRenderer. | |
125 | |
126 // Set the matrix so that rendered clip elements are transformed to mask spa
ce from clip | |
127 // space. | |
128 const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMa
skOffset.fY); | |
129 | |
130 for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get();
iter.next()) { | |
131 const Element* element = iter.get(); | |
132 | |
133 SkRegion::Op op = element->getOp(); | |
134 bool invert = element->isInverseFilled(); | |
135 bool needsStencil = invert || | |
136 SkRegion::kIntersect_Op == op || SkRegion::kReverseD
ifference_Op == op; | |
137 | |
138 if (PathNeedsSWRenderer(context, pipelineBuilder.hasUserStencilSettings(
), | |
139 drawContext, translate, element, nullptr, needsS
tencil)) { | |
140 return true; | |
141 } | |
142 } | |
143 return false; | |
144 } | |
145 | |
146 static bool get_analytic_clip_processor(const GrReducedClip::ElementList& elemen
ts, | |
147 bool abortIfAA, | |
148 SkVector& clipToRTOffset, | |
149 const SkRect& drawBounds, | |
150 sk_sp<GrFragmentProcessor>* resultFP) { | |
151 SkRect boundsInClipSpace; | |
152 boundsInClipSpace = drawBounds.makeOffset(-clipToRTOffset.fX, -clipToRTOffse
t.fY); | |
153 SkASSERT(elements.count() <= kMaxAnalyticElements); | |
154 SkSTArray<kMaxAnalyticElements, sk_sp<GrFragmentProcessor>> fps; | |
155 GrReducedClip::ElementList::Iter iter(elements); | |
156 while (iter.get()) { | |
157 SkRegion::Op op = iter.get()->getOp(); | |
158 bool invert; | |
159 bool skip = false; | |
160 switch (op) { | |
161 case SkRegion::kReplace_Op: | |
162 SkASSERT(iter.get() == elements.head()); | |
163 // Fallthrough, handled same as intersect. | |
164 case SkRegion::kIntersect_Op: | |
165 invert = false; | |
166 if (iter.get()->contains(boundsInClipSpace)) { | |
167 skip = true; | |
168 } | |
169 break; | |
170 case SkRegion::kDifference_Op: | |
171 invert = true; | |
172 // We don't currently have a cheap test for whether a rect is fu
lly outside an | |
173 // element's primitive, so don't attempt to set skip. | |
174 break; | |
175 default: | |
176 return false; | |
177 } | |
178 if (!skip) { | |
179 GrPrimitiveEdgeType edgeType; | |
180 if (iter.get()->isAA()) { | |
181 if (abortIfAA) { | |
182 return false; | |
183 } | |
184 edgeType = | |
185 invert ? kInverseFillAA_GrProcessorEdgeType : kFillAA_GrProc
essorEdgeType; | |
186 } else { | |
187 edgeType = | |
188 invert ? kInverseFillBW_GrProcessorEdgeType : kFillBW_GrProc
essorEdgeType; | |
189 } | |
190 | |
191 switch (iter.get()->getType()) { | |
192 case SkClipStack::Element::kPath_Type: | |
193 fps.emplace_back(GrConvexPolyEffect::Make(edgeType, iter.get
()->getPath(), | |
194 &clipToRTOffset)); | |
195 break; | |
196 case SkClipStack::Element::kRRect_Type: { | |
197 SkRRect rrect = iter.get()->getRRect(); | |
198 rrect.offset(clipToRTOffset.fX, clipToRTOffset.fY); | |
199 fps.emplace_back(GrRRectEffect::Make(edgeType, rrect)); | |
200 break; | |
201 } | |
202 case SkClipStack::Element::kRect_Type: { | |
203 SkRect rect = iter.get()->getRect(); | |
204 rect.offset(clipToRTOffset.fX, clipToRTOffset.fY); | |
205 fps.emplace_back(GrConvexPolyEffect::Make(edgeType, rect)); | |
206 break; | |
207 } | |
208 default: | |
209 break; | |
210 } | |
211 if (!fps.back()) { | |
212 return false; | |
213 } | |
214 } | |
215 iter.next(); | |
216 } | |
217 | |
218 *resultFP = nullptr; | |
219 if (fps.count()) { | |
220 *resultFP = GrFragmentProcessor::RunInSeries(fps.begin(), fps.count()); | |
221 } | |
222 return true; | |
223 } | |
224 | |
225 //////////////////////////////////////////////////////////////////////////////// | |
226 // sort out what kind of clip mask needs to be created: alpha, stencil, | |
227 // scissor, or entirely software | |
228 bool GrClipMaskManager::SetupClipping(GrContext* context, | |
229 const GrPipelineBuilder& pipelineBuilder, | |
230 GrDrawContext* drawContext, | |
231 const GrClipStackClip& clip, | |
232 const SkRect* origDevBounds, | |
233 GrAppliedClip* out) { | |
234 if (!clip.clipStack() || clip.clipStack()->isWideOpen()) { | |
235 return true; | |
236 } | |
237 | |
238 GrReducedClip::ElementList elements; | |
239 int32_t genID = 0; | |
240 GrReducedClip::InitialState initialState = GrReducedClip::kAllIn_InitialStat
e; | |
241 SkIRect clipSpaceIBounds; | |
242 bool requiresAA = false; | |
243 | |
244 SkIRect clipSpaceReduceQueryBounds; | |
245 SkRect devBounds; | |
246 if (origDevBounds) { | |
247 if (!devBounds.intersect(SkRect::MakeIWH(drawContext->width(), drawConte
xt->height()), | |
248 *origDevBounds)) { | |
249 return false; | |
250 } | |
251 devBounds.roundOut(&clipSpaceReduceQueryBounds); | |
252 clipSpaceReduceQueryBounds.offset(clip.origin()); | |
253 } else { | |
254 devBounds = SkRect::MakeIWH(drawContext->width(), drawContext->height())
; | |
255 clipSpaceReduceQueryBounds.setXYWH(0, 0, drawContext->width(), drawConte
xt->height()); | |
256 clipSpaceReduceQueryBounds.offset(clip.origin()); | |
257 } | |
258 GrReducedClip::ReduceClipStack(*clip.clipStack(), | |
259 clipSpaceReduceQueryBounds, | |
260 &elements, | |
261 &genID, | |
262 &initialState, | |
263 &clipSpaceIBounds, | |
264 &requiresAA); | |
265 if (elements.isEmpty()) { | |
266 if (GrReducedClip::kAllOut_InitialState == initialState) { | |
267 return false; | |
268 } else { | |
269 SkIRect scissorSpaceIBounds(clipSpaceIBounds); | |
270 scissorSpaceIBounds.offset(-clip.origin()); | |
271 if (!SkRect::Make(scissorSpaceIBounds).contains(devBounds)) { | |
272 out->makeScissored(scissorSpaceIBounds); | |
273 } | |
274 return true; | |
275 } | |
276 } | |
277 | |
278 // An element count of 4 was chosen because of the common pattern in Blink o
f: | |
279 // isect RR | |
280 // diff RR | |
281 // isect convex_poly | |
282 // isect convex_poly | |
283 // when drawing rounded div borders. This could probably be tuned based on a | |
284 // configuration's relative costs of switching RTs to generate a mask vs | |
285 // longer shaders. | |
286 if (elements.count() <= kMaxAnalyticElements) { | |
287 SkVector clipToRTOffset = { SkIntToScalar(-clip.origin().fX), | |
288 SkIntToScalar(-clip.origin().fY) }; | |
289 // When there are multiple samples we want to do per-sample clipping, no
t compute a | |
290 // fractional pixel coverage. | |
291 bool disallowAnalyticAA = drawContext->isStencilBufferMultisampled(); | |
292 if (disallowAnalyticAA && !drawContext->numColorSamples()) { | |
293 // With a single color sample, any coverage info is lost from color
once it hits the | |
294 // color buffer anyway, so we may as well use coverage AA if nothing
else in the pipe | |
295 // is multisampled. | |
296 disallowAnalyticAA = pipelineBuilder.isHWAntialias() || | |
297 pipelineBuilder.hasUserStencilSettings(); | |
298 } | |
299 sk_sp<GrFragmentProcessor> clipFP; | |
300 if (requiresAA && | |
301 get_analytic_clip_processor(elements, disallowAnalyticAA, clipToRTOf
fset, devBounds, | |
302 &clipFP)) { | |
303 SkIRect scissorSpaceIBounds(clipSpaceIBounds); | |
304 scissorSpaceIBounds.offset(-clip.origin()); | |
305 if (!SkRect::Make(scissorSpaceIBounds).contains(devBounds)) { | |
306 out->makeScissoredFPBased(std::move(clipFP), scissorSpaceIBounds
); | |
307 return true; | |
308 } | |
309 out->makeFPBased(std::move(clipFP), SkRect::Make(scissorSpaceIBounds
)); | |
310 return true; | |
311 } | |
312 } | |
313 | |
314 // If the stencil buffer is multisampled we can use it to do everything. | |
315 if (!drawContext->isStencilBufferMultisampled() && requiresAA) { | |
316 sk_sp<GrTexture> result; | |
317 | |
318 // The top-left of the mask corresponds to the top-left corner of the bo
unds. | |
319 SkVector clipToMaskOffset = { | |
320 SkIntToScalar(-clipSpaceIBounds.fLeft), | |
321 SkIntToScalar(-clipSpaceIBounds.fTop) | |
322 }; | |
323 | |
324 if (UseSWOnlyPath(context, pipelineBuilder, drawContext, | |
325 clipToMaskOffset, elements)) { | |
326 // The clip geometry is complex enough that it will be more efficien
t to create it | |
327 // entirely in software | |
328 result = CreateSoftwareClipMask(context->textureProvider(), | |
329 genID, | |
330 initialState, | |
331 elements, | |
332 clipToMaskOffset, | |
333 clipSpaceIBounds); | |
334 } else { | |
335 result = CreateAlphaClipMask(context, | |
336 genID, | |
337 initialState, | |
338 elements, | |
339 clipToMaskOffset, | |
340 clipSpaceIBounds); | |
341 // If createAlphaClipMask fails it means UseSWOnlyPath has a bug | |
342 SkASSERT(result); | |
343 } | |
344 | |
345 if (result) { | |
346 // The mask's top left coord should be pinned to the rounded-out top
left corner of | |
347 // clipSpace bounds. We determine the mask's position WRT to the ren
der target here. | |
348 SkIRect rtSpaceMaskBounds = clipSpaceIBounds; | |
349 rtSpaceMaskBounds.offset(-clip.origin()); | |
350 out->makeFPBased(create_fp_for_mask(result.get(), rtSpaceMaskBounds)
, | |
351 SkRect::Make(rtSpaceMaskBounds)); | |
352 return true; | |
353 } | |
354 // if alpha clip mask creation fails fall through to the non-AA code pat
hs | |
355 } | |
356 | |
357 // use the stencil clip if we can't represent the clip as a rectangle. | |
358 SkIPoint clipSpaceToStencilSpaceOffset = -clip.origin(); | |
359 CreateStencilClipMask(context, | |
360 drawContext, | |
361 genID, | |
362 initialState, | |
363 elements, | |
364 clipSpaceIBounds, | |
365 clipSpaceToStencilSpaceOffset); | |
366 | |
367 // This must occur after createStencilClipMask. That function may change the
scissor. Also, it | |
368 // only guarantees that the stencil mask is correct within the bounds it was
passed, so we must | |
369 // use both stencil and scissor test to the bounds for the final draw. | |
370 SkIRect scissorSpaceIBounds(clipSpaceIBounds); | |
371 scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset); | |
372 out->makeScissoredStencil(scissorSpaceIBounds); | |
373 return true; | |
374 } | |
375 | |
376 static bool stencil_element(GrDrawContext* dc, | |
377 const GrFixedClip& clip, | |
378 const GrUserStencilSettings* ss, | |
379 const SkMatrix& viewMatrix, | |
380 const SkClipStack::Element* element) { | |
381 | |
382 // TODO: Draw rrects directly here. | |
383 switch (element->getType()) { | |
384 case Element::kEmpty_Type: | |
385 SkDEBUGFAIL("Should never get here with an empty element."); | |
386 break; | |
387 case Element::kRect_Type: | |
388 return dc->drawContextPriv().drawAndStencilRect(clip, ss, | |
389 element->getOp(), | |
390 element->isInverseFi
lled(), | |
391 element->isAA(), | |
392 viewMatrix, element-
>getRect()); | |
393 break; | |
394 default: { | |
395 SkPath path; | |
396 element->asPath(&path); | |
397 if (path.isInverseFillType()) { | |
398 path.toggleInverseFillType(); | |
399 } | |
400 | |
401 return dc->drawContextPriv().drawAndStencilPath(clip, ss, | |
402 element->getOp(), | |
403 element->isInverseFi
lled(), | |
404 element->isAA(), vie
wMatrix, path); | |
405 break; | |
406 } | |
407 } | |
408 | |
409 return false; | |
410 } | |
411 | |
412 static void draw_element(GrDrawContext* dc, | |
413 const GrClip& clip, // TODO: can this just always be Wi
deOpen? | |
414 const GrPaint &paint, | |
415 const SkMatrix& viewMatrix, | |
416 const SkClipStack::Element* element) { | |
417 | |
418 // TODO: Draw rrects directly here. | |
419 switch (element->getType()) { | |
420 case Element::kEmpty_Type: | |
421 SkDEBUGFAIL("Should never get here with an empty element."); | |
422 break; | |
423 case Element::kRect_Type: | |
424 dc->drawRect(clip, paint, viewMatrix, element->getRect()); | |
425 break; | |
426 default: { | |
427 SkPath path; | |
428 element->asPath(&path); | |
429 if (path.isInverseFillType()) { | |
430 path.toggleInverseFillType(); | |
431 } | |
432 | |
433 dc->drawPath(clip, paint, viewMatrix, path, GrStyle::SimpleFill()); | |
434 break; | |
435 } | |
436 } | |
437 } | |
438 | |
439 //////////////////////////////////////////////////////////////////////////////// | |
440 // Create a 8-bit clip mask in alpha | |
441 | |
442 static void GetClipMaskKey(int32_t clipGenID, const SkIRect& bounds, GrUniqueKey
* key) { | |
443 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); | |
444 GrUniqueKey::Builder builder(key, kDomain, 3); | |
445 builder[0] = clipGenID; | |
446 builder[1] = SkToU16(bounds.fLeft) | (SkToU16(bounds.fRight) << 16); | |
447 builder[2] = SkToU16(bounds.fTop) | (SkToU16(bounds.fBottom) << 16); | |
448 } | |
449 | |
450 sk_sp<GrTexture> GrClipMaskManager::CreateAlphaClipMask(GrContext* context, | |
451 int32_t elementsGenID, | |
452 GrReducedClip::InitialSt
ate initialState, | |
453 const GrReducedClip::Ele
mentList& elements, | |
454 const SkVector& clipToMa
skOffset, | |
455 const SkIRect& clipSpace
IBounds) { | |
456 GrResourceProvider* resourceProvider = context->resourceProvider(); | |
457 GrUniqueKey key; | |
458 GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key); | |
459 if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)
) { | |
460 return sk_sp<GrTexture>(texture); | |
461 } | |
462 | |
463 // There's no texture in the cache. Let's try to allocate it then. | |
464 GrPixelConfig config = kRGBA_8888_GrPixelConfig; | |
465 if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { | |
466 config = kAlpha_8_GrPixelConfig; | |
467 } | |
468 | |
469 sk_sp<GrDrawContext> dc(context->newDrawContext(SkBackingFit::kApprox, | |
470 clipSpaceIBounds.width(), | |
471 clipSpaceIBounds.height(), | |
472 config)); | |
473 if (!dc) { | |
474 return nullptr; | |
475 } | |
476 | |
477 // The texture may be larger than necessary, this rect represents the part o
f the texture | |
478 // we populate with a rasterization of the clip. | |
479 SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpa
ceIBounds.height()); | |
480 | |
481 // The scratch texture that we are drawing into can be substantially larger
than the mask. Only | |
482 // clear the part that we care about. | |
483 dc->clear(&maskSpaceIBounds, | |
484 GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff :
0x00000000, | |
485 true); | |
486 | |
487 // Set the matrix so that rendered clip elements are transformed to mask spa
ce from clip | |
488 // space. | |
489 const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMa
skOffset.fY); | |
490 | |
491 // It is important that we use maskSpaceIBounds as the stencil rect in the b
elow loop. | |
492 // The second pass that zeros the stencil buffer renders the rect maskSpaceI
Bounds so the first | |
493 // pass must not set values outside of this bounds or stencil values outside
the rect won't be | |
494 // cleared. | |
495 | |
496 // walk through each clip element and perform its set op | |
497 for (GrReducedClip::ElementList::Iter iter = elements.headIter(); iter.get()
; iter.next()) { | |
498 const Element* element = iter.get(); | |
499 SkRegion::Op op = element->getOp(); | |
500 bool invert = element->isInverseFilled(); | |
501 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDiffere
nce_Op == op) { | |
502 GrFixedClip clip(maskSpaceIBounds); | |
503 | |
504 // draw directly into the result with the stencil set to make the pi
xels affected | |
505 // by the clip shape be non-zero. | |
506 static constexpr GrUserStencilSettings kStencilInElement( | |
507 GrUserStencilSettings::StaticInit< | |
508 0xffff, | |
509 GrUserStencilTest::kAlways, | |
510 0xffff, | |
511 GrUserStencilOp::kReplace, | |
512 GrUserStencilOp::kReplace, | |
513 0xffff>() | |
514 ); | |
515 if (!stencil_element(dc.get(), clip, &kStencilInElement, | |
516 translate, element)) { | |
517 return nullptr; | |
518 } | |
519 | |
520 // Draw to the exterior pixels (those with a zero stencil value). | |
521 static constexpr GrUserStencilSettings kDrawOutsideElement( | |
522 GrUserStencilSettings::StaticInit< | |
523 0x0000, | |
524 GrUserStencilTest::kEqual, | |
525 0xffff, | |
526 GrUserStencilOp::kZero, | |
527 GrUserStencilOp::kZero, | |
528 0xffff>() | |
529 ); | |
530 if (!dc->drawContextPriv().drawAndStencilRect(clip, &kDrawOutsideEle
ment, | |
531 op, !invert, false, | |
532 translate, | |
533 SkRect::Make(clipSpace
IBounds))) { | |
534 return nullptr; | |
535 } | |
536 } else { | |
537 // all the remaining ops can just be directly draw into the accumula
tion buffer | |
538 GrPaint paint; | |
539 paint.setAntiAlias(element->isAA()); | |
540 paint.setCoverageSetOpXPFactory(op, false); | |
541 | |
542 draw_element(dc.get(), GrNoClip(), paint, translate, element); | |
543 } | |
544 } | |
545 | |
546 sk_sp<GrTexture> texture(dc->asTexture()); | |
547 SkASSERT(texture); | |
548 texture->resourcePriv().setUniqueKey(key); | |
549 return texture; | |
550 } | |
551 | |
552 //////////////////////////////////////////////////////////////////////////////// | |
553 // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device | |
554 // (as opposed to canvas) coordinates | |
555 bool GrClipMaskManager::CreateStencilClipMask(GrContext* context, | |
556 GrDrawContext* drawContext, | |
557 int32_t elementsGenID, | |
558 GrReducedClip::InitialState initia
lState, | |
559 const GrReducedClip::ElementList&
elements, | |
560 const SkIRect& clipSpaceIBounds, | |
561 const SkIPoint& clipSpaceToStencil
Offset) { | |
562 SkASSERT(drawContext); | |
563 | |
564 GrStencilAttachment* stencilAttachment = context->resourceProvider()->attach
StencilAttachment( | |
565 drawContext->accessRenderTar
get()); | |
566 if (nullptr == stencilAttachment) { | |
567 return false; | |
568 } | |
569 | |
570 // TODO: these need to be swapped over to using a StencilAttachmentProxy | |
571 if (stencilAttachment->mustRenderClip(elementsGenID, clipSpaceIBounds, clipS
paceToStencilOffset)) { | |
572 stencilAttachment->setLastClip(elementsGenID, clipSpaceIBounds, clipSpac
eToStencilOffset); | |
573 // Set the matrix so that rendered clip elements are transformed from cl
ip to stencil space. | |
574 SkVector translate = { | |
575 SkIntToScalar(clipSpaceToStencilOffset.fX), | |
576 SkIntToScalar(clipSpaceToStencilOffset.fY) | |
577 }; | |
578 SkMatrix viewMatrix; | |
579 viewMatrix.setTranslate(translate); | |
580 | |
581 // We set the current clip to the bounds so that our recursive draws are
scissored to them. | |
582 SkIRect stencilSpaceIBounds(clipSpaceIBounds); | |
583 stencilSpaceIBounds.offset(clipSpaceToStencilOffset); | |
584 GrFixedClip clip(stencilSpaceIBounds); | |
585 | |
586 drawContext->drawContextPriv().clearStencilClip( | |
587 stencilSpaceIBounds, | |
588 GrReducedClip::kAllIn_InitialState =
= initialState); | |
589 | |
590 // walk through each clip element and perform its set op | |
591 // with the existing clip. | |
592 for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.ge
t(); iter.next()) { | |
593 const Element* element = iter.get(); | |
594 bool useHWAA = element->isAA() && drawContext->isStencilBufferMultis
ampled(); | |
595 | |
596 bool fillInverted = false; | |
597 // enabled at bottom of loop | |
598 clip.disableStencilClip(); | |
599 | |
600 // This will be used to determine whether the clip shape can be rend
ered into the | |
601 // stencil with arbitrary stencil settings. | |
602 GrPathRenderer::StencilSupport stencilSupport; | |
603 | |
604 SkRegion::Op op = element->getOp(); | |
605 | |
606 GrPathRenderer* pr = nullptr; | |
607 SkPath clipPath; | |
608 if (Element::kRect_Type == element->getType()) { | |
609 stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; | |
610 fillInverted = false; | |
611 } else { | |
612 element->asPath(&clipPath); | |
613 fillInverted = clipPath.isInverseFillType(); | |
614 if (fillInverted) { | |
615 clipPath.toggleInverseFillType(); | |
616 } | |
617 | |
618 GrShape shape(clipPath, GrStyle::SimpleFill()); | |
619 GrPathRenderer::CanDrawPathArgs canDrawArgs; | |
620 canDrawArgs.fShaderCaps = context->caps()->shaderCaps(); | |
621 canDrawArgs.fViewMatrix = &viewMatrix; | |
622 canDrawArgs.fShape = &shape; | |
623 canDrawArgs.fAntiAlias = false; | |
624 canDrawArgs.fHasUserStencilSettings = false; | |
625 canDrawArgs.fIsStencilBufferMSAA = drawContext->isStencilBufferM
ultisampled(); | |
626 | |
627 pr = context->drawingManager()->getPathRenderer(canDrawArgs, fal
se, | |
628 GrPathRendererCh
ain::kStencilOnly_DrawType, | |
629 &stencilSupport)
; | |
630 if (!pr) { | |
631 return false; | |
632 } | |
633 } | |
634 | |
635 bool canRenderDirectToStencil = | |
636 GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport; | |
637 bool drawDirectToClip; // Given the renderer, the element, | |
638 // fill rule, and set operation should | |
639 // we render the element directly to | |
640 // stencil bit used for clipping. | |
641 GrUserStencilSettings const* const* stencilPasses = | |
642 GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, f
illInverted, | |
643 &drawDirectToClip); | |
644 | |
645 // draw the element to the client stencil bits if necessary | |
646 if (!drawDirectToClip) { | |
647 static constexpr GrUserStencilSettings kDrawToStencil( | |
648 GrUserStencilSettings::StaticInit< | |
649 0x0000, | |
650 GrUserStencilTest::kAlways, | |
651 0xffff, | |
652 GrUserStencilOp::kIncMaybeClamp, | |
653 GrUserStencilOp::kIncMaybeClamp, | |
654 0xffff>() | |
655 ); | |
656 if (Element::kRect_Type == element->getType()) { | |
657 drawContext->drawContextPriv().stencilRect(clip, &kDrawToSte
ncil, useHWAA, | |
658 viewMatrix, eleme
nt->getRect()); | |
659 } else { | |
660 if (!clipPath.isEmpty()) { | |
661 GrShape shape(clipPath, GrStyle::SimpleFill()); | |
662 if (canRenderDirectToStencil) { | |
663 GrPaint paint; | |
664 paint.setXPFactory(GrDisableColorXPFactory::Make()); | |
665 paint.setAntiAlias(element->isAA()); | |
666 | |
667 GrPathRenderer::DrawPathArgs args; | |
668 args.fResourceProvider = context->resourceProvider()
; | |
669 args.fPaint = &paint; | |
670 args.fUserStencilSettings = &kDrawToStencil; | |
671 args.fDrawContext = drawContext; | |
672 args.fClip = &clip; | |
673 args.fViewMatrix = &viewMatrix; | |
674 args.fShape = &shape; | |
675 args.fAntiAlias = false; | |
676 args.fGammaCorrect = false; | |
677 pr->drawPath(args); | |
678 } else { | |
679 GrPathRenderer::StencilPathArgs args; | |
680 args.fResourceProvider = context->resourceProvider()
; | |
681 args.fDrawContext = drawContext; | |
682 args.fClip = &clip; | |
683 args.fViewMatrix = &viewMatrix; | |
684 args.fIsAA = element->isAA(); | |
685 args.fShape = &shape; | |
686 pr->stencilPath(args); | |
687 } | |
688 } | |
689 } | |
690 } | |
691 | |
692 // now we modify the clip bit by rendering either the clip | |
693 // element directly or a bounding rect of the entire clip. | |
694 for (GrUserStencilSettings const* const* pass = stencilPasses; *pass
; ++pass) { | |
695 | |
696 if (drawDirectToClip) { | |
697 if (Element::kRect_Type == element->getType()) { | |
698 clip.enableStencilClip(element->getRect().makeOffset(tra
nslate.fX, | |
699 tra
nslate.fY)); | |
700 drawContext->drawContextPriv().stencilRect(clip, *pass,
useHWAA, viewMatrix, | |
701 element->getR
ect()); | |
702 } else { | |
703 GrShape shape(clipPath, GrStyle::SimpleFill()); | |
704 GrPaint paint; | |
705 SkRect bounds = clipPath.getBounds(); | |
706 bounds.offset(translate.fX, translate.fY); | |
707 clip.enableStencilClip(bounds); | |
708 paint.setXPFactory(GrDisableColorXPFactory::Make()); | |
709 paint.setAntiAlias(element->isAA()); | |
710 GrPathRenderer::DrawPathArgs args; | |
711 args.fResourceProvider = context->resourceProvider(); | |
712 args.fPaint = &paint; | |
713 args.fUserStencilSettings = *pass; | |
714 args.fDrawContext = drawContext; | |
715 args.fClip = &clip; | |
716 args.fViewMatrix = &viewMatrix; | |
717 args.fShape = &shape; | |
718 args.fAntiAlias = false; | |
719 args.fGammaCorrect = false; | |
720 pr->drawPath(args); | |
721 } | |
722 } else { | |
723 // The view matrix is setup to do clip space -> stencil spac
e translation, so | |
724 // draw rect in clip space. | |
725 SkRect bounds = SkRect::Make(clipSpaceIBounds); | |
726 bounds.offset(translate.fX, translate.fY); | |
727 clip.enableStencilClip(bounds); | |
728 drawContext->drawContextPriv().stencilRect(clip, *pass, fals
e, viewMatrix, | |
729 SkRect::Make(clip
SpaceIBounds)); | |
730 } | |
731 } | |
732 } | |
733 } | |
734 return true; | |
735 } | |
736 | |
737 //////////////////////////////////////////////////////////////////////////////// | |
738 sk_sp<GrTexture> GrClipMaskManager::CreateSoftwareClipMask( | |
739 GrTextureProvider* texProvid
er, | |
740 int32_t elementsGenID, | |
741 GrReducedClip::InitialState
initialState, | |
742 const GrReducedClip::Element
List& elements, | |
743 const SkVector& clipToMaskOf
fset, | |
744 const SkIRect& clipSpaceIBou
nds) { | |
745 GrUniqueKey key; | |
746 GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key); | |
747 if (GrTexture* texture = texProvider->findAndRefTextureByUniqueKey(key)) { | |
748 return sk_sp<GrTexture>(texture); | |
749 } | |
750 | |
751 // The mask texture may be larger than necessary. We round out the clip spac
e bounds and pin | |
752 // the top left corner of the resulting rect to the top left of the texture. | |
753 SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpa
ceIBounds.height()); | |
754 | |
755 GrSWMaskHelper helper(texProvider); | |
756 | |
757 // Set the matrix so that rendered clip elements are transformed to mask spa
ce from clip | |
758 // space. | |
759 SkMatrix translate; | |
760 translate.setTranslate(clipToMaskOffset); | |
761 | |
762 helper.init(maskSpaceIBounds, &translate); | |
763 helper.clear(GrReducedClip::kAllIn_InitialState == initialState ? 0xFF : 0x0
0); | |
764 | |
765 for (GrReducedClip::ElementList::Iter iter(elements.headIter()) ; iter.get()
; iter.next()) { | |
766 const Element* element = iter.get(); | |
767 SkRegion::Op op = element->getOp(); | |
768 | |
769 if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op ==
op) { | |
770 // Intersect and reverse difference require modifying pixels outside
of the geometry | |
771 // that is being "drawn". In both cases we erase all the pixels outs
ide of the geometry | |
772 // but leave the pixels inside the geometry alone. For reverse diffe
rence we invert all | |
773 // the pixels before clearing the ones outside the geometry. | |
774 if (SkRegion::kReverseDifference_Op == op) { | |
775 SkRect temp = SkRect::Make(clipSpaceIBounds); | |
776 // invert the entire scene | |
777 helper.drawRect(temp, SkRegion::kXOR_Op, false, 0xFF); | |
778 } | |
779 SkPath clipPath; | |
780 element->asPath(&clipPath); | |
781 clipPath.toggleInverseFillType(); | |
782 GrShape shape(clipPath, GrStyle::SimpleFill()); | |
783 helper.drawShape(shape, SkRegion::kReplace_Op, element->isAA(), 0x00
); | |
784 continue; | |
785 } | |
786 | |
787 // The other ops (union, xor, diff) only affect pixels inside | |
788 // the geometry so they can just be drawn normally | |
789 if (Element::kRect_Type == element->getType()) { | |
790 helper.drawRect(element->getRect(), op, element->isAA(), 0xFF); | |
791 } else { | |
792 SkPath path; | |
793 element->asPath(&path); | |
794 GrShape shape(path, GrStyle::SimpleFill()); | |
795 helper.drawShape(shape, op, element->isAA(), 0xFF); | |
796 } | |
797 } | |
798 | |
799 // Allocate clip mask texture | |
800 GrSurfaceDesc desc; | |
801 desc.fWidth = clipSpaceIBounds.width(); | |
802 desc.fHeight = clipSpaceIBounds.height(); | |
803 desc.fConfig = kAlpha_8_GrPixelConfig; | |
804 | |
805 sk_sp<GrTexture> result(texProvider->createApproxTexture(desc)); | |
806 if (!result) { | |
807 return nullptr; | |
808 } | |
809 result->resourcePriv().setUniqueKey(key); | |
810 | |
811 helper.toTexture(result.get()); | |
812 | |
813 return result; | |
814 } | |
OLD | NEW |