Index: src/gpu/gl/GrGLPathRendering.cpp |
diff --git a/src/gpu/gl/GrGLPathRendering.cpp b/src/gpu/gl/GrGLPathRendering.cpp |
index 249d98139ad65b9d888e161ba32c7f6281a52046..53d7cf007411ab4fbc7dcd3bf55f5ac2b5915d5c 100644 |
--- a/src/gpu/gl/GrGLPathRendering.cpp |
+++ b/src/gpu/gl/GrGLPathRendering.cpp |
@@ -6,17 +6,49 @@ |
*/ |
#include "gl/GrGLPathRendering.h" |
-#include "gl/GrGLInterface.h" |
#include "gl/GrGLNameAllocator.h" |
#include "gl/GrGLUtil.h" |
+#include "gl/GrGpuGL.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) |
+#include "GrGLPath.h" |
+#include "GrGLPathRange.h" |
+#include "GrGLPathRendering.h" |
+ |
+#define GL_CALL(X) GR_GL_CALL(fGpu->glInterface(), X) |
+#define GL_CALL_RET(RET, X) GR_GL_CALL_RET(fGpu->glInterface(), 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) |
+ : GrGLPathRendering(gpu) { |
} |
virtual GrGLvoid stencilThenCoverFillPath(GrGLuint path, GrGLenum fillMode, |
@@ -35,8 +67,8 @@ public: |
class GrGLPathRenderingV13 : public GrGLPathRenderingV12 { |
public: |
- GrGLPathRenderingV13(const GrGLInterface* glInterface) |
- : GrGLPathRenderingV12(glInterface) { |
+ GrGLPathRenderingV13(GrGpuGL* gpu) |
+ : GrGLPathRenderingV12(gpu) { |
fCaps.fragmentInputGenSupport = true; |
} |
@@ -46,24 +78,26 @@ public: |
}; |
-GrGLPathRendering* GrGLPathRendering::Create(const GrGLInterface* glInterface) { |
+GrGLPathRendering* GrGLPathRendering::Create(GrGpuGL* gpu) { |
+ const GrGLInterface* glInterface = gpu->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); |
} |
if (NULL == glInterface->fFunctions.fProgramPathFragmentInputGen) { |
- return new GrGLPathRenderingV12(glInterface); |
+ return new GrGLPathRenderingV12(gpu); |
} |
- return new GrGLPathRenderingV13(glInterface); |
+ return new GrGLPathRenderingV13(gpu); |
} |
-GrGLPathRendering::GrGLPathRendering(const GrGLInterface* glInterface) |
- : fGLInterface(SkRef(glInterface)) { |
+GrGLPathRendering::GrGLPathRendering(GrGpuGL* gpu) |
+ : fGpu(gpu) { |
memset(&fCaps, 0, sizeof(fCaps)); |
+ fHWPathTexGenSettings.reset(fGpu->glCaps().maxFixedFunctionTextureCoords()); |
} |
GrGLPathRendering::~GrGLPathRendering() { |
@@ -73,6 +107,281 @@ void GrGLPathRendering::abandonGpuResources() { |
fPathNameAllocator.reset(NULL); |
} |
+void GrGLPathRendering::resetContext() { |
+ 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); |
+ 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); |
+ 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); |
+ SkASSERT(!fHWPathStencilSettings.isTwoSided()); |
+ |
+ const SkStrokeRec& stroke = path->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()) { |
+ 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); |
+ 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()) { |
+ 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 +465,7 @@ 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. |
GL_CALL(StencilFillPath(path, fillMode, mask)); |
} |