//////////////////////////////////////////////////////////////////////////////// // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device // (as opposed to canvas) coordinates bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn, const GrIRect& devClipBounds) { GrAssert(kNone_ClipMaskType == fCurrClipMaskType); GrDrawState* drawState = fGpu->drawState(); GrAssert(drawState->isClipState()); GrRenderTarget* rt = drawState->getRenderTarget(); GrAssert(NULL != rt); // TODO: dynamically attach a SB when needed. GrStencilBuffer* stencilBuffer = rt->getStencilBuffer(); if (NULL == stencilBuffer) { return false; } if (stencilBuffer->mustRenderClip(clipDataIn, rt->width(), rt->height())) { stencilBuffer->setLastClip(clipDataIn, rt->width(), rt->height()); // we set the current clip to the bounds so that our recursive // draws are scissored to them. We use the copy of the complex clip // we just stashed on the SB to render from. We set it back after // we finish drawing it into the stencil. const GrClipData* oldClipData = fGpu->getClip(); // The origin of 'newClipData' is (0, 0) so it is okay to place // a device-coordinate bound in 'newClipStack' SkClipStack newClipStack(devClipBounds); GrClipData newClipData; newClipData.fClipStack = &newClipStack; fGpu->setClip(&newClipData); GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); drawState = fGpu->drawState(); drawState->setRenderTarget(rt); GrDrawTarget::AutoGeometryPush agp(fGpu); if (0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) { // Add the saveLayer's offset to the view matrix rather than // offset each individual draw drawState->viewMatrix()->setTranslate( SkIntToScalar(-clipDataIn.fOrigin.fX), SkIntToScalar(-clipDataIn.fOrigin.fY)); } #if !VISUALIZE_COMPLEX_CLIP drawState->enableState(GrDrawState::kNoColorWrites_StateBit); #endif int clipBit = stencilBuffer->bits(); SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); clipBit = (1 << (clipBit-1)); GrIRect devRTRect = GrIRect::MakeWH(rt->width(), rt->height()); bool clearToInside; SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning SkClipStack::Iter iter(*oldClipData->fClipStack, SkClipStack::Iter::kBottom_IterStart); const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter, devRTRect, &clearToInside, &firstOp, clipDataIn); fGpu->clearStencilClip(devClipBounds, clearToInside); bool first = true; // walk through each clip element and perform its set op // with the existing clip. for ( ; NULL != clip; clip = iter.next()) { GrPathFill fill; bool fillInverted = false; // enabled at bottom of loop drawState->disableState(GrGpu::kModifyStencilClip_StateBit); // if the target is MSAA then we want MSAA enabled when the clip is soft if (rt->isMultisampled()) { if (clip->fDoAA) { drawState->enableState(GrDrawState::kHWAntialias_StateBit); } else { drawState->disableState(GrDrawState::kHWAntialias_StateBit); } } // Can the clip element be drawn directly to the stencil buffer // with a non-inverted fill rule without extra passes to // resolve in/out status? bool canRenderDirectToStencil = false; SkRegion::Op op = clip->fOp; if (first) { first = false; op = firstOp; } GrPathRenderer* pr = NULL; const SkPath* clipPath = NULL; if (NULL != clip->fRect) { canRenderDirectToStencil = true; fill = kEvenOdd_GrPathFill; fillInverted = false; // there is no point in intersecting a screen filling // rectangle. if (SkRegion::kIntersect_Op == op && contains(*clip->fRect, devRTRect, oldClipData->fOrigin)) { continue; } } else { GrAssert(NULL != clip->fPath); fill = get_path_fill(*clip->fPath); fillInverted = GrIsFillInverted(fill); fill = GrNonInvertedFill(fill); clipPath = clip->fPath; pr = this->getContext()->getPathRenderer(*clipPath, fill, fGpu, false, true); if (NULL == pr) { fGpu->setClip(oldClipData); return false; } canRenderDirectToStencil = !pr->requiresStencilPass(*clipPath, fill, fGpu); } int passes; GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses]; bool canDrawDirectToClip; // Given the renderer, the element, // fill rule, and set operation can // we render the element directly to // stencil bit used for clipping. canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, clipBit, fillInverted, &passes, stencilSettings); // draw the element to the client stencil bits if necessary if (!canDrawDirectToClip) { GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil, kIncClamp_StencilOp, kIncClamp_StencilOp, kAlways_StencilFunc, 0xffff, 0x0000, 0xffff); SET_RANDOM_COLOR if (NULL != clip->fRect) { *drawState->stencil() = gDrawToStencil; fGpu->drawSimpleRect(*clip->fRect, NULL); } else { if (canRenderDirectToStencil) { *drawState->stencil() = gDrawToStencil; pr->drawPath(*clipPath, fill, NULL, fGpu, false); } else { pr->drawPathToStencil(*clipPath, fill, fGpu); } } } // now we modify the clip bit by rendering either the clip // element directly or a bounding rect of the entire clip. drawState->enableState(GrGpu::kModifyStencilClip_StateBit); for (int p = 0; p < passes; ++p) { *drawState->stencil() = stencilSettings[p]; if (canDrawDirectToClip) { if (NULL != clip->fRect) { SET_RANDOM_COLOR fGpu->drawSimpleRect(*clip->fRect, NULL); } else { SET_RANDOM_COLOR pr->drawPath(*clipPath, fill, NULL, fGpu, false); } } else { SET_RANDOM_COLOR // 'devClipBounds' is already in device coordinates so the // translation in the view matrix is inappropriate. // Convert it to canvas space so the drawn rect will // be in the correct location GrRect canvClipBounds; canvClipBounds.set(devClipBounds); device_to_canvas(&canvClipBounds, clipDataIn.fOrigin); fGpu->drawSimpleRect(canvClipBounds, NULL); } } }
void GrGLPathRendering::drawPaths(const GrPathRange* pathRange, const uint32_t indices[], int count, const float transforms[], PathTransformType transformsType, SkPath::FillType fill) { SkASSERT(fGpu->caps()->pathRenderingSupport()); SkASSERT(NULL != fGpu->drawState()->getRenderTarget()); SkASSERT(NULL != fGpu->drawState()->getRenderTarget()->getStencilBuffer()); GrGLuint baseID = static_cast<const GrGLPathRange*>(pathRange)->basePathID(); this->flushPathStencilSettings(fill); SkASSERT(!fHWPathStencilSettings.isTwoSided()); const SkStrokeRec& stroke = pathRange->getStroke(); SkPath::FillType nonInvertedFill = SkPath::ConvertToNonInverseFillType(fill); GrGLenum fillMode = gr_stencil_op_to_gl_path_rendering_fill_mode( fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face)); GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face); if (nonInvertedFill == fill) { if (stroke.needToApply()) { if (SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) { GL_CALL(StencilFillPathInstanced( count, GR_GL_UNSIGNED_INT, indices, baseID, fillMode, writeMask, gXformType2GLType[transformsType], transforms)); } this->stencilThenCoverStrokePathInstanced( count, GR_GL_UNSIGNED_INT, indices, baseID, 0xffff, writeMask, GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES, gXformType2GLType[transformsType], transforms); } else { this->stencilThenCoverFillPathInstanced( count, GR_GL_UNSIGNED_INT, indices, baseID, fillMode, writeMask, GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES, gXformType2GLType[transformsType], transforms); } } else { if (stroke.isFillStyle() || SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) { GL_CALL(StencilFillPathInstanced( count, GR_GL_UNSIGNED_INT, indices, baseID, fillMode, writeMask, gXformType2GLType[transformsType], transforms)); } if (stroke.needToApply()) { GL_CALL(StencilStrokePathInstanced( count, GR_GL_UNSIGNED_INT, indices, baseID, 0xffff, writeMask, gXformType2GLType[transformsType], transforms)); } GrDrawState* drawState = fGpu->drawState(); GrDrawState::AutoViewMatrixRestore avmr; SkRect bounds = SkRect::MakeLTRB(0, 0, SkIntToScalar(drawState->getRenderTarget()->width()), SkIntToScalar(drawState->getRenderTarget()->height())); SkMatrix vmi; // mapRect through persp matrix may not be correct if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) { vmi.mapRect(&bounds); // theoretically could set bloat = 0, instead leave it because of matrix inversion // precision. SkScalar bloat = drawState->getViewMatrix().getMaxScale() * SK_ScalarHalf; bounds.outset(bloat, bloat); } else { avmr.setIdentity(drawState); } fGpu->drawSimpleRect(bounds); } }
bool GrGLProgramDesc::Build(const GrDrawState& drawState, GrGpu::DrawType drawType, GrDrawState::BlendOptFlags blendOpts, GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff, const GrGpuGL* gpu, const GrDeviceCoordTexture* dstCopy, const GrEffectStage** geometryProcessor, SkTArray<const GrEffectStage*, true>* colorStages, SkTArray<const GrEffectStage*, true>* coverageStages, GrGLProgramDesc* desc) { colorStages->reset(); coverageStages->reset(); // This should already have been caught SkASSERT(!(GrDrawState::kSkipDraw_BlendOptFlag & blendOpts)); bool skipCoverage = SkToBool(blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag); bool skipColor = SkToBool(blendOpts & (GrDrawState::kEmitTransBlack_BlendOptFlag | GrDrawState::kEmitCoverage_BlendOptFlag)); int firstEffectiveColorStage = 0; bool inputColorIsUsed = true; if (!skipColor) { firstEffectiveColorStage = drawState.numColorStages(); while (firstEffectiveColorStage > 0 && inputColorIsUsed) { --firstEffectiveColorStage; const GrEffect* effect = drawState.getColorStage(firstEffectiveColorStage).getEffect(); inputColorIsUsed = effect->willUseInputColor(); } } int firstEffectiveCoverageStage = 0; bool inputCoverageIsUsed = true; if (!skipCoverage) { firstEffectiveCoverageStage = drawState.numCoverageStages(); while (firstEffectiveCoverageStage > 0 && inputCoverageIsUsed) { --firstEffectiveCoverageStage; const GrEffect* effect = drawState.getCoverageStage(firstEffectiveCoverageStage).getEffect(); inputCoverageIsUsed = effect->willUseInputColor(); } } // The descriptor is used as a cache key. Thus when a field of the // descriptor will not affect program generation (because of the attribute // bindings in use or other descriptor field settings) it should be set // to a canonical value to avoid duplicate programs with different keys. bool requiresColorAttrib = !skipColor && drawState.hasColorVertexAttribute(); bool requiresCoverageAttrib = !skipCoverage && drawState.hasCoverageVertexAttribute(); // we only need the local coords if we're actually going to generate effect code bool requiresLocalCoordAttrib = !(skipCoverage && skipColor) && drawState.hasLocalCoordAttribute(); bool readsDst = false; bool readFragPosition = false; // Provide option for shader programs without vertex shader only when drawing paths. bool requiresVertexShader = !GrGpu::IsPathRenderingDrawType(drawType); int numStages = 0; if (drawState.hasGeometryProcessor()) { numStages++; } if (!skipColor) { numStages += drawState.numColorStages() - firstEffectiveColorStage; } if (!skipCoverage) { numStages += drawState.numCoverageStages() - firstEffectiveCoverageStage; } GR_STATIC_ASSERT(0 == kEffectKeyOffsetsAndLengthOffset % sizeof(uint32_t)); // Make room for everything up to and including the array of offsets to effect keys. desc->fKey.reset(); desc->fKey.push_back_n(kEffectKeyOffsetsAndLengthOffset + 2 * sizeof(uint16_t) * numStages); int offsetAndSizeIndex = 0; bool effectKeySuccess = true; KeyHeader* header = desc->header(); // make sure any padding in the header is zeroed. memset(desc->header(), 0, kHeaderSize); // We can only have one effect which touches the vertex shader if (drawState.hasGeometryProcessor()) { uint16_t* offsetAndSize = reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAndLengthOffset + offsetAndSizeIndex * 2 * sizeof(uint16_t)); GrEffectKeyBuilder b(&desc->fKey); uint16_t effectKeySize; uint32_t effectOffset = desc->fKey.count(); effectKeySuccess |= GetEffectKeyAndUpdateStats( *drawState.getGeometryProcessor(), gpu->glCaps(), requiresLocalCoordAttrib, &b, &effectKeySize, &readsDst, &readFragPosition, &requiresVertexShader); effectKeySuccess |= (effectOffset <= SK_MaxU16); offsetAndSize[0] = SkToU16(effectOffset); offsetAndSize[1] = effectKeySize; ++offsetAndSizeIndex; *geometryProcessor = drawState.getGeometryProcessor(); SkASSERT(requiresVertexShader); header->fHasGeometryProcessor = true; } if (!skipColor) { for (int s = firstEffectiveColorStage; s < drawState.numColorStages(); ++s) { uint16_t* offsetAndSize = reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAndLengthOffset + offsetAndSizeIndex * 2 * sizeof(uint16_t)); bool effectRequiresVertexShader = false; GrEffectKeyBuilder b(&desc->fKey); uint16_t effectKeySize; uint32_t effectOffset = desc->fKey.count(); effectKeySuccess |= GetEffectKeyAndUpdateStats( drawState.getColorStage(s), gpu->glCaps(), requiresLocalCoordAttrib, &b, &effectKeySize, &readsDst, &readFragPosition, &effectRequiresVertexShader); effectKeySuccess |= (effectOffset <= SK_MaxU16); offsetAndSize[0] = SkToU16(effectOffset); offsetAndSize[1] = effectKeySize; ++offsetAndSizeIndex; SkASSERT(!effectRequiresVertexShader); } } if (!skipCoverage) { for (int s = firstEffectiveCoverageStage; s < drawState.numCoverageStages(); ++s) { uint16_t* offsetAndSize = reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAndLengthOffset + offsetAndSizeIndex * 2 * sizeof(uint16_t)); bool effectRequiresVertexShader = false; GrEffectKeyBuilder b(&desc->fKey); uint16_t effectKeySize; uint32_t effectOffset = desc->fKey.count(); effectKeySuccess |= GetEffectKeyAndUpdateStats( drawState.getCoverageStage(s), gpu->glCaps(), requiresLocalCoordAttrib, &b, &effectKeySize, &readsDst, &readFragPosition, &effectRequiresVertexShader); effectKeySuccess |= (effectOffset <= SK_MaxU16); offsetAndSize[0] = SkToU16(effectOffset); offsetAndSize[1] = effectKeySize; ++offsetAndSizeIndex; SkASSERT(!effectRequiresVertexShader); } } if (!effectKeySuccess) { desc->fKey.reset(); return false; } // Because header is a pointer into the dynamic array, we can't push any new data into the key // below here. header->fRequiresVertexShader = requiresVertexShader || requiresLocalCoordAttrib; header->fEmitsPointSize = GrGpu::kDrawPoints_DrawType == drawType; // Currently the experimental GS will only work with triangle prims (and it doesn't do anything // other than pass through values from the VS to the FS anyway). #if GR_GL_EXPERIMENTAL_GS #if 0 header->fExperimentalGS = gpu->caps().geometryShaderSupport(); #else header->fExperimentalGS = false; #endif #endif bool defaultToUniformInputs = GR_GL_NO_CONSTANT_ATTRIBUTES || gpu->caps()->pathRenderingSupport(); if (!inputColorIsUsed && !skipColor) { header->fColorInput = kAllOnes_ColorInput; } else if (defaultToUniformInputs && !requiresColorAttrib && inputColorIsUsed) { header->fColorInput = kUniform_ColorInput; } else { header->fColorInput = kAttribute_ColorInput; header->fRequiresVertexShader = true; } bool covIsSolidWhite = !requiresCoverageAttrib && 0xffffffff == drawState.getCoverageColor(); if ((covIsSolidWhite || !inputCoverageIsUsed) && !skipCoverage) { header->fCoverageInput = kAllOnes_ColorInput; } else if (defaultToUniformInputs && !requiresCoverageAttrib && inputCoverageIsUsed) { header->fCoverageInput = kUniform_ColorInput; } else { header->fCoverageInput = kAttribute_ColorInput; header->fRequiresVertexShader = true; } if (readsDst) { SkASSERT(NULL != dstCopy || gpu->caps()->dstReadInShaderSupport()); const GrTexture* dstCopyTexture = NULL; if (NULL != dstCopy) { dstCopyTexture = dstCopy->texture(); } header->fDstReadKey = GrGLFragmentShaderBuilder::KeyForDstRead(dstCopyTexture, gpu->glCaps()); SkASSERT(0 != header->fDstReadKey); } else { header->fDstReadKey = 0; } if (readFragPosition) { header->fFragPosKey = GrGLFragmentShaderBuilder::KeyForFragmentPosition( drawState.getRenderTarget(), gpu->glCaps()); } else { header->fFragPosKey = 0; } // Record attribute indices header->fPositionAttributeIndex = drawState.positionAttributeIndex(); header->fLocalCoordAttributeIndex = drawState.localCoordAttributeIndex(); // For constant color and coverage we need an attribute with an index beyond those already set int availableAttributeIndex = drawState.getVertexAttribCount(); if (requiresColorAttrib) { header->fColorAttributeIndex = drawState.colorVertexAttributeIndex(); } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fColorInput) { SkASSERT(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt); header->fColorAttributeIndex = availableAttributeIndex; availableAttributeIndex++; } else { header->fColorAttributeIndex = -1; } if (requiresCoverageAttrib) { header->fCoverageAttributeIndex = drawState.coverageVertexAttributeIndex(); } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fCoverageInput) { SkASSERT(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt); header->fCoverageAttributeIndex = availableAttributeIndex; } else { header->fCoverageAttributeIndex = -1; } // Here we deal with whether/how we handle color and coverage separately. // Set this default and then possibly change our mind if there is coverage. header->fCoverageOutput = kModulate_CoverageOutput; // If we do have coverage determine whether it matters. bool separateCoverageFromColor = drawState.hasGeometryProcessor(); if (!drawState.isCoverageDrawing() && !skipCoverage && (drawState.numCoverageStages() > 0 || drawState.hasGeometryProcessor() || requiresCoverageAttrib)) { if (gpu->caps()->dualSourceBlendingSupport() && !(blendOpts & (GrDrawState::kEmitCoverage_BlendOptFlag | GrDrawState::kCoverageAsAlpha_BlendOptFlag))) { if (kZero_GrBlendCoeff == dstCoeff) { // write the coverage value to second color header->fCoverageOutput = kSecondaryCoverage_CoverageOutput; separateCoverageFromColor = true; } else if (kSA_GrBlendCoeff == dstCoeff) { // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. header->fCoverageOutput = kSecondaryCoverageISA_CoverageOutput; separateCoverageFromColor = true; } else if (kSC_GrBlendCoeff == dstCoeff) { // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. header->fCoverageOutput = kSecondaryCoverageISC_CoverageOutput; separateCoverageFromColor = true; } } else if (readsDst && kOne_GrBlendCoeff == srcCoeff && kZero_GrBlendCoeff == dstCoeff) { header->fCoverageOutput = kCombineWithDst_CoverageOutput; separateCoverageFromColor = true; } } if (!skipColor) { for (int s = firstEffectiveColorStage; s < drawState.numColorStages(); ++s) { colorStages->push_back(&drawState.getColorStage(s)); } } if (!skipCoverage) { SkTArray<const GrEffectStage*, true>* array; if (separateCoverageFromColor) { array = coverageStages; } else { array = colorStages; } for (int s = firstEffectiveCoverageStage; s < drawState.numCoverageStages(); ++s) { array->push_back(&drawState.getCoverageStage(s)); } } header->fColorEffectCnt = colorStages->count(); header->fCoverageEffectCnt = coverageStages->count(); desc->finalize(); return true; }
//////////////////////////////////////////////////////////////////////////////// // sort out what kind of clip mask needs to be created: alpha, stencil, // scissor, or entirely software bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn) { fCurrClipMaskType = kNone_ClipMaskType; GrDrawState* drawState = fGpu->drawState(); if (!drawState->isClipState() || clipDataIn->fClipStack->isWideOpen()) { fGpu->disableScissor(); this->setGpuStencil(); return true; } GrRenderTarget* rt = drawState->getRenderTarget(); // GrDrawTarget should have filtered this for us GrAssert(NULL != rt); GrIRect devClipBounds; bool isIntersectionOfRects = false; clipDataIn->getConservativeBounds(rt, &devClipBounds, &isIntersectionOfRects); if (devClipBounds.isEmpty()) { return false; } bool requiresAA = requires_AA(*clipDataIn->fClipStack); #if GR_SW_CLIP // If MSAA is enabled we can do everything in the stencil buffer. // Otherwise check if we should just create the entire clip mask // in software (this will only happen if the clip mask is anti-aliased // and too complex for the gpu to handle in its entirety) if (0 == rt->numSamples() && requiresAA && this->useSWOnlyPath(*clipDataIn->fClipStack)) { // The clip geometry is complex enough that it will be more // efficient to create it entirely in software GrTexture* result = NULL; GrIRect devBound; if (this->createSoftwareClipMask(*clipDataIn, &result, &devBound)) { setup_drawstate_aaclip(fGpu, result, devBound); fGpu->disableScissor(); this->setGpuStencil(); return true; } // if SW clip mask creation fails fall through to the other // two possible methods (bottoming out at stencil clipping) } #endif // GR_SW_CLIP #if GR_AA_CLIP // If MSAA is enabled use the (faster) stencil path for AA clipping // otherwise the alpha clip mask is our only option if (0 == rt->numSamples() && requiresAA) { // Since we are going to create a destination texture of the correct // size for the mask (rather than being bound by the size of the // render target) we aren't going to use scissoring like the stencil // path does (see scissorSettings below) GrTexture* result = NULL; GrIRect devBound; if (this->createAlphaClipMask(*clipDataIn, &result, &devBound)) { setup_drawstate_aaclip(fGpu, result, devBound); fGpu->disableScissor(); this->setGpuStencil(); return true; } // if alpha clip mask creation fails fall through to the stencil // buffer method } #endif // GR_AA_CLIP // Either a hard (stencil buffer) clip was explicitly requested or // an antialiased clip couldn't be created. In either case, free up // the texture in the antialiased mask cache. // TODO: this may require more investigation. Ganesh performs a lot of // utility draws (e.g., clears, InOrderDrawBuffer playbacks) that hit // the stencil buffer path. These may be "incorrectly" clearing the // AA cache. fAACache.reset(); // If the clip is a rectangle then just set the scissor. Otherwise, create // a stencil mask. if (isIntersectionOfRects) { fGpu->enableScissor(devClipBounds); this->setGpuStencil(); return true; } // use the stencil clip if we can't represent the clip as a rectangle. bool useStencil = !clipDataIn->fClipStack->isWideOpen() && !devClipBounds.isEmpty(); if (useStencil) { this->createStencilClipMask(*clipDataIn, devClipBounds); } // This must occur after createStencilClipMask. That function may change // the scissor. Also, it only guarantees that the stencil mask is correct // within the bounds it was passed, so we must use both stencil and scissor // test to the bounds for the final draw. fGpu->enableScissor(devClipBounds); this->setGpuStencil(); return true; }
void GrGLProgramDesc::Build(const GrDrawState& drawState, bool isPoints, GrDrawState::BlendOptFlags blendOpts, GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff, const GrGpuGL* gpu, const GrDeviceCoordTexture* dstCopy, SkTArray<const GrEffectStage*, true>* colorStages, SkTArray<const GrEffectStage*, true>* coverageStages, GrGLProgramDesc* desc) { colorStages->reset(); coverageStages->reset(); // This should already have been caught SkASSERT(!(GrDrawState::kSkipDraw_BlendOptFlag & blendOpts)); bool skipCoverage = SkToBool(blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag); bool skipColor = SkToBool(blendOpts & (GrDrawState::kEmitTransBlack_BlendOptFlag | GrDrawState::kEmitCoverage_BlendOptFlag)); int firstEffectiveColorStage = 0; bool inputColorIsUsed = true; if (!skipColor) { firstEffectiveColorStage = drawState.numColorStages(); while (firstEffectiveColorStage > 0 && inputColorIsUsed) { --firstEffectiveColorStage; const GrEffect* effect = drawState.getColorStage(firstEffectiveColorStage).getEffect()->get(); inputColorIsUsed = effect->willUseInputColor(); } } int firstEffectiveCoverageStage = 0; bool inputCoverageIsUsed = true; if (!skipCoverage) { firstEffectiveCoverageStage = drawState.numCoverageStages(); while (firstEffectiveCoverageStage > 0 && inputCoverageIsUsed) { --firstEffectiveCoverageStage; const GrEffect* effect = drawState.getCoverageStage(firstEffectiveCoverageStage).getEffect()->get(); inputCoverageIsUsed = effect->willUseInputColor(); } } // The descriptor is used as a cache key. Thus when a field of the // descriptor will not affect program generation (because of the attribute // bindings in use or other descriptor field settings) it should be set // to a canonical value to avoid duplicate programs with different keys. bool requiresColorAttrib = !skipColor && drawState.hasColorVertexAttribute(); bool requiresCoverageAttrib = !skipCoverage && drawState.hasCoverageVertexAttribute(); // we only need the local coords if we're actually going to generate effect code bool requiresLocalCoordAttrib = !(skipCoverage && skipColor) && drawState.hasLocalCoordAttribute(); bool colorIsTransBlack = SkToBool(blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag); bool colorIsSolidWhite = (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) || (!requiresColorAttrib && 0xffffffff == drawState.getColor()) || (!inputColorIsUsed); int numEffects = (skipColor ? 0 : (drawState.numColorStages() - firstEffectiveColorStage)) + (skipCoverage ? 0 : (drawState.numCoverageStages() - firstEffectiveCoverageStage)); size_t newKeyLength = KeyLength(numEffects); bool allocChanged; desc->fKey.reset(newKeyLength, SkAutoMalloc::kAlloc_OnShrink, &allocChanged); if (allocChanged || !desc->fInitialized) { // make sure any padding in the header is zero if we we haven't used this allocation before. memset(desc->header(), 0, kHeaderSize); } // write the key length *desc->atOffset<uint32_t, kLengthOffset>() = SkToU32(newKeyLength); KeyHeader* header = desc->header(); EffectKey* effectKeys = desc->effectKeys(); int currEffectKey = 0; bool readsDst = false; bool readFragPosition = false; bool hasVertexCode = false; if (!skipColor) { for (int s = firstEffectiveColorStage; s < drawState.numColorStages(); ++s) { effectKeys[currEffectKey++] = get_key_and_update_stats(drawState.getColorStage(s), gpu->glCaps(), requiresLocalCoordAttrib, &readsDst, &readFragPosition, &hasVertexCode); } } if (!skipCoverage) { for (int s = firstEffectiveCoverageStage; s < drawState.numCoverageStages(); ++s) { effectKeys[currEffectKey++] = get_key_and_update_stats(drawState.getCoverageStage(s), gpu->glCaps(), requiresLocalCoordAttrib, &readsDst, &readFragPosition, &hasVertexCode); } } header->fHasVertexCode = hasVertexCode || requiresLocalCoordAttrib; header->fEmitsPointSize = isPoints; // Currently the experimental GS will only work with triangle prims (and it doesn't do anything // other than pass through values from the VS to the FS anyway). #if GR_GL_EXPERIMENTAL_GS #if 0 header->fExperimentalGS = gpu->caps().geometryShaderSupport(); #else header->fExperimentalGS = false; #endif #endif bool defaultToUniformInputs = GR_GL_NO_CONSTANT_ATTRIBUTES || gpu->caps()->pathRenderingSupport(); if (colorIsTransBlack) { header->fColorInput = kTransBlack_ColorInput; } else if (colorIsSolidWhite) { header->fColorInput = kSolidWhite_ColorInput; } else if (defaultToUniformInputs && !requiresColorAttrib) { header->fColorInput = kUniform_ColorInput; } else { header->fColorInput = kAttribute_ColorInput; header->fHasVertexCode = true; } bool covIsSolidWhite = !requiresCoverageAttrib && 0xffffffff == drawState.getCoverageColor(); if (skipCoverage) { header->fCoverageInput = kTransBlack_ColorInput; } else if (covIsSolidWhite || !inputCoverageIsUsed) { header->fCoverageInput = kSolidWhite_ColorInput; } else if (defaultToUniformInputs && !requiresCoverageAttrib) { header->fCoverageInput = kUniform_ColorInput; } else { header->fCoverageInput = kAttribute_ColorInput; header->fHasVertexCode = true; } if (readsDst) { SkASSERT(NULL != dstCopy || gpu->caps()->dstReadInShaderSupport()); const GrTexture* dstCopyTexture = NULL; if (NULL != dstCopy) { dstCopyTexture = dstCopy->texture(); } header->fDstReadKey = GrGLShaderBuilder::KeyForDstRead(dstCopyTexture, gpu->glCaps()); SkASSERT(0 != header->fDstReadKey); } else { header->fDstReadKey = 0; } if (readFragPosition) { header->fFragPosKey = GrGLShaderBuilder::KeyForFragmentPosition(drawState.getRenderTarget(), gpu->glCaps()); } else { header->fFragPosKey = 0; } // Record attribute indices header->fPositionAttributeIndex = drawState.positionAttributeIndex(); header->fLocalCoordAttributeIndex = drawState.localCoordAttributeIndex(); // For constant color and coverage we need an attribute with an index beyond those already set int availableAttributeIndex = drawState.getVertexAttribCount(); if (requiresColorAttrib) { header->fColorAttributeIndex = drawState.colorVertexAttributeIndex(); } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fColorInput) { SkASSERT(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt); header->fColorAttributeIndex = availableAttributeIndex; availableAttributeIndex++; } else { header->fColorAttributeIndex = -1; } if (requiresCoverageAttrib) { header->fCoverageAttributeIndex = drawState.coverageVertexAttributeIndex(); } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fCoverageInput) { SkASSERT(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt); header->fCoverageAttributeIndex = availableAttributeIndex; } else { header->fCoverageAttributeIndex = -1; } // Here we deal with whether/how we handle color and coverage separately. // Set these defaults and then possibly change our mind if there is coverage. header->fDiscardIfZeroCoverage = false; header->fCoverageOutput = kModulate_CoverageOutput; // If we do have coverage determine whether it matters. bool separateCoverageFromColor = false; if (!drawState.isCoverageDrawing() && !skipCoverage && (drawState.numCoverageStages() > 0 || requiresCoverageAttrib)) { // If we're stenciling then we want to discard samples that have zero coverage if (drawState.getStencil().doesWrite()) { header->fDiscardIfZeroCoverage = true; separateCoverageFromColor = true; } if (gpu->caps()->dualSourceBlendingSupport() && !(blendOpts & (GrDrawState::kEmitCoverage_BlendOptFlag | GrDrawState::kCoverageAsAlpha_BlendOptFlag))) { if (kZero_GrBlendCoeff == dstCoeff) { // write the coverage value to second color header->fCoverageOutput = kSecondaryCoverage_CoverageOutput; separateCoverageFromColor = true; } else if (kSA_GrBlendCoeff == dstCoeff) { // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. header->fCoverageOutput = kSecondaryCoverageISA_CoverageOutput; separateCoverageFromColor = true; } else if (kSC_GrBlendCoeff == dstCoeff) { // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. header->fCoverageOutput = kSecondaryCoverageISC_CoverageOutput; separateCoverageFromColor = true; } } else if (readsDst && kOne_GrBlendCoeff == srcCoeff && kZero_GrBlendCoeff == dstCoeff) { header->fCoverageOutput = kCombineWithDst_CoverageOutput; separateCoverageFromColor = true; } } if (!skipColor) { for (int s = firstEffectiveColorStage; s < drawState.numColorStages(); ++s) { colorStages->push_back(&drawState.getColorStage(s)); } } if (!skipCoverage) { SkTArray<const GrEffectStage*, true>* array; if (separateCoverageFromColor) { array = coverageStages; } else { array = colorStages; } for (int s = firstEffectiveCoverageStage; s < drawState.numCoverageStages(); ++s) { array->push_back(&drawState.getCoverageStage(s)); } } header->fColorEffectCnt = colorStages->count(); header->fCoverageEffectCnt = coverageStages->count(); *desc->checksum() = 0; *desc->checksum() = SkChecksum::Compute(reinterpret_cast<uint32_t*>(desc->fKey.get()), newKeyLength); desc->fInitialized = true; }
bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path, const SkStroke& stroke, GrDrawTarget* target, bool antiAlias) { GrAssert(!antiAlias); GrAssert(0 != stroke.getWidthIfStroked()); GrDrawState* drawState = target->drawState(); GrAssert(drawState->getStencil().isDisabled()); SkAutoTUnref<GrPath> p(fGpu->createPath(path)); SkPath::FillType nonInvertedFill = SkPath::NonInverseFill(path.getFillType()); target->stencilPath(p, stroke, nonInvertedFill); // TODO: Use built in cover operation rather than a rect draw. This will require making our // fragment shaders be able to eat varyings generated by a matrix. // fill the path, zero out the stencil GrRect bounds = p->getBounds(); SkScalar bloat = drawState->getViewMatrix().getMaxStretch() * SK_ScalarHalf; GrDrawState::AutoDeviceCoordDraw adcd; if (nonInvertedFill == path.getFillType()) { GR_STATIC_CONST_SAME_STENCIL(kStencilPass, kZero_StencilOp, kZero_StencilOp, kNotEqual_StencilFunc, 0xffff, 0x0000, 0xffff); *drawState->stencil() = kStencilPass; } else { GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass, kZero_StencilOp, kZero_StencilOp, // We know our rect will hit pixels outside the clip and the user bits will be 0 // outside the clip. So we can't just fill where the user bits are 0. We also need to // check that the clip bit is set. kEqualIfInClip_StencilFunc, 0xffff, 0x0000, 0xffff); SkMatrix vmi; bounds.setLTRB(0, 0, SkIntToScalar(drawState->getRenderTarget()->width()), SkIntToScalar(drawState->getRenderTarget()->height())); // mapRect through persp matrix may not be correct if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) { vmi.mapRect(&bounds); // theoretically could set bloat = 0, instead leave it because of matrix inversion // precision. } else { adcd.set(drawState); bloat = 0; } *drawState->stencil() = kInvertedStencilPass; } bounds.outset(bloat, bloat); target->drawSimpleRect(bounds, NULL); target->drawState()->stencil()->setDisabled(); return true; }
bool GrDefaultPathRenderer::internalDrawPath(const SkPath& path, GrPathFill fill, GrDrawTarget* target, bool stencilOnly) { GrMatrix viewM = target->getDrawState().getViewMatrix(); GrScalar tol = GR_Scalar1; tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds()); int vertexCnt; int indexCnt; GrPrimitiveType primType; GrDrawTarget::AutoReleaseGeometry arg; if (!this->createGeom(path, fill, tol, target, &primType, &vertexCnt, &indexCnt, &arg)) { return false; } GrAssert(NULL != target); GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kPreserve_ASRInit); GrDrawState* drawState = target->drawState(); bool colorWritesWereDisabled = drawState->isColorWriteDisabled(); // face culling doesn't make sense here GrAssert(GrDrawState::kBoth_DrawFace == drawState->getDrawFace()); int passCount = 0; const GrStencilSettings* passes[3]; GrDrawState::DrawFace drawFace[3]; bool reverse = false; bool lastPassIsBounds; if (kHairLine_GrPathFill == fill) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; } else { passes[0] = NULL; } lastPassIsBounds = false; drawFace[0] = GrDrawState::kBoth_DrawFace; } else { if (single_pass_path(path, fill)) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; } else { passes[0] = NULL; } drawFace[0] = GrDrawState::kBoth_DrawFace; lastPassIsBounds = false; } else { switch (fill) { case kInverseEvenOdd_GrPathFill: reverse = true; // fallthrough case kEvenOdd_GrPathFill: passes[0] = &gEOStencilPass; if (stencilOnly) { passCount = 1; lastPassIsBounds = false; } else { passCount = 2; lastPassIsBounds = true; if (reverse) { passes[1] = &gInvEOColorPass; } else { passes[1] = &gEOColorPass; } } drawFace[0] = drawFace[1] = GrDrawState::kBoth_DrawFace; break; case kInverseWinding_GrPathFill: reverse = true; // fallthrough case kWinding_GrPathFill: if (fSeparateStencil) { if (fStencilWrapOps) { passes[0] = &gWindStencilSeparateWithWrap; } else { passes[0] = &gWindStencilSeparateNoWrap; } passCount = 2; drawFace[0] = GrDrawState::kBoth_DrawFace; } else { if (fStencilWrapOps) { passes[0] = &gWindSingleStencilWithWrapInc; passes[1] = &gWindSingleStencilWithWrapDec; } else { passes[0] = &gWindSingleStencilNoWrapInc; passes[1] = &gWindSingleStencilNoWrapDec; } // which is cw and which is ccw is arbitrary. drawFace[0] = GrDrawState::kCW_DrawFace; drawFace[1] = GrDrawState::kCCW_DrawFace; passCount = 3; } if (stencilOnly) { lastPassIsBounds = false; --passCount; } else { lastPassIsBounds = true; drawFace[passCount-1] = GrDrawState::kBoth_DrawFace; if (reverse) { passes[passCount-1] = &gInvWindColorPass; } else { passes[passCount-1] = &gWindColorPass; } } break; default: GrAssert(!"Unknown path fFill!"); return false; } } } { for (int p = 0; p < passCount; ++p) { drawState->setDrawFace(drawFace[p]); if (NULL != passes[p]) { *drawState->stencil() = *passes[p]; } if (lastPassIsBounds && (p == passCount-1)) { if (!colorWritesWereDisabled) { drawState->disableState(GrDrawState::kNoColorWrites_StateBit); } GrRect bounds; GrDrawState::AutoDeviceCoordDraw adcd; if (reverse) { GrAssert(NULL != drawState->getRenderTarget()); // draw over the whole world. bounds.setLTRB(0, 0, GrIntToScalar(drawState->getRenderTarget()->width()), GrIntToScalar(drawState->getRenderTarget()->height())); GrMatrix vmi; // mapRect through persp matrix may not be correct if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) { vmi.mapRect(&bounds); } else { adcd.set(drawState); } } else { bounds = path.getBounds(); } GrDrawTarget::AutoGeometryPush agp(target); target->drawSimpleRect(bounds, NULL); } else { if (passCount > 1) { drawState->enableState(GrDrawState::kNoColorWrites_StateBit); } if (indexCnt) { target->drawIndexed(primType, 0, 0, vertexCnt, indexCnt); } else { target->drawNonIndexed(primType, 0, vertexCnt); } } } } return true; }
bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) { const GrIRect* r = NULL; GrIRect clipRect; GrDrawState* drawState = this->drawState(); const GrRenderTarget* rt = drawState->getRenderTarget(); // GrDrawTarget should have filtered this for us GrAssert(NULL != rt); if (drawState->isClipState()) { GrRect bounds; GrRect rtRect; rtRect.setLTRB(0, 0, GrIntToScalar(rt->width()), GrIntToScalar(rt->height())); if (fClip.hasConservativeBounds()) { bounds = fClip.getConservativeBounds(); if (!bounds.intersect(rtRect)) { bounds.setEmpty(); } } else { bounds = rtRect; } bounds.roundOut(&clipRect); if (clipRect.isEmpty()) { clipRect.setLTRB(0,0,0,0); } r = &clipRect; // use the stencil clip if we can't represent the clip as a rectangle. fClipInStencil = !fClip.isRect() && !fClip.isEmpty() && !bounds.isEmpty(); // TODO: dynamically attach a SB when needed. GrStencilBuffer* stencilBuffer = rt->getStencilBuffer(); if (fClipInStencil && NULL == stencilBuffer) { return false; } if (fClipInStencil && stencilBuffer->mustRenderClip(fClip, rt->width(), rt->height())) { stencilBuffer->setLastClip(fClip, rt->width(), rt->height()); // we set the current clip to the bounds so that our recursive // draws are scissored to them. We use the copy of the complex clip // we just stashed on the SB to render from. We set it back after // we finish drawing it into the stencil. const GrClip& clip = stencilBuffer->getLastClip(); fClip.setFromRect(bounds); AutoStateRestore asr(this); AutoGeometryPush agp(this); drawState->setViewMatrix(GrMatrix::I()); this->flushScissor(NULL); #if !VISUALIZE_COMPLEX_CLIP drawState->enableState(GrDrawState::kNoColorWrites_StateBit); #else drawState->disableState(GrDrawState::kNoColorWrites_StateBit); #endif int count = clip.getElementCount(); int clipBit = stencilBuffer->bits(); SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); clipBit = (1 << (clipBit-1)); bool clearToInside; GrSetOp startOp = kReplace_SetOp; // suppress warning int start = process_initial_clip_elements(clip, rtRect, &clearToInside, &startOp); this->clearStencilClip(clipRect, clearToInside); // walk through each clip element and perform its set op // with the existing clip. for (int c = start; c < count; ++c) { GrPathFill fill; bool fillInverted; // enabled at bottom of loop drawState->disableState(kModifyStencilClip_StateBit); bool canRenderDirectToStencil; // can the clip element be drawn // directly to the stencil buffer // with a non-inverted fill rule // without extra passes to // resolve in/out status. GrPathRenderer* pr = NULL; const GrPath* clipPath = NULL; GrPathRenderer::AutoClearPath arp; if (kRect_ClipType == clip.getElementType(c)) { canRenderDirectToStencil = true; fill = kEvenOdd_PathFill; fillInverted = false; // there is no point in intersecting a screen filling // rectangle. if (kIntersect_SetOp == clip.getOp(c) && clip.getRect(c).contains(rtRect)) { continue; } } else { fill = clip.getPathFill(c); fillInverted = GrIsFillInverted(fill); fill = GrNonInvertedFill(fill); clipPath = &clip.getPath(c); pr = this->getClipPathRenderer(*clipPath, fill); if (NULL == pr) { fClipInStencil = false; fClip = clip; return false; } canRenderDirectToStencil = !pr->requiresStencilPass(this, *clipPath, fill); arp.set(pr, this, clipPath, fill, false, NULL); } GrSetOp op = (c == start) ? startOp : clip.getOp(c); int passes; GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses]; bool canDrawDirectToClip; // Given the renderer, the element, // fill rule, and set operation can // we render the element directly to // stencil bit used for clipping. canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, clipBit, fillInverted, &passes, stencilSettings); // draw the element to the client stencil bits if necessary if (!canDrawDirectToClip) { GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil, kIncClamp_StencilOp, kIncClamp_StencilOp, kAlways_StencilFunc, 0xffff, 0x0000, 0xffff); SET_RANDOM_COLOR if (kRect_ClipType == clip.getElementType(c)) { *drawState->stencil() = gDrawToStencil; this->drawSimpleRect(clip.getRect(c), NULL, 0); } else { if (canRenderDirectToStencil) { *drawState->stencil() = gDrawToStencil; pr->drawPath(0); } else { pr->drawPathToStencil(); } } } // now we modify the clip bit by rendering either the clip // element directly or a bounding rect of the entire clip. drawState->enableState(kModifyStencilClip_StateBit); for (int p = 0; p < passes; ++p) { *drawState->stencil() = stencilSettings[p]; if (canDrawDirectToClip) { if (kRect_ClipType == clip.getElementType(c)) { SET_RANDOM_COLOR this->drawSimpleRect(clip.getRect(c), NULL, 0); } else { SET_RANDOM_COLOR pr->drawPath(0); } } else { SET_RANDOM_COLOR this->drawSimpleRect(bounds, NULL, 0); } } }
bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path, const SkStrokeRec& stroke, GrDrawTarget* target, bool antiAlias) { SkASSERT(!antiAlias); SkASSERT(!stroke.isHairlineStyle()); GrDrawState* drawState = target->drawState(); SkASSERT(drawState->getStencil().isDisabled()); SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke)); if (path.isInverseFillType()) { GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass, kZero_StencilOp, kZero_StencilOp, // We know our rect will hit pixels outside the clip and the user bits will be 0 // outside the clip. So we can't just fill where the user bits are 0. We also need to // check that the clip bit is set. kEqualIfInClip_StencilFunc, 0xffff, 0x0000, 0xffff); drawState->setStencil(kInvertedStencilPass); // fake inverse with a stencil and cover target->stencilPath(p, convert_skpath_filltype(path.getFillType())); GrDrawState::AutoViewMatrixRestore avmr; SkRect bounds = SkRect::MakeLTRB(0, 0, SkIntToScalar(drawState->getRenderTarget()->width()), SkIntToScalar(drawState->getRenderTarget()->height())); SkMatrix vmi; // mapRect through persp matrix may not be correct if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) { vmi.mapRect(&bounds); // theoretically could set bloat = 0, instead leave it because of matrix inversion // precision. SkScalar bloat = drawState->getViewMatrix().getMaxScale() * SK_ScalarHalf; bounds.outset(bloat, bloat); } else { avmr.setIdentity(drawState); } target->drawSimpleRect(bounds); } else { GR_STATIC_CONST_SAME_STENCIL(kStencilPass, kZero_StencilOp, kZero_StencilOp, kNotEqual_StencilFunc, 0xffff, 0x0000, 0xffff); drawState->setStencil(kStencilPass); target->drawPath(p, convert_skpath_filltype(path.getFillType())); } target->drawState()->stencil()->setDisabled(); return true; }
void GrInOrderDrawBuffer::drawRect(const GrRect& rect, const SkMatrix* matrix, const GrRect* srcRects[], const SkMatrix* srcMatrices[]) { GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad)); GrAssert(!(fDraws.empty() && fCurrQuad)); GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer)); GrDrawState* drawState = this->drawState(); // if we have a quad IB then either append to the previous run of // rects or start a new run if (fMaxQuads) { bool appendToPreviousDraw = false; GrVertexLayout layout = GetRectVertexLayout(srcRects); // Batching across colors means we move the draw color into the // rect's vertex colors to allow greater batching (a lot of rects // in a row differing only in color is a common occurence in tables). bool batchAcrossColors = true; if (!this->getCaps().dualSourceBlendingSupport()) { for (int s = 0; s < GrDrawState::kNumStages; ++s) { if (this->getDrawState().isStageEnabled(s)) { // We disable batching across colors when there is a texture // present because (by pushing the the color to the vertices) // Ganesh loses track of the rect's opacity. This, in turn, can // cause some of the blending optimizations to be disabled. This // becomes a huge problem on some of the smaller devices where // shader derivatives and dual source blending aren't supported. // In those cases paths are often drawn to a texture and then // drawn as a texture (using this method). Because dual source // blending is disabled (and the blend optimizations are short // circuited) some of the more esoteric blend modes can no longer // be supported. // TODO: add tracking of batchAcrossColors's opacity batchAcrossColors = false; break; } } } if (batchAcrossColors) { layout |= GrDrawState::kColor_VertexLayoutBit; } AutoReleaseGeometry geo(this, layout, 4, 0); if (!geo.succeeded()) { GrPrintf("Failed to get space for vertices!\n"); return; } SkMatrix combinedMatrix = drawState->getViewMatrix(); // We go to device space so that matrix changes allow us to concat // rect draws. When the caller has provided explicit source rects // then we don't want to modify the stages' matrices. Otherwise // we have to account for the view matrix change in the stage // matrices. uint32_t explicitCoordMask = 0; if (srcRects) { for (int s = 0; s < GrDrawState::kNumStages; ++s) { if (srcRects[s]) { explicitCoordMask |= (1 << s); } } } GrDrawState::AutoDeviceCoordDraw adcd(this->drawState(), explicitCoordMask); if (!adcd.succeeded()) { return; } if (NULL != matrix) { combinedMatrix.preConcat(*matrix); } SetRectVertices(rect, &combinedMatrix, srcRects, srcMatrices, this->getDrawState().getColor(), layout, geo.vertices()); // Now that the paint's color is stored in the vertices set it to // white so that the following code can batch all the rects regardless // of paint color GrDrawState::AutoColorRestore acr(this->drawState(), batchAcrossColors ? SK_ColorWHITE : this->getDrawState().getColor()); // we don't want to miss an opportunity to batch rects together // simply because the clip has changed if the clip doesn't affect // the rect. bool disabledClip = false; if (drawState->isClipState()) { GrRect devClipRect; bool isIntersectionOfRects = false; const GrClipData* clip = this->getClip(); clip->fClipStack->getConservativeBounds(-clip->fOrigin.fX, -clip->fOrigin.fY, drawState->getRenderTarget()->width(), drawState->getRenderTarget()->height(), &devClipRect, &isIntersectionOfRects); if (isIntersectionOfRects) { // If the clip rect touches the edge of the viewport, extended it // out (close) to infinity to avoid bogus intersections. // We might consider a more exact clip to viewport if this // conservative test fails. const GrRenderTarget* target = drawState->getRenderTarget(); if (0 >= devClipRect.fLeft) { devClipRect.fLeft = SK_ScalarMin; } if (target->width() <= devClipRect.fRight) { devClipRect.fRight = SK_ScalarMax; } if (0 >= devClipRect.top()) { devClipRect.fTop = SK_ScalarMin; } if (target->height() <= devClipRect.fBottom) { devClipRect.fBottom = SK_ScalarMax; } int stride = GrDrawState::VertexSize(layout); bool insideClip = true; for (int v = 0; v < 4; ++v) { const GrPoint& p = *GrDrawState::GetVertexPoint(geo.vertices(), v, stride); if (!devClipRect.contains(p)) { insideClip = false; break; } } if (insideClip) { drawState->disableState(GrDrawState::kClip_StateBit); disabledClip = true; } } } if (!this->needsNewClip() && !this->needsNewState() && fCurrQuad > 0 && fCurrQuad < fMaxQuads && layout == fLastRectVertexLayout) { int vsize = GrDrawState::VertexSize(layout); Draw& lastDraw = fDraws.back(); GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer); GrAssert(kTriangles_GrPrimitiveType == lastDraw.fPrimitiveType); GrAssert(0 == lastDraw.fVertexCount % 4); GrAssert(0 == lastDraw.fIndexCount % 6); GrAssert(0 == lastDraw.fStartIndex); GeometryPoolState& poolState = fGeoPoolStateStack.back(); appendToPreviousDraw = kDraw_Cmd == fCmds.back() && lastDraw.fVertexBuffer == poolState.fPoolVertexBuffer && (fCurrQuad * 4 + lastDraw.fStartVertex) == poolState.fPoolStartVertex; if (appendToPreviousDraw) { lastDraw.fVertexCount += 4; lastDraw.fIndexCount += 6; fCurrQuad += 1; // we reserved above, so we should be the first // use of this vertex reservation. GrAssert(0 == poolState.fUsedPoolVertexBytes); poolState.fUsedPoolVertexBytes = 4 * vsize; } } if (!appendToPreviousDraw) { this->setIndexSourceToBuffer(fQuadIndexBuffer); this->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 4, 6); fCurrQuad = 1; fLastRectVertexLayout = layout; } if (disabledClip) { drawState->enableState(GrDrawState::kClip_StateBit); } fInstancedDrawTracker.reset(); } else { INHERITED::drawRect(rect, matrix, srcRects, srcMatrices); } }
void GrDefaultPathRenderer::onDrawPath(GrDrawState::StageMask stageMask, bool stencilOnly) { GrMatrix viewM = fTarget->getDrawState().getViewMatrix(); GrScalar tol = GR_Scalar1; tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, fPath->getBounds()); GrDrawState* drawState = fTarget->drawState(); // FIXME: It's really dumb that we recreate the verts for a new vertex // layout. We only do that because the GrDrawTarget API doesn't allow // us to change the vertex layout after reserveVertexSpace(). We won't // actually change the vertex data when the layout changes since all the // stages reference the positions (rather than having separate tex coords) // and we don't ever have per-vert colors. In practice our call sites // won't change the stages in use inside a setPath / removePath pair. But // it is a silly limitation of the GrDrawTarget design that should be fixed. if (tol != fPreviousSrcTol || stageMask != fPreviousStages) { if (!this->createGeom(tol, stageMask)) { return; } } GrAssert(NULL != fTarget); GrDrawTarget::AutoStateRestore asr(fTarget); bool colorWritesWereDisabled = drawState->isColorWriteDisabled(); // face culling doesn't make sense here GrAssert(GrDrawState::kBoth_DrawFace == drawState->getDrawFace()); int passCount = 0; const GrStencilSettings* passes[3]; GrDrawState::DrawFace drawFace[3]; bool reverse = false; bool lastPassIsBounds; if (kHairLine_PathFill == fFill) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; } else { passes[0] = NULL; } lastPassIsBounds = false; drawFace[0] = GrDrawState::kBoth_DrawFace; } else { if (single_pass_path(*fTarget, *fPath, fFill)) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; } else { passes[0] = NULL; } drawFace[0] = GrDrawState::kBoth_DrawFace; lastPassIsBounds = false; } else { switch (fFill) { case kInverseEvenOdd_PathFill: reverse = true; // fallthrough case kEvenOdd_PathFill: passes[0] = &gEOStencilPass; if (stencilOnly) { passCount = 1; lastPassIsBounds = false; } else { passCount = 2; lastPassIsBounds = true; if (reverse) { passes[1] = &gInvEOColorPass; } else { passes[1] = &gEOColorPass; } } drawFace[0] = drawFace[1] = GrDrawState::kBoth_DrawFace; break; case kInverseWinding_PathFill: reverse = true; // fallthrough case kWinding_PathFill: if (fSeparateStencil) { if (fStencilWrapOps) { passes[0] = &gWindStencilSeparateWithWrap; } else { passes[0] = &gWindStencilSeparateNoWrap; } passCount = 2; drawFace[0] = GrDrawState::kBoth_DrawFace; } else { if (fStencilWrapOps) { passes[0] = &gWindSingleStencilWithWrapInc; passes[1] = &gWindSingleStencilWithWrapDec; } else { passes[0] = &gWindSingleStencilNoWrapInc; passes[1] = &gWindSingleStencilNoWrapDec; } // which is cw and which is ccw is arbitrary. drawFace[0] = GrDrawState::kCW_DrawFace; drawFace[1] = GrDrawState::kCCW_DrawFace; passCount = 3; } if (stencilOnly) { lastPassIsBounds = false; --passCount; } else { lastPassIsBounds = true; drawFace[passCount-1] = GrDrawState::kBoth_DrawFace; if (reverse) { passes[passCount-1] = &gInvWindColorPass; } else { passes[passCount-1] = &gWindColorPass; } } break; default: GrAssert(!"Unknown path fFill!"); return; } } } { for (int p = 0; p < passCount; ++p) { drawState->setDrawFace(drawFace[p]); if (NULL != passes[p]) { *drawState->stencil() = *passes[p]; } if (lastPassIsBounds && (p == passCount-1)) { if (!colorWritesWereDisabled) { drawState->disableState(GrDrawState::kNoColorWrites_StateBit); } GrRect bounds; if (reverse) { GrAssert(NULL != drawState->getRenderTarget()); // draw over the whole world. bounds.setLTRB(0, 0, GrIntToScalar(drawState->getRenderTarget()->width()), GrIntToScalar(drawState->getRenderTarget()->height())); GrMatrix vmi; // mapRect through persp matrix may not be correct if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) { vmi.mapRect(&bounds); } else { if (stageMask) { if (!drawState->getViewInverse(&vmi)) { GrPrintf("Could not invert matrix."); return; } drawState->preConcatSamplerMatrices(stageMask, vmi); } drawState->setViewMatrix(GrMatrix::I()); } } else { bounds = fPath->getBounds(); bounds.offset(fTranslate); } GrDrawTarget::AutoGeometryPush agp(fTarget); fTarget->drawSimpleRect(bounds, NULL, stageMask); } else { if (passCount > 1) { drawState->enableState(GrDrawState::kNoColorWrites_StateBit); } if (fUseIndexedDraw) { fTarget->drawIndexed(fPrimitiveType, 0, 0, fVertexCnt, fIndexCnt); } else { int baseVertex = 0; for (int sp = 0; sp < fSubpathCount; ++sp) { fTarget->drawNonIndexed(fPrimitiveType, baseVertex, fSubpathVertCount[sp]); baseVertex += fSubpathVertCount[sp]; } } } } } }
//////////////////////////////////////////////////////////////////////////////// // Create a 1-bit clip mask in the stencil buffer bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu, const GrClip& clipIn, const GrRect& bounds, ScissoringSettings* scissorSettings) { GrAssert(fClipMaskInStencil); GrDrawState* drawState = gpu->drawState(); GrAssert(drawState->isClipState()); GrRenderTarget* rt = drawState->getRenderTarget(); GrAssert(NULL != rt); // TODO: dynamically attach a SB when needed. GrStencilBuffer* stencilBuffer = rt->getStencilBuffer(); if (NULL == stencilBuffer) { return false; } if (stencilBuffer->mustRenderClip(clipIn, rt->width(), rt->height())) { stencilBuffer->setLastClip(clipIn, rt->width(), rt->height()); // we set the current clip to the bounds so that our recursive // draws are scissored to them. We use the copy of the complex clip // we just stashed on the SB to render from. We set it back after // we finish drawing it into the stencil. const GrClip& clipCopy = stencilBuffer->getLastClip(); gpu->setClip(GrClip(bounds)); GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit); drawState = gpu->drawState(); drawState->setRenderTarget(rt); GrDrawTarget::AutoGeometryPush agp(gpu); gpu->disableScissor(); #if !VISUALIZE_COMPLEX_CLIP drawState->enableState(GrDrawState::kNoColorWrites_StateBit); #endif int count = clipCopy.getElementCount(); int clipBit = stencilBuffer->bits(); SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); clipBit = (1 << (clipBit-1)); GrIRect rtRect = GrIRect::MakeWH(rt->width(), rt->height()); bool clearToInside; SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning int start = process_initial_clip_elements(clipCopy, rtRect, &clearToInside, &startOp); gpu->clearStencilClip(scissorSettings->fScissorRect, clearToInside); // walk through each clip element and perform its set op // with the existing clip. for (int c = start; c < count; ++c) { GrPathFill fill; bool fillInverted; // enabled at bottom of loop drawState->disableState(GrGpu::kModifyStencilClip_StateBit); bool canRenderDirectToStencil; // can the clip element be drawn // directly to the stencil buffer // with a non-inverted fill rule // without extra passes to // resolve in/out status. SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c); GrPathRenderer* pr = NULL; const SkPath* clipPath = NULL; if (kRect_ClipType == clipCopy.getElementType(c)) { canRenderDirectToStencil = true; fill = kEvenOdd_PathFill; fillInverted = false; // there is no point in intersecting a screen filling // rectangle. if (SkRegion::kIntersect_Op == op && contains(clipCopy.getRect(c), rtRect)) { continue; } } else { fill = clipCopy.getPathFill(c); fillInverted = GrIsFillInverted(fill); fill = GrNonInvertedFill(fill); clipPath = &clipCopy.getPath(c); pr = this->getContext()->getPathRenderer(*clipPath, fill, gpu, false, true); if (NULL == pr) { fClipMaskInStencil = false; gpu->setClip(clipCopy); // restore to the original return false; } canRenderDirectToStencil = !pr->requiresStencilPass(*clipPath, fill, gpu); } int passes; GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses]; bool canDrawDirectToClip; // Given the renderer, the element, // fill rule, and set operation can // we render the element directly to // stencil bit used for clipping. canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, clipBit, fillInverted, &passes, stencilSettings); // draw the element to the client stencil bits if necessary if (!canDrawDirectToClip) { GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil, kIncClamp_StencilOp, kIncClamp_StencilOp, kAlways_StencilFunc, 0xffff, 0x0000, 0xffff); SET_RANDOM_COLOR if (kRect_ClipType == clipCopy.getElementType(c)) { *drawState->stencil() = gDrawToStencil; gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0); } else { if (canRenderDirectToStencil) { *drawState->stencil() = gDrawToStencil; pr->drawPath(*clipPath, fill, NULL, gpu, 0, false); } else { pr->drawPathToStencil(*clipPath, fill, gpu); } } } // now we modify the clip bit by rendering either the clip // element directly or a bounding rect of the entire clip. drawState->enableState(GrGpu::kModifyStencilClip_StateBit); for (int p = 0; p < passes; ++p) { *drawState->stencil() = stencilSettings[p]; if (canDrawDirectToClip) { if (kRect_ClipType == clipCopy.getElementType(c)) { SET_RANDOM_COLOR gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0); } else { SET_RANDOM_COLOR pr->drawPath(*clipPath, fill, NULL, gpu, 0, false); } } else { SET_RANDOM_COLOR gpu->drawSimpleRect(bounds, NULL, 0); } } }
//////////////////////////////////////////////////////////////////////////////// // sort out what kind of clip mask needs to be created: alpha, stencil, // scissor, or entirely software bool GrClipMaskManager::createClipMask(GrGpu* gpu, const GrClip& clipIn, ScissoringSettings* scissorSettings) { GrAssert(scissorSettings); scissorSettings->fEnableScissoring = false; fClipMaskInStencil = false; fClipMaskInAlpha = false; GrDrawState* drawState = gpu->drawState(); if (!drawState->isClipState()) { return true; } GrRenderTarget* rt = drawState->getRenderTarget(); // GrDrawTarget should have filtered this for us GrAssert(NULL != rt); #if GR_SW_CLIP // If MSAA is enabled we can do everything in the stencil buffer. // Otherwise check if we should just create the entire clip mask // in software (this will only happen if the clip mask is anti-aliased // and too complex for the gpu to handle in its entirety) if (0 == rt->numSamples() && useSWOnlyPath(gpu, clipIn)) { // The clip geometry is complex enough that it will be more // efficient to create it entirely in software GrTexture* result = NULL; GrIRect bound; if (this->createSoftwareClipMask(gpu, clipIn, &result, &bound)) { fClipMaskInAlpha = true; setup_drawstate_aaclip(gpu, result, bound); return true; } // if SW clip mask creation fails fall through to the other // two possible methods (bottoming out at stencil clipping) } #endif // GR_SW_CLIP #if GR_AA_CLIP // If MSAA is enabled use the (faster) stencil path for AA clipping // otherwise the alpha clip mask is our only option if (0 == rt->numSamples() && clipIn.requiresAA()) { // Since we are going to create a destination texture of the correct // size for the mask (rather than being bound by the size of the // render target) we aren't going to use scissoring like the stencil // path does (see scissorSettings below) GrTexture* result = NULL; GrIRect bound; if (this->createAlphaClipMask(gpu, clipIn, &result, &bound)) { fClipMaskInAlpha = true; setup_drawstate_aaclip(gpu, result, bound); return true; } // if alpha clip mask creation fails fall through to the stencil // buffer method } #endif // GR_AA_CLIP // Either a hard (stencil buffer) clip was explicitly requested or // an antialiased clip couldn't be created. In either case, free up // the texture in the antialiased mask cache. // TODO: this may require more investigation. Ganesh performs a lot of // utility draws (e.g., clears, InOrderDrawBuffer playbacks) that hit // the stencil buffer path. These may be "incorrectly" clearing the // AA cache. fAACache.reset(); GrRect bounds; GrRect rtRect; rtRect.setLTRB(0, 0, GrIntToScalar(rt->width()), GrIntToScalar(rt->height())); if (clipIn.hasConservativeBounds()) { bounds = clipIn.getConservativeBounds(); if (!bounds.intersect(rtRect)) { bounds.setEmpty(); } } else { bounds = rtRect; } bounds.roundOut(&scissorSettings->fScissorRect); if (scissorSettings->fScissorRect.isEmpty()) { scissorSettings->fScissorRect.setLTRB(0,0,0,0); // TODO: I think we can do an early exit here - after refactoring try: // set fEnableScissoring to true but leave fClipMaskInStencil false // and return - everything is going to be scissored away anyway! } scissorSettings->fEnableScissoring = true; // use the stencil clip if we can't represent the clip as a rectangle. fClipMaskInStencil = !clipIn.isRect() && !clipIn.isEmpty() && !bounds.isEmpty(); if (fClipMaskInStencil) { return this->createStencilClipMask(gpu, clipIn, bounds, scissorSettings); } return true; }