'skia_scalar%': 'float',
'skia_mesa%': 0,
+ 'skia_nv_path_rendering%': 0,
# Do not turn on 'skia_angle' - it is currently experimental
'skia_angle%': 0,
'skia_arch_type%': 'x86',
'os_posix%': '<(os_posix)',
'skia_scalar%': '<(skia_scalar)',
'skia_mesa%': '<(skia_mesa)',
+ 'skia_nv_path_rendering%': '<(skia_nv_path_rendering)',
'skia_angle%': '<(skia_angle)',
'skia_arch_type%': '<(skia_arch_type)',
'skia_arch_width%': '<(skia_arch_width)',
'../src/gpu/GrResourceCache.h',
'../src/gpu/GrStencil.cpp',
'../src/gpu/GrStencil.h',
+ '../src/gpu/GrStencilAndCoverPathRenderer.cpp',
+ '../src/gpu/GrStencilAndCoverPathRenderer.h',
'../src/gpu/GrStencilBuffer.cpp',
'../src/gpu/GrStencilBuffer.h',
'../src/gpu/GrStringBuilder.h',
'GR_IMPLEMENTATION=1',
],
'conditions': [
+ [ 'skia_nv_path_rendering', {
+ 'defines': [
+ 'GR_GL_USE_NV_PATH_RENDERING=1',
+ ],
+ }],
[ 'skia_os == "linux"', {
'sources!': [
'../src/gpu/gl/GrGLDefaultInterface_none.cpp',
* check the first time we use a color format or a combination of color /
* stencil formats as attachments. If the FBO is complete we will assume
* subsequent attachments with the same formats are complete as well.
+ *
+ * GR_GL_USE_NV_PATH_RENDERING: Enable experimental support for
+ * GL_NV_path_rendering. There are known issues with clipping, non-AA paths, and
+ * perspective.
*/
#if !defined(GR_GL_LOG_CALLS)
#define GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT 0
#endif
+#if !defined(GR_GL_USE_NV_PATH_RENDERING)
+ #define GR_GL_USE_NV_PATH_RENDERING 0
+#endif
+
/**
* There is a strange bug that occurs on Macs with NVIDIA GPUs. We don't
* fully understand it. When (element) array buffers are continually
* found in the LICENSE file.
*/
-
+
+#include "GrStencilAndCoverPathRenderer.h"
#include "GrAAHairLinePathRenderer.h"
#include "GrAAConvexPathRenderer.h"
#include "GrSoftwarePathRenderer.h"
void GrPathRenderer::AddPathRenderers(GrContext* ctx,
GrPathRendererChain::UsageFlags flags,
GrPathRendererChain* chain) {
+ if (GrPathRenderer* pr = GrStencilAndCoverPathRenderer::Create(ctx)) {
+ chain->addPathRenderer(pr)->unref();
+ }
if (!(GrPathRendererChain::kNonAAOnly_UsageFlag & flags)) {
if (GrPathRenderer* pr = GrAAHairLinePathRenderer::Create(ctx)) {
bool clearToInside;
SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
int start = process_initial_clip_elements(clipCopy,
- rtRect,
- &clearToInside,
- &startOp);
+ rtRect,
+ &clearToInside,
+ &startOp);
fGpu->clearStencilClip(bounds, clearToInside);
bool fillInverted;
// 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 (clipCopy.getDoAA(c)) {
+ drawState->enableState(GrDrawState::kHWAntialias_StateBit);
+ } else {
+ drawState->disableState(GrDrawState::kHWAntialias_StateBit);
+ }
+ }
bool canRenderDirectToStencil; // can the clip element be drawn
// directly to the stencil buffer
}
fDrawState = ds;
}
+ bool isSet() const { return NULL != fDrawState; }
private:
GrDrawState* fDrawState;
GrMatrix fSavedMatrix;
}
}
-void GrDrawTarget::stencilPath(const GrPath& path, GrPathFill fill) {
+void GrDrawTarget::stencilPath(const GrPath* path, GrPathFill fill) {
// TODO: extract portions of checkDraw that are relevant to path stenciling.
+ GrAssert(NULL != path);
GrAssert(fCaps.fPathStencilingSupport);
GrAssert(kHairLine_GrPathFill != fill);
GrAssert(!GrIsFillInverted(fill));
* winding (not inverse or hairline). It will respect the HW antialias flag
* on the draw state (if possible in the 3D API).
*/
- void stencilPath(const GrPath& path, GrPathFill fill);
+ void stencilPath(const GrPath*, GrPathFill);
/**
* Helper function for drawing rects. This does not use the current index
virtual void onDrawNonIndexed(GrPrimitiveType type,
int startVertex,
int vertexCount) = 0;
- virtual void onStencilPath(const GrPath& path, GrPathFill fill) = 0;
+ virtual void onStencilPath(const GrPath*, GrPathFill) = 0;
// subclass overrides to be notified when clip is set. Must call
// INHERITED::clipwillBeSet
this->onGpuDrawNonIndexed(type, sVertex, vertexCount);
}
-void GrGpu::onStencilPath(const GrPath& path, GrPathFill fill) {
+void GrGpu::onStencilPath(const GrPath* path, GrPathFill fill) {
this->handleDirtyContext();
+ // TODO: make this more effecient (don't copy and copy back)
+ GrAutoTRestore<GrStencilSettings> asr(this->drawState()->stencil());
+
+ this->setStencilPathSettings(*path, fill, this->drawState()->stencil());
if (!this->setupClipAndFlushState(kStencilPath_DrawType)) {
return;
}
virtual void onGpuDrawNonIndexed(GrPrimitiveType type,
uint32_t vertexCount,
uint32_t numVertices) = 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&,
+ GrPathFill,
+ GrStencilSettings* settings) = 0;
// overridden by API-specific derived class to perform the path stenciling.
- virtual void onGpuStencilPath(const GrPath& path, GrPathFill fill) = 0;
+ virtual void onGpuStencilPath(const GrPath*, GrPathFill) = 0;
// overridden by API-specific derived class to perform flush
virtual void onForceRenderTargetFlush() = 0;
virtual void onDrawNonIndexed(GrPrimitiveType type,
int startVertex,
int vertexCount) SK_OVERRIDE;
- virtual void onStencilPath(const GrPath& path, GrPathFill fill) SK_OVERRIDE;
+ virtual void onStencilPath(const GrPath* path, GrPathFill fill) SK_OVERRIDE;
// readies the pools to provide vertex/index data.
void prepareVertexPool();
#include "GrInOrderDrawBuffer.h"
-#include "GrRenderTarget.h"
-#include "GrTexture.h"
#include "GrBufferAllocPool.h"
+#include "GrGpu.h"
#include "GrIndexBuffer.h"
+#include "GrPath.h"
+#include "GrRenderTarget.h"
+#include "GrTexture.h"
#include "GrVertexBuffer.h"
-#include "GrGpu.h"
GrInOrderDrawBuffer::GrInOrderDrawBuffer(const GrGpu* gpu,
GrVertexBufferAllocPool* vertexPool,
draw->fIndexBuffer = NULL;
}
-void GrInOrderDrawBuffer::onStencilPath(const GrPath&, GrPathFill) {
- GrCrash("Not implemented yet. Should not get here.");
+void GrInOrderDrawBuffer::onStencilPath(const GrPath* path, GrPathFill fill) {
+ if (this->needsNewClip()) {
+ this->recordClip();
+ }
+ // Only compare the subset of GrDrawState relevant to path stenciling?
+ if (this->needsNewState()) {
+ this->recordState();
+ }
+ StencilPath* sp = this->recordStencilPath();
+ sp->fPath.reset(path);
+ path->ref();
+ sp->fFill = fill;
}
void GrInOrderDrawBuffer::clear(const GrIRect* rect,
}
fCmds.reset();
fDraws.reset();
+ fStencilPaths.reset();
fStates.reset();
fClears.reset();
fVertexPool.reset();
GrDrawState* prevDrawState = target->drawState();
prevDrawState->ref();
- int currState = 0;
- int currClip = 0;
- int currClear = 0;
- int currDraw = 0;
+ int currState = 0;
+ int currClip = 0;
+ int currClear = 0;
+ int currDraw = 0;
+ int currStencilPath = 0;
for (int c = 0; c < numCmds; ++c) {
switch (fCmds[c]) {
++currDraw;
break;
}
+ case kStencilPath_Cmd: {
+ const StencilPath& sp = fStencilPaths[currStencilPath];
+ target->stencilPath(sp.fPath.get(), sp.fFill);
+ ++currStencilPath;
+ break;
+ }
case kSetState_Cmd:
target->setDrawState(&fStates[currState]);
++currState;
return &fDraws.push_back();
}
+GrInOrderDrawBuffer::StencilPath* GrInOrderDrawBuffer::recordStencilPath() {
+ fCmds.push_back(kStencilPath_Cmd);
+ return &fStencilPaths.push_back();
+}
+
GrInOrderDrawBuffer::Clear* GrInOrderDrawBuffer::recordClear() {
fCmds.push_back(kClear_Cmd);
return &fClears.push_back();
#include "GrDrawTarget.h"
#include "GrAllocPool.h"
#include "GrAllocator.h"
+#include "GrPath.h"
#include "GrClip.h"
+#include "SkTemplates.h"
+
class GrGpu;
class GrIndexBufferAllocPool;
class GrVertexBufferAllocPool;
int indexCount) SK_OVERRIDE;
private:
enum Cmd {
- kDraw_Cmd = 1,
- kSetState_Cmd = 2,
- kSetClip_Cmd = 3,
- kClear_Cmd = 4,
+ kDraw_Cmd = 1,
+ kStencilPath_Cmd = 2,
+ kSetState_Cmd = 3,
+ kSetClip_Cmd = 4,
+ kClear_Cmd = 5,
};
struct Draw {
const GrIndexBuffer* fIndexBuffer;
};
+ struct StencilPath {
+ SkAutoTUnref<const GrPath> fPath;
+ GrPathFill fFill;
+ };
+
struct Clear {
Clear() : fRenderTarget(NULL) {}
~Clear() { GrSafeUnref(fRenderTarget); }
virtual void onDrawNonIndexed(GrPrimitiveType primitiveType,
int startVertex,
int vertexCount) SK_OVERRIDE;
- virtual void onStencilPath(const GrPath&, GrPathFill) SK_OVERRIDE;
+ virtual void onStencilPath(const GrPath*, GrPathFill) SK_OVERRIDE;
virtual bool onReserveVertexSpace(GrVertexLayout layout,
int vertexCount,
void** vertices) SK_OVERRIDE;
bool needsNewClip() const;
// these functions record a command
- void recordState();
- void recordDefaultState();
- void recordClip();
- void recordDefaultClip();
- Draw* recordDraw();
- Clear* recordClear();
+ void recordState();
+ void recordDefaultState();
+ void recordClip();
+ void recordDefaultClip();
+ Draw* recordDraw();
+ StencilPath* recordStencilPath();
+ Clear* recordClear();
// call this to invalidate the tracking data that is used to concatenate
// multiple draws into a single draw.
enum {
kCmdPreallocCnt = 32,
kDrawPreallocCnt = 8,
+ kStencilPathPreallocCnt = 8,
kStatePreallocCnt = 8,
kClipPreallocCnt = 8,
kClearPreallocCnt = 4,
SkSTArray<kCmdPreallocCnt, uint8_t, true> fCmds;
GrSTAllocator<kDrawPreallocCnt, Draw> fDraws;
+ GrSTAllocator<kStatePreallocCnt, StencilPath> fStencilPaths;
GrSTAllocator<kStatePreallocCnt, GrDrawState> fStates;
GrSTAllocator<kClearPreallocCnt, Clear> fClears;
GrSTAllocator<kClipPreallocCnt, GrClip> fClips;
public:
GrPath(GrGpu* gpu) : INHERITED(gpu) {}
- const GrIRect& getBounds() const { return fBounds; }
+ const GrRect& getBounds() const { return fBounds; }
protected:
- GrIRect fBounds;
+ GrRect fBounds;
private:
typedef GrResource INHERITED;
--- /dev/null
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrStencilAndCoverPathRenderer.h"
+#include "GrContext.h"
+#include "GrGpu.h"
+#include "GrPath.h"
+
+GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrContext* context) {
+ GrAssert(NULL != context);
+ GrAssert(NULL != context->getGpu());
+ if (context->getGpu()->getCaps().fPathStencilingSupport) {
+ return new GrStencilAndCoverPathRenderer(context->getGpu());
+ } else {
+ return NULL;
+ }
+}
+
+GrStencilAndCoverPathRenderer::GrStencilAndCoverPathRenderer(GrGpu* gpu) {
+ GrAssert(gpu->getCaps().fPathStencilingSupport);
+ fGpu = gpu;
+ gpu->ref();
+}
+
+GrStencilAndCoverPathRenderer::~GrStencilAndCoverPathRenderer() {
+ fGpu->unref();
+}
+
+bool GrStencilAndCoverPathRenderer::canDrawPath(const SkPath& path,
+ GrPathFill fill,
+ const GrDrawTarget* target,
+ bool antiAlias) const {
+ return kHairLine_GrPathFill != fill &&
+ !antiAlias && // doesn't do per-path AA, relies on the target having MSAA
+ target->getDrawState().getStencil().isDisabled();
+}
+
+bool GrStencilAndCoverPathRenderer::requiresStencilPass(const SkPath& path,
+ GrPathFill fill,
+ const GrDrawTarget* target) const {
+ return true;
+}
+
+void GrStencilAndCoverPathRenderer::drawPathToStencil(const SkPath& path,
+ GrPathFill fill,
+ GrDrawTarget* target) {
+ GrAssert(kEvenOdd_GrPathFill == fill || kWinding_GrPathFill == fill);
+ SkAutoTUnref<GrPath> p(fGpu->createPath(path));
+ target->stencilPath(p, fill);
+}
+
+bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path,
+ GrPathFill fill,
+ const GrVec* translate,
+ GrDrawTarget* target,
+ GrDrawState::StageMask stageMask,
+ bool antiAlias){
+ GrAssert(!antiAlias);
+ GrAssert(kHairLine_GrPathFill != fill);
+
+ GrDrawState* drawState = target->drawState();
+ GrAssert(drawState->getStencil().isDisabled());
+
+ SkAutoTUnref<GrPath> p(fGpu->createPath(path));
+ GrDrawState::AutoViewMatrixRestore avmr;
+ if (translate) {
+ avmr.set(drawState);
+ drawState->viewMatrix()->postTranslate(translate->fX, translate->fY);
+ }
+ GrPathFill nonInvertedFill = GrNonInvertedFill(fill);
+ target->stencilPath(p, 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();
+ GrScalar bloat = drawState->getViewMatrix().getMaxStretch() * GR_ScalarHalf;
+ if (nonInvertedFill == fill) {
+ 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,
+ kEqual_StencilFunc,
+ 0xffff,
+ 0x0000,
+ 0xffff);
+ GrMatrix vmi;
+ bounds.setLTRB(0, 0,
+ GrIntToScalar(drawState->getRenderTarget()->width()),
+ GrIntToScalar(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 {
+ if (stageMask) {
+ if (!drawState->getViewInverse(&vmi)) {
+ GrPrintf("Could not invert matrix.");
+ return false;
+ }
+ drawState->preConcatSamplerMatrices(stageMask, vmi);
+ }
+ if (avmr.isSet()) {
+ avmr.set(drawState);
+ }
+ drawState->viewMatrix()->reset();
+ bloat = 0;
+ }
+ *drawState->stencil() = kInvertedStencilPass;
+ }
+ bounds.outset(bloat, bloat);
+ target->drawSimpleRect(bounds, NULL, stageMask);
+ target->drawState()->stencil()->setDisabled();
+ return true;
+}
--- /dev/null
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrBuiltInPathRenderer_DEFINED
+#define GrBuiltInPathRenderer_DEFINED
+
+#include "GrPathRenderer.h"
+
+class GrContext;
+class GrGpu;
+
+/**
+ * Uses GrGpu::stencilPath followed by a cover rectangle. This subclass doesn't apply AA; it relies
+ * on the target having MSAA if AA is desired.
+ */
+class GrStencilAndCoverPathRenderer : public GrPathRenderer {
+public:
+
+ static GrPathRenderer* Create(GrContext* context);
+
+ virtual ~GrStencilAndCoverPathRenderer();
+
+ virtual bool canDrawPath(const SkPath& path,
+ GrPathFill fill,
+ const GrDrawTarget* target,
+ bool antiAlias) const SK_OVERRIDE;
+
+ virtual bool requiresStencilPass(const SkPath& path,
+ GrPathFill fill,
+ const GrDrawTarget* target) const SK_OVERRIDE;
+
+ virtual void drawPathToStencil(const SkPath& path,
+ GrPathFill fill,
+ GrDrawTarget* target) SK_OVERRIDE;
+
+protected:
+ virtual bool onDrawPath(const SkPath& path,
+ GrPathFill fill,
+ const GrVec* translate,
+ GrDrawTarget* target,
+ GrDrawState::StageMask stageMask,
+ bool antiAlias) SK_OVERRIDE;
+
+private:
+ GrStencilAndCoverPathRenderer(GrGpu* gpu);
+
+ GrGpu* fGpu;
+
+ typedef GrPathRenderer INHERITED;
+};
+
+#endif
GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path) : INHERITED(gpu) {
GL_CALL_RET(fPathID, GenPaths(1));
+ //GrPrintf("\tGenPaths ID: %d\n", fPathID);
SkPath::Iter iter(path, true);
SkSTArray<16, GrGLubyte, true> pathCommands;
#ifndef SK_SCALAR_IS_FLOAT
- GrCrash("Expected scalar is float.");
+ GR_STATIC_ASSERT(false);
#endif
SkSTArray<16, SkPoint, true> pathPoints;
GL_CALL(PathCommands(fPathID,
verbCnt, &pathCommands[0],
2 * pointCnt, GR_GL_FLOAT, &pathPoints[0]));
- GrRect bounds = path.getBounds();
- bounds.roundOut(&fBounds);
+ //GrPrintf("\tPathCommands ID: %d\n", fPathID);
+ fBounds = path.getBounds();
}
GrGLPath::~GrGLPath() {
void GrGLPath::onRelease() {
if (0 != fPathID) {
+ // FIXME: When we draw a clipped path we may get a call sequence that looks
+ // like this:
+ // GenPaths(1, &fPathID); // fPathID = 1, the path to draw
+ // PathCommands(1, ...);
+ // GenPaths(1, &fPathID); // fPathID = 2, the clip path
+ // PathCommands(2, ...);
+ // PathStencilFunc(...);
+ // StencilFillPath(2, ...); // draw the clip
+ // DeletePath(1, &fPathID); // fPathID == 2
+ // PathStencilFunc(...);
+ // StencilFillPath(2, ...); // draw the path
+ // DeletePath(1, &fPathID); // fPathID == 1
+ //
+ // Deleting the clip path causes the second StencilFillPath to fail with
+ // INVALID_OPERATION.
+#if 0
GL_CALL(DeletePaths(1, fPathID));
+ //GrPrintf("\tDeletePaths ID: %d\n", fPathID);
+#else
+ //GrPrintf("\tLeak Path ID: %d\n", fPathID);
+#endif
fPathID = 0;
}
}
fCaps.fMaxRenderTargetSize = GrMin(fCaps.fMaxTextureSize, fCaps.fMaxRenderTargetSize);
fCaps.fFSAASupport = GrGLCaps::kNone_MSFBOType != this->glCaps().msFBOType();
+ fCaps.fPathStencilingSupport = GR_GL_USE_NV_PATH_RENDERING &&
+ this->hasExtension("GL_NV_path_rendering");
// Enable supported shader-related caps
if (kDesktop_GrGLBinding == this->glBinding()) {
fHWBoundRenderTarget = NULL;
+ fHWPathMatrixState.invalidate();
+
// we assume these values
if (this->glCaps().unpackRowLengthSupport()) {
GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
}
GrPath* GrGpuGL::onCreatePath(const SkPath& inPath) {
- GrPath* path = NULL;
- if (fCaps.fPathStencilingSupport) {
- path = new GrGLPath(this, inPath);
- }
- return path;
+ GrAssert(fCaps.fPathStencilingSupport);
+ return new GrGLPath(this, inPath);
}
void GrGpuGL::flushScissor() {
#endif
}
-void GrGpuGL::onGpuStencilPath(const GrPath&, GrPathFill) {
- GrCrash("Not implemented yet. Should not get here.");
+namespace {
+const GrStencilSettings& winding_nv_path_stencil_settings() {
+ GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
+ kIncClamp_StencilOp,
+ kIncClamp_StencilOp,
+ kAlwaysIfInClip_StencilFunc,
+ ~0, ~0, ~0);
+ 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,
+ ~0, ~0, ~0);
+ return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
+}
+}
+
+
+void GrGpuGL::setStencilPathSettings(const GrPath&,
+ GrPathFill fill,
+ GrStencilSettings* settings) {
+ switch (fill) {
+ case kEvenOdd_GrPathFill:
+ *settings = even_odd_nv_path_stencil_settings();
+ return;
+ case kWinding_GrPathFill:
+ *settings = winding_nv_path_stencil_settings();
+ return;
+ default:
+ GrCrash("Unexpected path fill.");
+ }
+}
+
+void GrGpuGL::onGpuStencilPath(const GrPath* path, GrPathFill fill) {
+ GrAssert(fCaps.fPathStencilingSupport);
+
+ GrGLuint id = static_cast<const GrGLPath*>(path)->pathID();
+ GrDrawState* drawState = this->drawState();
+ GrAssert(NULL != drawState->getRenderTarget());
+ if (NULL == drawState->getRenderTarget()->getStencilBuffer()) {
+ return;
+ }
+
+ // Decide how to manipulate the stencil buffer based on the fill rule.
+ // Also, assert that the stencil settings we set in setStencilPathSettings
+ // are present.
+ GrAssert(!fStencilSettings.isTwoSided());
+ GrGLenum fillMode;
+ switch (fill) {
+ case kWinding_GrPathFill:
+ fillMode = GR_GL_COUNT_UP;
+ GrAssert(kIncClamp_StencilOp ==
+ fStencilSettings.passOp(GrStencilSettings::kFront_Face));
+ GrAssert(kIncClamp_StencilOp ==
+ fStencilSettings.failOp(GrStencilSettings::kFront_Face));
+ break;
+ case kEvenOdd_GrPathFill:
+ fillMode = GR_GL_INVERT;
+ GrAssert(kInvert_StencilOp ==
+ fStencilSettings.passOp(GrStencilSettings::kFront_Face));
+ GrAssert(kInvert_StencilOp ==
+ fStencilSettings.failOp(GrStencilSettings::kFront_Face));
+ break;
+ default:
+ // Only the above two fill rules are allowed.
+ GrCrash("Unexpected path fill.");
+ }
+ GrGLint writeMask = fStencilSettings.writeMask(GrStencilSettings::kFront_Face);
+ GL_CALL(StencilFillPath(id, fillMode, writeMask));
+ //GrPrintf("\tStencilFillPath ID: %d\n", id);
}
void GrGpuGL::onResolveRenderTarget(GrRenderTarget* target) {
}
}
-void GrGpuGL::flushStencil() {
- if (fStencilSettings.isDisabled()) {
- if (kNo_TriState != fHWStencilTestEnabled) {
- GL_CALL(Disable(GR_GL_STENCIL_TEST));
- fHWStencilTestEnabled = kNo_TriState;
- }
- } else {
- if (kYes_TriState != fHWStencilTestEnabled) {
- GL_CALL(Enable(GR_GL_STENCIL_TEST));
- fHWStencilTestEnabled = kYes_TriState;
+void GrGpuGL::flushStencil(DrawType type) {
+ if (kStencilPath_DrawType == type) {
+ GrAssert(!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 (fStencilSettings.isDisabled()) {
+ if (kNo_TriState != fHWStencilTestEnabled) {
+ GL_CALL(Disable(GR_GL_STENCIL_TEST));
+ fHWStencilTestEnabled = kNo_TriState;
+ }
+ } else {
+ if (kYes_TriState != fHWStencilTestEnabled) {
+ GL_CALL(Enable(GR_GL_STENCIL_TEST));
+ fHWStencilTestEnabled = kYes_TriState;
+ }
}
- }
- if (fHWStencilSettings != fStencilSettings) {
if (!fStencilSettings.isDisabled()) {
if (this->getCaps().fTwoSidedStencilSupport) {
set_gl_stencil(this->glInterface(),
}
}
-void GrGpuGL::flushAAState(bool isLines) {
+void GrGpuGL::flushAAState(DrawType type) {
const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
if (kDesktop_GrGLBinding == this->glBinding()) {
// ES doesn't support toggling GL_MULTISAMPLE and doesn't have
// we prefer smooth lines over multisampled lines
bool smoothLines = false;
- if (isLines) {
+ if (kDrawLines_DrawType == type) {
smoothLines = this->willUseHWAALines();
if (smoothLines) {
if (kYes_TriState != fHWAAState.fSmoothLineEnabled) {
}
}
}
- if (!smoothLines && rt->isMultisampled()) {
- if (this->getDrawState().isHWAntialiasState()) {
+ if (!smoothLines && rt->isMultisampled()) {
+ // FIXME: GL_NV_pr doesn't seem to like MSAA disabled. The paths
+ // convex hulls of each segment appear to get filled.
+ bool enableMSAA = kStencilPath_DrawType == type ||
+ this->getDrawState().isHWAntialiasState();
+ if (enableMSAA) {
if (kYes_TriState != fHWAAState.fMSAAEnabled) {
GL_CALL(Enable(GR_GL_MULTISAMPLE));
fHWAAState.fMSAAEnabled = kYes_TriState;
virtual void onResolveRenderTarget(GrRenderTarget* target) SK_OVERRIDE;
-
virtual void onGpuDrawIndexed(GrPrimitiveType type,
uint32_t startVertex,
uint32_t startIndex,
virtual void onGpuDrawNonIndexed(GrPrimitiveType type,
uint32_t vertexCount,
uint32_t numVertices) SK_OVERRIDE;
- virtual void onGpuStencilPath(const GrPath&, GrPathFill) SK_OVERRIDE;
+
+ virtual void setStencilPathSettings(const GrPath&,
+ GrPathFill,
+ GrStencilSettings* settings)
+ SK_OVERRIDE;
+ virtual void onGpuStencilPath(const GrPath*, GrPathFill) SK_OVERRIDE;
virtual void clearStencil() SK_OVERRIDE;
virtual void clearStencilClip(const GrIRect& rect,
void flushCoverage(GrColor color);
// sets the MVP matrix uniform for currently bound program
- void flushViewMatrix();
+ void flushViewMatrix(DrawType type);
// flushes the parameters to two point radial gradient
void flushRadial2(int stage);
// bound is region that may be modified and therefore has to be resolved.
// NULL means whole target. Can be an empty rect.
void flushRenderTarget(const GrIRect* bound);
- void flushStencil();
- void flushAAState(bool isLines);
+ void flushStencil(DrawType);
+ void flushAAState(DrawType);
bool configToGLFormats(GrPixelConfig config,
bool getSizedInternal,
}
} fHWAAState;
+ struct {
+ GrMatrix fViewMatrix;
+ SkISize fRTSize;
+ void invalidate() {
+ fViewMatrix = GrMatrix::InvalidMatrix();
+ fRTSize.fWidth = -1; // just make the first value compared illegal.
+ }
+ } fHWPathMatrixState;
+
GrStencilSettings fHWStencilSettings;
TriState fHWStencilTestEnabled;
#define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
-void GrGpuGL::flushViewMatrix() {
+void GrGpuGL::flushViewMatrix(DrawType type) {
const GrGLRenderTarget* rt = static_cast<const GrGLRenderTarget*>(this->getDrawState().getRenderTarget());
SkISize viewportSize;
const GrGLIRect& viewport = rt->getViewport();
const GrMatrix& vm = this->getDrawState().getViewMatrix();
- if (!fProgramData->fViewMatrix.cheapEqualTo(vm) ||
+ if (kStencilPath_DrawType == type) {
+ if (fHWPathMatrixState.fViewMatrix != vm) {
+ // We use the GL model view matrix to hold the draw state's view
+ // matrix and the GL projection matrix to convert to normalized y-up
+ // coords.
+ GrGLfloat mv[] = {
+ // col 0
+ GrScalarToFloat(vm[GrMatrix::kMScaleX]),
+ GrScalarToFloat(vm[GrMatrix::kMSkewY]),
+ 0,
+ GrScalarToFloat(vm[GrMatrix::kMPersp0]),
+
+ // col 1
+ GrScalarToFloat(vm[GrMatrix::kMSkewX]),
+ GrScalarToFloat(vm[GrMatrix::kMScaleY]),
+ 0,
+ GrScalarToFloat(vm[GrMatrix::kMPersp1]),
+
+ // col 2
+ 0, 0, 0, 0,
+
+ // col3
+ GrScalarToFloat(vm[GrMatrix::kMTransX]),
+ GrScalarToFloat(vm[GrMatrix::kMTransY]),
+ 0.5f,
+ GrScalarToFloat(vm[GrMatrix::kMPersp2])
+ };
+ GL_CALL(MatrixMode(GR_GL_MODELVIEW));
+ GL_CALL(LoadMatrixf(mv));
+ fHWPathMatrixState.fViewMatrix = vm;
+ }
+ if (fHWPathMatrixState.fRTSize != viewportSize) {
+ GrGLfloat p[] = {
+ // col 0
+ 2.f / rt->width(), 0, 0, 0,
+
+ // col 1
+ 0, -2.f / rt->height(), 0, 0,
+
+ // col 2
+ 0, 0, 1.f, 0,
+
+ // col 3
+ -1.f, 1.f, 0, 1.f,
+ };
+ GL_CALL(MatrixMode(GR_GL_PROJECTION));
+ GL_CALL(LoadMatrixf(p));
+ fHWPathMatrixState.fRTSize = viewportSize;
+ }
+ } else if (!fProgramData->fViewMatrix.cheapEqualTo(vm) ||
fProgramData->fViewportSize != viewportSize) {
GrMatrix m;
// and bailed if not true.
GrAssert(NULL != drawState.getRenderTarget());
- this->flushMiscFixedFunctionState();
- this->flushStencil();
- this->flushAAState(kDrawLines_DrawType == type);
-
- GrBlendCoeff srcCoeff;
- GrBlendCoeff dstCoeff;
- BlendOptFlags blendOpts = this->getBlendOpts(false, &srcCoeff, &dstCoeff);
- if (kSkipDraw_BlendOptFlag & blendOpts) {
- return false;
- }
-
- GrCustomStage* customStages [GrDrawState::kNumStages];
- this->buildProgram(kDrawPoints_DrawType == type,
- blendOpts, dstCoeff, customStages);
- fProgramData = fProgramCache->getProgramData(fCurrentProgram,
- customStages);
- if (NULL == fProgramData) {
- GrAssert(!"Failed to create program!");
- return false;
- }
-
- if (fHWProgramID != fProgramData->fProgramID) {
- GL_CALL(UseProgram(fProgramData->fProgramID));
- fHWProgramID = fProgramData->fProgramID;
- }
- fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff);
- this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff);
-
- GrColor color;
- GrColor coverage;
- if (blendOpts & kEmitTransBlack_BlendOptFlag) {
- color = 0;
- coverage = 0;
- } else if (blendOpts & kEmitCoverage_BlendOptFlag) {
- color = 0xffffffff;
- coverage = drawState.getCoverage();
- } else {
- color = drawState.getColor();
- coverage = drawState.getCoverage();
- }
- this->flushColor(color);
- this->flushCoverage(coverage);
+ if (kStencilPath_DrawType != type) {
+ this->flushMiscFixedFunctionState();
- this->flushViewMatrix();
+ GrBlendCoeff srcCoeff;
+ GrBlendCoeff dstCoeff;
+ BlendOptFlags blendOpts = this->getBlendOpts(false, &srcCoeff, &dstCoeff);
+ if (kSkipDraw_BlendOptFlag & blendOpts) {
+ return false;
+ }
- for (int s = 0; s < GrDrawState::kNumStages; ++s) {
- if (this->isStageEnabled(s)) {
+ GrCustomStage* customStages [GrDrawState::kNumStages];
+ this->buildProgram(kDrawPoints_DrawType == type,
+ blendOpts, dstCoeff, customStages);
+ fProgramData = fProgramCache->getProgramData(fCurrentProgram,
+ customStages);
+ if (NULL == fProgramData) {
+ GrAssert(!"Failed to create program!");
+ return false;
+ }
+ if (fHWProgramID != fProgramData->fProgramID) {
+ GL_CALL(UseProgram(fProgramData->fProgramID));
+ fHWProgramID = fProgramData->fProgramID;
+ }
+ fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff);
+ this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff);
+
+ GrColor color;
+ GrColor coverage;
+ if (blendOpts & kEmitTransBlack_BlendOptFlag) {
+ color = 0;
+ coverage = 0;
+ } else if (blendOpts & kEmitCoverage_BlendOptFlag) {
+ color = 0xffffffff;
+ coverage = drawState.getCoverage();
+ } else {
+ color = drawState.getColor();
+ coverage = drawState.getCoverage();
+ }
+ this->flushColor(color);
+ this->flushCoverage(coverage);
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ if (this->isStageEnabled(s)) {
#if GR_DEBUG
- // check for circular rendering
- GrAssert(NULL == drawState.getRenderTarget() ||
- NULL == drawState.getTexture(s) ||
- drawState.getTexture(s)->asRenderTarget() !=
- drawState.getRenderTarget());
+ // check for circular rendering
+ GrAssert(NULL == drawState.getRenderTarget() ||
+ NULL == drawState.getTexture(s) ||
+ drawState.getTexture(s)->asRenderTarget() !=
+ drawState.getRenderTarget());
#endif
-
- this->flushBoundTextureAndParams(s);
-
- this->flushTextureMatrixAndDomain(s);
-
- if (NULL != fProgramData->fCustomStage[s]) {
- const GrSamplerState& sampler =
- this->getDrawState().getSampler(s);
- const GrGLTexture* texture =
- static_cast<const GrGLTexture*>(
- this->getDrawState().getTexture(s));
- fProgramData->fCustomStage[s]->setData(
- this->glInterface(), *texture,
- *sampler.getCustomStage(), s);
+ this->flushBoundTextureAndParams(s);
+
+ this->flushTextureMatrixAndDomain(s);
+
+ if (NULL != fProgramData->fCustomStage[s]) {
+ const GrSamplerState& sampler =
+ this->getDrawState().getSampler(s);
+ const GrGLTexture* texture =
+ static_cast<const GrGLTexture*>(
+ this->getDrawState().getTexture(s));
+ fProgramData->fCustomStage[s]->setData(
+ this->glInterface(), *texture,
+ *sampler.getCustomStage(), s);
+ }
}
}
+ this->flushColorMatrix();
}
- this->flushColorMatrix();
+ this->flushStencil(type);
+ this->flushViewMatrix(type);
this->flushScissor();
+ this->flushAAState(type);
GrIRect* rect = NULL;
GrIRect clipBounds;