Chromium Code Reviews| Index: src/gpu/gl/GrGLPathRendering.cpp | 
| diff --git a/src/gpu/gl/GrGLPathRendering.cpp b/src/gpu/gl/GrGLPathRendering.cpp | 
| index 249d98139ad65b9d888e161ba32c7f6281a52046..00848bd9ed5e45f89d47a2861d86e799613ed98b 100644 | 
| --- a/src/gpu/gl/GrGLPathRendering.cpp | 
| +++ b/src/gpu/gl/GrGLPathRendering.cpp | 
| @@ -9,14 +9,47 @@ | 
| #include "gl/GrGLInterface.h" | 
| #include "gl/GrGLNameAllocator.h" | 
| #include "gl/GrGLUtil.h" | 
| +#include "gl/GrGpuGL.h" | 
| + | 
| +#include "GrGLPath.h" | 
| +#include "GrGLPathRange.h" | 
| +#include "GrGLPathRendering.h" | 
| #define GL_CALL(X) GR_GL_CALL(fGLInterface.get(), X) | 
| #define GL_CALL_RET(RET, X) GR_GL_CALL_RET(fGLInterface.get(), RET, X) | 
| + | 
| +static const GrGLenum gXformType2GLType[] = { | 
| + GR_GL_NONE, | 
| + GR_GL_TRANSLATE_X, | 
| + GR_GL_TRANSLATE_Y, | 
| + GR_GL_TRANSLATE_2D, | 
| + GR_GL_TRANSPOSE_AFFINE_2D | 
| +}; | 
| + | 
| +GR_STATIC_ASSERT(0 == GrPathRendering::kNone_PathTransformType); | 
| +GR_STATIC_ASSERT(1 == GrPathRendering::kTranslateX_PathTransformType); | 
| +GR_STATIC_ASSERT(2 == GrPathRendering::kTranslateY_PathTransformType); | 
| +GR_STATIC_ASSERT(3 == GrPathRendering::kTranslate_PathTransformType); | 
| +GR_STATIC_ASSERT(4 == GrPathRendering::kAffine_PathTransformType); | 
| +GR_STATIC_ASSERT(GrPathRendering::kAffine_PathTransformType == GrPathRendering::kLast_PathTransformType); | 
| + | 
| +static GrGLenum gr_stencil_op_to_gl_path_rendering_fill_mode(GrStencilOp op) { | 
| + switch (op) { | 
| + default: | 
| + SkFAIL("Unexpected path fill."); | 
| + /* fallthrough */; | 
| + case kIncClamp_StencilOp: | 
| + return GR_GL_COUNT_UP; | 
| + case kInvert_StencilOp: | 
| + return GR_GL_INVERT; | 
| + } | 
| +} | 
| + | 
| class GrGLPathRenderingV12 : public GrGLPathRendering { | 
| public: | 
| - GrGLPathRenderingV12(const GrGLInterface* glInterface) | 
| - : GrGLPathRendering(glInterface) { | 
| + GrGLPathRenderingV12(GrGpuGL* gpu, const GrGLInterface* glInterface) | 
| + : GrGLPathRendering(gpu, glInterface) { | 
| } | 
| virtual GrGLvoid stencilThenCoverFillPath(GrGLuint path, GrGLenum fillMode, | 
| @@ -35,8 +68,8 @@ public: | 
| class GrGLPathRenderingV13 : public GrGLPathRenderingV12 { | 
| public: | 
| - GrGLPathRenderingV13(const GrGLInterface* glInterface) | 
| - : GrGLPathRenderingV12(glInterface) { | 
| + GrGLPathRenderingV13(GrGpuGL* gpu, const GrGLInterface* glInterface) | 
| + : GrGLPathRenderingV12(gpu, glInterface) { | 
| fCaps.fragmentInputGenSupport = true; | 
| } | 
| @@ -46,24 +79,26 @@ public: | 
| }; | 
| -GrGLPathRendering* GrGLPathRendering::Create(const GrGLInterface* glInterface) { | 
| +GrGLPathRendering* GrGLPathRendering::Create(GrGpuGL* gpu, const GrGLInterface* glInterface) { | 
| if (NULL == glInterface->fFunctions.fStencilThenCoverFillPath || | 
| NULL == glInterface->fFunctions.fStencilThenCoverStrokePath || | 
| NULL == glInterface->fFunctions.fStencilThenCoverFillPathInstanced || | 
| NULL == glInterface->fFunctions.fStencilThenCoverStrokePathInstanced) { | 
| - return new GrGLPathRendering(glInterface); | 
| + return new GrGLPathRendering(gpu, glInterface); | 
| } | 
| if (NULL == glInterface->fFunctions.fProgramPathFragmentInputGen) { | 
| - return new GrGLPathRenderingV12(glInterface); | 
| + return new GrGLPathRenderingV12(gpu, glInterface); | 
| } | 
| - return new GrGLPathRenderingV13(glInterface); | 
| + return new GrGLPathRenderingV13(gpu, glInterface); | 
| } | 
| -GrGLPathRendering::GrGLPathRendering(const GrGLInterface* glInterface) | 
| - : fGLInterface(SkRef(glInterface)) { | 
| +GrGLPathRendering::GrGLPathRendering(GrGpuGL* gpu, const GrGLInterface* glInterface) | 
| + : fGpu(gpu), | 
| + fGLInterface(SkRef(glInterface)) { | 
| memset(&fCaps, 0, sizeof(fCaps)); | 
| + fHWPathTexGenSettings.reset(fGpu->glCaps().maxFixedFunctionTextureCoords()); | 
| } | 
| GrGLPathRendering::~GrGLPathRendering() { | 
| @@ -73,6 +108,277 @@ void GrGLPathRendering::abandonGpuResources() { | 
| fPathNameAllocator.reset(NULL); | 
| } | 
| +void GrGLPathRendering::onResetContext() { | 
| + fHWProjectionMatrixState.invalidate(); | 
| + // we don't use the model view matrix. | 
| + GL_CALL(MatrixLoadIdentity(GR_GL_MODELVIEW)); | 
| + | 
| + for (int i = 0; i < fGpu->glCaps().maxFixedFunctionTextureCoords(); ++i) { | 
| + this->pathTexGen(GR_GL_TEXTURE0 + i, GR_GL_NONE, 0, NULL); | 
| + fHWPathTexGenSettings[i].fMode = GR_GL_NONE; | 
| + fHWPathTexGenSettings[i].fNumComponents = 0; | 
| + } | 
| + fHWActivePathTexGenSets = 0; | 
| + fHWPathStencilSettings.invalidate(); | 
| +} | 
| + | 
| +GrPath* GrGLPathRendering::createPath(const SkPath& inPath, const SkStrokeRec& stroke) { | 
| + return SkNEW_ARGS(GrGLPath, (fGpu, inPath, stroke)); | 
| +} | 
| + | 
| +GrPathRange* GrGLPathRendering::createPathRange(size_t size, const SkStrokeRec& stroke) { | 
| + return SkNEW_ARGS(GrGLPathRange, (fGpu, size, stroke)); | 
| +} | 
| + | 
| +void GrGLPathRendering::enablePathTexGen(int unitIdx, PathTexGenComponents components, | 
| + const GrGLfloat* coefficients) { | 
| + SkASSERT(components >= kS_PathTexGenComponents && | 
| + components <= kSTR_PathTexGenComponents); | 
| + SkASSERT(fGpu->glCaps().maxFixedFunctionTextureCoords() >= unitIdx); | 
| + | 
| + if (GR_GL_OBJECT_LINEAR == fHWPathTexGenSettings[unitIdx].fMode && | 
| + components == fHWPathTexGenSettings[unitIdx].fNumComponents && | 
| + !memcmp(coefficients, fHWPathTexGenSettings[unitIdx].fCoefficients, | 
| + 3 * components * sizeof(GrGLfloat))) { | 
| + return; | 
| + } | 
| + | 
| + fGpu->setTextureUnit(unitIdx); | 
| + | 
| + fHWPathTexGenSettings[unitIdx].fNumComponents = components; | 
| + this->pathTexGen(GR_GL_TEXTURE0 + unitIdx, GR_GL_OBJECT_LINEAR, components, coefficients); | 
| + | 
| + memcpy(fHWPathTexGenSettings[unitIdx].fCoefficients, coefficients, | 
| + 3 * components * sizeof(GrGLfloat)); | 
| +} | 
| + | 
| +void GrGLPathRendering::enablePathTexGen(int unitIdx, PathTexGenComponents components, | 
| + const SkMatrix& matrix) { | 
| + GrGLfloat coefficients[3 * 3]; | 
| + SkASSERT(components >= kS_PathTexGenComponents && | 
| + components <= kSTR_PathTexGenComponents); | 
| + | 
| + coefficients[0] = SkScalarToFloat(matrix[SkMatrix::kMScaleX]); | 
| + coefficients[1] = SkScalarToFloat(matrix[SkMatrix::kMSkewX]); | 
| + coefficients[2] = SkScalarToFloat(matrix[SkMatrix::kMTransX]); | 
| + | 
| + if (components >= kST_PathTexGenComponents) { | 
| + coefficients[3] = SkScalarToFloat(matrix[SkMatrix::kMSkewY]); | 
| + coefficients[4] = SkScalarToFloat(matrix[SkMatrix::kMScaleY]); | 
| + coefficients[5] = SkScalarToFloat(matrix[SkMatrix::kMTransY]); | 
| + } | 
| + | 
| + if (components >= kSTR_PathTexGenComponents) { | 
| + coefficients[6] = SkScalarToFloat(matrix[SkMatrix::kMPersp0]); | 
| + coefficients[7] = SkScalarToFloat(matrix[SkMatrix::kMPersp1]); | 
| + coefficients[8] = SkScalarToFloat(matrix[SkMatrix::kMPersp2]); | 
| + } | 
| + | 
| + this->enablePathTexGen(unitIdx, components, coefficients); | 
| +} | 
| + | 
| +void GrGLPathRendering::flushPathTexGenSettings(int numUsedTexCoordSets) { | 
| + SkASSERT(fGpu->glCaps().maxFixedFunctionTextureCoords() >= numUsedTexCoordSets); | 
| + | 
| + // Only write the inactive path tex gens, since active path tex gens were | 
| + // written when they were enabled. | 
| + | 
| + SkDEBUGCODE( | 
| + for (int i = 0; i < numUsedTexCoordSets; i++) { | 
| + SkASSERT(0 != fHWPathTexGenSettings[i].fNumComponents); | 
| + } | 
| + ); | 
| + | 
| + for (int i = numUsedTexCoordSets; i < fHWActivePathTexGenSets; i++) { | 
| + SkASSERT(0 != fHWPathTexGenSettings[i].fNumComponents); | 
| + | 
| + fGpu->setTextureUnit(i); | 
| + GL_CALL(PathTexGen(GR_GL_TEXTURE0 + i, GR_GL_NONE, 0, NULL)); | 
| + fHWPathTexGenSettings[i].fNumComponents = 0; | 
| + } | 
| + | 
| + fHWActivePathTexGenSets = numUsedTexCoordSets; | 
| +} | 
| + | 
| +void GrGLPathRendering::stencilPath(const GrPath* path, SkPath::FillType fill) { | 
| + GrGLuint id = static_cast<const GrGLPath*>(path)->pathID(); | 
| + SkASSERT(NULL != fGpu->drawState()->getRenderTarget()); | 
| + SkASSERT(NULL != fGpu->drawState()->getRenderTarget()->getStencilBuffer()); | 
| + | 
| + this->flushPathStencilSettings(fill); | 
| + | 
| + GrGLenum fillMode = | 
| + gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face)); | 
| + GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face); | 
| + this->stencilFillPath(id, fillMode, writeMask); | 
| +} | 
| + | 
| +void GrGLPathRendering::drawPath(const GrPath* path, SkPath::FillType fill) { | 
| + GrGLuint id = static_cast<const GrGLPath*>(path)->pathID(); | 
| + SkASSERT(NULL != fGpu->drawState()->getRenderTarget()); | 
| + SkASSERT(NULL != fGpu->drawState()->getRenderTarget()->getStencilBuffer()); | 
| + SkASSERT(!fGpu->fCurrentProgram->hasVertexShader()); | 
| + | 
| + this->flushPathStencilSettings(fill); | 
| + const SkStrokeRec& stroke = path->getStroke(); | 
| + | 
| + SkPath::FillType nonInvertedFill = SkPath::ConvertToNonInverseFillType(fill); | 
| + SkASSERT(!fHWPathStencilSettings.isTwoSided()); | 
| + 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()) { | 
| + this->stencilFillPath(id, fillMode, writeMask); | 
| + } | 
| + this->stencilThenCoverStrokePath(id, 0xffff, writeMask, GR_GL_BOUNDING_BOX); | 
| + } else { | 
| + this->stencilThenCoverFillPath(id, fillMode, writeMask, GR_GL_BOUNDING_BOX); | 
| + } | 
| + } else { | 
| + if (stroke.isFillStyle() || SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) { | 
| + this->stencilFillPath(id, fillMode, writeMask); | 
| + } | 
| + if (stroke.needToApply()) { | 
| + this->stencilStrokePath(id, 0xffff, writeMask); | 
| + } | 
| + | 
| + 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); | 
| + } | 
| +} | 
| + | 
| +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()); | 
| + SkASSERT(!fGpu->fCurrentProgram->hasVertexShader()); | 
| + | 
| + GrGLuint baseID = static_cast<const GrGLPathRange*>(pathRange)->basePathID(); | 
| + | 
| + this->flushPathStencilSettings(fill); | 
| + const SkStrokeRec& stroke = pathRange->getStroke(); | 
| + | 
| + SkPath::FillType nonInvertedFill = | 
| + SkPath::ConvertToNonInverseFillType(fill); | 
| + | 
| + SkASSERT(!fHWPathStencilSettings.isTwoSided()); | 
| + 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()) { | 
| + this->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()) { | 
| + this->stencilFillPathInstanced( | 
| + count, GR_GL_UNSIGNED_INT, indices, baseID, fillMode, | 
| + writeMask, gXformType2GLType[transformsType], | 
| + transforms); | 
| + } | 
| + if (stroke.needToApply()) { | 
| + this->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); | 
| + } | 
| +} | 
| + | 
| +void GrGLPathRendering::flushPathStencilSettings(SkPath::FillType fill) { | 
| + GrStencilSettings pathStencilSettings; | 
| + fGpu->getPathStencilSettingsForFillType(fill, &pathStencilSettings); | 
| + if (fHWPathStencilSettings != pathStencilSettings) { | 
| + // Just the func, ref, and mask is set here. The op and write mask are params to the call | 
| + // that draws the path to the SB (glStencilFillPath) | 
| + GrGLenum func = | 
| + GrToGLStencilFunc(pathStencilSettings.func(GrStencilSettings::kFront_Face)); | 
| + this->pathStencilFunc(func, pathStencilSettings.funcRef(GrStencilSettings::kFront_Face), | 
| + pathStencilSettings.funcMask(GrStencilSettings::kFront_Face)); | 
| + | 
| + fHWPathStencilSettings = pathStencilSettings; | 
| + } | 
| +} | 
| + | 
| +void GrGLPathRendering::setProjectionMatrix(const SkMatrix& matrix, | 
| + const SkISize& renderTargetSize, | 
| + GrSurfaceOrigin renderTargetOrigin) { | 
| + | 
| + SkASSERT(fGpu->glCaps().pathRenderingSupport()); | 
| + | 
| + if (renderTargetOrigin == fHWProjectionMatrixState.fRenderTargetOrigin && | 
| + renderTargetSize == fHWProjectionMatrixState.fRenderTargetSize && | 
| + matrix.cheapEqualTo(fHWProjectionMatrixState.fViewMatrix)) { | 
| + return; | 
| + } | 
| + | 
| + fHWProjectionMatrixState.fViewMatrix = matrix; | 
| + fHWProjectionMatrixState.fRenderTargetSize = renderTargetSize; | 
| + fHWProjectionMatrixState.fRenderTargetOrigin = renderTargetOrigin; | 
| + | 
| + GrGLfloat glMatrix[4 * 4]; | 
| + fHWProjectionMatrixState.getRTAdjustedGLMatrix<4>(glMatrix); | 
| + GL_CALL(MatrixLoadf(GR_GL_PROJECTION, glMatrix)); | 
| +} | 
| + | 
| + | 
| // NV_path_rendering | 
| GrGLuint GrGLPathRendering::genPaths(GrGLsizei range) { | 
| @@ -156,6 +462,9 @@ GrGLvoid GrGLPathRendering::pathStencilFunc(GrGLenum func, GrGLint ref, GrGLuint | 
| } | 
| GrGLvoid GrGLPathRendering::stencilFillPath(GrGLuint path, GrGLenum fillMode, GrGLuint mask) { | 
| + // Decide how to manipulate the stencil buffer based on the fill rule. | 
| + SkASSERT(!fHWPathStencilSettings.isTwoSided()); | 
| 
 
Chris Dalton
2014/08/14 16:22:09
Do we need similar checks in stencil(Then)Cover(St
 
Kimmo Kinnunen
2014/08/15 06:19:26
Done.
 
 | 
| + | 
| GL_CALL(StencilFillPath(path, fillMode, mask)); | 
| } |