Add a flag to GrSurfaceFlags that requires the texture to be cleared upon creation.
authorBrian Salomon <bsalomon@google.com>
Tue, 23 May 2017 14:43:51 +0000 (10:43 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Tue, 23 May 2017 18:07:25 +0000 (18:07 +0000)
Bug: chromium:656320

Change-Id: I940bfa24540516ab83a2ed52f761b96eb6ad19f1
Reviewed-on: https://skia-review.googlesource.com/17391
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>

14 files changed:
include/gpu/GrTypes.h
include/gpu/gl/GrGLFunctions.h
include/gpu/gl/GrGLInterface.h
include/private/GrSurfaceProxy.h
src/gpu/GrGpu.cpp
src/gpu/GrResourceProvider.cpp
src/gpu/GrSurfaceProxy.cpp
src/gpu/gl/GrGLAssembleInterface.cpp
src/gpu/gl/GrGLCaps.cpp
src/gpu/gl/GrGLCaps.h
src/gpu/gl/GrGLGpu.cpp
src/gpu/gl/GrGLGpu.h
src/gpu/vk/GrVkGpu.cpp
tests/GrSurfaceTest.cpp

index e963027..51f24b4 100644 (file)
@@ -443,6 +443,7 @@ static inline bool GrPixelConfigIsOpaque(GrPixelConfig config) {
     switch (config) {
         case kRGB_565_GrPixelConfig:
         case kGray_8_GrPixelConfig:
+        case kRG_float_GrPixelConfig:
             return true;
         case kAlpha_8_GrPixelConfig:
         case kRGBA_4444_GrPixelConfig:
@@ -454,7 +455,6 @@ static inline bool GrPixelConfigIsOpaque(GrPixelConfig config) {
         case kRGBA_8888_sint_GrPixelConfig:
         case kRGBA_half_GrPixelConfig:
         case kRGBA_float_GrPixelConfig:
-        case kRG_float_GrPixelConfig:
         case kUnknown_GrPixelConfig:
             return false;
     }
@@ -516,12 +516,17 @@ static inline bool GrPixelConfigIsSint(GrPixelConfig config) {
  * Optional bitfield flags that can be set on GrSurfaceDesc (below).
  */
 enum GrSurfaceFlags {
-    kNone_GrSurfaceFlags            = 0x0,
+    kNone_GrSurfaceFlags = 0x0,
     /**
      * Creates a texture that can be rendered to as a GrRenderTarget. Use
      * GrTexture::asRenderTarget() to access.
      */
-    kRenderTarget_GrSurfaceFlag     = 0x1,
+    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)
index 9cb7ddc..930a0c1 100644 (file)
@@ -40,6 +40,8 @@ typedef GrGLenum (GR_GL_FUNCTION_TYPE* GrGLCheckFramebufferStatusProc)(GrGLenum
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearProc)(GrGLbitfield mask);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearColorProc)(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearStencilProc)(GrGLint s);
+typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearTexImageProc)(GrGLuint texture, GrGLint level, GrGLenum format, GrGLenum type,const GrGLvoid * data);
+typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearTexSubImageProc)(GrGLuint texture, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint zoffset, GrGLsizei width, GrGLsizei height, GrGLsizei depth, GrGLenum format, GrGLenum type,const GrGLvoid * data);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLColorMaskProc)(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCompileShaderProc)(GrGLuint shader);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCompressedTexImage2DProc)(GrGLenum target, GrGLint level, GrGLenum internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLsizei imageSize, const GrGLvoid* data);
index c84eca2..e5479eb 100644 (file)
@@ -125,6 +125,8 @@ public:
         GrGLFunction<GrGLClearProc> fClear;
         GrGLFunction<GrGLClearColorProc> fClearColor;
         GrGLFunction<GrGLClearStencilProc> fClearStencil;
+        GrGLFunction<GrGLClearTexImageProc> fClearTexImage;
+        GrGLFunction<GrGLClearTexSubImageProc> fClearTexSubImage;
         GrGLFunction<GrGLColorMaskProc> fColorMask;
         GrGLFunction<GrGLCompileShaderProc> fCompileShader;
         GrGLFunction<GrGLCompressedTexImage2DProc> fCompressedTexImage2D;
