aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-10-09 14:11:33 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-10-09 14:11:33 +0000
commitc4dc0ad8e252a7e30d19b47d3d0d9f2c69faf854 (patch)
tree305c63cae824c4d5ae904a7c19c9785f601a7b72 /src
parent6ec1abf21e752abcca33ce3f95e31bc9ac9bb9eb (diff)
Implement filling a path with nv_path_rendering cover
Implement filling a path with nv_path_rendering cover functionality. The nv_path_rendering cover can be used if the fill is non-inverted and the draw operation does not require use of vertex shaders. Moves code for the inverted fill from GrStencilAndCoverPathRenderer down to GrGpuGL. R=bsalomon@google.com, markkilgard@gmail.com, cdalton@nvidia.com Author: kkinnunen@nvidia.com Review URL: https://codereview.chromium.org/22686002 git-svn-id: http://skia.googlecode.com/svn/trunk@11667 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src')
-rw-r--r--src/gpu/GrClipMaskManager.cpp26
-rw-r--r--src/gpu/GrClipMaskManager.h2
-rw-r--r--src/gpu/GrDrawTarget.cpp51
-rw-r--r--src/gpu/GrDrawTarget.h30
-rw-r--r--src/gpu/GrDrawTargetCaps.h4
-rw-r--r--src/gpu/GrGpu.cpp56
-rw-r--r--src/gpu/GrGpu.h15
-rw-r--r--src/gpu/GrInOrderDrawBuffer.cpp34
-rw-r--r--src/gpu/GrInOrderDrawBuffer.h15
-rw-r--r--src/gpu/GrStencilAndCoverPathRenderer.cpp56
-rw-r--r--src/gpu/gl/GrGLCaps.cpp4
-rw-r--r--src/gpu/gl/GrGLProgram.cpp2
-rw-r--r--src/gpu/gl/GrGLProgramDesc.cpp6
-rw-r--r--src/gpu/gl/GrGLShaderBuilder.cpp2
-rw-r--r--src/gpu/gl/GrGpuGL.cpp191
-rw-r--r--src/gpu/gl/GrGpuGL.h7
-rw-r--r--src/gpu/gl/GrGpuGL_program.cpp3
17 files changed, 328 insertions, 176 deletions
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 58b6a4bf61..1a10336172 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -1012,3 +1012,29 @@ void GrClipMaskManager::setGpu(GrGpu* gpu) {
fGpu = gpu;
fAACache.setContext(gpu->getContext());
}
+
+void GrClipMaskManager::adjustPathStencilParams(GrStencilSettings* settings) {
+ const GrDrawState& drawState = fGpu->getDrawState();
+ GrClipMaskManager::StencilClipMode clipMode;
+ if (this->isClipInStencil() && drawState.isClipState()) {
+ clipMode = GrClipMaskManager::kRespectClip_StencilClipMode;
+ // We can't be modifying the clip and respecting it at the same time.
+ SkASSERT(!drawState.isStateFlagEnabled(
+ GrGpu::kModifyStencilClip_StateBit));
+ } else if (drawState.isStateFlagEnabled(
+ GrGpu::kModifyStencilClip_StateBit)) {
+ clipMode = GrClipMaskManager::kModifyClip_StencilClipMode;
+ } else {
+ clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode;
+ }
+
+ // TODO: dynamically attach a stencil buffer
+ int stencilBits = 0;
+ GrStencilBuffer* stencilBuffer =
+ drawState.getRenderTarget()->getStencilBuffer();
+ if (NULL != stencilBuffer) {
+ stencilBits = stencilBuffer->bits();
+ this->adjustStencilParams(settings, clipMode, stencilBits);
+ }
+}
+
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index 54b7892a29..fa9398786c 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -70,6 +70,8 @@ public:
}
void setGpu(GrGpu* gpu);
+
+ void adjustPathStencilParams(GrStencilSettings* settings);
private:
/**
* Informs the helper function adjustStencilParams() about how the stencil
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index c2256a84d8..64eedfbef7 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -11,6 +11,7 @@
#include "GrDrawTarget.h"
#include "GrContext.h"
#include "GrDrawTargetCaps.h"
+#include "GrPath.h"
#include "GrRenderTarget.h"
#include "GrTexture.h"
#include "GrVertexBuffer.h"
@@ -413,17 +414,18 @@ bool GrDrawTarget::checkDraw(GrPrimitiveType type, int startVertex,
return true;
}
-bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) {
+bool GrDrawTarget::setupDstReadIfNecessary(GrDeviceCoordTexture* dstCopy, const SkRect* drawBounds) {
if (this->caps()->dstReadInShaderSupport() || !this->getDrawState().willEffectReadDstColor()) {
return true;
}
GrRenderTarget* rt = this->drawState()->getRenderTarget();
-
- const GrClipData* clip = this->getClip();
SkIRect copyRect;
- clip->getConservativeBounds(this->getDrawState().getRenderTarget(), &copyRect);
- SkIRect drawIBounds;
- if (info->getDevIBounds(&drawIBounds)) {
+ const GrClipData* clip = this->getClip();
+ clip->getConservativeBounds(rt, &copyRect);
+
+ if (NULL != drawBounds) {
+ SkIRect drawIBounds;
+ drawBounds->roundOut(&drawIBounds);
if (!copyRect.intersect(drawIBounds)) {
#ifdef SK_DEBUG
GrPrintf("Missed an early reject. Bailing on draw from setupDstReadIfNecessary.\n");
@@ -451,8 +453,8 @@ bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) {
}
SkIPoint dstPoint = {0, 0};
if (this->copySurface(ast.texture(), rt, copyRect, dstPoint)) {
- info->fDstCopy.setTexture(ast.texture());
- info->fDstCopy.setOffset(copyRect.fLeft, copyRect.fTop);
+ dstCopy->setTexture(ast.texture());
+ dstCopy->setOffset(copyRect.fLeft, copyRect.fTop);
return true;
} else {
return false;
@@ -518,12 +520,37 @@ void GrDrawTarget::drawNonIndexed(GrPrimitiveType type,
void GrDrawTarget::stencilPath(const GrPath* path, const SkStrokeRec& stroke, SkPath::FillType fill) {
// TODO: extract portions of checkDraw that are relevant to path stenciling.
SkASSERT(NULL != path);
- SkASSERT(this->caps()->pathStencilingSupport());
+ SkASSERT(this->caps()->pathRenderingSupport());
SkASSERT(!stroke.isHairlineStyle());
SkASSERT(!SkPath::IsInverseFillType(fill));
this->onStencilPath(path, stroke, fill);
}
+void GrDrawTarget::fillPath(const GrPath* path, const SkStrokeRec& stroke, SkPath::FillType fill) {
+ // TODO: extract portions of checkDraw that are relevant to path rendering.
+ SkASSERT(NULL != path);
+ SkASSERT(this->caps()->pathRenderingSupport());
+ SkASSERT(!stroke.isHairlineStyle());
+ const GrDrawState* drawState = &getDrawState();
+
+ SkRect devBounds;
+ if (SkPath::IsInverseFillType(fill)) {
+ devBounds = SkRect::MakeWH(SkIntToScalar(drawState->getRenderTarget()->width()),
+ SkIntToScalar(drawState->getRenderTarget()->height()));
+ } else {
+ devBounds = path->getBounds();
+ }
+ SkMatrix viewM = drawState->getViewMatrix();
+ viewM.mapRect(&devBounds);
+
+ GrDeviceCoordTexture dstCopy;
+ if (!this->setupDstReadIfNecessary(&dstCopy, &devBounds)) {
+ return;
+ }
+
+ this->onFillPath(path, stroke, fill, dstCopy.texture() ? &dstCopy : NULL);
+}
+
////////////////////////////////////////////////////////////////////////////////
bool GrDrawTarget::willUseHWAALines() const {
@@ -949,7 +976,7 @@ void GrDrawTargetCaps::reset() {
fGeometryShaderSupport = false;
fDualSourceBlendingSupport = false;
fBufferLockSupport = false;
- fPathStencilingSupport = false;
+ fPathRenderingSupport = false;
fDstReadInShaderSupport = false;
fReuseScratchTextures = true;
@@ -968,7 +995,7 @@ GrDrawTargetCaps& GrDrawTargetCaps::operator=(const GrDrawTargetCaps& other) {
fGeometryShaderSupport = other.fGeometryShaderSupport;
fDualSourceBlendingSupport = other.fDualSourceBlendingSupport;
fBufferLockSupport = other.fBufferLockSupport;
- fPathStencilingSupport = other.fPathStencilingSupport;
+ fPathRenderingSupport = other.fPathRenderingSupport;
fDstReadInShaderSupport = other.fDstReadInShaderSupport;
fReuseScratchTextures = other.fReuseScratchTextures;
@@ -990,7 +1017,7 @@ void GrDrawTargetCaps::print() const {
GrPrintf("Geometry Shader Support : %s\n", gNY[fGeometryShaderSupport]);
GrPrintf("Dual Source Blending Support: %s\n", gNY[fDualSourceBlendingSupport]);
GrPrintf("Buffer Lock Support : %s\n", gNY[fBufferLockSupport]);
- GrPrintf("Path Stenciling Support : %s\n", gNY[fPathStencilingSupport]);
+ GrPrintf("Path Rendering Support : %s\n", gNY[fPathRenderingSupport]);
GrPrintf("Dst Read In Shader Support : %s\n", gNY[fDstReadInShaderSupport]);
GrPrintf("Reuse Scratch Textures : %s\n", gNY[fReuseScratchTextures]);
GrPrintf("Max Texture Size : %d\n", fMaxTextureSize);
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index 47460ac527..49dd3880c8 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -329,6 +329,12 @@ public:
void stencilPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType fill);
/**
+ * Fills a path. Fill must not be a hairline. It will respect the HW
+ * antialias flag on the draw state (if possible in the 3D API).
+ */
+ void fillPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType fill);
+
+ /**
* Helper function for drawing rects. It performs a geometry src push and pop
* and thus will finalize any reserved geometry.
*
@@ -448,6 +454,14 @@ public:
*/
void executeDraw(const DrawInfo& info) { this->onDraw(info); }
+ /**
+ * For subclass internal use to invoke a call to onFillPath().
+ */
+ void executeFillPath(const GrPath* path, const SkStrokeRec& stroke,
+ SkPath::FillType fill, const GrDeviceCoordTexture* dstCopy) {
+ this->onFillPath(path, stroke, fill, dstCopy);
+ }
+
////////////////////////////////////////////////////////////////////////////
/**
@@ -761,15 +775,6 @@ protected:
}
const SkRect* getDevBounds() const { return fDevBounds; }
- bool getDevIBounds(SkIRect* bounds) const {
- if (NULL != fDevBounds) {
- fDevBounds->roundOut(bounds);
- return true;
- } else {
- return false;
- }
- }
-
// NULL if no copy of the dst is needed for the draw.
const GrDeviceCoordTexture* getDstCopy() const {
if (NULL != fDstCopy.texture()) {
@@ -834,6 +839,8 @@ private:
const SkRect* localRect,
const SkMatrix* localMatrix);
virtual void onStencilPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType fill) = 0;
+ virtual void onFillPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType fill,
+ const GrDeviceCoordTexture* dstCopy) = 0;
// helpers for reserving vertex and index space.
bool reserveVertexSpace(size_t vertexSize,
@@ -852,7 +859,10 @@ private:
// Makes a copy of the dst if it is necessary for the draw. Returns false if a copy is required
// but couldn't be made. Otherwise, returns true.
- bool setupDstReadIfNecessary(DrawInfo* info);
+ bool setupDstReadIfNecessary(DrawInfo* info) {
+ return this->setupDstReadIfNecessary(&info->fDstCopy, info->getDevBounds());
+ }
+ bool setupDstReadIfNecessary(GrDeviceCoordTexture* dstCopy, const SkRect* drawBounds);
// Check to see if this set of draw commands has been sent out
virtual bool isIssued(uint32_t drawID) { return true; }
diff --git a/src/gpu/GrDrawTargetCaps.h b/src/gpu/GrDrawTargetCaps.h
index 61c9fede08..111b35bf74 100644
--- a/src/gpu/GrDrawTargetCaps.h
+++ b/src/gpu/GrDrawTargetCaps.h
@@ -34,7 +34,7 @@ public:
bool geometryShaderSupport() const { return fGeometryShaderSupport; }
bool dualSourceBlendingSupport() const { return fDualSourceBlendingSupport; }
bool bufferLockSupport() const { return fBufferLockSupport; }
- bool pathStencilingSupport() const { return fPathStencilingSupport; }
+ bool pathRenderingSupport() const { return fPathRenderingSupport; }
bool dstReadInShaderSupport() const { return fDstReadInShaderSupport; }
bool reuseScratchTextures() const { return fReuseScratchTextures; }
@@ -53,7 +53,7 @@ protected:
bool fGeometryShaderSupport : 1;
bool fDualSourceBlendingSupport : 1;
bool fBufferLockSupport : 1;
- bool fPathStencilingSupport : 1;
+ bool fPathRenderingSupport : 1;
bool fDstReadInShaderSupport : 1;
bool fReuseScratchTextures : 1;
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 910c57146b..5b1c23afe4 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -198,7 +198,7 @@ GrIndexBuffer* GrGpu::createIndexBuffer(uint32_t size, bool dynamic) {
}
GrPath* GrGpu::createPath(const SkPath& path) {
- SkASSERT(this->caps()->pathStencilingSupport());
+ SkASSERT(this->caps()->pathRenderingSupport());
this->handleDirtyContext();
return this->onCreatePath(path);
}
@@ -247,6 +247,42 @@ void GrGpu::resolveRenderTarget(GrRenderTarget* target) {
this->onResolveRenderTarget(target);
}
+static const GrStencilSettings& winding_path_stencil_settings() {
+ GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
+ kIncClamp_StencilOp,
+ kIncClamp_StencilOp,
+ kAlwaysIfInClip_StencilFunc,
+ 0xFFFF, 0xFFFF, 0xFFFF);
+ return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
+}
+
+static const GrStencilSettings& even_odd_path_stencil_settings() {
+ GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
+ kInvert_StencilOp,
+ kInvert_StencilOp,
+ kAlwaysIfInClip_StencilFunc,
+ 0xFFFF, 0xFFFF, 0xFFFF);
+ return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
+}
+
+void GrGpu::getPathStencilSettingsForFillType(SkPath::FillType fill, GrStencilSettings* outStencilSettings) {
+
+ switch (fill) {
+ default:
+ GrCrash("Unexpected path fill.");
+ /* fallthrough */;
+ case SkPath::kWinding_FillType:
+ case SkPath::kInverseWinding_FillType:
+ *outStencilSettings = winding_path_stencil_settings();
+ break;
+ case SkPath::kEvenOdd_FillType:
+ case SkPath::kInverseEvenOdd_FillType:
+ *outStencilSettings = even_odd_path_stencil_settings();
+ break;
+ }
+ fClipMaskManager.adjustPathStencilParams(outStencilSettings);
+}
+
////////////////////////////////////////////////////////////////////////////////
@@ -349,10 +385,6 @@ void GrGpu::onDraw(const DrawInfo& info) {
void GrGpu::onStencilPath(const GrPath* path, const SkStrokeRec&, SkPath::FillType fill) {
this->handleDirtyContext();
- // TODO: make this more efficient (don't copy and copy back)
- GrAutoTRestore<GrStencilSettings> asr(this->drawState()->stencil());
-
- this->setStencilPathSettings(*path, fill, this->drawState()->stencil());
GrDrawState::AutoRestoreEffects are;
if (!this->setupClipAndFlushState(kStencilPath_DrawType, NULL, &are)) {
return;
@@ -361,6 +393,20 @@ void GrGpu::onStencilPath(const GrPath* path, const SkStrokeRec&, SkPath::FillTy
this->onGpuStencilPath(path, fill);
}
+void GrGpu::onFillPath(const GrPath* path, const SkStrokeRec& stroke, SkPath::FillType fill,
+ const GrDeviceCoordTexture* dstCopy) {
+ this->handleDirtyContext();
+
+ drawState()->setDefaultVertexAttribs();
+
+ GrDrawState::AutoRestoreEffects are;
+ if (!this->setupClipAndFlushState(kFillPath_DrawType, dstCopy, &are)) {
+ return;
+ }
+
+ this->onGpuFillPath(path, fill);
+}
+
void GrGpu::finalizeReservedVertices() {
SkASSERT(NULL != fVertexPool);
fVertexPool->unlock();
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 239ae6bd83..6a9dfa2b58 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -335,12 +335,15 @@ public:
// clipping.
};
+ void getPathStencilSettingsForFillType(SkPath::FillType fill, GrStencilSettings* outStencilSettings);
+
protected:
enum DrawType {
kDrawPoints_DrawType,
kDrawLines_DrawType,
kDrawTriangles_DrawType,
kStencilPath_DrawType,
+ kFillPath_DrawType,
};
DrawType PrimTypeToDrawType(GrPrimitiveType type) {
@@ -440,15 +443,10 @@ private:
// overridden by backend-specific derived class to perform the draw call.
virtual void onGpuDraw(const DrawInfo&) = 0;
- // when GrDrawTarget::stencilPath is called the draw state's current stencil
- // settings are ignored. Instead the GrGpu decides the stencil rules
- // necessary to stencil the path. These are still subject to filtering by
- // the clip mask manager.
- virtual void setStencilPathSettings(const GrPath&,
- SkPath::FillType,
- GrStencilSettings* settings) = 0;
+
// overridden by backend-specific derived class to perform the path stenciling.
virtual void onGpuStencilPath(const GrPath*, SkPath::FillType) = 0;
+ virtual void onGpuFillPath(const GrPath*, SkPath::FillType) = 0;
// overridden by backend-specific derived class to perform flush
virtual void onForceRenderTargetFlush() = 0;
@@ -494,6 +492,9 @@ private:
virtual void onStencilPath(const GrPath* path, const SkStrokeRec& stroke,
SkPath::FillType) SK_OVERRIDE;
+ virtual void onFillPath(const GrPath* path, const SkStrokeRec& stroke, SkPath::FillType,
+ const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE;
+
// readies the pools to provide vertex/index data.
void prepareVertexPool();
void prepareIndexPool();
diff --git a/src/gpu/GrInOrderDrawBuffer.cpp b/src/gpu/GrInOrderDrawBuffer.cpp
index 8c8fbfc4ce..dbbdf163e6 100644
--- a/src/gpu/GrInOrderDrawBuffer.cpp
+++ b/src/gpu/GrInOrderDrawBuffer.cpp
@@ -385,6 +385,7 @@ void GrInOrderDrawBuffer::onDraw(const DrawInfo& info) {
}
GrInOrderDrawBuffer::StencilPath::StencilPath() : fStroke(SkStrokeRec::kFill_InitStyle) {}
+GrInOrderDrawBuffer::FillPath::FillPath() : fStroke(SkStrokeRec::kFill_InitStyle) {}
void GrInOrderDrawBuffer::onStencilPath(const GrPath* path, const SkStrokeRec& stroke,
SkPath::FillType fill) {
@@ -402,6 +403,25 @@ void GrInOrderDrawBuffer::onStencilPath(const GrPath* path, const SkStrokeRec& s
sp->fStroke = stroke;
}
+void GrInOrderDrawBuffer::onFillPath(const GrPath* path, const SkStrokeRec& stroke,
+ SkPath::FillType fill, const GrDeviceCoordTexture* dstCopy) {
+ if (this->needsNewClip()) {
+ this->recordClip();
+ }
+ // TODO: Only compare the subset of GrDrawState relevant to path covering?
+ if (this->needsNewState()) {
+ this->recordState();
+ }
+ FillPath* cp = this->recordFillPath();
+ cp->fPath.reset(path);
+ path->ref();
+ cp->fFill = fill;
+ cp->fStroke = stroke;
+ if (NULL != dstCopy) {
+ cp->fDstCopy = *dstCopy;
+ }
+}
+
void GrInOrderDrawBuffer::clear(const SkIRect* rect, GrColor color, GrRenderTarget* renderTarget) {
SkIRect r;
if (NULL == renderTarget) {
@@ -436,6 +456,7 @@ void GrInOrderDrawBuffer::reset() {
fCmds.reset();
fDraws.reset();
fStencilPaths.reset();
+ fFillPaths.reset();
fStates.reset();
fClears.reset();
fVertexPool.reset();
@@ -480,6 +501,7 @@ void GrInOrderDrawBuffer::flush() {
int currClear = 0;
int currDraw = 0;
int currStencilPath = 0;
+ int currFillPath = 0;
int currCopySurface = 0;
for (int c = 0; c < numCmds; ++c) {
@@ -501,6 +523,13 @@ void GrInOrderDrawBuffer::flush() {
++currStencilPath;
break;
}
+ case kFillPath_Cmd: {
+ const FillPath& cp = fFillPaths[currFillPath];
+ fDstGpu->executeFillPath(cp.fPath.get(), cp.fStroke, cp.fFill,
+ NULL != cp.fDstCopy.texture() ? &cp.fDstCopy : NULL);
+ ++currFillPath;
+ break;
+ }
case kSetState_Cmd:
fStates[currState].restoreTo(&playbackState);
++currState;
@@ -810,6 +839,11 @@ GrInOrderDrawBuffer::StencilPath* GrInOrderDrawBuffer::recordStencilPath() {
return &fStencilPaths.push_back();
}
+GrInOrderDrawBuffer::FillPath* GrInOrderDrawBuffer::recordFillPath() {
+ fCmds.push_back(kFillPath_Cmd);
+ return &fFillPaths.push_back();
+}
+
GrInOrderDrawBuffer::Clear* GrInOrderDrawBuffer::recordClear() {
fCmds.push_back(kClear_Cmd);
return &fClears.push_back();
diff --git a/src/gpu/GrInOrderDrawBuffer.h b/src/gpu/GrInOrderDrawBuffer.h
index ca6af3fe8b..b9e5ca8718 100644
--- a/src/gpu/GrInOrderDrawBuffer.h
+++ b/src/gpu/GrInOrderDrawBuffer.h
@@ -87,6 +87,7 @@ private:
kSetClip_Cmd = 4,
kClear_Cmd = 5,
kCopySurface_Cmd = 6,
+ kFillPath_Cmd = 7,
};
class DrawRecord : public DrawInfo {
@@ -104,6 +105,15 @@ private:
SkPath::FillType fFill;
};
+ struct FillPath : public ::SkNoncopyable {
+ FillPath();
+
+ SkAutoTUnref<const GrPath> fPath;
+ SkStrokeRec fStroke;
+ SkPath::FillType fFill;
+ GrDeviceCoordTexture fDstCopy;
+ };
+
struct Clear : public ::SkNoncopyable {
Clear() : fRenderTarget(NULL) {}
~Clear() { SkSafeUnref(fRenderTarget); }
@@ -127,6 +137,8 @@ private:
const SkRect* localRect,
const SkMatrix* localMatrix) SK_OVERRIDE;
virtual void onStencilPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType) SK_OVERRIDE;
+ virtual void onFillPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType,
+ const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE;
virtual bool onReserveVertexSpace(size_t vertexSize,
int vertexCount,
void** vertices) SK_OVERRIDE;
@@ -169,6 +181,7 @@ private:
void recordClip();
DrawRecord* recordDraw(const DrawInfo&);
StencilPath* recordStencilPath();
+ FillPath* recordFillPath();
Clear* recordClear();
CopySurface* recordCopySurface();
@@ -177,6 +190,7 @@ private:
kCmdPreallocCnt = 32,
kDrawPreallocCnt = 8,
kStencilPathPreallocCnt = 8,
+ kFillPathPreallocCnt = 8,
kStatePreallocCnt = 8,
kClipPreallocCnt = 8,
kClearPreallocCnt = 4,
@@ -187,6 +201,7 @@ private:
SkSTArray<kCmdPreallocCnt, uint8_t, true> fCmds;
GrSTAllocator<kDrawPreallocCnt, DrawRecord> fDraws;
GrSTAllocator<kStatePreallocCnt, StencilPath> fStencilPaths;
+ GrSTAllocator<kStatePreallocCnt, FillPath> fFillPaths;
GrSTAllocator<kStatePreallocCnt, GrDrawState::DeferredState> fStates;
GrSTAllocator<kClearPreallocCnt, Clear> fClears;
GrSTAllocator<kCopySurfacePreallocCnt, CopySurface> fCopySurfaces;
diff --git a/src/gpu/GrStencilAndCoverPathRenderer.cpp b/src/gpu/GrStencilAndCoverPathRenderer.cpp
index f7330a81f6..ef3b953861 100644
--- a/src/gpu/GrStencilAndCoverPathRenderer.cpp
+++ b/src/gpu/GrStencilAndCoverPathRenderer.cpp
@@ -17,7 +17,7 @@
GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrContext* context) {
SkASSERT(NULL != context);
SkASSERT(NULL != context->getGpu());
- if (context->getGpu()->caps()->pathStencilingSupport()) {
+ if (context->getGpu()->caps()->pathRenderingSupport()) {
return SkNEW_ARGS(GrStencilAndCoverPathRenderer, (context->getGpu()));
} else {
return NULL;
@@ -25,7 +25,7 @@ GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrContext* context) {
}
GrStencilAndCoverPathRenderer::GrStencilAndCoverPathRenderer(GrGpu* gpu) {
- SkASSERT(gpu->caps()->pathStencilingSupport());
+ SkASSERT(gpu->caps()->pathRenderingSupport());
fGpu = gpu;
gpu->ref();
}
@@ -40,6 +40,7 @@ bool GrStencilAndCoverPathRenderer::canDrawPath(const SkPath& path,
bool antiAlias) const {
return stroke.isFillStyle() &&
!antiAlias && // doesn't do per-path AA, relies on the target having MSAA
+ NULL != target->getDrawState().getRenderTarget()->getStencilBuffer() &&
target->getDrawState().getStencil().isDisabled();
}
@@ -70,27 +71,7 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path,
SkAutoTUnref<GrPath> p(fGpu->createPath(path));
- SkPath::FillType nonInvertedFill = SkPath::ConvertToNonInverseFillType(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
- SkRect bounds = p->getBounds();
- SkScalar bloat = drawState->getViewMatrix().getMaxStretch() * SK_ScalarHalf;
- GrDrawState::AutoViewMatrixRestore avmr;
-
- if (nonInvertedFill == path.getFillType()) {
- GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
- kZero_StencilOp,
- kZero_StencilOp,
- kNotEqual_StencilFunc,
- 0xffff,
- 0x0000,
- 0xffff);
- *drawState->stencil() = kStencilPass;
- } else {
+ if (path.isInverseFillType()) {
GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass,
kZero_StencilOp,
kZero_StencilOp,
@@ -101,23 +82,22 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path,
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 {
- avmr.setIdentity(drawState);
- bloat = 0;
- }
+
*drawState->stencil() = kInvertedStencilPass;
+ } else {
+ GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
+ kZero_StencilOp,
+ kZero_StencilOp,
+ kNotEqual_StencilFunc,
+ 0xffff,
+ 0x0000,
+ 0xffff);
+
+ *drawState->stencil() = kStencilPass;
}
- bounds.outset(bloat, bloat);
- target->drawSimpleRect(bounds, NULL);
+
+ target->fillPath(p, stroke, path.getFillType());
+
target->drawState()->stencil()->setDisabled();
return true;
}
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index aac97361f8..17a4743b67 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -302,8 +302,8 @@ void GrGLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
// attachment, hence this min:
fMaxRenderTargetSize = GrMin(fMaxTextureSize, fMaxRenderTargetSize);
- fPathStencilingSupport = GR_GL_USE_NV_PATH_RENDERING &&
- ctxInfo.hasExtension("GL_NV_path_rendering");
+ fPathRenderingSupport = GR_GL_USE_NV_PATH_RENDERING &&
+ ctxInfo.hasExtension("GL_NV_path_rendering");
fDstReadInShaderSupport = kNone_FBFetchType != fFBFetchType;
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 1ca61e3748..a69333f437 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -52,7 +52,7 @@ GrGLProgram::GrGLProgram(GrGpuGL* gpu,
if (fDesc.getHeader().fHasVertexCode ||
!fGpu->glCaps().fixedFunctionSupport() ||
- !fGpu->glCaps().pathStencilingSupport()) {
+ !fGpu->glCaps().pathRenderingSupport()) {
GrGLFullShaderBuilder fullBuilder(fGpu, fUniformManager, fDesc);
if (this->genProgram(&fullBuilder, colorStages, coverageStages)) {
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 8b731fbc2c..f9728ff556 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -122,11 +122,13 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState,
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 (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresColorAttrib) {
+ } else if (defaultToUniformInputs && !requiresColorAttrib) {
header->fColorInput = kUniform_ColorInput;
} else {
header->fColorInput = kAttribute_ColorInput;
@@ -139,7 +141,7 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState,
header->fCoverageInput = kTransBlack_ColorInput;
} else if (covIsSolidWhite) {
header->fCoverageInput = kSolidWhite_ColorInput;
- } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresCoverageAttrib) {
+ } else if (defaultToUniformInputs && !requiresCoverageAttrib) {
header->fCoverageInput = kUniform_ColorInput;
} else {
header->fCoverageInput = kAttribute_ColorInput;
diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp
index 00fefe1305..eb7cfa72f4 100644
--- a/src/gpu/gl/GrGLShaderBuilder.cpp
+++ b/src/gpu/gl/GrGLShaderBuilder.cpp
@@ -930,7 +930,7 @@ GrGLFragmentOnlyShaderBuilder::GrGLFragmentOnlyShaderBuilder(GrGpuGL* gpu,
SkASSERT(!desc.getHeader().fHasVertexCode);
SkASSERT(gpu->glCaps().fixedFunctionSupport());
- SkASSERT(gpu->glCaps().pathStencilingSupport());
+ SkASSERT(gpu->glCaps().pathRenderingSupport());
SkASSERT(GrGLProgramDesc::kAttribute_ColorInput != desc.getHeader().fColorInput);
SkASSERT(GrGLProgramDesc::kAttribute_ColorInput != desc.getHeader().fCoverageInput);
}
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index 18165d9ca3..113866759e 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -365,26 +365,30 @@ void GrGpuGL::onResetContext(uint32_t resetBits) {
fHWBoundRenderTarget = NULL;
}
- if (resetBits & kFixedFunction_GrGLBackendState && this->glCaps().fixedFunctionSupport()) {
-
- fHWProjectionMatrixState.invalidate();
- // we don't use the model view matrix.
- GL_CALL(MatrixMode(GR_GL_MODELVIEW));
- GL_CALL(LoadIdentity());
-
- for (int i = 0; i < this->glCaps().maxFixedFunctionTextureCoords(); ++i) {
- GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + i));
- GL_CALL(Disable(GR_GL_TEXTURE_GEN_S));
- GL_CALL(Disable(GR_GL_TEXTURE_GEN_T));
- GL_CALL(Disable(GR_GL_TEXTURE_GEN_Q));
- GL_CALL(Disable(GR_GL_TEXTURE_GEN_R));
- if (this->caps()->pathStencilingSupport()) {
- GL_CALL(PathTexGen(GR_GL_TEXTURE0 + i, GR_GL_NONE, 0, NULL));
+ if (resetBits & (kFixedFunction_GrGLBackendState | kPathRendering_GrGLBackendState)) {
+ if (this->glCaps().fixedFunctionSupport()) {
+ fHWProjectionMatrixState.invalidate();
+ // we don't use the model view matrix.
+ GL_CALL(MatrixMode(GR_GL_MODELVIEW));
+ GL_CALL(LoadIdentity());
+
+ for (int i = 0; i < this->glCaps().maxFixedFunctionTextureCoords(); ++i) {
+ GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + i));
+ GL_CALL(Disable(GR_GL_TEXTURE_GEN_S));
+ GL_CALL(Disable(GR_GL_TEXTURE_GEN_T));
+ GL_CALL(Disable(GR_GL_TEXTURE_GEN_Q));
+ GL_CALL(Disable(GR_GL_TEXTURE_GEN_R));
+ if (this->caps()->pathRenderingSupport()) {
+ GL_CALL(PathTexGen(GR_GL_TEXTURE0 + i, GR_GL_NONE, 0, NULL));
+ }
+ fHWTexGenSettings[i].fMode = GR_GL_NONE;
+ fHWTexGenSettings[i].fNumComponents = 0;
}
- fHWTexGenSettings[i].fMode = GR_GL_NONE;
- fHWTexGenSettings[i].fNumComponents = 0;
+ fHWActiveTexGenSets = 0;
+ }
+ if (this->caps()->pathRenderingSupport()) {
+ fHWPathStencilSettings.invalidate();
}
- fHWActiveTexGenSets = 0;
}
// we assume these values
@@ -1273,7 +1277,7 @@ GrIndexBuffer* GrGpuGL::onCreateIndexBuffer(uint32_t size, bool dynamic) {
}
GrPath* GrGpuGL::onCreatePath(const SkPath& inPath) {
- SkASSERT(this->caps()->pathStencilingSupport());
+ SkASSERT(this->caps()->pathRenderingSupport());
return SkNEW_ARGS(GrGLPath, (this, inPath));
}
@@ -1666,79 +1670,75 @@ void GrGpuGL::onGpuDraw(const DrawInfo& info) {
#endif
}
-namespace {
-
-static const uint16_t kOnes16 = static_cast<uint16_t>(~0);
-const GrStencilSettings& winding_nv_path_stencil_settings() {
- GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
- kIncClamp_StencilOp,
- kIncClamp_StencilOp,
- kAlwaysIfInClip_StencilFunc,
- kOnes16, kOnes16, kOnes16);
- return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
-}
-const GrStencilSettings& even_odd_nv_path_stencil_settings() {
- GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
- kInvert_StencilOp,
- kInvert_StencilOp,
- kAlwaysIfInClip_StencilFunc,
- kOnes16, kOnes16, kOnes16);
- return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
-}
-}
-
-void GrGpuGL::setStencilPathSettings(const GrPath&,
- SkPath::FillType fill,
- GrStencilSettings* settings) {
- switch (fill) {
- case SkPath::kEvenOdd_FillType:
- *settings = even_odd_nv_path_stencil_settings();
- return;
- case SkPath::kWinding_FillType:
- *settings = winding_nv_path_stencil_settings();
- return;
+static GrGLenum gr_stencil_op_to_gl_path_rendering_fill_mode(GrStencilOp op) {
+ switch (op) {
default:
GrCrash("Unexpected path fill.");
+ /* fallthrough */;
+ case kIncClamp_StencilOp:
+ return GR_GL_COUNT_UP;
+ case kInvert_StencilOp:
+ return GR_GL_INVERT;
}
}
void GrGpuGL::onGpuStencilPath(const GrPath* path, SkPath::FillType fill) {
- SkASSERT(this->caps()->pathStencilingSupport());
+ SkASSERT(this->caps()->pathRenderingSupport());
GrGLuint id = static_cast<const GrGLPath*>(path)->pathID();
- GrDrawState* drawState = this->drawState();
- SkASSERT(NULL != drawState->getRenderTarget());
- if (NULL == drawState->getRenderTarget()->getStencilBuffer()) {
- return;
- }
+ SkASSERT(NULL != this->drawState()->getRenderTarget());
+ SkASSERT(NULL != this->drawState()->getRenderTarget()->getStencilBuffer());
+
+ flushPathStencilSettings(fill);
// Decide how to manipulate the stencil buffer based on the fill rule.
- // Also, assert that the stencil settings we set in setStencilPathSettings
- // are present.
- SkASSERT(!fStencilSettings.isTwoSided());
- GrGLenum fillMode;
- switch (fill) {
- case SkPath::kWinding_FillType:
- fillMode = GR_GL_COUNT_UP;
- SkASSERT(kIncClamp_StencilOp ==
- fStencilSettings.passOp(GrStencilSettings::kFront_Face));
- SkASSERT(kIncClamp_StencilOp ==
- fStencilSettings.failOp(GrStencilSettings::kFront_Face));
- break;
- case SkPath::kEvenOdd_FillType:
- fillMode = GR_GL_INVERT;
- SkASSERT(kInvert_StencilOp ==
- fStencilSettings.passOp(GrStencilSettings::kFront_Face));
- SkASSERT(kInvert_StencilOp ==
- fStencilSettings.failOp(GrStencilSettings::kFront_Face));
- break;
- default:
- // Only the above two fill rules are allowed.
- GrCrash("Unexpected path fill.");
- return; // suppress unused var warning.
- }
- GrGLint writeMask = fStencilSettings.writeMask(GrStencilSettings::kFront_Face);
+ 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);
+ GL_CALL(StencilFillPath(id, fillMode, writeMask));
+}
+
+void GrGpuGL::onGpuFillPath(const GrPath* path, SkPath::FillType fill) {
+ SkASSERT(this->caps()->pathRenderingSupport());
+
+ GrGLuint id = static_cast<const GrGLPath*>(path)->pathID();
+ SkASSERT(NULL != this->drawState()->getRenderTarget());
+ SkASSERT(NULL != this->drawState()->getRenderTarget()->getStencilBuffer());
+ SkASSERT(!fCurrentProgram->hasVertexShader());
+
+ flushPathStencilSettings(fill);
+
+ 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);
GL_CALL(StencilFillPath(id, fillMode, writeMask));
+
+ if (nonInvertedFill == fill) {
+ GL_CALL(CoverFillPath(id, GR_GL_BOUNDING_BOX));
+ } else {
+ GrDrawState* drawState = this->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().getMaxStretch() * SK_ScalarHalf;
+ bounds.outset(bloat, bloat);
+ } else {
+ avmr.setIdentity(drawState);
+ }
+
+ this->drawSimpleRect(bounds, NULL);
+ }
}
void GrGpuGL::onResolveRenderTarget(GrRenderTarget* target) {
@@ -1862,16 +1862,7 @@ void set_gl_stencil(const GrGLInterface* gl,
}
void GrGpuGL::flushStencil(DrawType type) {
- if (kStencilPath_DrawType == type) {
- SkASSERT(!fStencilSettings.isTwoSided());
- // 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 =
- gr_to_gl_stencil_func(fStencilSettings.func(GrStencilSettings::kFront_Face));
- GL_CALL(PathStencilFunc(func,
- fStencilSettings.funcRef(GrStencilSettings::kFront_Face),
- fStencilSettings.funcMask(GrStencilSettings::kFront_Face)));
- } else if (fHWStencilSettings != fStencilSettings) {
+ if (kStencilPath_DrawType != type && fHWStencilSettings != fStencilSettings) {
if (fStencilSettings.isDisabled()) {
if (kNo_TriState != fHWStencilTestEnabled) {
GL_CALL(Disable(GR_GL_STENCIL_TEST));
@@ -1961,6 +1952,22 @@ void GrGpuGL::flushAAState(DrawType type) {
}
}
+void GrGpuGL::flushPathStencilSettings(SkPath::FillType fill) {
+ GrStencilSettings pathStencilSettings;
+ this->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 =
+ gr_to_gl_stencil_func(pathStencilSettings.func(GrStencilSettings::kFront_Face));
+ GL_CALL(PathStencilFunc(func,
+ pathStencilSettings.funcRef(GrStencilSettings::kFront_Face),
+ pathStencilSettings.funcMask(GrStencilSettings::kFront_Face)));
+
+ fHWPathStencilSettings = pathStencilSettings;
+ }
+}
+
void GrGpuGL::flushBlend(bool isLines,
GrBlendCoeff srcCoeff,
GrBlendCoeff dstCoeff) {
@@ -2148,7 +2155,7 @@ void GrGpuGL::enableTexGen(int unitIdx,
const GrGLfloat* coefficients) {
SkASSERT(this->glCaps().fixedFunctionSupport());
- SkASSERT(this->caps()->pathStencilingSupport());
+ SkASSERT(this->caps()->pathRenderingSupport());
SkASSERT(components >= kS_TexGenComponents && components <= kSTR_TexGenComponents);
if (GR_GL_OBJECT_LINEAR == fHWTexGenSettings[unitIdx].fMode &&
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index a59654a0f8..a945e54c99 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -151,11 +151,8 @@ private:
virtual void onGpuDraw(const DrawInfo&) SK_OVERRIDE;
- virtual void setStencilPathSettings(const GrPath&,
- SkPath::FillType,
- GrStencilSettings* settings)
- SK_OVERRIDE;
virtual void onGpuStencilPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
+ virtual void onGpuFillPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
virtual void clearStencil() SK_OVERRIDE;
virtual void clearStencilClip(const SkIRect& rect,
@@ -245,6 +242,7 @@ private:
void flushRenderTarget(const SkIRect* bound);
void flushStencil(DrawType);
void flushAAState(DrawType);
+ void flushPathStencilSettings(SkPath::FillType fill);
bool configToGLFormats(GrPixelConfig config,
bool getSizedInternal,
@@ -434,6 +432,7 @@ private:
GrStencilSettings fHWStencilSettings;
TriState fHWStencilTestEnabled;
+ GrStencilSettings fHWPathStencilSettings;
GrDrawState::DrawFace fHWDrawFace;
TriState fHWWriteToColor;
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index 92eed8d9bb..f3b566c209 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -252,6 +252,9 @@ bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstC
SkDEBUGFAIL("Failed to create program!");
return false;
}
+
+ SkASSERT(kFillPath_DrawType != type || !fCurrentProgram->hasVertexShader());
+
fCurrentProgram.get()->ref();
GrGLuint programID = fCurrentProgram->programID();