1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxGradientCache.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,215 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/gfx/2D.h" 1.10 +#include "nsTArray.h" 1.11 +#include "pldhash.h" 1.12 +#include "nsExpirationTracker.h" 1.13 +#include "nsClassHashtable.h" 1.14 +#include "mozilla/Telemetry.h" 1.15 +#include "gfxGradientCache.h" 1.16 +#include <time.h> 1.17 + 1.18 +namespace mozilla { 1.19 +namespace gfx { 1.20 + 1.21 +using namespace mozilla; 1.22 + 1.23 +struct GradientCacheKey : public PLDHashEntryHdr { 1.24 + typedef const GradientCacheKey& KeyType; 1.25 + typedef const GradientCacheKey* KeyTypePointer; 1.26 + enum { ALLOW_MEMMOVE = true }; 1.27 + const nsTArray<GradientStop> mStops; 1.28 + ExtendMode mExtend; 1.29 + BackendType mBackendType; 1.30 + 1.31 + GradientCacheKey(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType) 1.32 + : mStops(aStops), mExtend(aExtend), mBackendType(aBackendType) 1.33 + { } 1.34 + 1.35 + GradientCacheKey(const GradientCacheKey* aOther) 1.36 + : mStops(aOther->mStops), mExtend(aOther->mExtend), mBackendType(aOther->mBackendType) 1.37 + { } 1.38 + 1.39 + union FloatUint32 1.40 + { 1.41 + float f; 1.42 + uint32_t u; 1.43 + }; 1.44 + 1.45 + static PLDHashNumber 1.46 + HashKey(const KeyTypePointer aKey) 1.47 + { 1.48 + PLDHashNumber hash = 0; 1.49 + FloatUint32 convert; 1.50 + hash = AddToHash(hash, int(aKey->mBackendType)); 1.51 + hash = AddToHash(hash, int(aKey->mExtend)); 1.52 + for (uint32_t i = 0; i < aKey->mStops.Length(); i++) { 1.53 + hash = AddToHash(hash, aKey->mStops[i].color.ToABGR()); 1.54 + // Use the float bits as hash, except for the cases of 0.0 and -0.0 which both map to 0 1.55 + convert.f = aKey->mStops[i].offset; 1.56 + hash = AddToHash(hash, convert.f ? convert.u : 0); 1.57 + } 1.58 + return hash; 1.59 + } 1.60 + 1.61 + bool KeyEquals(KeyTypePointer aKey) const 1.62 + { 1.63 + bool sameStops = true; 1.64 + if (aKey->mStops.Length() != mStops.Length()) { 1.65 + sameStops = false; 1.66 + } else { 1.67 + for (uint32_t i = 0; i < mStops.Length(); i++) { 1.68 + if (mStops[i].color.ToABGR() != aKey->mStops[i].color.ToABGR() || 1.69 + mStops[i].offset != aKey->mStops[i].offset) { 1.70 + sameStops = false; 1.71 + break; 1.72 + } 1.73 + } 1.74 + } 1.75 + 1.76 + return sameStops && 1.77 + (aKey->mBackendType == mBackendType) && 1.78 + (aKey->mExtend == mExtend); 1.79 + } 1.80 + static KeyTypePointer KeyToPointer(KeyType aKey) 1.81 + { 1.82 + return &aKey; 1.83 + } 1.84 +}; 1.85 + 1.86 +/** 1.87 + * This class is what is cached. It need to be allocated in an object separated 1.88 + * to the cache entry to be able to be tracked by the nsExpirationTracker. 1.89 + * */ 1.90 +struct GradientCacheData { 1.91 + GradientCacheData(GradientStops* aStops, const GradientCacheKey& aKey) 1.92 + : mStops(aStops), 1.93 + mKey(aKey) 1.94 + {} 1.95 + 1.96 + GradientCacheData(const GradientCacheData& aOther) 1.97 + : mStops(aOther.mStops), 1.98 + mKey(aOther.mKey) 1.99 + { } 1.100 + 1.101 + nsExpirationState *GetExpirationState() { 1.102 + return &mExpirationState; 1.103 + } 1.104 + 1.105 + nsExpirationState mExpirationState; 1.106 + const RefPtr<GradientStops> mStops; 1.107 + GradientCacheKey mKey; 1.108 +}; 1.109 + 1.110 +/** 1.111 + * This class implements a cache with no maximum size, that retains the 1.112 + * gfxPatterns used to draw the gradients. 1.113 + * 1.114 + * The key is the nsStyleGradient that defines the gradient, and the size of the 1.115 + * gradient. 1.116 + * 1.117 + * The value is the gfxPattern, and whether or not we perform an optimization 1.118 + * based on the actual gradient property. 1.119 + * 1.120 + * An entry stays in the cache as long as it is used often. As long as a cache 1.121 + * entry is in the cache, all the references it has are guaranteed to be valid: 1.122 + * the nsStyleRect for the key, the gfxPattern for the value. 1.123 + */ 1.124 +class GradientCache MOZ_FINAL : public nsExpirationTracker<GradientCacheData,4> 1.125 +{ 1.126 + public: 1.127 + GradientCache() 1.128 + : nsExpirationTracker<GradientCacheData, 4>(MAX_GENERATION_MS) 1.129 + { 1.130 + srand(time(nullptr)); 1.131 + mTimerPeriod = rand() % MAX_GENERATION_MS + 1; 1.132 + Telemetry::Accumulate(Telemetry::GRADIENT_RETENTION_TIME, mTimerPeriod); 1.133 + } 1.134 + 1.135 + virtual void NotifyExpired(GradientCacheData* aObject) 1.136 + { 1.137 + // This will free the gfxPattern. 1.138 + RemoveObject(aObject); 1.139 + mHashEntries.Remove(aObject->mKey); 1.140 + } 1.141 + 1.142 + GradientCacheData* Lookup(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType) 1.143 + { 1.144 + GradientCacheData* gradient = 1.145 + mHashEntries.Get(GradientCacheKey(aStops, aExtend, aBackendType)); 1.146 + 1.147 + if (gradient) { 1.148 + MarkUsed(gradient); 1.149 + } 1.150 + 1.151 + return gradient; 1.152 + } 1.153 + 1.154 + // Returns true if we successfully register the gradient in the cache, false 1.155 + // otherwise. 1.156 + bool RegisterEntry(GradientCacheData* aValue) 1.157 + { 1.158 + nsresult rv = AddObject(aValue); 1.159 + if (NS_FAILED(rv)) { 1.160 + // We are OOM, and we cannot track this object. We don't want stall 1.161 + // entries in the hash table (since the expiration tracker is responsible 1.162 + // for removing the cache entries), so we avoid putting that entry in the 1.163 + // table, which is a good things considering we are short on memory 1.164 + // anyway, we probably don't want to retain things. 1.165 + return false; 1.166 + } 1.167 + mHashEntries.Put(aValue->mKey, aValue); 1.168 + return true; 1.169 + } 1.170 + 1.171 + protected: 1.172 + uint32_t mTimerPeriod; 1.173 + static const uint32_t MAX_GENERATION_MS = 10000; 1.174 + /** 1.175 + * FIXME use nsTHashtable to avoid duplicating the GradientCacheKey. 1.176 + * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47 1.177 + */ 1.178 + nsClassHashtable<GradientCacheKey, GradientCacheData> mHashEntries; 1.179 +}; 1.180 + 1.181 +static GradientCache* gGradientCache = nullptr; 1.182 + 1.183 +GradientStops * 1.184 +gfxGradientCache::GetGradientStops(DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend) 1.185 +{ 1.186 + if (!gGradientCache) { 1.187 + gGradientCache = new GradientCache(); 1.188 + } 1.189 + GradientCacheData* cached = gGradientCache->Lookup(aStops, aExtend, aDT->GetType()); 1.190 + return cached ? cached->mStops : nullptr; 1.191 +} 1.192 + 1.193 +GradientStops * 1.194 +gfxGradientCache::GetOrCreateGradientStops(DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend) 1.195 +{ 1.196 + RefPtr<GradientStops> gs = GetGradientStops(aDT, aStops, aExtend); 1.197 + if (!gs) { 1.198 + gs = aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), aExtend); 1.199 + if (!gs) { 1.200 + return nullptr; 1.201 + } 1.202 + GradientCacheData *cached = new GradientCacheData(gs, GradientCacheKey(aStops, aExtend, aDT->GetType())); 1.203 + if (!gGradientCache->RegisterEntry(cached)) { 1.204 + delete cached; 1.205 + } 1.206 + } 1.207 + return gs; 1.208 +} 1.209 + 1.210 +void 1.211 +gfxGradientCache::Shutdown() 1.212 +{ 1.213 + delete gGradientCache; 1.214 + gGradientCache = nullptr; 1.215 +} 1.216 + 1.217 +} 1.218 +}