aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDerek Sollenberger <djsollen@google.com>2018-03-29 16:01:48 -0400
committerSkia Commit-Bot <skia-commit-bot@chromium.org>2018-03-30 03:17:46 +0000
commit67914ebddfc81b5f564dfa119eca3c8bad33db74 (patch)
tree4f51c847ab2db5f76c2f327d4bd80ab538a635f3
parent6ab4e8f2e499169071bd4168082e8b6cdcb4a107 (diff)
downloadskia-67914ebddfc81b5f564dfa119eca3c8bad33db74.tar.gz
Dump additional details about the gpu resources.
GpuResources now dump optional string values that describe the type and category of the resource. The type provides a description of the kind of resource it is (e.g. texture, buffer object, stencil, etc.) and the category describes what the resource is currently tasked to do (e.g. path masks, images, scratch, etc.) This CL also refactors the dump logic in an attempt to consolidate duplicated code into GrGpuResources.cpp. Bug: b/74435803 Change-Id: I83cae825f41e6450a21398ab3ecea349c7c61c15 Reviewed-on: https://skia-review.googlesource.com/115989 Commit-Queue: Derek Sollenberger <djsollen@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com> (cherry picked from commit cf6da8c0f29877249a55949efd8b952b5b9bc01d) No-Tree-Checks: true No-Try: true No-Presubmit: true Change-Id: I3ec7ae7e36fe7224b8b052f1eabc31383b29b43a Reviewed-on: https://skia-review.googlesource.com/117260 Reviewed-by: Derek Sollenberger <djsollen@google.com> Commit-Queue: Derek Sollenberger <djsollen@google.com>
-rw-r--r--bench/GrResourceCacheBench.cpp1
-rw-r--r--include/core/SkTraceMemoryDump.h4
-rw-r--r--include/gpu/GrGpuResource.h26
-rw-r--r--include/gpu/GrResourceKey.h12
-rw-r--r--include/gpu/GrSurface.h2
-rw-r--r--src/effects/GrCircleBlurFragmentProcessor.cpp2
-rw-r--r--src/gpu/GrBuffer.h1
-rw-r--r--src/gpu/GrGpuResource.cpp31
-rw-r--r--src/gpu/GrPath.cpp2
-rw-r--r--src/gpu/GrPath.h1
-rw-r--r--src/gpu/GrPathRange.h2
-rw-r--r--src/gpu/GrSoftwarePathRenderer.cpp6
-rw-r--r--src/gpu/GrStencilAttachment.h1
-rw-r--r--src/gpu/SkGr.cpp2
-rw-r--r--src/gpu/effects/GrRRectBlurEffect.h2
-rw-r--r--src/gpu/effects/GrRectBlurEffect.h2
-rw-r--r--src/gpu/gl/GrGLRenderTarget.cpp13
-rw-r--r--src/gpu/gl/GrGLTexture.cpp15
-rw-r--r--src/gpu/gl/GrGLTextureRenderTarget.cpp7
-rw-r--r--src/gpu/ops/GrTessellatingPathRenderer.cpp2
-rw-r--r--tests/ResourceCacheTest.cpp8
21 files changed, 96 insertions, 46 deletions
diff --git a/bench/GrResourceCacheBench.cpp b/bench/GrResourceCacheBench.cpp
index a530e628e8..cc185cf04f 100644
--- a/bench/GrResourceCacheBench.cpp
+++ b/bench/GrResourceCacheBench.cpp
@@ -38,6 +38,7 @@ public:
private:
size_t onGpuMemorySize() const override { return 100; }
+ const char* getResourceType() const override { return "bench"; }
typedef GrGpuResource INHERITED;
};
diff --git a/include/core/SkTraceMemoryDump.h b/include/core/SkTraceMemoryDump.h
index 03656b2c37..e9cb5b20e4 100644
--- a/include/core/SkTraceMemoryDump.h
+++ b/include/core/SkTraceMemoryDump.h
@@ -50,6 +50,10 @@ public:
const char* units,
uint64_t value) = 0;
+ virtual void dumpStringValue(const char* /*dumpName*/,
+ const char* /*valueName*/,
+ const char* /*value*/) { }
+
/**
* Sets the memory backing for an existing dump.
* backingType and backingObjectId are used by the embedder to associate the memory dumped via
diff --git a/include/gpu/GrGpuResource.h b/include/gpu/GrGpuResource.h
index 70c94c0e55..2da8085908 100644
--- a/include/gpu/GrGpuResource.h
+++ b/include/gpu/GrGpuResource.h
@@ -249,6 +249,15 @@ public:
**/
virtual void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
+ /**
+ * Describes the type of gpu resource that is represented by the implementing
+ * class (e.g. texture, buffer object, stencil). This data is used for diagnostic
+ * purposes by dumpMemoryStatistics().
+ *
+ * The value returned is expected to be long lived and will not be copied by the caller.
+ */
+ virtual const char* getResourceType() const = 0;
+
static uint32_t CreateUniqueID();
protected:
@@ -280,11 +289,24 @@ protected:
void didChangeGpuMemorySize() const;
/**
- * Allows subclasses to add additional backing information to the SkTraceMemoryDump. Called by
- * onMemoryDump. The default implementation adds no backing information.
+ * Allows subclasses to add additional backing information to the SkTraceMemoryDump.
**/
virtual void setMemoryBacking(SkTraceMemoryDump*, const SkString&) const {}
+ /**
+ * Returns a string that uniquely identifies this resource.
+ */
+ SkString getResourceName() const;
+
+ /**
+ * A helper for subclasses that override dumpMemoryStatistics(). This method using a format
+ * consistent with the default implementation of dumpMemoryStatistics() but allows the caller
+ * to customize various inputs.
+ */
+ void dumpMemoryStatisticsPriv(SkTraceMemoryDump* traceMemoryDump, const SkString& resourceName,
+ const char* type, size_t size) const;
+
+
private:
/**
* Called by the registerWithCache if the resource is available to be used as scratch.
diff --git a/include/gpu/GrResourceKey.h b/include/gpu/GrResourceKey.h
index c0a08f8add..ef319ad1fa 100644
--- a/include/gpu/GrResourceKey.h
+++ b/include/gpu/GrResourceKey.h
@@ -241,7 +241,7 @@ public:
GrUniqueKey& operator=(const GrUniqueKey& that) {
this->INHERITED::operator=(that);
this->setCustomData(sk_ref_sp(that.getCustomData()));
- SkDEBUGCODE(fTag = that.fTag;)
+ fTag = that.fTag;
return *this;
}
@@ -257,14 +257,13 @@ public:
return fData.get();
}
- SkDEBUGCODE(const char* tag() const { return fTag.c_str(); })
+ const char* tag() const { return fTag; }
class Builder : public INHERITED::Builder {
public:
Builder(GrUniqueKey* key, Domain type, int data32Count, const char* tag = nullptr)
: INHERITED::Builder(key, type, data32Count) {
- SkDEBUGCODE(key->fTag = tag;)
- (void) tag; // suppress unused named param warning.
+ key->fTag = tag;
}
/** Used to build a key that wraps another key and adds additional data. */
@@ -277,8 +276,7 @@ public:
const uint32_t* srcData = innerKey.data();
(*innerKeyData++) = innerKey.domain();
memcpy(innerKeyData, srcData, innerKey.dataSize());
- SkDEBUGCODE(key->fTag = tag;)
- (void) tag; // suppress unused named param warning.
+ key->fTag = tag;
}
private:
@@ -290,7 +288,7 @@ public:
private:
sk_sp<SkData> fData;
- SkDEBUGCODE(SkString fTag;)
+ const char* fTag;
};
/**
diff --git a/include/gpu/GrSurface.h b/include/gpu/GrSurface.h
index 1056bdd224..40f81a0295 100644
--- a/include/gpu/GrSurface.h
+++ b/include/gpu/GrSurface.h
@@ -84,6 +84,8 @@ protected:
void onAbandon() override;
private:
+ const char* getResourceType() const override { return "Surface"; }
+
GrPixelConfig fConfig;
int fWidth;
int fHeight;
diff --git a/src/effects/GrCircleBlurFragmentProcessor.cpp b/src/effects/GrCircleBlurFragmentProcessor.cpp
index 31311e2e1c..8a1239c3f0 100644
--- a/src/effects/GrCircleBlurFragmentProcessor.cpp
+++ b/src/effects/GrCircleBlurFragmentProcessor.cpp
@@ -203,7 +203,7 @@ static sk_sp<GrTextureProxy> create_profile_texture(GrProxyProvider* proxyProvid
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey key;
- GrUniqueKey::Builder builder(&key, kDomain, 1);
+ GrUniqueKey::Builder builder(&key, kDomain, 1, "1-D Circular Blur");
builder[0] = sigmaToCircleRRatioFixed;
builder.finish();
diff --git a/src/gpu/GrBuffer.h b/src/gpu/GrBuffer.h
index b2201a140f..33a7f354fa 100644
--- a/src/gpu/GrBuffer.h
+++ b/src/gpu/GrBuffer.h
@@ -123,6 +123,7 @@ private:
virtual bool onUpdateData(const void* src, size_t srcSizeInBytes);
size_t onGpuMemorySize() const override { return fSizeInBytes; } // TODO: zero for cpu backed?
+ const char* getResourceType() const override { return "Buffer Object"; }
void computeScratchKey(GrScratchKey* key) const override;
size_t fSizeInBytes;
diff --git a/src/gpu/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp
index d90498d6f4..ec7113562b 100644
--- a/src/gpu/GrGpuResource.cpp
+++ b/src/gpu/GrGpuResource.cpp
@@ -73,20 +73,33 @@ void GrGpuResource::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) con
return;
}
- // Dump resource as "skia/gpu_resources/resource_#".
- SkString dumpName("skia/gpu_resources/resource_");
- dumpName.appendU32(this->uniqueID().asUInt());
+ this->dumpMemoryStatisticsPriv(traceMemoryDump, this->getResourceName(),
+ this->getResourceType(), this->gpuMemorySize());
+}
- traceMemoryDump->dumpNumericValue(dumpName.c_str(), "size", "bytes", this->gpuMemorySize());
+void GrGpuResource::dumpMemoryStatisticsPriv(SkTraceMemoryDump* traceMemoryDump,
+ const SkString& resourceName,
+ const char* type, size_t size) const {
+ const char* tag = "Scratch";
+ if (fUniqueKey.isValid()) {
+ tag = (fUniqueKey.tag() != nullptr) ? fUniqueKey.tag() : "Other";
+ }
+ traceMemoryDump->dumpNumericValue(resourceName.c_str(), "size", "bytes", size);
+ traceMemoryDump->dumpStringValue(resourceName.c_str(), "type", type);
+ traceMemoryDump->dumpStringValue(resourceName.c_str(), "category", tag);
if (this->isPurgeable()) {
- traceMemoryDump->dumpNumericValue(dumpName.c_str(), "purgeable_size", "bytes",
- this->gpuMemorySize());
+ traceMemoryDump->dumpNumericValue(resourceName.c_str(), "purgeable_size", "bytes", size);
}
- // Call setMemoryBacking to allow sub-classes with implementation specific backings (such as GL
- // objects) to provide additional information.
- this->setMemoryBacking(traceMemoryDump, dumpName);
+ this->setMemoryBacking(traceMemoryDump, resourceName);
+}
+
+SkString GrGpuResource::getResourceName() const {
+ // Dump resource as "skia/gpu_resources/resource_#".
+ SkString resourceName("skia/gpu_resources/resource_");
+ resourceName.appendU32(this->uniqueID().asUInt());
+ return resourceName;
}
const GrContext* GrGpuResource::getContext() const {
diff --git a/src/gpu/GrPath.cpp b/src/gpu/GrPath.cpp
index 836cc5ed55..c0c56430a6 100644
--- a/src/gpu/GrPath.cpp
+++ b/src/gpu/GrPath.cpp
@@ -25,7 +25,7 @@ void GrPath::ComputeKey(const GrShape& shape, GrUniqueKey* key, bool* outIsVolat
return;
}
static const GrUniqueKey::Domain kGeneralPathDomain = GrUniqueKey::GenerateDomain();
- GrUniqueKey::Builder builder(key, kGeneralPathDomain, geoCnt + styleCnt);
+ GrUniqueKey::Builder builder(key, kGeneralPathDomain, geoCnt + styleCnt, "Path");
shape.writeUnstyledKey(&builder[0]);
if (styleCnt) {
write_style_key(&builder[geoCnt], shape.style());
diff --git a/src/gpu/GrPath.h b/src/gpu/GrPath.h
index 19538370d5..cf4a11d4fd 100644
--- a/src/gpu/GrPath.h
+++ b/src/gpu/GrPath.h
@@ -51,6 +51,7 @@ protected:
#endif
private:
+ const char* getResourceType() const override { return "Path Data"; }
typedef GrGpuResource INHERITED;
};
diff --git a/src/gpu/GrPathRange.h b/src/gpu/GrPathRange.h
index 39779a7008..62992ad4cd 100644
--- a/src/gpu/GrPathRange.h
+++ b/src/gpu/GrPathRange.h
@@ -139,6 +139,8 @@ protected:
virtual void onInitPath(int index, const SkPath&) const = 0;
private:
+ const char* getResourceType() const override { return "Path Range"; }
+
enum {
kPathsPerGroup = 16 // Paths get tracked in groups of 16 for lazy loading.
};
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
index ff6a20fbe7..212b094641 100644
--- a/src/gpu/GrSoftwarePathRenderer.cpp
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -291,14 +291,16 @@ bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) {
// Fractional translate does not affect caching on Android. This is done for better cache
// hit ratio and speed, but it is matching HWUI behavior, which doesn't consider the matrix
// at all when caching paths.
- GrUniqueKey::Builder builder(&maskKey, kDomain, 4 + args.fShape->unstyledKeySize());
+ GrUniqueKey::Builder builder(&maskKey, kDomain, 4 + args.fShape->unstyledKeySize(),
+ "SW Path Mask");
#else
SkScalar tx = args.fViewMatrix->get(SkMatrix::kMTransX);
SkScalar ty = args.fViewMatrix->get(SkMatrix::kMTransY);
// Allow 8 bits each in x and y of subpixel positioning.
SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
- GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + args.fShape->unstyledKeySize());
+ GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + args.fShape->unstyledKeySize(),
+ "SW Path Mask");
#endif
builder[0] = SkFloat2Bits(sx);
builder[1] = SkFloat2Bits(sy);
diff --git a/src/gpu/GrStencilAttachment.h b/src/gpu/GrStencilAttachment.h
index df5677d163..e8c56cca4d 100644
--- a/src/gpu/GrStencilAttachment.h
+++ b/src/gpu/GrStencilAttachment.h
@@ -45,6 +45,7 @@ protected:
}
private:
+ const char* getResourceType() const override { return "Stencil"; }
int fWidth;
int fHeight;
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 095a867605..f09db6a924 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -58,7 +58,7 @@ void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& ima
SkASSERT(imageID);
SkASSERT(!imageBounds.isEmpty());
static const GrUniqueKey::Domain kImageIDDomain = GrUniqueKey::GenerateDomain();
- GrUniqueKey::Builder builder(key, kImageIDDomain, 5);
+ GrUniqueKey::Builder builder(key, kImageIDDomain, 5, "Image");
builder[0] = imageID;
builder[1] = imageBounds.fLeft;
builder[2] = imageBounds.fTop;
diff --git a/src/gpu/effects/GrRRectBlurEffect.h b/src/gpu/effects/GrRRectBlurEffect.h
index 7ba02c7dd0..73e0ed33cd 100644
--- a/src/gpu/effects/GrRRectBlurEffect.h
+++ b/src/gpu/effects/GrRRectBlurEffect.h
@@ -33,7 +33,7 @@ public:
float xformedSigma) {
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey key;
- GrUniqueKey::Builder builder(&key, kDomain, 9);
+ GrUniqueKey::Builder builder(&key, kDomain, 9, "RoundRect Blur Mask");
builder[0] = SkScalarCeilToInt(xformedSigma - 1 / 6.0f);
int index = 1;
diff --git a/src/gpu/effects/GrRectBlurEffect.h b/src/gpu/effects/GrRectBlurEffect.h
index b6b8a029f1..9d81d8f615 100644
--- a/src/gpu/effects/GrRectBlurEffect.h
+++ b/src/gpu/effects/GrRectBlurEffect.h
@@ -25,7 +25,7 @@ public:
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey key;
- GrUniqueKey::Builder builder(&key, kDomain, 1);
+ GrUniqueKey::Builder builder(&key, kDomain, 1, "Rect Blur Mask");
builder[0] = profileSize;
builder.finish();
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
index a1c9425920..4fcf59d2dd 100644
--- a/src/gpu/gl/GrGLRenderTarget.cpp
+++ b/src/gpu/gl/GrGLRenderTarget.cpp
@@ -213,19 +213,14 @@ void GrGLRenderTarget::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump)
// Due to this resource having both a texture and a renderbuffer component, dump as
// skia/gpu_resources/resource_#/renderbuffer
- SkString dumpName("skia/gpu_resources/resource_");
- dumpName.appendU32(this->uniqueID().asUInt());
- dumpName.append("/renderbuffer");
+ SkString resourceName = this->getResourceName();
+ resourceName.append("/renderbuffer");
- traceMemoryDump->dumpNumericValue(dumpName.c_str(), "size", "bytes", size);
-
- if (this->isPurgeable()) {
- traceMemoryDump->dumpNumericValue(dumpName.c_str(), "purgeable_size", "bytes", size);
- }
+ this->dumpMemoryStatisticsPriv(traceMemoryDump, resourceName, "RenderTarget", size);
SkString renderbuffer_id;
renderbuffer_id.appendU32(fMSColorRenderbufferID);
- traceMemoryDump->setMemoryBacking(dumpName.c_str(), "gl_renderbuffer",
+ traceMemoryDump->setMemoryBacking(resourceName.c_str(), "gl_renderbuffer",
renderbuffer_id.c_str());
}
}
diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp
index 5175ea88e7..fa14d5e64f 100644
--- a/src/gpu/gl/GrGLTexture.cpp
+++ b/src/gpu/gl/GrGLTexture.cpp
@@ -132,21 +132,16 @@ void GrGLTexture::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const
// Dump as skia/gpu_resources/resource_#/texture, to avoid conflicts in the
// GrGLTextureRenderTarget case, where multiple things may dump to the same resource. This
// has no downside in the normal case.
- SkString dumpName("skia/gpu_resources/resource_");
- dumpName.appendU32(this->uniqueID().asUInt());
- dumpName.append("/texture");
+ SkString resourceName = this->getResourceName();
+ resourceName.append("/texture");
// As we are only dumping our texture memory (not any additional memory tracked by classes
// which may inherit from us), specifically call GrGLTexture::gpuMemorySize to avoid
// hitting an override.
- size_t size = GrGLTexture::gpuMemorySize();
- traceMemoryDump->dumpNumericValue(dumpName.c_str(), "size", "bytes", size);
-
- if (this->isPurgeable()) {
- traceMemoryDump->dumpNumericValue(dumpName.c_str(), "purgeable_size", "bytes", size);
- }
+ this->dumpMemoryStatisticsPriv(traceMemoryDump, resourceName, "Texture",
+ GrGLTexture::gpuMemorySize());
SkString texture_id;
texture_id.appendU32(this->textureID());
- traceMemoryDump->setMemoryBacking(dumpName.c_str(), "gl_texture", texture_id.c_str());
+ traceMemoryDump->setMemoryBacking(resourceName.c_str(), "gl_texture", texture_id.c_str());
}
diff --git a/src/gpu/gl/GrGLTextureRenderTarget.cpp b/src/gpu/gl/GrGLTextureRenderTarget.cpp
index e9f224b71b..049cb31a9b 100644
--- a/src/gpu/gl/GrGLTextureRenderTarget.cpp
+++ b/src/gpu/gl/GrGLTextureRenderTarget.cpp
@@ -37,9 +37,16 @@ GrGLTextureRenderTarget::GrGLTextureRenderTarget(GrGLGpu* gpu,
void GrGLTextureRenderTarget::dumpMemoryStatistics(
SkTraceMemoryDump* traceMemoryDump) const {
+#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
// Delegate to the base classes
GrGLRenderTarget::dumpMemoryStatistics(traceMemoryDump);
GrGLTexture::dumpMemoryStatistics(traceMemoryDump);
+#else
+ SkString resourceName = this->getResourceName();
+ resourceName.append("/texture_renderbuffer");
+ this->dumpMemoryStatisticsPriv(traceMemoryDump, resourceName, "RenderTarget",
+ this->gpuMemorySize());
+#endif
}
bool GrGLTextureRenderTarget::canAttemptStencilAttachment() const {
diff --git a/src/gpu/ops/GrTessellatingPathRenderer.cpp b/src/gpu/ops/GrTessellatingPathRenderer.cpp
index 8eaadf0ac3..651d34fae7 100644
--- a/src/gpu/ops/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/ops/GrTessellatingPathRenderer.cpp
@@ -245,7 +245,7 @@ private:
static constexpr int kClipBoundsCnt = sizeof(fDevClipBounds) / sizeof(uint32_t);
int shapeKeyDataCnt = fShape.unstyledKeySize();
SkASSERT(shapeKeyDataCnt >= 0);
- GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt);
+ GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt, "Path");
fShape.writeUnstyledKey(&builder[0]);
// For inverse fills, the tessellation is dependent on clip bounds.
if (inverseFill) {
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index 4fa13b32d6..bcc8b30a86 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -337,6 +337,7 @@ private:
}
size_t onGpuMemorySize() const override { return fSize; }
+ const char* getResourceType() const override { return "Test"; }
TestResource* fToDelete;
size_t fSize;
@@ -1658,19 +1659,24 @@ static void test_tags(skiatest::Reporter* reporter) {
GrResourceCache* cache = mock.cache();
GrGpu* gpu = context->contextPriv().getGpu();
+ // tag strings are expected to be long lived
+ std::vector<SkString> tagStrings;
+
SkString tagStr;
int tagIdx = 0;
int currTagCnt = 0;
for (int i = 0; i < kNumResources; ++i, ++currTagCnt) {
+
sk_sp<GrGpuResource> resource(new TestResource(gpu));
GrUniqueKey key;
if (currTagCnt == tagIdx) {
tagIdx += 1;
currTagCnt = 0;
tagStr.printf("tag%d", tagIdx);
+ tagStrings.emplace_back(tagStr);
}
- make_unique_key<1>(&key, i, tagStr.c_str());
+ make_unique_key<1>(&key, i, tagStrings.back().c_str());
resource->resourcePriv().setUniqueKey(key);
}
SkASSERT(kLastTagIdx == tagIdx);