Make GrContext cache the gpu paths

Creating paths for nv_path_rendering is costly. Try to reduce this
cost by caching paths based on the SkPath "hash" (i.e. SkPathRef
generation id) and stroke properties.

Adds the paths to GrContext::fTextureCache instance. Later this should
be renamed and the GrContext API should reflect the nature of the cache
better.

R=bsalomon@google.com, mtklein@google.com

Author: kkinnunen@nvidia.com

Review URL: https://codereview.chromium.org/26557003

git-svn-id: http://skia.googlecode.com/svn/trunk@12083 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2013-11-01 15:23:44 +00:00
parent e1e99ef0af
commit 5c8ee2539b
8 changed files with 80 additions and 14 deletions

View File

@ -81,6 +81,14 @@ public:
*/
bool applyToPath(SkPath* dst, const SkPath& src) const;
bool operator==(const SkStrokeRec& other) const {
return fWidth == other.fWidth &&
fMiterLimit == other.fMiterLimit &&
fCap == other.fCap &&
fJoin == other.fJoin &&
fStrokeAndFill == other.fStrokeAndFill;
}
private:
SkScalar fWidth;
SkScalar fMiterLimit;

View File

@ -29,6 +29,7 @@ class GrIndexBuffer;
class GrIndexBufferAllocPool;
class GrInOrderDrawBuffer;
class GrOvalRenderer;
class GrPath;
class GrPathRenderer;
class GrResourceEntry;
class GrResourceCache;
@ -925,6 +926,7 @@ private:
// Needed so GrTexture's returnToCache helper function can call
// addExistingTextureToCache
friend class GrTexture;
friend class GrStencilAndCoverPathRenderer;
// Add an existing texture to the texture cache. This is intended solely
// for use with textures released from an GrAutoScratchTexture.
@ -948,6 +950,15 @@ private:
*/
static bool OverbudgetCB(void* data);
/** Creates a new gpu path, based on the specified path and stroke and returns it.
* The caller owns a ref on the returned path which must be balanced by a call to unref.
*
* @param skPath the path geometry.
* @param stroke the path stroke.
* @return a new path or NULL if the operation is not supported by the backend.
*/
GrPath* createPath(const SkPath& skPath, const SkStrokeRec& stroke);
typedef SkRefCnt INHERITED;
};

View File

