Rework how initial clearing of texture works.
1) It only applies when a texture is created, not when recycled from cache
2) It is all textures or none, not a flag GrSurfaceDesc
3) It is implemented by GrGpu clearing the texture after creation if
such a thing is supported in underlying API. Otherwise, GrResourceProvider
must provide pre-zeroed mip levels.
4) Works for MIP mapped textures (all levels without initial data are cleared)
This could cause performance regressions in WebGL until we re-add the
ability to clear using glCear() in GL. Doing that requires making the "can
clear using GrGpu" caps query be per-format. Deferring doing that until
GrPixelConfig work is farther along.
Bug: skia:6718
Change-Id: I234715b9faaf61e8b44d54464497a17cd553585d
start
Change-Id: Ib84a8c3ece010cc3164b18895107e78484cbf76b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/226977
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/include/gpu/GrContextOptions.h b/include/gpu/GrContextOptions.h
index 4b9e8f9..7c2d97b 100644
--- a/include/gpu/GrContextOptions.h
+++ b/include/gpu/GrContextOptions.h
@@ -235,6 +235,11 @@
bool fCacheSKSL = false;
/**
+ * Enforces clearing of all textures when they're created.
+ */
+ bool fClearAllTextures = false;
+
+ /**
* Include or exclude specific GPU path renderers.
*/
GpuPathRenderers fGpuPathRenderers = GpuPathRenderers::kAll;
diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h
index 6f81205..192744d 100644
--- a/include/private/GrTypesPriv.h
+++ b/include/private/GrTypesPriv.h
@@ -153,11 +153,6 @@
* GrTexture::asRenderTarget() to access.
*/
kRenderTarget_GrSurfaceFlag = 0x1,
- /**
- * Clears to zero on creation. It will cause creation failure if initial data is supplied to the
- * texture. This only affects the base level if the texture is created with MIP levels.
- */
- kPerformInitialClear_GrSurfaceFlag = 0x2
};
GR_MAKE_BITFIELD_OPS(GrSurfaceFlags)
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index daffcef..145c3aa 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -111,6 +111,9 @@
if (options.fSuppressGeometryShaders) {
fShaderCaps->fGeometryShaderSupport = false;
}
+ if (options.fClearAllTextures) {
+ fShouldInitializeTextures = true;
+ }
#endif
if (fMaxWindowRectangles > GrWindowRectangles::kMaxWindows) {
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
index 4e30936..5bd4234 100644
--- a/src/gpu/GrCaps.h
+++ b/src/gpu/GrCaps.h
@@ -291,6 +291,16 @@
*/
bool shouldInitializeTextures() const { return fShouldInitializeTextures; }
+ /**
+ * When this is true it is required that all textures are initially cleared. However, the
+ * clearing must be implemented by passing level data to GrGpu::createTexture() rather than
+ * be implemeted by GrGpu::createTexture().
+ *
+ * TODO: Make this take GrBacknedFormat when canClearTextureOnCreation() does as well.
+ */
+ bool createTextureMustSpecifyAllLevels() const {
+ return this->shouldInitializeTextures() && !this->canClearTextureOnCreation();
+ }
/** Returns true if the given backend supports importing AHardwareBuffers via the
* GrAHardwarebufferImageGenerator. This will only ever be supported on Android devices with API
@@ -392,6 +402,17 @@
virtual GrBackendFormat getBackendFormatFromCompressionType(SkImage::CompressionType) const = 0;
/**
+ * Used by implementation of shouldInitializeTextures(). Indicates whether GrGpu implements the
+ * clear in GrGpu::createTexture() or if false then the caller must provide cleared MIP level
+ * data or GrGpu::createTexture() will fail.
+ *
+ * TODO: Make this take a GrBackendFormat so that GL can make this faster for cases
+ * when the format is renderable and glTexClearImage is not available. Doing this
+ * is overly complicated until the GrPixelConfig/format mess is straightened out..
+ */
+ virtual bool canClearTextureOnCreation() const = 0;
+
+ /**
* The CLAMP_TO_BORDER wrap mode for texture coordinates was added to desktop GL in 1.3, and
* GLES 3.2, but is also available in extensions. Vulkan and Metal always have support.
*/
diff --git a/src/gpu/GrDrawOpAtlas.cpp b/src/gpu/GrDrawOpAtlas.cpp
index 2142647..7fb8aca 100644
--- a/src/gpu/GrDrawOpAtlas.cpp
+++ b/src/gpu/GrDrawOpAtlas.cpp
@@ -514,13 +514,6 @@
SkASSERT(SkIsPow2(fTextureWidth) && SkIsPow2(fTextureHeight));
GrSurfaceDesc desc;
- if (proxyProvider->caps()->shouldInitializeTextures()) {
- // The atlas isn't guaranteed to touch all its pixels so, for platforms that benefit
- // from complete initialization, clear everything.
- desc.fFlags = kPerformInitialClear_GrSurfaceFlag;
- } else {
- desc.fFlags = kNone_GrSurfaceFlags;
- }
desc.fWidth = fTextureWidth;
desc.fHeight = fTextureHeight;
desc.fConfig = GrColorTypeToPixelConfig(fColorType);
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index f007304..addbc15 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -100,7 +100,7 @@
}
static bool validate_levels(int w, int h, const GrMipLevel texels[], int mipLevelCount, int bpp,
- const GrCaps* caps) {
+ const GrCaps* caps, bool mustHaveDataForAllLevels = false) {
SkASSERT(mipLevelCount > 0);
bool hasBasePixels = texels[0].fPixels;
int levelsWithPixelsCnt = 0;
@@ -138,7 +138,10 @@
if (!hasBasePixels) {
return levelsWithPixelsCnt == 0;
}
- return levelsWithPixelsCnt == 1 || levelsWithPixelsCnt == mipLevelCount;
+ if (levelsWithPixelsCnt == 1 && !mustHaveDataForAllLevels) {
+ return true;
+ }
+ return levelsWithPixelsCnt == mipLevelCount;
}
sk_sp<GrTexture> GrGpu::createTexture(const GrSurfaceDesc& origDesc, SkBudgeted budgeted,
@@ -162,14 +165,15 @@
// Attempt to catch un- or wrongly initialized sample counts.
SkASSERT(desc.fSampleCnt > 0 && desc.fSampleCnt <= 64);
+ bool mustHaveDataForAllLevels = this->caps()->createTextureMustSpecifyAllLevels();
if (mipLevelCount) {
- if (desc.fFlags & kPerformInitialClear_GrSurfaceFlag) {
- return nullptr;
- }
int bpp = GrBytesPerPixel(desc.fConfig);
- if (!validate_levels(desc.fWidth, desc.fHeight, texels, mipLevelCount, bpp, this->caps())) {
+ if (!validate_levels(desc.fWidth, desc.fHeight, texels, mipLevelCount, bpp, this->caps(),
+ mustHaveDataForAllLevels)) {
return nullptr;
}
+ } else if (mustHaveDataForAllLevels) {
+ return nullptr;
}
this->handleDirtyContext();
@@ -201,6 +205,8 @@
height < 1 || height > this->caps()->maxTextureSize()) {
return nullptr;
}
+ // Note if we relax the requirement that data must be provided then we must check
+ // caps()->shouldInitializeTextures() here.
if (!data) {
return nullptr;
}
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index fc3d061..69f6c0f 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -45,13 +45,20 @@
// Ensures the row bytes are populated (not 0) and makes a copy to a temporary
// to make the row bytes tight if necessary. Returns false if the input row bytes are invalid.
static bool prepare_level(const GrMipLevel& inLevel, size_t bpp, int w, int h, bool rowBytesSupport,
- GrMipLevel* outLevel, std::unique_ptr<char[]>* data) {
+ bool mustInitializeAllLevels, GrMipLevel* outLevel,
+ std::unique_ptr<char[]>* data) {
+ size_t minRB = w * bpp;
if (!inLevel.fPixels) {
- outLevel->fPixels = nullptr;
- outLevel->fRowBytes = 0;
+ if (mustInitializeAllLevels) {
+ data->reset(new char[minRB * h]());
+ outLevel->fPixels = data->get();
+ outLevel->fRowBytes = minRB;
+ } else {
+ outLevel->fPixels = nullptr;
+ outLevel->fRowBytes = 0;
+ }
return true;
}
- size_t minRB = w * bpp;
size_t actualRB = inLevel.fRowBytes ? inLevel.fRowBytes : minRB;
if (actualRB < minRB) {
return false;
@@ -83,7 +90,8 @@
if (!fCaps->validateSurfaceDesc(desc, mipMapped)) {
return nullptr;
}
-
+ bool mustInitializeAllLevels = this->caps()->createTextureMustSpecifyAllLevels();
+ bool rowBytesSupport = this->caps()->writePixelsRowBytesSupport();
SkAutoSTMalloc<14, GrMipLevel> tmpTexels;
SkAutoSTArray<14, std::unique_ptr<char[]>> tmpDatas;
if (mipLevelCount > 0 && texels) {
@@ -93,7 +101,7 @@
int h = desc.fHeight;
size_t bpp = GrBytesPerPixel(desc.fConfig);
for (int i = 0; i < mipLevelCount; ++i) {
- if (!prepare_level(texels[i], bpp, w, h, this->caps()->writePixelsRowBytesSupport(),
+ if (!prepare_level(texels[i], bpp, w, h, rowBytesSupport, mustInitializeAllLevels,
&tmpTexels[i], &tmpDatas[i])) {
return nullptr;
}
@@ -136,11 +144,14 @@
GrContext* context = fGpu->getContext();
GrProxyProvider* proxyProvider = context->priv().proxyProvider();
+ bool mustInitialize = this->caps()->createTextureMustSpecifyAllLevels();
+ bool rowBytesSupport = this->caps()->writePixelsRowBytesSupport();
+
size_t bpp = GrBytesPerPixel(desc.fConfig);
std::unique_ptr<char[]> tmpData;
GrMipLevel tmpLevel;
- if (!prepare_level(mipLevel, bpp, desc.fWidth, desc.fHeight,
- this->caps()->writePixelsRowBytesSupport(), &tmpLevel, &tmpData)) {
+ if (!prepare_level(mipLevel, bpp, desc.fWidth, desc.fHeight, rowBytesSupport, mustInitialize,
+ &tmpLevel, &tmpData)) {
return nullptr;
}
@@ -200,6 +211,16 @@
}
}
+ if (fCaps->createTextureMustSpecifyAllLevels()) {
+ size_t rowBytes = GrBytesPerPixel(desc.fConfig) * desc.fWidth;
+ size_t size = rowBytes * desc.fHeight;
+ std::unique_ptr<char[]> zeros(new char[size]());
+ GrMipLevel level;
+ level.fRowBytes = rowBytes;
+ level.fPixels = zeros.get();
+ return fGpu->createTexture(desc, budgeted, &level, 1);
+ }
+
return fGpu->createTexture(desc, budgeted);
}
@@ -254,8 +275,7 @@
SkTCopyOnFirstWrite<GrSurfaceDesc> copyDesc(desc);
// bin by some multiple or power of 2 with a reasonable min
- if (!SkToBool(desc.fFlags & kPerformInitialClear_GrSurfaceFlag) &&
- (fGpu->caps()->reuseScratchTextures() || (desc.fFlags & kRenderTarget_GrSurfaceFlag))) {
+ if (fGpu->caps()->reuseScratchTextures() || (desc.fFlags & kRenderTarget_GrSurfaceFlag)) {
GrSurfaceDesc* wdesc = copyDesc.writable();
wdesc->fWidth = MakeApprox(wdesc->fWidth);
wdesc->fHeight = MakeApprox(wdesc->fHeight);
@@ -265,6 +285,15 @@
return tex;
}
+ if (this->caps()->createTextureMustSpecifyAllLevels()) {
+ size_t rowBytes = GrBytesPerPixel(copyDesc->fConfig) * copyDesc->fWidth;
+ size_t size = rowBytes * copyDesc->fHeight;
+ std::unique_ptr<char[]> zeros(new char[size]());
+ GrMipLevel level;
+ level.fRowBytes = rowBytes;
+ level.fPixels = zeros.get();
+ return fGpu->createTexture(*copyDesc, SkBudgeted::kYes, &level, 1);
+ }
return fGpu->createTexture(*copyDesc, SkBudgeted::kYes);
}
@@ -276,9 +305,7 @@
// We could make initial clears work with scratch textures but it is a rare case so we just opt
// to fall back to making a new texture.
- if (!SkToBool(desc.fFlags & kPerformInitialClear_GrSurfaceFlag) &&
- (fGpu->caps()->reuseScratchTextures() || (desc.fFlags & kRenderTarget_GrSurfaceFlag))) {
-
+ if (fGpu->caps()->reuseScratchTextures() || (desc.fFlags & kRenderTarget_GrSurfaceFlag)) {
GrScratchKey key;
GrTexturePriv::ComputeScratchKey(desc, &key);
auto scratchFlags = GrResourceCache::ScratchFlags::kNone;
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
index 8f3b39e..afd687a 100644
--- a/src/gpu/GrSWMaskHelper.cpp
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -107,10 +107,6 @@
return nullptr;
}
- auto clearFlag = kNone_GrSurfaceFlags;
- if (context->priv().caps()->shouldInitializeTextures() && fit == SkBackingFit::kApprox) {
- clearFlag = kPerformInitialClear_GrSurfaceFlag;
- }
- return context->priv().proxyProvider()->createTextureProxy(
- std::move(img), clearFlag, 1, SkBudgeted::kYes, fit);
+ return context->priv().proxyProvider()->createTextureProxy(std::move(img), kNone_GrSurfaceFlags,
+ 1, SkBudgeted::kYes, fit);
}
diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp
index 9bcfa7c..b24dc45 100644
--- a/src/gpu/GrSurfaceProxy.cpp
+++ b/src/gpu/GrSurfaceProxy.cpp
@@ -69,7 +69,6 @@
, fBudgeted(budgeted)
, fLazyInstantiateCallback(std::move(callback))
, fLazyInstantiationType(lazyType)
- , fNeedsClear(SkToBool(desc.fFlags & kPerformInitialClear_GrSurfaceFlag))
, fIsProtected(desc.fIsProtected)
, fGpuMemorySize(kInvalidGpuMemorySize)
, fLastOpList(nullptr) {
@@ -103,7 +102,6 @@
? SkBudgeted::kYes
: SkBudgeted::kNo)
, fUniqueID(fTarget->uniqueID()) // Note: converting from unique resource ID to a proxy ID!
- , fNeedsClear(false)
, fIsProtected(fTarget->isProtected() ? GrProtected::kYes : GrProtected::kNo)
, fGpuMemorySize(kInvalidGpuMemorySize)
, fLastOpList(nullptr) {
@@ -141,9 +139,6 @@
SkASSERT(!fTarget);
GrSurfaceDesc desc;
desc.fFlags = descFlags;
- if (fNeedsClear) {
- desc.fFlags |= kPerformInitialClear_GrSurfaceFlag;
- }
desc.fWidth = fWidth;
desc.fHeight = fHeight;
desc.fIsProtected = fIsProtected;
diff --git a/src/gpu/GrSurfaceProxy.h b/src/gpu/GrSurfaceProxy.h
index 9dd5f92..522cdc8 100644
--- a/src/gpu/GrSurfaceProxy.h
+++ b/src/gpu/GrSurfaceProxy.h
@@ -423,7 +423,6 @@
virtual size_t onUninstantiatedGpuMemorySize() const = 0;
- bool fNeedsClear;
bool fIgnoredByResourceAllocator = false;
GrProtected fIsProtected;
diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp
index 21da8a0..f7a1e50 100644
--- a/src/gpu/GrYUVProvider.cpp
+++ b/src/gpu/GrYUVProvider.cpp
@@ -151,9 +151,6 @@
auto proxyProvider = ctx->priv().proxyProvider();
auto clearFlag = kNone_GrSurfaceFlags;
- if (ctx->priv().caps()->shouldInitializeTextures() && fit == SkBackingFit::kApprox) {
- clearFlag = kPerformInitialClear_GrSurfaceFlag;
- }
yuvTextureProxies[i] = proxyProvider->createTextureProxy(yuvImage, clearFlag,
1, SkBudgeted::kYes, fit);
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 802586e..a3ecdd9 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -3919,6 +3919,8 @@
return {};
}
+bool GrGLCaps::canClearTextureOnCreation() const { return fClearTextureSupport; }
+
#ifdef SK_DEBUG
static bool format_color_type_valid_pair(GrGLenum format, GrColorType colorType) {
switch (colorType) {
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index 839a4fa..7b8c61a 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -438,6 +438,8 @@
GrBackendFormat getBackendFormatFromColorType(GrColorType ct) const override;
GrBackendFormat getBackendFormatFromCompressionType(SkImage::CompressionType) const override;
+ bool canClearTextureOnCreation() const override;
+
GrSwizzle getTextureSwizzle(const GrBackendFormat&, GrColorType) const override;
GrSwizzle getOutputSwizzle(const GrBackendFormat&, GrColorType) const override;
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index fa92dff..1cd837c 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -1469,31 +1469,9 @@
int mipLevelCount) {
// We fail if the MSAA was requested and is not available.
if (GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType() && desc.fSampleCnt > 1) {
- //SkDebugf("MSAA RT requested but not supported on this platform.");
return return_null_texture();
}
- GrGLFormat glFormat =
- GrGLFormatFromGLEnum(this->glCaps().configSizedInternalFormat(desc.fConfig));
-
- bool performClear = (desc.fFlags & kPerformInitialClear_GrSurfaceFlag) &&
- !GrGLFormatIsCompressed(glFormat);
-
- GrMipLevel zeroLevel;
- std::unique_ptr<uint8_t[]> zeros;
- if (performClear && !this->glCaps().clearTextureSupport() &&
- !this->glCaps().canConfigBeFBOColorAttachment(desc.fConfig)) {
- size_t rowBytes = GrGLBytesPerFormat(glFormat) * desc.fWidth;
- size_t size = rowBytes * desc.fHeight;
- zeros.reset(new uint8_t[size]);
- memset(zeros.get(), 0, size);
- zeroLevel.fPixels = zeros.get();
- zeroLevel.fRowBytes = rowBytes;
- texels = &zeroLevel;
- mipLevelCount = 1;
- performClear = false;
- }
-
bool isRenderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
GrGLTexture::IDDesc idDesc;
@@ -1529,19 +1507,17 @@
SkDebugf("--- new texture [%d] size=(%d %d) config=%d\n",
idDesc.fInfo.fID, desc.fWidth, desc.fHeight, desc.fConfig);
#endif
- if (tex && performClear) {
- if (this->glCaps().clearTextureSupport()) {
- static constexpr uint32_t kZero = 0;
- GL_CALL(ClearTexImage(tex->textureID(), 0, GR_GL_RGBA, GR_GL_UNSIGNED_BYTE, &kZero));
- } else {
- this->bindSurfaceFBOForPixelOps(tex.get(), GR_GL_FRAMEBUFFER, kDst_TempFBOTarget);
- this->disableScissor();
- this->disableWindowRectangles();
- this->flushColorWrite(true);
- this->flushClearColor(0, 0, 0, 0);
- GL_CALL(Clear(GR_GL_COLOR_BUFFER_BIT));
- this->unbindTextureFBOForPixelOps(GR_GL_FRAMEBUFFER, tex.get());
- fHWBoundRenderTargetUniqueID.makeInvalid();
+ bool clearLevelsWithoutData =
+ this->caps()->shouldInitializeTextures() && this->glCaps().clearTextureSupport();
+
+ if (clearLevelsWithoutData) {
+ static constexpr uint32_t kZero = 0;
+ int levelCnt = SkTMax(1, tex->texturePriv().maxMipMapLevel());
+ for (int i = 0; i < levelCnt; ++i) {
+ if (i >= mipLevelCount || !texels[i].fPixels) {
+ GL_CALL(ClearTexImage(tex->textureID(), i, GR_GL_RGBA, GR_GL_UNSIGNED_BYTE,
+ &kZero));
+ }
}
}
return std::move(tex);
diff --git a/src/gpu/mock/GrMockCaps.h b/src/gpu/mock/GrMockCaps.h
index 08ce87f..461b332 100644
--- a/src/gpu/mock/GrMockCaps.h
+++ b/src/gpu/mock/GrMockCaps.h
@@ -144,6 +144,8 @@
return {};
}
+ bool canClearTextureOnCreation() const override { return true; }
+
GrSwizzle getTextureSwizzle(const GrBackendFormat&, GrColorType) const override {
return GrSwizzle();
}
diff --git a/src/gpu/mtl/GrMtlCaps.h b/src/gpu/mtl/GrMtlCaps.h
index d431022..5271019 100644
--- a/src/gpu/mtl/GrMtlCaps.h
+++ b/src/gpu/mtl/GrMtlCaps.h
@@ -75,6 +75,8 @@
GrBackendFormat getBackendFormatFromColorType(GrColorType ct) const override;
GrBackendFormat getBackendFormatFromCompressionType(SkImage::CompressionType) const override;
+ bool canClearTextureOnCreation() const override { return true; }
+
GrSwizzle getTextureSwizzle(const GrBackendFormat&, GrColorType) const override;
GrSwizzle getOutputSwizzle(const GrBackendFormat&, GrColorType) const override;
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index 42f684d..4b68cff 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -208,8 +208,8 @@
// Function that uploads data onto textures with private storage mode (GPU access only).
bool uploadToTexture(GrMtlTexture* tex, int left, int top, int width, int height,
GrColorType dataColorType, const GrMipLevel texels[], int mipLevels);
- // Function that fills texture with transparent black
- bool clearTexture(GrMtlTexture*, GrColorType);
+ // Function that fills texture levels with transparent black based on levelMask.
+ bool clearTexture(GrMtlTexture*, GrColorType, uint32_t levelMask);
GrStencilAttachment* createStencilAttachmentForRenderTarget(
const GrRenderTarget*, int width, int height, int numStencilSamples) override;
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index a474256..6cf0f8c 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -302,9 +302,13 @@
return true;
}
-bool GrMtlGpu::clearTexture(GrMtlTexture* tex, GrColorType dataColorType) {
+bool GrMtlGpu::clearTexture(GrMtlTexture* tex, GrColorType dataColorType, uint32_t levelMask) {
SkASSERT(this->caps()->isConfigTexturable(tex->config()));
+ if (!levelMask) {
+ return true;
+ }
+
id<MTLTexture> mtlTexture = tex->mtlTexture();
SkASSERT(mtlTexture);
// Either upload only the first miplevel or all miplevels
@@ -314,34 +318,30 @@
size_t bpp = GrColorTypeBytesPerPixel(dataColorType);
SkTArray<size_t> individualMipOffsets(mipLevelCount);
- individualMipOffsets.push_back(0);
- int width = tex->width();
- int height = tex->height();
- size_t combinedBufferSize = width * bpp * height;
- int currentWidth = width;
- int currentHeight = height;
+ size_t combinedBufferSize = 0;
+ int currentWidth = tex->width();
+ int currentHeight = tex->height();
// The alignment must be at least 4 bytes and a multiple of the bytes per pixel of the image
// config. This works with the assumption that the bytes in pixel config is always a power of 2.
- // TODO: can we just copy from a single buffer the size of the top level w/o a perf penalty?
+ // TODO: can we just copy from a single buffer the size of the largest cleared level w/o a perf
+ // penalty?
SkASSERT((bpp & (bpp - 1)) == 0);
const size_t alignmentMask = 0x3 | (bpp - 1);
- for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; currentMipLevel++) {
+ for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
+ if (levelMask & (1 << currentMipLevel)) {
+ const size_t trimmedSize = currentWidth * bpp * currentHeight;
+ const size_t alignmentDiff = combinedBufferSize & alignmentMask;
+ if (alignmentDiff != 0) {
+ combinedBufferSize += alignmentMask - alignmentDiff + 1;
+ }
+ individualMipOffsets.push_back(combinedBufferSize);
+ combinedBufferSize += trimmedSize;
+ }
currentWidth = SkTMax(1, currentWidth/2);
currentHeight = SkTMax(1, currentHeight/2);
-
- const size_t trimmedSize = currentWidth * bpp * currentHeight;
- const size_t alignmentDiff = combinedBufferSize & alignmentMask;
- if (alignmentDiff != 0) {
- combinedBufferSize += alignmentMask - alignmentDiff + 1;
- }
- individualMipOffsets.push_back(combinedBufferSize);
- combinedBufferSize += trimmedSize;
}
- if (0 == combinedBufferSize) {
- // We don't actually have any data to upload so just return success
- return true;
- }
+ SkASSERT(combinedBufferSize > 0 && !individualMipOffsets.empty());
// TODO: Create GrMtlTransferBuffer
id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: combinedBufferSize
@@ -360,21 +360,23 @@
value: 0];
// now copy buffer to texture
- currentWidth = width;
- currentHeight = height;
+ currentWidth = tex->width();
+ currentHeight = tex->height();
MTLOrigin origin = MTLOriginMake(0, 0, 0);
for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
- const size_t rowBytes = currentWidth * bpp;
+ if (levelMask & (1 << currentMipLevel)) {
+ const size_t rowBytes = currentWidth * bpp;
- [blitCmdEncoder copyFromBuffer: transferBuffer
- sourceOffset: individualMipOffsets[currentMipLevel]
- sourceBytesPerRow: rowBytes
- sourceBytesPerImage: rowBytes*currentHeight
- sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
- toTexture: mtlTexture
- destinationSlice: 0
- destinationLevel: currentMipLevel
- destinationOrigin: origin];
+ [blitCmdEncoder copyFromBuffer: transferBuffer
+ sourceOffset: individualMipOffsets[currentMipLevel]
+ sourceBytesPerRow: rowBytes
+ sourceBytesPerImage: rowBytes * currentHeight
+ sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
+ toTexture: mtlTexture
+ destinationSlice: 0
+ destinationLevel: currentMipLevel
+ destinationOrigin: origin];
+ }
currentWidth = SkTMax(1, currentWidth/2);
currentHeight = SkTMax(1, currentHeight/2);
}
@@ -473,8 +475,15 @@
}
}
- if (desc.fFlags & kPerformInitialClear_GrSurfaceFlag) {
- this->clearTexture(tex.get(), colorType);
+ if (this->caps()->shouldInitializeTextures()) {
+ uint32_t levelMask = ~0;
+ SkASSERT(mipLevelCount < 32);
+ for (int i = 0; i < mipLevelCount; ++i) {
+ if (!texels[i].fPixels) {
+ levelMask &= ~(1 << i);
+ }
+ }
+ this->clearTexture(tex.get(), colorType, levelMask);
}
return std::move(tex);
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index ff56ebe..3fbc63b 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -1147,6 +1147,8 @@
return {};
}
+bool GrVkCaps::canClearTextureOnCreation() const { return true; }
+
#ifdef SK_DEBUG
static bool format_color_type_valid_pair(VkFormat vkFormat, GrColorType colorType) {
switch (colorType) {
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index 911c80f..8e3eff6 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -169,6 +169,8 @@
GrBackendFormat getBackendFormatFromColorType(GrColorType ct) const override;
GrBackendFormat getBackendFormatFromCompressionType(SkImage::CompressionType) const override;
+ bool canClearTextureOnCreation() const override;
+
GrSwizzle getTextureSwizzle(const GrBackendFormat&, GrColorType) const override;
GrSwizzle getOutputSwizzle(const GrBackendFormat&, GrColorType) const override;
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 7a9811a..0cbafbe 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1018,18 +1018,35 @@
}
}
- if (SkToBool(desc.fFlags & kPerformInitialClear_GrSurfaceFlag)) {
- VkClearColorValue zeroClearColor;
- memset(&zeroClearColor, 0, sizeof(zeroClearColor));
- VkImageSubresourceRange range;
- range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
- range.baseArrayLayer = 0;
- range.baseMipLevel = 0;
- range.layerCount = 1;
- range.levelCount = 1;
- tex->setImageLayout(this, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
- VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, false);
- this->currentCommandBuffer()->clearColorImage(this, tex.get(), &zeroClearColor, 1, &range);
+ if (this->caps()->shouldInitializeTextures()) {
+ SkSTArray<1, VkImageSubresourceRange> ranges;
+ bool inRange = false;
+ for (uint32_t i = 0; i < tex->mipLevels(); ++i) {
+ if (i >= static_cast<uint32_t>(mipLevelCount) || !texels[i].fPixels) {
+ if (inRange) {
+ ranges.back().levelCount++;
+ } else {
+ auto& range = ranges.push_back();
+ range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ range.baseArrayLayer = 0;
+ range.baseMipLevel = i;
+ range.layerCount = 1;
+ range.levelCount = 1;
+ inRange = true;
+ }
+ } else if (inRange) {
+ inRange = false;
+ }
+ }
+
+ if (!ranges.empty()) {
+ static constexpr VkClearColorValue kZeroClearColor = {};
+ tex->setImageLayout(this, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ false);
+ this->currentCommandBuffer()->clearColorImage(this, tex.get(), &kZeroClearColor,
+ ranges.count(), ranges.begin());
+ }
}
return std::move(tex);
}
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index e95fd47..e16eb89 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -194,33 +194,40 @@
#include "src/gpu/GrSurfaceProxy.h"
#include "src/gpu/GrTextureContext.h"
-DEF_GPUTEST_FOR_RENDERING_CONTEXTS(InitialTextureClear, reporter, context_info) {
+DEF_GPUTEST(InitialTextureClear, reporter, baseOptions) {
+ GrContextOptions options = baseOptions;
+ options.fClearAllTextures = true;
static constexpr int kSize = 100;
- GrSurfaceDesc desc;
- desc.fWidth = desc.fHeight = kSize;
std::unique_ptr<uint32_t[]> data(new uint32_t[kSize * kSize]);
-
- GrContext* context = context_info.grContext();
- const GrCaps* caps = context->priv().caps();
- GrProxyProvider* proxyProvider = context->priv().proxyProvider();
-
- for (int c = 0; c <= kLast_GrPixelConfig; ++c) {
- desc.fConfig = static_cast<GrPixelConfig>(c);
- if (!caps->isConfigTexturable(desc.fConfig)) {
+ for (int ct = 0; ct < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++ct) {
+ sk_gpu_test::GrContextFactory factory(options);
+ auto contextType = static_cast<sk_gpu_test::GrContextFactory::ContextType>(ct);
+ if (!sk_gpu_test::GrContextFactory::IsRenderingContext(contextType)) {
continue;
}
- desc.fFlags = kPerformInitialClear_GrSurfaceFlag;
- for (bool rt : {false, true}) {
- if (rt && !caps->isConfigRenderable(desc.fConfig)) {
+ auto context = factory.get(contextType);
+ if (!context) {
+ continue;
+ }
+ GrSurfaceDesc desc;
+ desc.fWidth = desc.fHeight = kSize;
+
+ const GrCaps* caps = context->priv().caps();
+ GrProxyProvider* proxyProvider = context->priv().proxyProvider();
+
+ for (int c = 0; c <= kLast_GrPixelConfig; ++c) {
+ desc.fConfig = static_cast<GrPixelConfig>(c);
+ if (!caps->isConfigTexturable(desc.fConfig)) {
continue;
}
- desc.fFlags |= rt ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
- for (GrSurfaceOrigin origin :
- {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
- for (auto fit : { SkBackingFit::kApprox, SkBackingFit::kExact }) {
- // Try directly creating the texture.
- // Do this twice in an attempt to hit the cache on the second time through.
- for (int i = 0; i < 2; ++i) {
+ for (bool rt : {false, true}) {
+ if (rt && !caps->isConfigRenderable(desc.fConfig)) {
+ continue;
+ }
+ desc.fFlags |= rt ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
+ for (GrSurfaceOrigin origin :
+ {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
+ for (auto fit : {SkBackingFit::kApprox, SkBackingFit::kExact}) {
auto proxy = proxyProvider->testingOnly_createInstantiatedProxy(
desc, origin, fit, SkBudgeted::kYes);
if (!proxy) {
@@ -229,8 +236,8 @@
auto texCtx = context->priv().makeWrappedSurfaceContext(
std::move(proxy), GrPixelConfigToColorType(desc.fConfig),
kPremul_SkAlphaType);
- SkImageInfo info = SkImageInfo::Make(
- kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+ SkImageInfo info = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType,
+ kPremul_SkAlphaType);
memset(data.get(), 0xAB, kSize * kSize * sizeof(uint32_t));
if (texCtx->readPixels(info, data.get(), 0, {0, 0})) {
uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0;
@@ -241,73 +248,64 @@
}
}
}
- memset(data.get(), 0xBC, kSize * kSize * sizeof(uint32_t));
- // Here we overwrite the texture so that the second time through we
- // test against recycling without reclearing.
- if (0 == i) {
- texCtx->writePixels(info, data.get(), 0, {0, 0});
- }
- }
- context->priv().testingOnly_purgeAllUnlockedResources();
+ context->priv().testingOnly_purgeAllUnlockedResources();
- // We don't round trip correctly going from pixelConfig to colorType to
- // backendFormat with the RGBX config. The actual config stored on the GrSurface
- // will be RGBA_8888 but the format we create below will say it is RGB_888.
- if (desc.fConfig == kRGB_888X_GrPixelConfig) {
- continue;
- }
-
- // The specific kAlpha_* pixel configs also cause difficulties with OpenGL.
- // The mapping from config -> colorType -> format doesn't necessarily
- // resolve back to the expected pixel config. In this case we just test
- // the generics.
- if (GrBackendApi::kOpenGL == context->backend() &&
- (desc.fConfig == kAlpha_8_as_Alpha_GrPixelConfig ||
- desc.fConfig == kAlpha_8_as_Red_GrPixelConfig ||
- desc.fConfig == kAlpha_half_as_Red_GrPixelConfig)) {
- continue;
- }
-
- // The specific kGray_8_* pixel configs also cause difficulties with OpenGL.
- // The mapping from config -> colorType -> format doesn't necessarily
- // resolve back to the expected pixel config. In this case we just test
- // the generic.
- if (GrBackendApi::kOpenGL == context->backend() &&
- (desc.fConfig == kGray_8_as_Lum_GrPixelConfig ||
- desc.fConfig == kGray_8_as_Red_GrPixelConfig)) {
- continue;
- }
-
- GrColorType colorType = GrPixelConfigToColorType(desc.fConfig);
- const GrBackendFormat format = caps->getBackendFormatFromColorType(colorType);
-
- // Try creating the texture as a deferred proxy.
- for (int i = 0; i < 2; ++i) {
- auto surfCtx = context->priv().makeDeferredSurfaceContext(
- format, desc, origin, GrMipMapped::kNo, fit, SkBudgeted::kYes,
- colorType, kPremul_SkAlphaType);
- if (!surfCtx) {
+ // We don't round trip correctly going from pixelConfig to colorType to
+ // backendFormat with the RGBX config. The actual config stored on the
+ // GrSurface will be RGBA_8888 but the format we create below will say it is
+ // RGB_888.
+ if (desc.fConfig == kRGB_888X_GrPixelConfig) {
continue;
}
- SkImageInfo info = SkImageInfo::Make(
- kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
- memset(data.get(), 0xAB, kSize * kSize * sizeof(uint32_t));
- if (surfCtx->readPixels(info, data.get(), 0, {0, 0})) {
- uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0;
- for (int i = 0; i < kSize * kSize; ++i) {
- if (cmp != data.get()[i]) {
- ERRORF(reporter, "Failed on config %d", desc.fConfig);
- break;
+
+ // The specific kAlpha_* pixel configs also cause difficulties with OpenGL.
+ // The mapping from config -> colorType -> format doesn't necessarily
+ // resolve back to the expected pixel config. In this case we just test
+ // the generics.
+ if (GrBackendApi::kOpenGL == context->backend() &&
+ (desc.fConfig == kAlpha_8_as_Alpha_GrPixelConfig ||
+ desc.fConfig == kAlpha_8_as_Red_GrPixelConfig ||
+ desc.fConfig == kAlpha_half_as_Red_GrPixelConfig)) {
+ continue;
+ }
+
+ // The specific kGray_8_* pixel configs also cause difficulties with OpenGL.
+ // The mapping from config -> colorType -> format doesn't necessarily
+ // resolve back to the expected pixel config. In this case we just test
+ // the generic.
+ if (GrBackendApi::kOpenGL == context->backend() &&
+ (desc.fConfig == kGray_8_as_Lum_GrPixelConfig ||
+ desc.fConfig == kGray_8_as_Red_GrPixelConfig)) {
+ continue;
+ }
+
+ GrColorType colorType = GrPixelConfigToColorType(desc.fConfig);
+ const GrBackendFormat format =
+ caps->getBackendFormatFromColorType(colorType);
+
+ // Try creating the texture as a deferred proxy.
+ for (int i = 0; i < 2; ++i) {
+ auto surfCtx = context->priv().makeDeferredSurfaceContext(
+ format, desc, origin, GrMipMapped::kNo, fit, SkBudgeted::kYes,
+ colorType, kPremul_SkAlphaType);
+ if (!surfCtx) {
+ continue;
+ }
+ SkImageInfo info = SkImageInfo::Make(
+ kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+ memset(data.get(), 0xAB, kSize * kSize * sizeof(uint32_t));
+ if (surfCtx->readPixels(info, data.get(), 0, {0, 0})) {
+ uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0;
+ for (int i = 0; i < kSize * kSize; ++i) {
+ if (cmp != data.get()[i]) {
+ ERRORF(reporter, "Failed on config %d", desc.fConfig);
+ break;
+ }
}
}
- }
- // Here we overwrite the texture so that the second time through we
- // test against recycling without reclearing.
- if (0 == i) {
- surfCtx->writePixels(info, data.get(), 0, {0, 0});
+ context->priv().testingOnly_purgeAllUnlockedResources();
}
}
- context->priv().testingOnly_purgeAllUnlockedResources();
}
}
}