1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/gpu/GrClipMaskManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1153 @@ 1.4 + 1.5 +/* 1.6 + * Copyright 2012 Google Inc. 1.7 + * 1.8 + * Use of this source code is governed by a BSD-style license that can be 1.9 + * found in the LICENSE file. 1.10 + */ 1.11 + 1.12 +#include "GrClipMaskManager.h" 1.13 +#include "GrAAConvexPathRenderer.h" 1.14 +#include "GrAAHairLinePathRenderer.h" 1.15 +#include "GrAARectRenderer.h" 1.16 +#include "GrDrawTargetCaps.h" 1.17 +#include "GrGpu.h" 1.18 +#include "GrPaint.h" 1.19 +#include "GrPathRenderer.h" 1.20 +#include "GrRenderTarget.h" 1.21 +#include "GrStencilBuffer.h" 1.22 +#include "GrSWMaskHelper.h" 1.23 +#include "effects/GrTextureDomain.h" 1.24 +#include "effects/GrConvexPolyEffect.h" 1.25 +#include "effects/GrRRectEffect.h" 1.26 +#include "SkRasterClip.h" 1.27 +#include "SkStrokeRec.h" 1.28 +#include "SkTLazy.h" 1.29 + 1.30 +#define GR_AA_CLIP 1 1.31 + 1.32 +typedef SkClipStack::Element Element; 1.33 + 1.34 +using namespace GrReducedClip; 1.35 + 1.36 +//////////////////////////////////////////////////////////////////////////////// 1.37 +namespace { 1.38 +// set up the draw state to enable the aa clipping mask. Besides setting up the 1.39 +// stage matrix this also alters the vertex layout 1.40 +void setup_drawstate_aaclip(GrGpu* gpu, 1.41 + GrTexture* result, 1.42 + const SkIRect &devBound) { 1.43 + GrDrawState* drawState = gpu->drawState(); 1.44 + SkASSERT(drawState); 1.45 + 1.46 + SkMatrix mat; 1.47 + // We want to use device coords to compute the texture coordinates. We set our matrix to be 1.48 + // equal to the view matrix followed by an offset to the devBound, and then a scaling matrix to 1.49 + // normalized coords. We apply this matrix to the vertex positions rather than local coords. 1.50 + mat.setIDiv(result->width(), result->height()); 1.51 + mat.preTranslate(SkIntToScalar(-devBound.fLeft), 1.52 + SkIntToScalar(-devBound.fTop)); 1.53 + mat.preConcat(drawState->getViewMatrix()); 1.54 + 1.55 + SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height()); 1.56 + // This could be a long-lived effect that is cached with the alpha-mask. 1.57 + drawState->addCoverageEffect( 1.58 + GrTextureDomainEffect::Create(result, 1.59 + mat, 1.60 + GrTextureDomain::MakeTexelDomain(result, domainTexels), 1.61 + GrTextureDomain::kDecal_Mode, 1.62 + GrTextureParams::kNone_FilterMode, 1.63 + kPosition_GrCoordSet))->unref(); 1.64 +} 1.65 + 1.66 +bool path_needs_SW_renderer(GrContext* context, 1.67 + GrGpu* gpu, 1.68 + const SkPath& origPath, 1.69 + const SkStrokeRec& stroke, 1.70 + bool doAA) { 1.71 + // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer 1.72 + SkTCopyOnFirstWrite<SkPath> path(origPath); 1.73 + if (path->isInverseFillType()) { 1.74 + path.writable()->toggleInverseFillType(); 1.75 + } 1.76 + // last (false) parameter disallows use of the SW path renderer 1.77 + GrPathRendererChain::DrawType type = doAA ? 1.78 + GrPathRendererChain::kColorAntiAlias_DrawType : 1.79 + GrPathRendererChain::kColor_DrawType; 1.80 + 1.81 + return NULL == context->getPathRenderer(*path, stroke, gpu, false, type); 1.82 +} 1.83 + 1.84 +} 1.85 + 1.86 +/* 1.87 + * This method traverses the clip stack to see if the GrSoftwarePathRenderer 1.88 + * will be used on any element. If so, it returns true to indicate that the 1.89 + * entire clip should be rendered in SW and then uploaded en masse to the gpu. 1.90 + */ 1.91 +bool GrClipMaskManager::useSWOnlyPath(const ElementList& elements) { 1.92 + 1.93 + // TODO: generalize this function so that when 1.94 + // a clip gets complex enough it can just be done in SW regardless 1.95 + // of whether it would invoke the GrSoftwarePathRenderer. 1.96 + SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 1.97 + 1.98 + for (ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) { 1.99 + const Element* element = iter.get(); 1.100 + // rects can always be drawn directly w/o using the software path 1.101 + // Skip rrects once we're drawing them directly. 1.102 + if (Element::kRect_Type != element->getType()) { 1.103 + SkPath path; 1.104 + element->asPath(&path); 1.105 + if (path_needs_SW_renderer(this->getContext(), fGpu, path, stroke, element->isAA())) { 1.106 + return true; 1.107 + } 1.108 + } 1.109 + } 1.110 + return false; 1.111 +} 1.112 + 1.113 +bool GrClipMaskManager::installClipEffects(const ElementList& elements, 1.114 + GrDrawState::AutoRestoreEffects* are, 1.115 + const SkVector& clipToRTOffset, 1.116 + const SkRect* drawBounds) { 1.117 + 1.118 + GrDrawState* drawState = fGpu->drawState(); 1.119 + SkRect boundsInClipSpace; 1.120 + if (NULL != drawBounds) { 1.121 + boundsInClipSpace = *drawBounds; 1.122 + boundsInClipSpace.offset(-clipToRTOffset.fX, -clipToRTOffset.fY); 1.123 + } 1.124 + 1.125 + are->set(drawState); 1.126 + GrRenderTarget* rt = drawState->getRenderTarget(); 1.127 + ElementList::Iter iter(elements); 1.128 + 1.129 + bool setARE = false; 1.130 + bool failed = false; 1.131 + 1.132 + while (NULL != iter.get()) { 1.133 + SkRegion::Op op = iter.get()->getOp(); 1.134 + bool invert; 1.135 + bool skip = false; 1.136 + switch (op) { 1.137 + case SkRegion::kReplace_Op: 1.138 + SkASSERT(iter.get() == elements.head()); 1.139 + // Fallthrough, handled same as intersect. 1.140 + case SkRegion::kIntersect_Op: 1.141 + invert = false; 1.142 + if (NULL != drawBounds && iter.get()->contains(boundsInClipSpace)) { 1.143 + skip = true; 1.144 + } 1.145 + break; 1.146 + case SkRegion::kDifference_Op: 1.147 + invert = true; 1.148 + // We don't currently have a cheap test for whether a rect is fully outside an 1.149 + // element's primitive, so don't attempt to set skip. 1.150 + break; 1.151 + default: 1.152 + failed = true; 1.153 + break; 1.154 + } 1.155 + if (failed) { 1.156 + break; 1.157 + } 1.158 + 1.159 + if (!skip) { 1.160 + GrEffectEdgeType edgeType; 1.161 + if (GR_AA_CLIP && iter.get()->isAA()) { 1.162 + if (rt->isMultisampled()) { 1.163 + // Coverage based AA clips don't place nicely with MSAA. 1.164 + failed = true; 1.165 + break; 1.166 + } 1.167 + edgeType = invert ? kInverseFillAA_GrEffectEdgeType : kFillAA_GrEffectEdgeType; 1.168 + } else { 1.169 + edgeType = invert ? kInverseFillBW_GrEffectEdgeType : kFillBW_GrEffectEdgeType; 1.170 + } 1.171 + SkAutoTUnref<GrEffectRef> effect; 1.172 + switch (iter.get()->getType()) { 1.173 + case SkClipStack::Element::kPath_Type: 1.174 + effect.reset(GrConvexPolyEffect::Create(edgeType, iter.get()->getPath(), 1.175 + &clipToRTOffset)); 1.176 + break; 1.177 + case SkClipStack::Element::kRRect_Type: { 1.178 + SkRRect rrect = iter.get()->getRRect(); 1.179 + rrect.offset(clipToRTOffset.fX, clipToRTOffset.fY); 1.180 + effect.reset(GrRRectEffect::Create(edgeType, rrect)); 1.181 + break; 1.182 + } 1.183 + case SkClipStack::Element::kRect_Type: { 1.184 + SkRect rect = iter.get()->getRect(); 1.185 + rect.offset(clipToRTOffset.fX, clipToRTOffset.fY); 1.186 + effect.reset(GrConvexPolyEffect::Create(edgeType, rect)); 1.187 + break; 1.188 + } 1.189 + default: 1.190 + break; 1.191 + } 1.192 + if (effect) { 1.193 + if (!setARE) { 1.194 + are->set(fGpu->drawState()); 1.195 + setARE = true; 1.196 + } 1.197 + fGpu->drawState()->addCoverageEffect(effect); 1.198 + } else { 1.199 + failed = true; 1.200 + break; 1.201 + } 1.202 + } 1.203 + iter.next(); 1.204 + } 1.205 + 1.206 + if (failed) { 1.207 + are->set(NULL); 1.208 + } 1.209 + 1.210 + return !failed; 1.211 +} 1.212 + 1.213 +//////////////////////////////////////////////////////////////////////////////// 1.214 +// sort out what kind of clip mask needs to be created: alpha, stencil, 1.215 +// scissor, or entirely software 1.216 +bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn, 1.217 + GrDrawState::AutoRestoreEffects* are, 1.218 + const SkRect* devBounds) { 1.219 + fCurrClipMaskType = kNone_ClipMaskType; 1.220 + 1.221 + ElementList elements(16); 1.222 + int32_t genID; 1.223 + InitialState initialState; 1.224 + SkIRect clipSpaceIBounds; 1.225 + bool requiresAA; 1.226 + bool isRect = false; 1.227 + 1.228 + GrDrawState* drawState = fGpu->drawState(); 1.229 + 1.230 + const GrRenderTarget* rt = drawState->getRenderTarget(); 1.231 + // GrDrawTarget should have filtered this for us 1.232 + SkASSERT(NULL != rt); 1.233 + 1.234 + bool ignoreClip = !drawState->isClipState() || clipDataIn->fClipStack->isWideOpen(); 1.235 + 1.236 + if (!ignoreClip) { 1.237 + SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height()); 1.238 + clipSpaceRTIBounds.offset(clipDataIn->fOrigin); 1.239 + ReduceClipStack(*clipDataIn->fClipStack, 1.240 + clipSpaceRTIBounds, 1.241 + &elements, 1.242 + &genID, 1.243 + &initialState, 1.244 + &clipSpaceIBounds, 1.245 + &requiresAA); 1.246 + if (elements.isEmpty()) { 1.247 + if (kAllIn_InitialState == initialState) { 1.248 + ignoreClip = clipSpaceIBounds == clipSpaceRTIBounds; 1.249 + isRect = true; 1.250 + } else { 1.251 + return false; 1.252 + } 1.253 + } 1.254 + } 1.255 + 1.256 + if (ignoreClip) { 1.257 + fGpu->disableScissor(); 1.258 + this->setGpuStencil(); 1.259 + return true; 1.260 + } 1.261 + 1.262 + // An element count of 4 was chosen because of the common pattern in Blink of: 1.263 + // isect RR 1.264 + // diff RR 1.265 + // isect convex_poly 1.266 + // isect convex_poly 1.267 + // when drawing rounded div borders. This could probably be tuned based on a 1.268 + // configuration's relative costs of switching RTs to generate a mask vs 1.269 + // longer shaders. 1.270 + if (elements.count() <= 4) { 1.271 + SkVector clipToRTOffset = { SkIntToScalar(-clipDataIn->fOrigin.fX), 1.272 + SkIntToScalar(-clipDataIn->fOrigin.fY) }; 1.273 + if (elements.isEmpty() || 1.274 + this->installClipEffects(elements, are, clipToRTOffset, devBounds)) { 1.275 + SkIRect scissorSpaceIBounds(clipSpaceIBounds); 1.276 + scissorSpaceIBounds.offset(-clipDataIn->fOrigin); 1.277 + if (NULL == devBounds || 1.278 + !SkRect::Make(scissorSpaceIBounds).contains(*devBounds)) { 1.279 + fGpu->enableScissor(scissorSpaceIBounds); 1.280 + } else { 1.281 + fGpu->disableScissor(); 1.282 + } 1.283 + this->setGpuStencil(); 1.284 + return true; 1.285 + } 1.286 + } 1.287 + 1.288 +#if GR_AA_CLIP 1.289 + // If MSAA is enabled we can do everything in the stencil buffer. 1.290 + if (0 == rt->numSamples() && requiresAA) { 1.291 + GrTexture* result = NULL; 1.292 + 1.293 + if (this->useSWOnlyPath(elements)) { 1.294 + // The clip geometry is complex enough that it will be more efficient to create it 1.295 + // entirely in software 1.296 + result = this->createSoftwareClipMask(genID, 1.297 + initialState, 1.298 + elements, 1.299 + clipSpaceIBounds); 1.300 + } else { 1.301 + result = this->createAlphaClipMask(genID, 1.302 + initialState, 1.303 + elements, 1.304 + clipSpaceIBounds); 1.305 + } 1.306 + 1.307 + if (NULL != result) { 1.308 + // The mask's top left coord should be pinned to the rounded-out top left corner of 1.309 + // clipSpace bounds. We determine the mask's position WRT to the render target here. 1.310 + SkIRect rtSpaceMaskBounds = clipSpaceIBounds; 1.311 + rtSpaceMaskBounds.offset(-clipDataIn->fOrigin); 1.312 + are->set(fGpu->drawState()); 1.313 + setup_drawstate_aaclip(fGpu, result, rtSpaceMaskBounds); 1.314 + fGpu->disableScissor(); 1.315 + this->setGpuStencil(); 1.316 + return true; 1.317 + } 1.318 + // if alpha clip mask creation fails fall through to the non-AA code paths 1.319 + } 1.320 +#endif // GR_AA_CLIP 1.321 + 1.322 + // Either a hard (stencil buffer) clip was explicitly requested or an anti-aliased clip couldn't 1.323 + // be created. In either case, free up the texture in the anti-aliased mask cache. 1.324 + // TODO: this may require more investigation. Ganesh performs a lot of utility draws (e.g., 1.325 + // clears, InOrderDrawBuffer playbacks) that hit the stencil buffer path. These may be 1.326 + // "incorrectly" clearing the AA cache. 1.327 + fAACache.reset(); 1.328 + 1.329 + // If the clip is a rectangle then just set the scissor. Otherwise, create 1.330 + // a stencil mask. 1.331 + if (isRect) { 1.332 + SkIRect clipRect = clipSpaceIBounds; 1.333 + clipRect.offset(-clipDataIn->fOrigin); 1.334 + fGpu->enableScissor(clipRect); 1.335 + this->setGpuStencil(); 1.336 + return true; 1.337 + } 1.338 + 1.339 + // use the stencil clip if we can't represent the clip as a rectangle. 1.340 + SkIPoint clipSpaceToStencilSpaceOffset = -clipDataIn->fOrigin; 1.341 + this->createStencilClipMask(genID, 1.342 + initialState, 1.343 + elements, 1.344 + clipSpaceIBounds, 1.345 + clipSpaceToStencilSpaceOffset); 1.346 + 1.347 + // This must occur after createStencilClipMask. That function may change the scissor. Also, it 1.348 + // only guarantees that the stencil mask is correct within the bounds it was passed, so we must 1.349 + // use both stencil and scissor test to the bounds for the final draw. 1.350 + SkIRect scissorSpaceIBounds(clipSpaceIBounds); 1.351 + scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset); 1.352 + fGpu->enableScissor(scissorSpaceIBounds); 1.353 + this->setGpuStencil(); 1.354 + return true; 1.355 +} 1.356 + 1.357 +#define VISUALIZE_COMPLEX_CLIP 0 1.358 + 1.359 +#if VISUALIZE_COMPLEX_CLIP 1.360 + #include "SkRandom.h" 1.361 + SkRandom gRandom; 1.362 + #define SET_RANDOM_COLOR drawState->setColor(0xff000000 | gRandom.nextU()); 1.363 +#else 1.364 + #define SET_RANDOM_COLOR 1.365 +#endif 1.366 + 1.367 +namespace { 1.368 + 1.369 +//////////////////////////////////////////////////////////////////////////////// 1.370 +// set up the OpenGL blend function to perform the specified 1.371 +// boolean operation for alpha clip mask creation 1.372 +void setup_boolean_blendcoeffs(GrDrawState* drawState, SkRegion::Op op) { 1.373 + 1.374 + switch (op) { 1.375 + case SkRegion::kReplace_Op: 1.376 + drawState->setBlendFunc(kOne_GrBlendCoeff, kZero_GrBlendCoeff); 1.377 + break; 1.378 + case SkRegion::kIntersect_Op: 1.379 + drawState->setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff); 1.380 + break; 1.381 + case SkRegion::kUnion_Op: 1.382 + drawState->setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff); 1.383 + break; 1.384 + case SkRegion::kXOR_Op: 1.385 + drawState->setBlendFunc(kIDC_GrBlendCoeff, kISC_GrBlendCoeff); 1.386 + break; 1.387 + case SkRegion::kDifference_Op: 1.388 + drawState->setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff); 1.389 + break; 1.390 + case SkRegion::kReverseDifference_Op: 1.391 + drawState->setBlendFunc(kIDC_GrBlendCoeff, kZero_GrBlendCoeff); 1.392 + break; 1.393 + default: 1.394 + SkASSERT(false); 1.395 + break; 1.396 + } 1.397 +} 1.398 + 1.399 +} 1.400 + 1.401 +//////////////////////////////////////////////////////////////////////////////// 1.402 +bool GrClipMaskManager::drawElement(GrTexture* target, 1.403 + const SkClipStack::Element* element, 1.404 + GrPathRenderer* pr) { 1.405 + GrDrawState* drawState = fGpu->drawState(); 1.406 + 1.407 + drawState->setRenderTarget(target->asRenderTarget()); 1.408 + 1.409 + // TODO: Draw rrects directly here. 1.410 + switch (element->getType()) { 1.411 + case Element::kEmpty_Type: 1.412 + SkDEBUGFAIL("Should never get here with an empty element."); 1.413 + break; 1.414 + case Element::kRect_Type: 1.415 + // TODO: Do rects directly to the accumulator using a aa-rect GrEffect that covers the 1.416 + // entire mask bounds and writes 0 outside the rect. 1.417 + if (element->isAA()) { 1.418 + getContext()->getAARectRenderer()->fillAARect(fGpu, 1.419 + fGpu, 1.420 + element->getRect(), 1.421 + SkMatrix::I(), 1.422 + element->getRect(), 1.423 + false); 1.424 + } else { 1.425 + fGpu->drawSimpleRect(element->getRect(), NULL); 1.426 + } 1.427 + return true; 1.428 + default: { 1.429 + SkPath path; 1.430 + element->asPath(&path); 1.431 + if (path.isInverseFillType()) { 1.432 + path.toggleInverseFillType(); 1.433 + } 1.434 + SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 1.435 + if (NULL == pr) { 1.436 + GrPathRendererChain::DrawType type; 1.437 + type = element->isAA() ? GrPathRendererChain::kColorAntiAlias_DrawType : 1.438 + GrPathRendererChain::kColor_DrawType; 1.439 + pr = this->getContext()->getPathRenderer(path, stroke, fGpu, false, type); 1.440 + } 1.441 + if (NULL == pr) { 1.442 + return false; 1.443 + } 1.444 + pr->drawPath(path, stroke, fGpu, element->isAA()); 1.445 + break; 1.446 + } 1.447 + } 1.448 + return true; 1.449 +} 1.450 + 1.451 +bool GrClipMaskManager::canStencilAndDrawElement(GrTexture* target, 1.452 + const SkClipStack::Element* element, 1.453 + GrPathRenderer** pr) { 1.454 + GrDrawState* drawState = fGpu->drawState(); 1.455 + drawState->setRenderTarget(target->asRenderTarget()); 1.456 + 1.457 + if (Element::kRect_Type == element->getType()) { 1.458 + return true; 1.459 + } else { 1.460 + // We shouldn't get here with an empty clip element. 1.461 + SkASSERT(Element::kEmpty_Type != element->getType()); 1.462 + SkPath path; 1.463 + element->asPath(&path); 1.464 + if (path.isInverseFillType()) { 1.465 + path.toggleInverseFillType(); 1.466 + } 1.467 + SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 1.468 + GrPathRendererChain::DrawType type = element->isAA() ? 1.469 + GrPathRendererChain::kStencilAndColorAntiAlias_DrawType : 1.470 + GrPathRendererChain::kStencilAndColor_DrawType; 1.471 + *pr = this->getContext()->getPathRenderer(path, stroke, fGpu, false, type); 1.472 + return NULL != *pr; 1.473 + } 1.474 +} 1.475 + 1.476 +void GrClipMaskManager::mergeMask(GrTexture* dstMask, 1.477 + GrTexture* srcMask, 1.478 + SkRegion::Op op, 1.479 + const SkIRect& dstBound, 1.480 + const SkIRect& srcBound) { 1.481 + GrDrawState::AutoViewMatrixRestore avmr; 1.482 + GrDrawState* drawState = fGpu->drawState(); 1.483 + SkAssertResult(avmr.setIdentity(drawState)); 1.484 + GrDrawState::AutoRestoreEffects are(drawState); 1.485 + 1.486 + drawState->setRenderTarget(dstMask->asRenderTarget()); 1.487 + 1.488 + setup_boolean_blendcoeffs(drawState, op); 1.489 + 1.490 + SkMatrix sampleM; 1.491 + sampleM.setIDiv(srcMask->width(), srcMask->height()); 1.492 + 1.493 + drawState->addColorEffect( 1.494 + GrTextureDomainEffect::Create(srcMask, 1.495 + sampleM, 1.496 + GrTextureDomain::MakeTexelDomain(srcMask, srcBound), 1.497 + GrTextureDomain::kDecal_Mode, 1.498 + GrTextureParams::kNone_FilterMode))->unref(); 1.499 + fGpu->drawSimpleRect(SkRect::Make(dstBound), NULL); 1.500 +} 1.501 + 1.502 +// get a texture to act as a temporary buffer for AA clip boolean operations 1.503 +// TODO: given the expense of createTexture we may want to just cache this too 1.504 +void GrClipMaskManager::getTemp(int width, int height, GrAutoScratchTexture* temp) { 1.505 + if (NULL != temp->texture()) { 1.506 + // we've already allocated the temp texture 1.507 + return; 1.508 + } 1.509 + 1.510 + GrTextureDesc desc; 1.511 + desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit; 1.512 + desc.fWidth = width; 1.513 + desc.fHeight = height; 1.514 + desc.fConfig = kAlpha_8_GrPixelConfig; 1.515 + 1.516 + temp->set(this->getContext(), desc); 1.517 +} 1.518 + 1.519 +//////////////////////////////////////////////////////////////////////////////// 1.520 +// Handles caching & allocation (if needed) of a clip alpha-mask texture for both the sw-upload 1.521 +// or gpu-rendered cases. Returns true if there is no more work to be done (i.e., we got a cache 1.522 +// hit) 1.523 +bool GrClipMaskManager::getMaskTexture(int32_t elementsGenID, 1.524 + const SkIRect& clipSpaceIBounds, 1.525 + GrTexture** result, 1.526 + bool willUpload) { 1.527 + bool cached = fAACache.canReuse(elementsGenID, clipSpaceIBounds); 1.528 + if (!cached) { 1.529 + 1.530 + // There isn't a suitable entry in the cache so we create a new texture to store the mask. 1.531 + // Since we are setting up the cache we know the last lookup was a miss. Free up the 1.532 + // currently cached mask so it can be reused. 1.533 + fAACache.reset(); 1.534 + 1.535 + GrTextureDesc desc; 1.536 + desc.fFlags = willUpload ? kNone_GrTextureFlags : kRenderTarget_GrTextureFlagBit; 1.537 + desc.fWidth = clipSpaceIBounds.width(); 1.538 + desc.fHeight = clipSpaceIBounds.height(); 1.539 + desc.fConfig = kRGBA_8888_GrPixelConfig; 1.540 + if (willUpload || this->getContext()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { 1.541 + // We would always like A8 but it isn't supported on all platforms 1.542 + desc.fConfig = kAlpha_8_GrPixelConfig; 1.543 + } 1.544 + 1.545 + fAACache.acquireMask(elementsGenID, desc, clipSpaceIBounds); 1.546 + } 1.547 + 1.548 + *result = fAACache.getLastMask(); 1.549 + return cached; 1.550 +} 1.551 + 1.552 +//////////////////////////////////////////////////////////////////////////////// 1.553 +// Create a 8-bit clip mask in alpha 1.554 +GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID, 1.555 + InitialState initialState, 1.556 + const ElementList& elements, 1.557 + const SkIRect& clipSpaceIBounds) { 1.558 + SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); 1.559 + 1.560 + GrTexture* result; 1.561 + if (this->getMaskTexture(elementsGenID, clipSpaceIBounds, &result, false)) { 1.562 + fCurrClipMaskType = kAlpha_ClipMaskType; 1.563 + return result; 1.564 + } 1.565 + 1.566 + if (NULL == result) { 1.567 + fAACache.reset(); 1.568 + return NULL; 1.569 + } 1.570 + 1.571 + // The top-left of the mask corresponds to the top-left corner of the bounds. 1.572 + SkVector clipToMaskOffset = { 1.573 + SkIntToScalar(-clipSpaceIBounds.fLeft), 1.574 + SkIntToScalar(-clipSpaceIBounds.fTop) 1.575 + }; 1.576 + // The texture may be larger than necessary, this rect represents the part of the texture 1.577 + // we populate with a rasterization of the clip. 1.578 + SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); 1.579 + 1.580 + // Set the matrix so that rendered clip elements are transformed to mask space from clip space. 1.581 + SkMatrix translate; 1.582 + translate.setTranslate(clipToMaskOffset); 1.583 + GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit, &translate); 1.584 + 1.585 + GrDrawState* drawState = fGpu->drawState(); 1.586 + 1.587 + // We're drawing a coverage mask and want coverage to be run through the blend function. 1.588 + drawState->enableState(GrDrawState::kCoverageDrawing_StateBit); 1.589 + 1.590 + // The scratch texture that we are drawing into can be substantially larger than the mask. Only 1.591 + // clear the part that we care about. 1.592 + fGpu->clear(&maskSpaceIBounds, 1.593 + kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000, 1.594 + true, 1.595 + result->asRenderTarget()); 1.596 + 1.597 + // When we use the stencil in the below loop it is important to have this clip installed. 1.598 + // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first 1.599 + // pass must not set values outside of this bounds or stencil values outside the rect won't be 1.600 + // cleared. 1.601 + GrDrawTarget::AutoClipRestore acr(fGpu, maskSpaceIBounds); 1.602 + drawState->enableState(GrDrawState::kClip_StateBit); 1.603 + 1.604 + GrAutoScratchTexture temp; 1.605 + // walk through each clip element and perform its set op 1.606 + for (ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) { 1.607 + const Element* element = iter.get(); 1.608 + SkRegion::Op op = element->getOp(); 1.609 + bool invert = element->isInverseFilled(); 1.610 + 1.611 + if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { 1.612 + GrPathRenderer* pr = NULL; 1.613 + bool useTemp = !this->canStencilAndDrawElement(result, element, &pr); 1.614 + GrTexture* dst; 1.615 + // This is the bounds of the clip element in the space of the alpha-mask. The temporary 1.616 + // mask buffer can be substantially larger than the actually clip stack element. We 1.617 + // touch the minimum number of pixels necessary and use decal mode to combine it with 1.618 + // the accumulator. 1.619 + SkIRect maskSpaceElementIBounds; 1.620 + 1.621 + if (useTemp) { 1.622 + if (invert) { 1.623 + maskSpaceElementIBounds = maskSpaceIBounds; 1.624 + } else { 1.625 + SkRect elementBounds = element->getBounds(); 1.626 + elementBounds.offset(clipToMaskOffset); 1.627 + elementBounds.roundOut(&maskSpaceElementIBounds); 1.628 + } 1.629 + 1.630 + this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp); 1.631 + if (NULL == temp.texture()) { 1.632 + fAACache.reset(); 1.633 + return NULL; 1.634 + } 1.635 + dst = temp.texture(); 1.636 + // clear the temp target and set blend to replace 1.637 + fGpu->clear(&maskSpaceElementIBounds, 1.638 + invert ? 0xffffffff : 0x00000000, 1.639 + true, 1.640 + dst->asRenderTarget()); 1.641 + setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op); 1.642 + 1.643 + } else { 1.644 + // draw directly into the result with the stencil set to make the pixels affected 1.645 + // by the clip shape be non-zero. 1.646 + dst = result; 1.647 + GR_STATIC_CONST_SAME_STENCIL(kStencilInElement, 1.648 + kReplace_StencilOp, 1.649 + kReplace_StencilOp, 1.650 + kAlways_StencilFunc, 1.651 + 0xffff, 1.652 + 0xffff, 1.653 + 0xffff); 1.654 + drawState->setStencil(kStencilInElement); 1.655 + setup_boolean_blendcoeffs(drawState, op); 1.656 + } 1.657 + 1.658 + drawState->setAlpha(invert ? 0x00 : 0xff); 1.659 + 1.660 + if (!this->drawElement(dst, element, pr)) { 1.661 + fAACache.reset(); 1.662 + return NULL; 1.663 + } 1.664 + 1.665 + if (useTemp) { 1.666 + // Now draw into the accumulator using the real operation and the temp buffer as a 1.667 + // texture 1.668 + this->mergeMask(result, 1.669 + temp.texture(), 1.670 + op, 1.671 + maskSpaceIBounds, 1.672 + maskSpaceElementIBounds); 1.673 + } else { 1.674 + // Draw to the exterior pixels (those with a zero stencil value). 1.675 + drawState->setAlpha(invert ? 0xff : 0x00); 1.676 + GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement, 1.677 + kZero_StencilOp, 1.678 + kZero_StencilOp, 1.679 + kEqual_StencilFunc, 1.680 + 0xffff, 1.681 + 0x0000, 1.682 + 0xffff); 1.683 + drawState->setStencil(kDrawOutsideElement); 1.684 + fGpu->drawSimpleRect(clipSpaceIBounds); 1.685 + drawState->disableStencil(); 1.686 + } 1.687 + } else { 1.688 + // all the remaining ops can just be directly draw into the accumulation buffer 1.689 + drawState->setAlpha(0xff); 1.690 + setup_boolean_blendcoeffs(drawState, op); 1.691 + this->drawElement(result, element); 1.692 + } 1.693 + } 1.694 + 1.695 + fCurrClipMaskType = kAlpha_ClipMaskType; 1.696 + return result; 1.697 +} 1.698 + 1.699 +//////////////////////////////////////////////////////////////////////////////// 1.700 +// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device 1.701 +// (as opposed to canvas) coordinates 1.702 +bool GrClipMaskManager::createStencilClipMask(int32_t elementsGenID, 1.703 + InitialState initialState, 1.704 + const ElementList& elements, 1.705 + const SkIRect& clipSpaceIBounds, 1.706 + const SkIPoint& clipSpaceToStencilOffset) { 1.707 + 1.708 + SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); 1.709 + 1.710 + GrDrawState* drawState = fGpu->drawState(); 1.711 + SkASSERT(drawState->isClipState()); 1.712 + 1.713 + GrRenderTarget* rt = drawState->getRenderTarget(); 1.714 + SkASSERT(NULL != rt); 1.715 + 1.716 + // TODO: dynamically attach a SB when needed. 1.717 + GrStencilBuffer* stencilBuffer = rt->getStencilBuffer(); 1.718 + if (NULL == stencilBuffer) { 1.719 + return false; 1.720 + } 1.721 + 1.722 + if (stencilBuffer->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset)) { 1.723 + 1.724 + stencilBuffer->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset); 1.725 + 1.726 + // Set the matrix so that rendered clip elements are transformed from clip to stencil space. 1.727 + SkVector translate = { 1.728 + SkIntToScalar(clipSpaceToStencilOffset.fX), 1.729 + SkIntToScalar(clipSpaceToStencilOffset.fY) 1.730 + }; 1.731 + SkMatrix matrix; 1.732 + matrix.setTranslate(translate); 1.733 + GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit, &matrix); 1.734 + drawState = fGpu->drawState(); 1.735 + 1.736 + drawState->setRenderTarget(rt); 1.737 + 1.738 + // We set the current clip to the bounds so that our recursive draws are scissored to them. 1.739 + SkIRect stencilSpaceIBounds(clipSpaceIBounds); 1.740 + stencilSpaceIBounds.offset(clipSpaceToStencilOffset); 1.741 + GrDrawTarget::AutoClipRestore acr(fGpu, stencilSpaceIBounds); 1.742 + drawState->enableState(GrDrawState::kClip_StateBit); 1.743 + 1.744 +#if !VISUALIZE_COMPLEX_CLIP 1.745 + drawState->enableState(GrDrawState::kNoColorWrites_StateBit); 1.746 +#endif 1.747 + 1.748 + int clipBit = stencilBuffer->bits(); 1.749 + SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); 1.750 + clipBit = (1 << (clipBit-1)); 1.751 + 1.752 + fGpu->clearStencilClip(stencilSpaceIBounds, kAllIn_InitialState == initialState); 1.753 + 1.754 + // walk through each clip element and perform its set op 1.755 + // with the existing clip. 1.756 + for (ElementList::Iter iter(elements.headIter()); NULL != iter.get(); iter.next()) { 1.757 + const Element* element = iter.get(); 1.758 + bool fillInverted = false; 1.759 + // enabled at bottom of loop 1.760 + drawState->disableState(GrGpu::kModifyStencilClip_StateBit); 1.761 + // if the target is MSAA then we want MSAA enabled when the clip is soft 1.762 + if (rt->isMultisampled()) { 1.763 + drawState->setState(GrDrawState::kHWAntialias_StateBit, element->isAA()); 1.764 + } 1.765 + 1.766 + // This will be used to determine whether the clip shape can be rendered into the 1.767 + // stencil with arbitrary stencil settings. 1.768 + GrPathRenderer::StencilSupport stencilSupport; 1.769 + 1.770 + SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 1.771 + 1.772 + SkRegion::Op op = element->getOp(); 1.773 + 1.774 + GrPathRenderer* pr = NULL; 1.775 + SkPath clipPath; 1.776 + if (Element::kRect_Type == element->getType()) { 1.777 + stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; 1.778 + fillInverted = false; 1.779 + } else { 1.780 + element->asPath(&clipPath); 1.781 + fillInverted = clipPath.isInverseFillType(); 1.782 + if (fillInverted) { 1.783 + clipPath.toggleInverseFillType(); 1.784 + } 1.785 + pr = this->getContext()->getPathRenderer(clipPath, 1.786 + stroke, 1.787 + fGpu, 1.788 + false, 1.789 + GrPathRendererChain::kStencilOnly_DrawType, 1.790 + &stencilSupport); 1.791 + if (NULL == pr) { 1.792 + return false; 1.793 + } 1.794 + } 1.795 + 1.796 + int passes; 1.797 + GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses]; 1.798 + 1.799 + bool canRenderDirectToStencil = 1.800 + GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport; 1.801 + bool canDrawDirectToClip; // Given the renderer, the element, 1.802 + // fill rule, and set operation can 1.803 + // we render the element directly to 1.804 + // stencil bit used for clipping. 1.805 + canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, 1.806 + canRenderDirectToStencil, 1.807 + clipBit, 1.808 + fillInverted, 1.809 + &passes, 1.810 + stencilSettings); 1.811 + 1.812 + // draw the element to the client stencil bits if necessary 1.813 + if (!canDrawDirectToClip) { 1.814 + GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil, 1.815 + kIncClamp_StencilOp, 1.816 + kIncClamp_StencilOp, 1.817 + kAlways_StencilFunc, 1.818 + 0xffff, 1.819 + 0x0000, 1.820 + 0xffff); 1.821 + SET_RANDOM_COLOR 1.822 + if (Element::kRect_Type == element->getType()) { 1.823 + *drawState->stencil() = gDrawToStencil; 1.824 + fGpu->drawSimpleRect(element->getRect(), NULL); 1.825 + } else { 1.826 + if (!clipPath.isEmpty()) { 1.827 + if (canRenderDirectToStencil) { 1.828 + *drawState->stencil() = gDrawToStencil; 1.829 + pr->drawPath(clipPath, stroke, fGpu, false); 1.830 + } else { 1.831 + pr->stencilPath(clipPath, stroke, fGpu); 1.832 + } 1.833 + } 1.834 + } 1.835 + } 1.836 + 1.837 + // now we modify the clip bit by rendering either the clip 1.838 + // element directly or a bounding rect of the entire clip. 1.839 + drawState->enableState(GrGpu::kModifyStencilClip_StateBit); 1.840 + for (int p = 0; p < passes; ++p) { 1.841 + *drawState->stencil() = stencilSettings[p]; 1.842 + if (canDrawDirectToClip) { 1.843 + if (Element::kRect_Type == element->getType()) { 1.844 + SET_RANDOM_COLOR 1.845 + fGpu->drawSimpleRect(element->getRect(), NULL); 1.846 + } else { 1.847 + SET_RANDOM_COLOR 1.848 + pr->drawPath(clipPath, stroke, fGpu, false); 1.849 + } 1.850 + } else { 1.851 + SET_RANDOM_COLOR 1.852 + // The view matrix is setup to do clip space -> stencil space translation, so 1.853 + // draw rect in clip space. 1.854 + fGpu->drawSimpleRect(SkRect::Make(clipSpaceIBounds), NULL); 1.855 + } 1.856 + } 1.857 + } 1.858 + } 1.859 + // set this last because recursive draws may overwrite it back to kNone. 1.860 + SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); 1.861 + fCurrClipMaskType = kStencil_ClipMaskType; 1.862 + return true; 1.863 +} 1.864 + 1.865 + 1.866 +// mapping of clip-respecting stencil funcs to normal stencil funcs 1.867 +// mapping depends on whether stencil-clipping is in effect. 1.868 +static const GrStencilFunc 1.869 + gSpecialToBasicStencilFunc[2][kClipStencilFuncCount] = { 1.870 + {// Stencil-Clipping is DISABLED, we are effectively always inside the clip 1.871 + // In the Clip Funcs 1.872 + kAlways_StencilFunc, // kAlwaysIfInClip_StencilFunc 1.873 + kEqual_StencilFunc, // kEqualIfInClip_StencilFunc 1.874 + kLess_StencilFunc, // kLessIfInClip_StencilFunc 1.875 + kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc 1.876 + // Special in the clip func that forces user's ref to be 0. 1.877 + kNotEqual_StencilFunc, // kNonZeroIfInClip_StencilFunc 1.878 + // make ref 0 and do normal nequal. 1.879 + }, 1.880 + {// Stencil-Clipping is ENABLED 1.881 + // In the Clip Funcs 1.882 + kEqual_StencilFunc, // kAlwaysIfInClip_StencilFunc 1.883 + // eq stencil clip bit, mask 1.884 + // out user bits. 1.885 + 1.886 + kEqual_StencilFunc, // kEqualIfInClip_StencilFunc 1.887 + // add stencil bit to mask and ref 1.888 + 1.889 + kLess_StencilFunc, // kLessIfInClip_StencilFunc 1.890 + kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc 1.891 + // for both of these we can add 1.892 + // the clip bit to the mask and 1.893 + // ref and compare as normal 1.894 + // Special in the clip func that forces user's ref to be 0. 1.895 + kLess_StencilFunc, // kNonZeroIfInClip_StencilFunc 1.896 + // make ref have only the clip bit set 1.897 + // and make comparison be less 1.898 + // 10..0 < 1..user_bits.. 1.899 + } 1.900 +}; 1.901 + 1.902 +namespace { 1.903 +// Sets the settings to clip against the stencil buffer clip while ignoring the 1.904 +// client bits. 1.905 +const GrStencilSettings& basic_apply_stencil_clip_settings() { 1.906 + // stencil settings to use when clip is in stencil 1.907 + GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings, 1.908 + kKeep_StencilOp, 1.909 + kKeep_StencilOp, 1.910 + kAlwaysIfInClip_StencilFunc, 1.911 + 0x0000, 1.912 + 0x0000, 1.913 + 0x0000); 1.914 + return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings); 1.915 +} 1.916 +} 1.917 + 1.918 +void GrClipMaskManager::setGpuStencil() { 1.919 + // We make two copies of the StencilSettings here (except in the early 1.920 + // exit scenario. One copy from draw state to the stack var. Then another 1.921 + // from the stack var to the gpu. We could make this class hold a ptr to 1.922 + // GrGpu's fStencilSettings and eliminate the stack copy here. 1.923 + 1.924 + const GrDrawState& drawState = fGpu->getDrawState(); 1.925 + 1.926 + // use stencil for clipping if clipping is enabled and the clip 1.927 + // has been written into the stencil. 1.928 + GrClipMaskManager::StencilClipMode clipMode; 1.929 + if (this->isClipInStencil() && drawState.isClipState()) { 1.930 + clipMode = GrClipMaskManager::kRespectClip_StencilClipMode; 1.931 + // We can't be modifying the clip and respecting it at the same time. 1.932 + SkASSERT(!drawState.isStateFlagEnabled( 1.933 + GrGpu::kModifyStencilClip_StateBit)); 1.934 + } else if (drawState.isStateFlagEnabled( 1.935 + GrGpu::kModifyStencilClip_StateBit)) { 1.936 + clipMode = GrClipMaskManager::kModifyClip_StencilClipMode; 1.937 + } else { 1.938 + clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode; 1.939 + } 1.940 + 1.941 + GrStencilSettings settings; 1.942 + // The GrGpu client may not be using the stencil buffer but we may need to 1.943 + // enable it in order to respect a stencil clip. 1.944 + if (drawState.getStencil().isDisabled()) { 1.945 + if (GrClipMaskManager::kRespectClip_StencilClipMode == clipMode) { 1.946 + settings = basic_apply_stencil_clip_settings(); 1.947 + } else { 1.948 + fGpu->disableStencil(); 1.949 + return; 1.950 + } 1.951 + } else { 1.952 + settings = drawState.getStencil(); 1.953 + } 1.954 + 1.955 + // TODO: dynamically attach a stencil buffer 1.956 + int stencilBits = 0; 1.957 + GrStencilBuffer* stencilBuffer = 1.958 + drawState.getRenderTarget()->getStencilBuffer(); 1.959 + if (NULL != stencilBuffer) { 1.960 + stencilBits = stencilBuffer->bits(); 1.961 + } 1.962 + 1.963 + SkASSERT(fGpu->caps()->stencilWrapOpsSupport() || !settings.usesWrapOp()); 1.964 + SkASSERT(fGpu->caps()->twoSidedStencilSupport() || !settings.isTwoSided()); 1.965 + this->adjustStencilParams(&settings, clipMode, stencilBits); 1.966 + fGpu->setStencilSettings(settings); 1.967 +} 1.968 + 1.969 +void GrClipMaskManager::adjustStencilParams(GrStencilSettings* settings, 1.970 + StencilClipMode mode, 1.971 + int stencilBitCnt) { 1.972 + SkASSERT(stencilBitCnt > 0); 1.973 + 1.974 + if (kModifyClip_StencilClipMode == mode) { 1.975 + // We assume that this clip manager itself is drawing to the GrGpu and 1.976 + // has already setup the correct values. 1.977 + return; 1.978 + } 1.979 + 1.980 + unsigned int clipBit = (1 << (stencilBitCnt - 1)); 1.981 + unsigned int userBits = clipBit - 1; 1.982 + 1.983 + GrStencilSettings::Face face = GrStencilSettings::kFront_Face; 1.984 + bool twoSided = fGpu->caps()->twoSidedStencilSupport(); 1.985 + 1.986 + bool finished = false; 1.987 + while (!finished) { 1.988 + GrStencilFunc func = settings->func(face); 1.989 + uint16_t writeMask = settings->writeMask(face); 1.990 + uint16_t funcMask = settings->funcMask(face); 1.991 + uint16_t funcRef = settings->funcRef(face); 1.992 + 1.993 + SkASSERT((unsigned) func < kStencilFuncCount); 1.994 + 1.995 + writeMask &= userBits; 1.996 + 1.997 + if (func >= kBasicStencilFuncCount) { 1.998 + int respectClip = kRespectClip_StencilClipMode == mode; 1.999 + if (respectClip) { 1.1000 + // The GrGpu class should have checked this 1.1001 + SkASSERT(this->isClipInStencil()); 1.1002 + switch (func) { 1.1003 + case kAlwaysIfInClip_StencilFunc: 1.1004 + funcMask = clipBit; 1.1005 + funcRef = clipBit; 1.1006 + break; 1.1007 + case kEqualIfInClip_StencilFunc: 1.1008 + case kLessIfInClip_StencilFunc: 1.1009 + case kLEqualIfInClip_StencilFunc: 1.1010 + funcMask = (funcMask & userBits) | clipBit; 1.1011 + funcRef = (funcRef & userBits) | clipBit; 1.1012 + break; 1.1013 + case kNonZeroIfInClip_StencilFunc: 1.1014 + funcMask = (funcMask & userBits) | clipBit; 1.1015 + funcRef = clipBit; 1.1016 + break; 1.1017 + default: 1.1018 + GrCrash("Unknown stencil func"); 1.1019 + } 1.1020 + } else { 1.1021 + funcMask &= userBits; 1.1022 + funcRef &= userBits; 1.1023 + } 1.1024 + const GrStencilFunc* table = 1.1025 + gSpecialToBasicStencilFunc[respectClip]; 1.1026 + func = table[func - kBasicStencilFuncCount]; 1.1027 + SkASSERT(func >= 0 && func < kBasicStencilFuncCount); 1.1028 + } else { 1.1029 + funcMask &= userBits; 1.1030 + funcRef &= userBits; 1.1031 + } 1.1032 + 1.1033 + settings->setFunc(face, func); 1.1034 + settings->setWriteMask(face, writeMask); 1.1035 + settings->setFuncMask(face, funcMask); 1.1036 + settings->setFuncRef(face, funcRef); 1.1037 + 1.1038 + if (GrStencilSettings::kFront_Face == face) { 1.1039 + face = GrStencilSettings::kBack_Face; 1.1040 + finished = !twoSided; 1.1041 + } else { 1.1042 + finished = true; 1.1043 + } 1.1044 + } 1.1045 + if (!twoSided) { 1.1046 + settings->copyFrontSettingsToBack(); 1.1047 + } 1.1048 +} 1.1049 + 1.1050 +//////////////////////////////////////////////////////////////////////////////// 1.1051 +GrTexture* GrClipMaskManager::createSoftwareClipMask(int32_t elementsGenID, 1.1052 + GrReducedClip::InitialState initialState, 1.1053 + const GrReducedClip::ElementList& elements, 1.1054 + const SkIRect& clipSpaceIBounds) { 1.1055 + SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); 1.1056 + 1.1057 + GrTexture* result; 1.1058 + if (this->getMaskTexture(elementsGenID, clipSpaceIBounds, &result, true)) { 1.1059 + return result; 1.1060 + } 1.1061 + 1.1062 + if (NULL == result) { 1.1063 + fAACache.reset(); 1.1064 + return NULL; 1.1065 + } 1.1066 + 1.1067 + // The mask texture may be larger than necessary. We round out the clip space bounds and pin 1.1068 + // the top left corner of the resulting rect to the top left of the texture. 1.1069 + SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); 1.1070 + 1.1071 + GrSWMaskHelper helper(this->getContext()); 1.1072 + 1.1073 + SkMatrix matrix; 1.1074 + matrix.setTranslate(SkIntToScalar(-clipSpaceIBounds.fLeft), 1.1075 + SkIntToScalar(-clipSpaceIBounds.fTop)); 1.1076 + helper.init(maskSpaceIBounds, &matrix); 1.1077 + 1.1078 + helper.clear(kAllIn_InitialState == initialState ? 0xFF : 0x00); 1.1079 + 1.1080 + SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 1.1081 + 1.1082 + for (ElementList::Iter iter(elements.headIter()) ; NULL != iter.get(); iter.next()) { 1.1083 + 1.1084 + const Element* element = iter.get(); 1.1085 + SkRegion::Op op = element->getOp(); 1.1086 + 1.1087 + if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { 1.1088 + // Intersect and reverse difference require modifying pixels outside of the geometry 1.1089 + // that is being "drawn". In both cases we erase all the pixels outside of the geometry 1.1090 + // but leave the pixels inside the geometry alone. For reverse difference we invert all 1.1091 + // the pixels before clearing the ones outside the geometry. 1.1092 + if (SkRegion::kReverseDifference_Op == op) { 1.1093 + SkRect temp = SkRect::Make(clipSpaceIBounds); 1.1094 + // invert the entire scene 1.1095 + helper.draw(temp, SkRegion::kXOR_Op, false, 0xFF); 1.1096 + } 1.1097 + 1.1098 + SkPath clipPath; 1.1099 + element->asPath(&clipPath); 1.1100 + clipPath.toggleInverseFillType(); 1.1101 + helper.draw(clipPath, stroke, SkRegion::kReplace_Op, element->isAA(), 0x00); 1.1102 + 1.1103 + continue; 1.1104 + } 1.1105 + 1.1106 + // The other ops (union, xor, diff) only affect pixels inside 1.1107 + // the geometry so they can just be drawn normally 1.1108 + if (Element::kRect_Type == element->getType()) { 1.1109 + helper.draw(element->getRect(), op, element->isAA(), 0xFF); 1.1110 + } else { 1.1111 + SkPath path; 1.1112 + element->asPath(&path); 1.1113 + helper.draw(path, stroke, op, element->isAA(), 0xFF); 1.1114 + } 1.1115 + } 1.1116 + 1.1117 + helper.toTexture(result); 1.1118 + 1.1119 + fCurrClipMaskType = kAlpha_ClipMaskType; 1.1120 + return result; 1.1121 +} 1.1122 + 1.1123 +//////////////////////////////////////////////////////////////////////////////// 1.1124 +void GrClipMaskManager::releaseResources() { 1.1125 + fAACache.releaseResources(); 1.1126 +} 1.1127 + 1.1128 +void GrClipMaskManager::setGpu(GrGpu* gpu) { 1.1129 + fGpu = gpu; 1.1130 + fAACache.setContext(gpu->getContext()); 1.1131 +} 1.1132 + 1.1133 +void GrClipMaskManager::adjustPathStencilParams(GrStencilSettings* settings) { 1.1134 + const GrDrawState& drawState = fGpu->getDrawState(); 1.1135 + GrClipMaskManager::StencilClipMode clipMode; 1.1136 + if (this->isClipInStencil() && drawState.isClipState()) { 1.1137 + clipMode = GrClipMaskManager::kRespectClip_StencilClipMode; 1.1138 + // We can't be modifying the clip and respecting it at the same time. 1.1139 + SkASSERT(!drawState.isStateFlagEnabled( 1.1140 + GrGpu::kModifyStencilClip_StateBit)); 1.1141 + } else if (drawState.isStateFlagEnabled( 1.1142 + GrGpu::kModifyStencilClip_StateBit)) { 1.1143 + clipMode = GrClipMaskManager::kModifyClip_StencilClipMode; 1.1144 + } else { 1.1145 + clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode; 1.1146 + } 1.1147 + 1.1148 + // TODO: dynamically attach a stencil buffer 1.1149 + int stencilBits = 0; 1.1150 + GrStencilBuffer* stencilBuffer = 1.1151 + drawState.getRenderTarget()->getStencilBuffer(); 1.1152 + if (NULL != stencilBuffer) { 1.1153 + stencilBits = stencilBuffer->bits(); 1.1154 + this->adjustStencilParams(settings, clipMode, stencilBits); 1.1155 + } 1.1156 +}