LCOV - code coverage report
Current view: top level - gfx/thebes - gfxGradientCache.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 68 90 75.6 %
Date: 2017-07-14 16:53:18 Functions: 14 20 70.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "mozilla/gfx/2D.h"
       7             : #include "nsTArray.h"
       8             : #include "PLDHashTable.h"
       9             : #include "nsExpirationTracker.h"
      10             : #include "nsClassHashtable.h"
      11             : #include "mozilla/SystemGroup.h"
      12             : #include "mozilla/Telemetry.h"
      13             : #include "gfxGradientCache.h"
      14             : #include <time.h>
      15             : 
      16             : namespace mozilla {
      17             : namespace gfx {
      18             : 
      19             : using namespace mozilla;
      20             : 
      21          38 : struct GradientCacheKey : public PLDHashEntryHdr {
      22             :   typedef const GradientCacheKey& KeyType;
      23             :   typedef const GradientCacheKey* KeyTypePointer;
      24             :   enum { ALLOW_MEMMOVE = true };
      25             :   const nsTArray<GradientStop> mStops;
      26             :   ExtendMode mExtend;
      27             :   BackendType mBackendType;
      28             : 
      29          32 :   GradientCacheKey(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType)
      30          32 :     : mStops(aStops), mExtend(aExtend), mBackendType(aBackendType)
      31          32 :   { }
      32             : 
      33           6 :   explicit GradientCacheKey(const GradientCacheKey* aOther)
      34           6 :     : mStops(aOther->mStops), mExtend(aOther->mExtend), mBackendType(aOther->mBackendType)
      35           6 :   { }
      36             : 
      37             :   union FloatUint32
      38             :   {
      39             :     float    f;
      40             :     uint32_t u;
      41             :   };
      42             : 
      43             :   static PLDHashNumber
      44          31 :   HashKey(const KeyTypePointer aKey)
      45             :   {
      46          31 :     PLDHashNumber hash = 0;
      47             :     FloatUint32 convert;
      48          31 :     hash = AddToHash(hash, int(aKey->mBackendType));
      49          31 :     hash = AddToHash(hash, int(aKey->mExtend));
      50         126 :     for (uint32_t i = 0; i < aKey->mStops.Length(); i++) {
      51          95 :       hash = AddToHash(hash, aKey->mStops[i].color.ToABGR());
      52             :       // Use the float bits as hash, except for the cases of 0.0 and -0.0 which both map to 0
      53          95 :       convert.f = aKey->mStops[i].offset;
      54          95 :       hash = AddToHash(hash, convert.f ? convert.u : 0);
      55             :     }
      56          31 :     return hash;
      57             :   }
      58             : 
      59          20 :   bool KeyEquals(KeyTypePointer aKey) const
      60             :   {
      61          20 :     bool sameStops = true;
      62          20 :     if (aKey->mStops.Length() != mStops.Length()) {
      63           0 :       sameStops = false;
      64             :     } else {
      65          81 :       for (uint32_t i = 0; i < mStops.Length(); i++) {
      66         122 :         if (mStops[i].color.ToABGR() != aKey->mStops[i].color.ToABGR() ||
      67          61 :             mStops[i].offset != aKey->mStops[i].offset) {
      68           0 :           sameStops = false;
      69           0 :           break;
      70             :         }
      71             :       }
      72             :     }
      73             : 
      74          20 :     return sameStops &&
      75          40 :            (aKey->mBackendType == mBackendType) &&
      76          40 :            (aKey->mExtend == mExtend);
      77             :   }
      78          32 :   static KeyTypePointer KeyToPointer(KeyType aKey)
      79             :   {
      80          32 :     return &aKey;
      81             :   }
      82             : };
      83             : 
      84             : /**
      85             :  * This class is what is cached. It need to be allocated in an object separated
      86             :  * to the cache entry to be able to be tracked by the nsExpirationTracker.
      87             :  * */
      88           0 : struct GradientCacheData {
      89           6 :   GradientCacheData(GradientStops* aStops, const GradientCacheKey& aKey)
      90           6 :     : mStops(aStops),
      91           6 :       mKey(aKey)
      92           6 :   {}
      93             : 
      94             :   GradientCacheData(const GradientCacheData& aOther)
      95             :     : mStops(aOther.mStops),
      96             :       mKey(aOther.mKey)
      97             :   { }
      98             : 
      99          26 :   nsExpirationState *GetExpirationState() {
     100          26 :     return &mExpirationState;
     101             :   }
     102             : 
     103             :   nsExpirationState mExpirationState;
     104             :   const RefPtr<GradientStops> mStops;
     105             :   GradientCacheKey mKey;
     106             : };
     107             : 
     108             : /**
     109             :  * This class implements a cache with no maximum size, that retains the
     110             :  * gfxPatterns used to draw the gradients.
     111             :  *
     112             :  * The key is the nsStyleGradient that defines the gradient, and the size of the
     113             :  * gradient.
     114             :  *
     115             :  * The value is the gfxPattern, and whether or not we perform an optimization
     116             :  * based on the actual gradient property.
     117             :  *
     118             :  * An entry stays in the cache as long as it is used often. As long as a cache
     119             :  * entry is in the cache, all the references it has are guaranteed to be valid:
     120             :  * the nsStyleRect for the key, the gfxPattern for the value.
     121             :  */
     122           0 : class GradientCache final : public nsExpirationTracker<GradientCacheData,4>
     123             : {
     124             :   public:
     125           1 :     GradientCache()
     126           1 :       : nsExpirationTracker<GradientCacheData,4>(MAX_GENERATION_MS,
     127             :                                                  "GradientCache",
     128           1 :                                                  SystemGroup::EventTargetFor(TaskCategory::Other))
     129             :     {
     130           1 :       srand(time(nullptr));
     131           1 :       mTimerPeriod = rand() % MAX_GENERATION_MS + 1;
     132           1 :       Telemetry::Accumulate(Telemetry::GRADIENT_RETENTION_TIME, mTimerPeriod);
     133           1 :     }
     134             : 
     135           0 :     virtual void NotifyExpired(GradientCacheData* aObject)
     136             :     {
     137             :       // This will free the gfxPattern.
     138           0 :       RemoveObject(aObject);
     139           0 :       mHashEntries.Remove(aObject->mKey);
     140           0 :     }
     141             : 
     142          26 :     GradientCacheData* Lookup(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType)
     143             :     {
     144             :       GradientCacheData* gradient =
     145          26 :         mHashEntries.Get(GradientCacheKey(aStops, aExtend, aBackendType));
     146             : 
     147          26 :       if (gradient) {
     148          20 :         MarkUsed(gradient);
     149             :       }
     150             : 
     151          26 :       return gradient;
     152             :     }
     153             : 
     154             :     // Returns true if we successfully register the gradient in the cache, false
     155             :     // otherwise.
     156           6 :     bool RegisterEntry(GradientCacheData* aValue)
     157             :     {
     158           6 :       nsresult rv = AddObject(aValue);
     159           6 :       if (NS_FAILED(rv)) {
     160             :         // We are OOM, and we cannot track this object. We don't want stall
     161             :         // entries in the hash table (since the expiration tracker is responsible
     162             :         // for removing the cache entries), so we avoid putting that entry in the
     163             :         // table, which is a good things considering we are short on memory
     164             :         // anyway, we probably don't want to retain things.
     165           0 :         return false;
     166             :       }
     167           6 :       mHashEntries.Put(aValue->mKey, aValue);
     168           6 :       return true;
     169             :     }
     170             : 
     171             :   protected:
     172             :     uint32_t mTimerPeriod;
     173             :     static const uint32_t MAX_GENERATION_MS = 10000;
     174             :     /**
     175             :      * FIXME use nsTHashtable to avoid duplicating the GradientCacheKey.
     176             :      * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47
     177             :      */
     178             :     nsClassHashtable<GradientCacheKey, GradientCacheData> mHashEntries;
     179             : };
     180             : 
     181             : static GradientCache* gGradientCache = nullptr;
     182             : 
     183             : GradientStops *
     184          26 : gfxGradientCache::GetGradientStops(const DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend)
     185             : {
     186          26 :   if (!gGradientCache) {
     187           1 :     gGradientCache = new GradientCache();
     188             :   }
     189             :   GradientCacheData* cached =
     190          26 :     gGradientCache->Lookup(aStops, aExtend, aDT->GetBackendType());
     191          26 :   if (cached && cached->mStops) {
     192          20 :     if (!cached->mStops->IsValid()) {
     193           0 :       gGradientCache->NotifyExpired(cached);
     194             :     } else {
     195          20 :       return cached->mStops;
     196             :     }
     197             :   }
     198             : 
     199           6 :   return nullptr;
     200             : }
     201             : 
     202             : already_AddRefed<GradientStops>
     203          26 : gfxGradientCache::GetOrCreateGradientStops(const DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend)
     204             : {
     205          26 :   if (aDT->IsRecording()) {
     206           0 :     return aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), aExtend);
     207             :   }
     208             : 
     209          52 :   RefPtr<GradientStops> gs = GetGradientStops(aDT, aStops, aExtend);
     210          26 :   if (!gs) {
     211           6 :     gs = aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), aExtend);
     212           6 :     if (!gs) {
     213           0 :       return nullptr;
     214             :     }
     215             :     GradientCacheData *cached =
     216          12 :       new GradientCacheData(gs, GradientCacheKey(aStops, aExtend,
     217          18 :                                                  aDT->GetBackendType()));
     218           6 :     if (!gGradientCache->RegisterEntry(cached)) {
     219           0 :       delete cached;
     220             :     }
     221             :   }
     222          26 :   return gs.forget();
     223             : }
     224             : 
     225             : void
     226           0 : gfxGradientCache::PurgeAllCaches()
     227             : {
     228           0 :   if (gGradientCache) {
     229           0 :     gGradientCache->AgeAllGenerations();
     230             :   }
     231           0 : }
     232             : 
     233             : void
     234           0 : gfxGradientCache::Shutdown()
     235             : {
     236           0 :   delete gGradientCache;
     237           0 :   gGradientCache = nullptr;
     238           0 : }
     239             : 
     240             : } // namespace gfx
     241             : } // namespace mozilla

Generated by: LCOV version 1.13