One createTexture function, attempt to recycle scratch in createTexture.
authorbsalomon <bsalomon@google.com>
Fri, 6 Feb 2015 16:49:24 +0000 (08:49 -0800)
committerCommit bot <commit-bot@chromium.org>
Fri, 6 Feb 2015 16:49:24 +0000 (08:49 -0800)
Review URL: https://codereview.chromium.org/864383003

20 files changed:
gm/texdata.cpp
include/gpu/GrContext.h
src/effects/SkBlurMaskFilter.cpp
src/effects/SkColorCubeFilter.cpp
src/gpu/GrContext.cpp
src/gpu/SkGpuDevice.cpp
src/gpu/SkGr.cpp
src/gpu/SkGrPixelRef.cpp
src/gpu/effects/GrConfigConversionEffect.cpp
src/gpu/effects/GrTextureStripAtlas.cpp
tests/BlurTest.cpp
tests/ClipCacheTest.cpp
tests/FloatingPointTextureTest.cpp
tests/GLProgramsTest.cpp
tests/GrSurfaceTest.cpp
tests/ReadPixelsTest.cpp
tests/ReadWriteAlphaTest.cpp
tests/RecordReplaceDrawTest.cpp
tests/WritePixelsTest.cpp
tools/PictureRenderer.cpp

index 4644758..a1b1e6e 100644 (file)
@@ -84,8 +84,7 @@ protected:
                 desc.fConfig    = kSkia8888_GrPixelConfig;
                 desc.fWidth     = 2 * S;
                 desc.fHeight    = 2 * S;