index 1b7950c..4ae041d 100644 (file)
@@ -323,6 +323,7 @@ protected:
             , fFit(fit)
             , fBudgeted(budgeted)
             , fFlags(flags)
+            , fNeedsClear(SkToBool(desc.fFlags & kPerformInitialClear_GrSurfaceFlag))
             , fGpuMemorySize(kInvalidGpuMemorySize)
             , fLastOpList(nullptr) {
         // Note: this ctor pulls a new uniqueID from the same pool at the GrGpuResources
@@ -359,7 +360,6 @@ protected:
                                     // mutable bc of SkSurface/SkImage wishy-washiness
     const uint32_t       fFlags;
 
-
     const UniqueID       fUniqueID; // set from the backing resource for wrapped resources
 
     static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0);
@@ -368,6 +368,8 @@ protected:
 private:
     virtual size_t onUninstantiatedGpuMemorySize() const = 0;
 
+    bool                 fNeedsClear;
+
     // This entry is lazily evaluated so, when the proxy wraps a resource, the resource
     // will be called but, when the proxy is deferred, it will compute the answer itself.
     // If the proxy computes its own answer that answer is checked (in debug mode) in
index 14ce050..12a2d92 100644 (file)
@@ -138,11 +138,15 @@ GrTexture* GrGpu::createTexture(const GrSurfaceDesc& origDesc, SkBudgeted budget
     }
 
     desc.fSampleCnt = SkTMin(desc.fSampleCnt, caps->maxSampleCount());
-    // Attempt to catch un- or wrongly intialized sample counts;
+    // Attempt to catch un- or wrongly initialized sample counts.
     SkASSERT(desc.fSampleCnt >= 0 && desc.fSampleCnt <= 64);
 
     desc.fOrigin = resolve_origin(desc.fOrigin, isRT);
 