@ -217,6 +217,7 @@ void GrContext::contextDestroyed() {
fOvalRenderer->reset();
fTextureCache->purgeAllUnlocked();
fFontCache->freeAll();
fGpu->markContextDirty();
}
@ -1813,6 +1814,22 @@ const GrEffectRef* GrContext::createUPMToPMEffect(GrTexture* texture,
}
}
GrPath* GrContext::createPath(const SkPath& inPath, const SkStrokeRec& stroke) {
SkASSERT(fGpu->caps()->pathRenderingSupport());
// TODO: now we add to fTextureCache. This should change to fResourceCache.
GrResourceKey resourceKey = GrPath::ComputeKey(inPath, stroke);
GrPath* path = static_cast<GrPath*>(fTextureCache->find(resourceKey));
if (NULL != path && path->isEqualTo(inPath, stroke)) {
path->ref();
} else {
path = fGpu->createPath(inPath, stroke);
fTextureCache->purgeAsNeeded(1, path->sizeInBytes());
fTextureCache->addResource(resourceKey, path);
}
return path;
}
///////////////////////////////////////////////////////////////////////////////
#if GR_CACHE_STATS
void GrContext::printCacheStats() const {

View File

@ -8,3 +8,26 @@
#include "GrPath.h"
SK_DEFINE_INST_COUNT(GrPath)
GrResourceKey GrPath::ComputeKey(const SkPath& path, const SkStrokeRec& stroke) {
static const GrResourceKey::ResourceType gPathResourceType = GrResourceKey::GenerateResourceType();
static const GrCacheID::Domain gPathDomain = GrCacheID::GenerateDomain();
GrCacheID::Key key;
uint32_t* keyData = key.fData32;
keyData[0] = path.getGenerationID();
SK_COMPILE_ASSERT(SkPaint::kJoinCount <= 3, cap_shift_will_be_wrong);
keyData[1] = stroke.needToApply();
if (0 != keyData[1]) {
keyData[1] |= stroke.getJoin() << 1;
keyData[1] |= stroke.getCap() << 3;
keyData[2] = static_cast<uint32_t>(stroke.getMiter());
keyData[3] = static_cast<uint32_t>(stroke.getWidth());
} else {
keyData[2] = 0;
keyData[3] = 0;
}
return GrResourceKey(GrCacheID(gPathDomain, key), gPathResourceType, 0);
}

View File

@ -9,6 +9,8 @@
#define GrPath_DEFINED
#include "GrResource.h"
#include "GrResourceCache.h"
#include "SkPath.h"
#include "SkRect.h"
#include "SkStrokeRec.h"
@ -16,9 +18,17 @@ class GrPath : public GrResource {
public:
SK_DECLARE_INST_COUNT(GrPath);
GrPath(GrGpu* gpu, bool isWrapped, const SkStrokeRec& stroke)
GrPath(GrGpu* gpu, bool isWrapped, const SkPath& skPath, const SkStrokeRec& stroke)
: INHERITED(gpu, isWrapped),
fStroke(stroke) {
fSkPath(skPath),
fStroke(stroke),
fBounds(skPath.getBounds()) {
}
static GrResourceKey ComputeKey(const SkPath& path, const SkStrokeRec& stroke);
bool isEqualTo(const SkPath& path, const SkStrokeRec& stroke) {
return fSkPath == path && fStroke == stroke;
}
const SkRect& getBounds() const { return fBounds; }
@ -26,8 +36,9 @@ public:
const SkStrokeRec& getStroke() const { return fStroke; }
protected:
SkRect fBounds;
SkPath fSkPath;
SkStrokeRec fStroke;
SkRect fBounds;
private:
typedef GrResource INHERITED;

View File

@ -55,7 +55,7 @@ void GrStencilAndCoverPathRenderer::onStencilPath(const SkPath& path,
const SkStrokeRec& stroke,
GrDrawTarget* target) {
SkASSERT(!path.isInverseFillType());
SkAutoTUnref<GrPath> p(fGpu->createPath(path, stroke));
SkAutoTUnref<GrPath> p(fGpu->getContext()->createPath(path, stroke));
target->stencilPath(p, path.getFillType());
}
@ -69,7 +69,7 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path,
GrDrawState* drawState = target->drawState();
SkASSERT(drawState->getStencil().isDisabled());
SkAutoTUnref<GrPath> p(fGpu->createPath(path, stroke));
SkAutoTUnref<GrPath> p(fGpu->getContext()->createPath(path, stroke));
if (path.isInverseFillType()) {
GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass,

View File

@ -8,7 +8,6 @@
#include "GrGLPath.h"
#include "GrGpuGL.h"
#include "SkStrokeRec.h"
#define GPUGL static_cast<GrGpuGL*>(this->getGpu())
@ -87,7 +86,7 @@ inline GrGLenum cap_to_gl_cap(SkPaint::Cap cap) {
static const bool kIsWrapped = false; // The constructor creates the GL path object.
GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path, const SkStrokeRec& stroke)
: INHERITED(gpu, kIsWrapped, stroke) {
: INHERITED(gpu, kIsWrapped, path, stroke) {
#ifndef SK_SCALAR_IS_FLOAT
GrCrash("Assumes scalar is float.");
#endif
@ -98,14 +97,14 @@ GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path, const SkStrokeRec& stroke)
SkSTArray<16, GrGLubyte, true> pathCommands;
SkSTArray<16, SkPoint, true> pathPoints;
int verbCnt = path.countVerbs();
int pointCnt = path.countPoints();
int verbCnt = fSkPath.countVerbs();
int pointCnt = fSkPath.countPoints();
pathCommands.resize_back(verbCnt);
pathPoints.resize_back(pointCnt);
// TODO: Direct access to path points since we could pass them on directly.
path.getPoints(&pathPoints[0], pointCnt);
path.getVerbs(&pathCommands[0], verbCnt);
fSkPath.getPoints(&pathPoints[0], pointCnt);
fSkPath.getVerbs(&pathCommands[0], verbCnt);
SkDEBUGCODE(int numPts = 0);
for (int i = 0; i < verbCnt; ++i) {
@ -118,7 +117,6 @@ GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path, const SkStrokeRec& stroke)
GL_CALL(PathCommands(fPathID,
verbCnt, &pathCommands[0],
2 * pointCnt, GR_GL_FLOAT, &pathPoints[0]));
fBounds = path.getBounds();
if (stroke.needToApply()) {
GL_CALL(PathParameterf(fPathID, GR_GL_PATH_STROKE_WIDTH, SkScalarToFloat(stroke.getWidth())));

View File

@ -13,8 +13,6 @@
#include "gl/GrGLFunctions.h"
class GrGpuGL;
class SkPath;
class SkStrokeRec;
/**
* Currently this represents a path built using GL_NV_path_rendering. If we