-                GrTexture* texture =
-                    ctx->createUncachedTexture(desc, gTextureData.get(), 0);
+                GrTexture* texture = ctx->createTexture(desc, false, gTextureData.get(), 0);
 
                 if (!texture) {
                     return;
index bbba977..4a9701f 100644 (file)
@@ -218,28 +218,30 @@ public:
      * ref on the returned texture which must be balanced by a call to unref.
      *
      * @param desc      Description of the texture properties.
-     * @param srcData   Pointer to the pixel values.
+     * @param budgeted  Does the texture count against the resource cache budget?
+     * @param srcData   Pointer to the pixel values (optional).
      * @param rowBytes  The number of bytes between rows of the texture. Zero
      *                  implies tightly packed rows. For compressed pixel configs, this
      *                  field is ignored.
      */
-    GrTexture* createTexture(const GrSurfaceDesc& desc, const void* srcData, size_t rowBytes);
+    GrTexture* createTexture(const GrSurfaceDesc& desc, bool budgeted, const void* srcData,
+                             size_t rowBytes);
 
-    GrTexture* createTexture(const GrSurfaceDesc& desc) {
-        return this->createTexture(desc, NULL, 0);
+    GrTexture* createTexture(const GrSurfaceDesc& desc, bool budgeted) {
+        return this->createTexture(desc, budgeted, NULL, 0);
     }
 
     /**
-     * Creates a texture that is outside the cache. Does not count against
-     * cache's budget.
-     *
-     * TODO: Add a budgeted param to createTexture and remove this function.
+     * DEPRECATED: use createTexture().
      */
-    GrTexture* createUncachedTexture(const GrSurfaceDesc& desc, void* srcData, size_t rowBytes);
+    GrTexture* createUncachedTexture(const GrSurfaceDesc& desc, void* srcData, size_t rowBytes) {
+        return this->createTexture(desc, false, srcData, rowBytes);
+    }
 
     /**
      * Enum that determines how closely a returned scratch texture must match
-     * a provided GrSurfaceDesc.
+     * a provided GrSurfaceDesc. TODO: Remove this. createTexture() should be used
+     * for exact match and refScratchTexture() should be replaced with createApproxTexture().
      */
     enum ScratchTexMatch {
         /**
@@ -269,6 +271,10 @@ public:
      *
      * internalFlag is a temporary workaround until changes in the internal
      * architecture are complete. Use the default value.
+     *
+     * TODO: Once internal flag can be removed, this should be replaced with
+     * createApproxTexture() and exact textures should be created with
+     * createTexture().
      */
     GrTexture* refScratchTexture(const GrSurfaceDesc&, ScratchTexMatch match,
                                  bool internalFlag = false);
@@ -837,12 +843,7 @@ private:
                           const SkPath&,
                           const GrStrokeInfo&);
 
-    // TODO: Move this out of GrContext.
-    GrTexture* createResizedTexture(const GrSurfaceDesc&,
-                                    const GrContentKey& origKey,
-                                    const void* srcData,
-                                    size_t rowBytes,
-                                    bool filter);
+    GrTexture* internalRefScratchTexture(const GrSurfaceDesc&, uint32_t flags);
 
     /**
      * These functions create premul <-> unpremul effects if it is possible to generate a pair
index c33edc2..b521546 100644 (file)
@@ -771,7 +771,7 @@ bool GrRectBlurEffect::CreateBlurProfileTexture(GrContext *context, float sigma,
         SkBlurMask::ComputeBlurProfile(sigma, &profile);
         ada.reset(profile);
 
-        *blurProfileTexture = context->createTexture(texDesc, profile, 0);
+        *blurProfileTexture = context->createTexture(texDesc, true, profile, 0);
 
         if (NULL == *blurProfileTexture) {
             return false;
@@ -959,7 +959,7 @@ GrFragmentProcessor* GrRRectBlurEffect::Create(GrContext* context, float sigma,
         texDesc.fHeight = texSide;
         texDesc.fConfig = kAlpha_8_GrPixelConfig;
 
-        blurNinePatchTexture = context->createTexture(texDesc, blurred_mask.fImage, 0);
+        blurNinePatchTexture = context->createTexture(texDesc, true, blurred_mask.fImage, 0);
         SkAssertResult(context->addResourceToCache(key, blurNinePatchTexture));
 
         SkMask::FreeImage(blurred_mask.fImage);
index f4ffefc..df9f5ea 100644 (file)
@@ -356,7 +356,7 @@ GrFragmentProcessor* SkColorCubeFilter::asFragmentProcessor(GrContext* context)
 
     SkAutoTUnref<GrTexture> textureCube(context->findAndRefCachedTexture(key));
     if (!textureCube) {
-        textureCube.reset(context->createTexture(desc, fCubeData->data(), 0));
+        textureCube.reset(context->createTexture(desc, true, fCubeData->data(), 0));
         if (textureCube) {
             SkAssertResult(context->addResourceToCache(key, textureCube));
         }
index 5b157d2..7a910c5 100755 (executable)
@@ -222,6 +222,11 @@ GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget,
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+enum ScratchTextureFlags {
+    kExact_ScratchTextureFlag           = 0x1,
+    kNoPendingIO_ScratchTextureFlag     = 0x2,
+    kNoCreate_ScratchTextureFlag        = 0x4,
+};
 
 bool GrContext::isConfigTexturable(GrPixelConfig config) const {
     return fGpu->caps()->isConfigTexturable(config);
@@ -231,31 +236,57 @@ bool GrContext::npotTextureTileSupport() const {
     return fGpu->caps()->npotTextureTileSupport();
 }
 
-GrTexture* GrContext::createTexture(const GrSurfaceDesc& desc, const void* srcData,
+GrTexture* GrContext::createTexture(const GrSurfaceDesc& desc, bool budgeted, const void* srcData,
                                     size_t rowBytes) {
-    return fGpu->createTexture(desc, true, srcData, rowBytes);
+    if ((desc.fFlags & kRenderTarget_GrSurfaceFlag) &&
+        !this->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
+        return NULL;
+    }
+    if (!GrPixelConfigIsCompressed(desc.fConfig)) {
+        static const uint32_t kFlags = kExact_ScratchTextureFlag |
+                                       kNoCreate_ScratchTextureFlag;
+        if (GrTexture* texture = this->internalRefScratchTexture(desc, kFlags)) {
+            if (!srcData || texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
+                                                 srcData, rowBytes)) {
+                if (!budgeted) {
+                    texture->cacheAccess().makeUnbudgeted();
+                }
+                return texture;
+            }
+            texture->unref();
+        }
+    }
+    return fGpu->createTexture(desc, budgeted, srcData, rowBytes);
 }
 
-GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& inDesc, ScratchTexMatch match,
+GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& desc, ScratchTexMatch match,
                                         bool calledDuringFlush) {
     // Currently we don't recycle compressed textures as scratch.
-    if (GrPixelConfigIsCompressed(inDesc.fConfig)) {
+    if (GrPixelConfigIsCompressed(desc.fConfig)) {
         return NULL;
+    } else {
+        uint32_t flags = 0;
+        if (kExact_ScratchTexMatch == match) {
+            flags |= kExact_ScratchTextureFlag;
+        }
+        if (calledDuringFlush) {
+            flags |= kNoPendingIO_ScratchTextureFlag;
+        }
+        return this->internalRefScratchTexture(desc, flags);
     }
+}
 
+GrTexture* GrContext::internalRefScratchTexture(const GrSurfaceDesc& inDesc, uint32_t flags) {
+    SkASSERT(!GrPixelConfigIsCompressed(inDesc.fConfig));
     // kNoStencil has no meaning if kRT isn't set.
     SkASSERT((inDesc.fFlags & kRenderTarget_GrSurfaceFlag) ||
              !(inDesc.fFlags & kNoStencil_GrSurfaceFlag));
 
-    // Make sure caller has checked for renderability if kRT is set.
-    SkASSERT(!(inDesc.fFlags & kRenderTarget_GrSurfaceFlag) ||
-             this->isConfigRenderable(inDesc.fConfig, inDesc.fSampleCnt > 0));
-
     SkTCopyOnFirstWrite<GrSurfaceDesc> desc(inDesc);
 
     if (fGpu->caps()->reuseScratchTextures() || (desc->fFlags & kRenderTarget_GrSurfaceFlag)) {
         GrSurfaceFlags origFlags = desc->fFlags;
-        if (kApprox_ScratchTexMatch == match) {
+        if (!(kExact_ScratchTextureFlag & flags)) {
             // bin by pow2 with a reasonable min
             static const int MIN_SIZE = 16;
             GrSurfaceDesc* wdesc = desc.writable();
@@ -267,7 +298,7 @@ GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& inDesc, ScratchTexM
             GrScratchKey key;
             GrTexturePriv::ComputeScratchKey(*desc, &key);
             uint32_t scratchFlags = 0;
-            if (calledDuringFlush) {
+            if (kNoPendingIO_ScratchTextureFlag & flags) {
                 scratchFlags = GrResourceCache2::kRequireNoPendingIO_ScratchFlag;
             } else  if (!(desc->fFlags & kRenderTarget_GrSurfaceFlag)) {
                 // If it is not a render target then it will most likely be populated by
@@ -284,7 +315,7 @@ GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& inDesc, ScratchTexM
                 return surface->asTexture();
             }
 
-            if (kExact_ScratchTexMatch == match) {
+            if (kExact_ScratchTextureFlag & flags) {
                 break;
             }
             // We had a cache miss and we are in approx mode, relax the fit of the flags.
@@ -303,15 +334,19 @@ GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& inDesc, ScratchTexM
         desc.writable()->fFlags = origFlags;
     }
 
-    GrTexture* texture = fGpu->createTexture(*desc, true, NULL, 0);
-#ifdef SK_DEBUG
-    if (fGpu->caps()->reuseScratchTextures() || (desc->fFlags & kRenderTarget_GrSurfaceFlag)) {
-        GrScratchKey key;
-        GrTexturePriv::ComputeScratchKey(*desc, &key);
-        SkASSERT(NULL == texture || texture->cacheAccess().getScratchKey() == key);
+    if (!(kNoCreate_ScratchTextureFlag & flags)) {
+        GrTexture* texture = fGpu->createTexture(*desc, true, NULL, 0);
+    #ifdef SK_DEBUG
+        if (fGpu->caps()->reuseScratchTextures() || (desc->fFlags & kRenderTarget_GrSurfaceFlag)) {
+            GrScratchKey key;
+            GrTexturePriv::ComputeScratchKey(*desc, &key);
+            SkASSERT(NULL == texture || texture->cacheAccess().getScratchKey() == key);
+        }
+    #endif
+        return texture;
     }
-#endif
-    return texture;
+
+    return NULL;
 }
 
 void GrContext::OverBudgetCB(void* data) {
@@ -323,12 +358,6 @@ void GrContext::OverBudgetCB(void* data) {
     context->fFlushToReduceCacheSize = true;
 }
 
-GrTexture* GrContext::createUncachedTexture(const GrSurfaceDesc& desc,
-                                            void* srcData,
-                                            size_t rowBytes) {
-    return fGpu->createTexture(desc, false, srcData, rowBytes);
-}
-
 int GrContext::getMaxTextureSize() const {
     return SkTMin(fGpu->caps()->maxTextureSize(), fMaxTextureSizeOverride);
 }
index 1695f5d..8912851 100644 (file)
@@ -197,13 +197,7 @@ SkGpuDevice* SkGpuDevice::Create(GrContext* context, SkSurface::Budgeted budgete
     desc.fConfig = SkImageInfo2GrPixelConfig(info);
     desc.fSampleCnt = sampleCount;
 
-    SkAutoTUnref<GrTexture> texture;
-    if (SkSurface::kYes_Budgeted == budgeted) {
-        texture.reset(context->refScratchTexture(desc, GrContext::kExact_ScratchTexMatch));
-    } else {
-        texture.reset(context->createUncachedTexture(desc, NULL, 0));
-    }
-
+    SkAutoTUnref<GrTexture> texture(context->createTexture(desc, SkToBool(budgeted), NULL, 0));
     if (!texture) {
         return NULL;
     }
index f1c4d20..2209ea4 100644 (file)
@@ -179,17 +179,9 @@ static GrTexture* create_texture_for_bmp(GrContext* ctx,
                                          GrSurfaceDesc desc,
                                          const void* pixels,
                                          size_t rowBytes) {
-    GrTexture* result;
-    if (optionalKey.isValid() || GrPixelConfigIsCompressed(desc.fConfig)) {
-        result = ctx->createTexture(desc, pixels, rowBytes);
-        if (result) {
-            SkAssertResult(ctx->addResourceToCache(optionalKey, result));
-        }
-    } else {
-        result = ctx->refScratchTexture(desc, GrContext::kExact_ScratchTexMatch);
-        if (pixels && result) {
-            result->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, pixels, rowBytes);
-        }
+    GrTexture* result = ctx->createTexture(desc, true, pixels, rowBytes);
+    if (result && optionalKey.isValid()) {
+        SkAssertResult(ctx->addResourceToCache(optionalKey, result));
     }
     return result;
 }
index f752bf0..bc17879 100644 (file)
@@ -80,7 +80,7 @@ static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorTyp
     desc.fFlags = kRenderTarget_GrSurfaceFlag | kNoStencil_GrSurfaceFlag;
     desc.fConfig = SkImageInfo2GrPixelConfig(dstCT, kPremul_SkAlphaType, dstPT);
 
-    GrTexture* dst = context->createUncachedTexture(desc, NULL, 0);
+    GrTexture* dst = context->createTexture(desc, false, NULL, 0);
     if (NULL == dst) {
         return NULL;
     }
index 4412c43..ec44d08 100644 (file)
@@ -185,16 +185,16 @@ void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context
     desc.fHeight = 256;
     desc.fConfig = kRGBA_8888_GrPixelConfig;
 
-    SkAutoTUnref<GrTexture> readTex(context->createUncachedTexture(desc, NULL, 0));
+    SkAutoTUnref<GrTexture> readTex(context->createTexture(desc, true, NULL, 0));
     if (!readTex.get()) {
         return;
     }
-    SkAutoTUnref<GrTexture> tempTex(context->createUncachedTexture(desc, NULL, 0));
+    SkAutoTUnref<GrTexture> tempTex(context->createTexture(desc, true, NULL, 0));
     if (!tempTex.get()) {
         return;
     }
     desc.fFlags = kNone_GrSurfaceFlags;
-    SkAutoTUnref<GrTexture> dataTex(context->createUncachedTexture(desc, data, 0));
+    SkAutoTUnref<GrTexture> dataTex(context->createTexture(desc, true, data, 0));
     if (!dataTex.get()) {
         return;
     }
index 880a739..dfee607 100644 (file)
@@ -203,7 +203,7 @@ void GrTextureStripAtlas::lockTexture() {
 
     fTexture = fDesc.fContext->findAndRefCachedTexture(key);
     if (NULL == fTexture) {
-        fTexture = fDesc.fContext->createTexture(texDesc, NULL, 0);
+        fTexture = fDesc.fContext->createTexture(texDesc, true, NULL, 0);
         SkAssertResult(fDesc.fContext->addResourceToCache(key, fTexture));
         // This is a new texture, so all of our cache info is now invalid
         this->initLRU();
index 62c11e0..4dad71d 100644 (file)
@@ -291,7 +291,7 @@ static bool gpu_blur_path(GrContextFactory* factory, const SkPath& path,
     desc.fHeight = 30;
     desc.fSampleCnt = 0;
 
-    SkAutoTUnref<GrTexture> texture(grContext->createUncachedTexture(desc, NULL, 0));
+    SkAutoTUnref<GrTexture> texture(grContext->createTexture(desc, false, NULL, 0));
     SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice, (grContext, texture.get())));
     SkCanvas canvas(device.get());
 
index 1df1053..1e195cc 100644 (file)
@@ -31,7 +31,7 @@ static GrTexture* createTexture(GrContext* context) {
     desc.fHeight    = Y_SIZE;
 
     // We are initializing the texture with zeros here
-    GrTexture* texture = context->createUncachedTexture(desc, textureData, 0);
+    GrTexture* texture = context->createTexture(desc, false, textureData, 0);
     if (!texture) {
         return NULL;
     }
@@ -52,7 +52,7 @@ static void test_clip_bounds(skiatest::Reporter* reporter, GrContext* context) {
     desc.fWidth     = kXSize;
     desc.fHeight    = kYSize;
 
-    GrTexture* texture = context->createUncachedTexture(desc, NULL, 0);
+    GrTexture* texture = context->createTexture(desc, false, NULL, 0);
     if (!texture) {
         return;
     }
index 3bc87f6..7ac27cb 100644 (file)
@@ -59,8 +59,8 @@ DEF_GPUTEST(FloatingPointTextureTest, reporter, factory) {
                 continue;
             }
 
-            SkAutoTUnref<GrTexture> fpTexture(
-                    context->createUncachedTexture(desc, controlPixelData.begin(), 0));
+            SkAutoTUnref<GrTexture> fpTexture(context->createTexture(desc, false,
+                                                                     controlPixelData.begin(), 0));
             // Floating point textures are NOT supported everywhere
             if (NULL == fpTexture) {
                 continue;
@@ -106,8 +106,8 @@ DEF_GPUTEST(HalfFloatTextureTest, reporter, factory) {
                 continue;
             }
 
-            SkAutoTUnref<GrTexture> fpTexture(
-                    context->createUncachedTexture(desc, controlPixelData.begin(), 0));
+            SkAutoTUnref<GrTexture> fpTexture(context->createTexture(desc, false,
+                                                                     controlPixelData.begin(), 0));
             // 16-bit floating point textures are NOT supported everywhere
             if (NULL == fpTexture) {
                 continue;
index 30f3458..08222bf 100644 (file)
@@ -116,7 +116,7 @@ static GrRenderTarget* random_render_target(GrContext* context, SkRandom* random
 
     GrTexture* texture = context->findAndRefCachedTexture(key);
     if (!texture) {
-        texture = context->createTexture(texDesc);
+        texture = context->createTexture(texDesc, true);
         if (texture) {
             SkAssertResult(context->addResourceToCache(key, texture));            
         }
index 1e21df1..094ca78 100644 (file)
@@ -27,7 +27,7 @@ DEF_GPUTEST(GrSurface, reporter, factory) {
         desc.fWidth = 256;
         desc.fHeight = 256;
         desc.fSampleCnt = 0;
-        GrSurface* texRT1 = context->createUncachedTexture(desc, NULL, 0);
+        GrSurface* texRT1 = context->createTexture(desc, false, NULL, 0);
 
         REPORTER_ASSERT(reporter, texRT1 == texRT1->asRenderTarget());
         REPORTER_ASSERT(reporter, texRT1 == texRT1->asTexture());
@@ -39,7 +39,7 @@ DEF_GPUTEST(GrSurface, reporter, factory) {
                                   static_cast<GrSurface*>(texRT1->asTexture()));
 
         desc.fFlags = kNone_GrSurfaceFlags;
-        GrSurface* tex1 = context->createUncachedTexture(desc, NULL, 0);
+        GrSurface* tex1 = context->createTexture(desc, false, NULL, 0);
         REPORTER_ASSERT(reporter, NULL == tex1->asRenderTarget());
         REPORTER_ASSERT(reporter, tex1 == tex1->asTexture());
         REPORTER_ASSERT(reporter, static_cast<GrSurface*>(tex1) == tex1->asTexture());
index 63cc4d0..a4683b0 100644 (file)
@@ -318,8 +318,7 @@ DEF_GPUTEST(ReadPixels, reporter, factory) {
                 desc.fHeight = DEV_H;
                 desc.fConfig = kSkia8888_GrPixelConfig;
                 desc.fOrigin = 1 == dtype ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
-                SkAutoTUnref<GrTexture> texture(
-                    context->refScratchTexture(desc, GrContext::kExact_ScratchTexMatch));
+                SkAutoTUnref<GrTexture> texture(context->createTexture(desc, false));
                 surface.reset(SkSurface::NewRenderTargetDirect(texture->asRenderTarget()));
 #else
                 continue;
index 966bc7f..3f9ff1b 100644 (file)
@@ -41,7 +41,7 @@ DEF_GPUTEST(ReadWriteAlpha, reporter, factory) {
         desc.fHeight    = Y_SIZE;
 
         // We are initializing the texture with zeros here
-        GrTexture* texture = context->createUncachedTexture(desc, textureData, 0);
+        GrTexture* texture = context->createTexture(desc, false, textureData, 0);
         if (!texture) {
             return;
         }
index bbf0168..55f47ce 100644 (file)
@@ -124,7 +124,7 @@ void test_replacements(skiatest::Reporter* r, GrContext* context, bool useBBH) {
     desc.fHeight = kHeight;
     desc.fSampleCnt = 0;
 
-    SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0));
+    SkAutoTUnref<GrTexture> texture(context->createTexture(desc, false, NULL, 0));
     layer->setTexture(texture, SkIRect::MakeWH(kWidth, kHeight));
 
     SkAutoTUnref<SkBBoxHierarchy> bbh;
index 86effad..2d24a77 100644 (file)
@@ -319,8 +319,7 @@ static SkSurface* create_surface(const CanvasConfig& c, GrContext* grCtx) {
             desc.fConfig = kSkia8888_GrPixelConfig;
             desc.fOrigin = kGpu_TopLeft_DevType == c.fDevType ?
                 kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
-            SkAutoTUnref<GrTexture> texture(
-                grCtx->refScratchTexture(desc, GrContext::kExact_ScratchTexMatch));
+            SkAutoTUnref<GrTexture> texture(grCtx->createTexture(desc, false));
             return SkSurface::NewRenderTargetDirect(texture->asRenderTarget());
 #endif
     }
index 3fe246c..6acd13c 100644 (file)
@@ -155,7 +155,7 @@ SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
                 desc.fWidth = width;
                 desc.fHeight = height;
                 desc.fSampleCnt = fSampleCount;
-                target.reset(fGrContext->createUncachedTexture(desc, NULL, 0));
+                target.reset(fGrContext->createTexture(desc, false, NULL, 0));
             }
 
             uint32_t flags = fUseDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0;