+    if (texels.count() && (desc.fFlags & kPerformInitialClear_GrSurfaceFlag)) {
+        return nullptr;
+    }
+
     this->handleDirtyContext();
     GrTexture* tex = this->onCreateTexture(desc, budgeted, texels);
     if (tex) {
@@ -262,7 +266,7 @@ bool GrGpu::getReadPixelsInfo(GrSurface* srcSurface, int width, int height, size
         return false;
     }
 
-    if (!this->onGetReadPixelsInfo(srcSurface, width, height, rowBytes, readConfig, drawPreference,
+   if (!this->onGetReadPixelsInfo(srcSurface, width, height, rowBytes, readConfig, drawPreference,
                                    tempDrawInfo)) {
         return false;
     }
index 84e274e..128ef75 100644 (file)
@@ -221,7 +221,10 @@ GrTexture* GrResourceProvider::refScratchTexture(const GrSurfaceDesc& inDesc, ui
 
     SkTCopyOnFirstWrite<GrSurfaceDesc> desc(inDesc);
 
-    if (fGpu->caps()->reuseScratchTextures() || (desc->fFlags & kRenderTarget_GrSurfaceFlag)) {
+    // 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(inDesc.fFlags & kPerformInitialClear_GrSurfaceFlag) &&
+        (fGpu->caps()->reuseScratchTextures() || (desc->fFlags & kRenderTarget_GrSurfaceFlag))) {
         if (!(kExact_Flag & flags)) {
             // bin by pow2 with a reasonable min
             GrSurfaceDesc* wdesc = desc.writable();
index 3ced81b..0d9198f 100644 (file)
@@ -30,6 +30,7 @@ GrSurfaceProxy::GrSurfaceProxy(sk_sp<GrSurface> surface, SkBackingFit fit)
         , fBudgeted(fTarget->resourcePriv().isBudgeted())
         , fFlags(0)
         , fUniqueID(fTarget->uniqueID())  // Note: converting from unique resource ID to a proxy ID!
+        , fNeedsClear(false)
         , fGpuMemorySize(kInvalidGpuMemorySize)
         , fLastOpList(nullptr) {}
 
@@ -53,6 +54,9 @@ GrSurface* GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider,
     desc.fSampleCnt = sampleCnt;
     desc.fIsMipMapped = isMipMapped;
     desc.fFlags = flags;
+    if (fNeedsClear) {
+        desc.fFlags |= kPerformInitialClear_GrSurfaceFlag;
+    }
 
     if (SkBackingFit::kApprox == fFit) {
         fTarget = resourceProvider->createApproxTexture(desc, fFlags);
index 7914934..7305696 100644 (file)
@@ -105,6 +105,10 @@ const GrGLInterface* GrGLAssembleGLInterface(void* ctx, GrGLGetProc get) {
     GET_PROC(Clear);
     GET_PROC(ClearColor);
     GET_PROC(ClearStencil);
+    if (glVer >= GR_GL_VER(4,4) || extensions.has("GL_ARB_clear_texture")) {
+        GET_PROC(ClearTexImage);
+        GET_PROC(ClearTexSubImage);
+    }
     GET_PROC(ColorMask);
     GET_PROC(CompileShader);
     GET_PROC(CompressedTexImage2D);
@@ -584,6 +588,10 @@ const GrGLInterface* GrGLAssembleGLESInterface(void* ctx, GrGLGetProc get) {
     GET_PROC(Clear);
     GET_PROC(ClearColor);
     GET_PROC(ClearStencil);
+    if (extensions.has("GL_EXT_clear_texture")) {
+        GET_PROC_SUFFIX(ClearTexImage, EXT);
+        GET_PROC_SUFFIX(ClearTexSubImage, EXT);
+    }
     GET_PROC(ColorMask);
     GET_PROC(CompileShader);
     GET_PROC(CompressedTexImage2D);
index b9eee08..0d84a72 100644 (file)
@@ -54,6 +54,7 @@ GrGLCaps::GrGLCaps(const GrContextOptions& contextOptions,
     fSRGBDecodeDisableSupport = false;
     fSRGBDecodeDisableAffectsMipmaps = false;
     fClearToBoundaryValuesIsBroken = false;
+    fClearTextureSupport = false;
     fDrawArraysBaseVertexIsBroken = false;
 
     fBlitFramebufferFlags = kNoSupport_BlitFramebufferFlag;
@@ -252,6 +253,14 @@ void GrGLCaps::init(const GrContextOptions& contextOptions,
     // vis-versa.
     fRGBAToBGRAReadbackConversionsAreSlow = isMESA || isMAC;
 
+    if (kGL_GrGLStandard == standard) {
+        if (version >= GR_GL_VER(4,4) || ctxInfo.hasExtension("GL_ARB_clear_texture")) {
+            fClearTextureSupport = true;
+        }
+    } else if (ctxInfo.hasExtension("GL_EXT_clear_texture")) {
+        fClearTextureSupport = true;
+    }
+
     /**************************************************************************
     * GrShaderCaps fields
     **************************************************************************/
index 3059ea7..b7273a0 100644 (file)
@@ -360,6 +360,9 @@ public:
     // Certain Intel GPUs on Mac fail to clear if the glClearColor is made up of only 1s and 0s.
     bool clearToBoundaryValuesIsBroken() const { return fClearToBoundaryValuesIsBroken; }
 
+    /// glClearTex(Sub)Image support
+    bool clearTextureSupport() const { return fClearTextureSupport; }
+
     // Adreno/MSAA drops a draw on the imagefiltersbase GM if the base vertex param to
     // glDrawArrays is nonzero.
     // https://bugs.chromium.org/p/skia/issues/detail?id=6650
@@ -437,6 +440,7 @@ private:
     bool fSRGBDecodeDisableSupport : 1;
     bool fSRGBDecodeDisableAffectsMipmaps : 1;
     bool fClearToBoundaryValuesIsBroken : 1;
+    bool fClearTextureSupport : 1;
     bool fDrawArraysBaseVertexIsBroken : 1;
 
     uint32_t fBlitFramebufferFlags;
index 565e60a..81ee87c 100644 (file)
@@ -1320,24 +1320,44 @@ static void set_initial_texture_params(const GrGLInterface* interface,
 
 GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc,
                                     SkBudgeted budgeted,
-                                    const SkTArray<GrMipLevel>& texels) {
+                                    const SkTArray<GrMipLevel>& origTexels) {
     // We fail if the MSAA was requested and is not available.
     if (GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType() && desc.fSampleCnt) {
         //SkDebugf("MSAA RT requested but not supported on this platform.");
         return return_null_texture();
     }
 
+    bool performClear = (desc.fFlags & kPerformInitialClear_GrSurfaceFlag);
+    const SkTArray<GrMipLevel>* texels = &origTexels;
+
+    SkTArray<GrMipLevel> zeroLevels;
+    std::unique_ptr<uint8_t[]> zeros;
+    // TODO: remove the GrPixelConfigIsSint test. This is here because we have yet to add support
+    // for glClearBuffer* which must be used instead of glClearColor/glClear for integer FBO
+    // attachments.
+    if (performClear && !this->glCaps().clearTextureSupport() &&
+        (!this->glCaps().canConfigBeFBOColorAttachment(desc.fConfig) ||
+         GrPixelConfigIsSint(desc.fConfig))) {
+        size_t rowSize = GrBytesPerPixel(desc.fConfig) * desc.fWidth;
+        size_t size = rowSize * desc.fHeight;
+        zeros.reset(new uint8_t[size]);
+        memset(zeros.get(), 0, size);
+        zeroLevels.push_back(GrMipLevel{zeros.get(), 0});
+        texels = &zeroLevels;
+        performClear = false;
+    }
+
     bool isRenderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
 
     GrGLTexture::IDDesc idDesc;
     idDesc.fOwnership = GrBackendObjectOwnership::kOwned;
     GrGLTexture::TexParams initialTexParams;
-    if (!this->createTextureImpl(desc, &idDesc.fInfo, isRenderTarget, &initialTexParams, texels)) {
+    if (!this->createTextureImpl(desc, &idDesc.fInfo, isRenderTarget, &initialTexParams, *texels)) {
         return return_null_texture();
     }
 
     bool wasMipMapDataProvided = false;
-    if (texels.count() > 1) {
+    if (texels->count() > 1) {
         wasMipMapDataProvided = true;
     }
 
@@ -1361,6 +1381,25 @@ GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc,
     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()) {
+            GrGLenum format = GrPixelConfigIsSint(tex->config()) ? GR_GL_RGBA_INTEGER : GR_GL_RGBA;
+            static constexpr uint32_t kZero = 0;
+            GL_CALL(ClearTexImage(tex->textureID(), 0, format, GR_GL_UNSIGNED_BYTE, &kZero));
+        } else {
+            SkASSERT(!GrPixelConfigIsSint(desc.fConfig));
+            GrGLIRect viewport;
+            this->bindSurfaceFBOForPixelOps(tex, GR_GL_FRAMEBUFFER, &viewport, kDst_TempFBOTarget);
+            this->disableScissor();
+            this->disableWindowRectangles();
+            GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
+            fHWWriteToColor = kYes_TriState;
+            GL_CALL(ClearColor(0, 0, 0, 0));
+            GL_CALL(Clear(GR_GL_COLOR_BUFFER_BIT));
+            this->unbindTextureFBOForPixelOps(GR_GL_FRAMEBUFFER, tex);
+            fHWBoundRenderTargetUniqueID.makeInvalid();
+        }
+    }
     return tex;
 }
 
index a4706c7..d19f2ef 100644 (file)
@@ -371,9 +371,9 @@ private:
         kDst_TempFBOTarget
     };
 
-    // Binds a surface as a FBO for copying or reading. If the surface already owns an FBO ID then
-    // that ID is bound. If not the surface is temporarily bound to a FBO and that FBO is bound.
-    // This must be paired with a call to unbindSurfaceFBOForPixelOps().
+    // Binds a surface as a FBO for copying, reading, or clearing. If the surface already owns an
+    // FBO ID then that ID is bound. If not the surface is temporarily bound to a FBO and that FBO
+    // is bound. This must be paired with a call to unbindSurfaceFBOForPixelOps().
     void bindSurfaceFBOForPixelOps(GrSurface* surface, GrGLenum fboTarget, GrGLIRect* viewport,
                                    TempFBOTarget tempFBOTarget);
 
index 7fbca9f..4d305ac 100644 (file)
@@ -745,6 +745,19 @@ GrTexture* GrVkGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budget
         }
     }
 
+    if (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, &zeroClearColor, 1, &range);
+    }
     return tex;
 }
 
index 19f2df5..51b5362 100644 (file)
@@ -123,4 +123,101 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(GrSurfaceRenderability, reporter, ctxInfo) {
 }
 #endif
 
+#include "GrDrawingManager.h"
+#include "GrSurfaceProxy.h"
+#include "GrTextureContext.h"
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(InitialTextureClear, reporter, context_info) {
+    static constexpr int kSize = 100;
+    GrSurfaceDesc desc;
+    desc.fWidth = desc.fHeight = kSize;
+    uint32_t data[kSize * kSize];
+    GrContext* context = context_info.grContext();
+    for (int c = 0; c <= kLast_GrPixelConfig; ++c) {
+        desc.fConfig = static_cast<GrPixelConfig>(c);
+        if (!context_info.grContext()->caps()->isConfigTexturable(desc.fConfig)) {
+            continue;
+        }
+        desc.fFlags = kPerformInitialClear_GrSurfaceFlag;
+        for (bool rt : {false, true}) {
+            if (rt && !context->caps()->isConfigRenderable(desc.fConfig, false)) {
+                continue;
+            }
+            desc.fFlags |= rt ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
+            for (bool mipped : {false, true}) {
+                desc.fIsMipMapped = mipped;
+                for (GrSurfaceOrigin origin :
+                     {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
+                    desc.fOrigin = origin;
+                    for (bool approx : {false, true}) {
+                        auto resourceProvider = context->resourceProvider();
+                        // 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) {
+                            sk_sp<GrTexture> tex;
+                            if (approx) {
+                                tex = sk_sp<GrTexture>(
+                                        resourceProvider->createApproxTexture(desc, 0));
+                            } else {
+                                tex = resourceProvider->createTexture(desc, SkBudgeted::kYes);
+                            }
+                            if (!tex) {
+                                continue;
+                            }
+                            auto proxy = GrSurfaceProxy::MakeWrapped(std::move(tex));
+                            auto texCtx = context->contextPriv().makeWrappedSurfaceContext(
+                                    std::move(proxy), nullptr);
+                            SkImageInfo info = SkImageInfo::Make(
+                                    kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+                            memset(data, 0xAB, kSize * kSize * sizeof(uint32_t));
+                            if (texCtx->readPixels(info, data, 0, 0, 0)) {
+                                uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0;
+                                for (int i = 0; i < kSize * kSize; ++i) {
+                                    if (cmp != data[i]) {
+                                        ERRORF(reporter, "Failed on config %d", desc.fConfig);
+                                        break;
+                                    }
+                                }
+                            }
+                            memset(data, 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, 0, 0, 0);
+                            }
+                        }
+                        context->purgeAllUnlockedResources();
+                        // Try creating the texture as a deferred proxy.
+                        for (int i = 0; i < 2; ++i) {
+                            auto surfCtx = context->contextPriv().makeDeferredSurfaceContext(
+                                    desc, approx ? SkBackingFit::kApprox : SkBackingFit::kExact,
+                                    SkBudgeted::kYes);
+                            if (!surfCtx) {
+                                continue;
+                            }
+                            SkImageInfo info = SkImageInfo::Make(
+                                    kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+                            memset(data, 0xAB, kSize * kSize * sizeof(uint32_t));
+                            if (surfCtx->readPixels(info, data, 0, 0, 0)) {
+                                uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0;
+                                for (int i = 0; i < kSize * kSize; ++i) {
+                                    if (cmp != data[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, 0, 0, 0);
+                            }
+                        }
+                        context->purgeAllUnlockedResources();
+                    }
+                }
+            }
+        }
+    }
+}
 #endif