Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/gfx/thebes/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 9 kB image not shown  

Quelle  gfxGradientCache.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "gfxGradientCache.h"

#include "MainThreadUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/DataMutex.h"
#include "nsTArray.h"
#include "PLDHashTable.h"
#include "nsExpirationTracker.h"
#include "nsClassHashtable.h"
#include <time.h>

namespace mozilla {
namespace gfx {

using namespace mozilla;

struct GradientCacheKey : public PLDHashEntryHdr {
  typedef const GradientCacheKey& KeyType;
  typedef const GradientCacheKey* KeyTypePointer;
  enum { ALLOW_MEMMOVE = true };
  const CopyableTArray<GradientStop> mStops;
  ExtendMode mExtend;
  BackendType mBackendType;

  GradientCacheKey(const nsTArray<GradientStop>& aStops, ExtendMode aExtend,
                   BackendType aBackendType)
      : mStops(aStops), mExtend(aExtend), mBackendType(aBackendType) {}

  explicit GradientCacheKey(const GradientCacheKey* aOther)
      : mStops(aOther->mStops),
        mExtend(aOther->mExtend),
        mBackendType(aOther->mBackendType) {}

  GradientCacheKey(GradientCacheKey&& aOther) = default;

  union FloatUint32 {
    float f;
    uint32_t u;
  };

  static PLDHashNumber HashKey(const KeyTypePointer aKey) {
    PLDHashNumber hash = 0;
    FloatUint32 convert;
    hash = AddToHash(hash, int(aKey->mBackendType));
    hash = AddToHash(hash, int(aKey->mExtend));
    for (uint32_t i = 0; i < aKey->mStops.Length(); i++) {
      hash = AddToHash(hash, aKey->mStops[i].color.ToABGR());
      // Use the float bits as hash, except for the cases of 0.0 and -0.0 which
      // both map to 0
      convert.f = aKey->mStops[i].offset;
      hash = AddToHash(hash, convert.f ? convert.u : 0);
    }
    return hash;
  }

  bool KeyEquals(KeyTypePointer aKey) const {
    bool sameStops = true;
    if (aKey->mStops.Length() != mStops.Length()) {
      sameStops = false;
    } else {
      for (uint32_t i = 0; i < mStops.Length(); i++) {
        if (mStops[i].color.ToABGR() != aKey->mStops[i].color.ToABGR() ||
            mStops[i].offset != aKey->mStops[i].offset) {
          sameStops = false;
          break;
        }
      }
    }

    return sameStops && (aKey->mBackendType == mBackendType) &&
           (aKey->mExtend == mExtend);
  }
  static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
};

/**
 * This class is what is cached. It need to be allocated in an object separated
 * to the cache entry to be able to be tracked by the nsExpirationTracker.
 * */

struct GradientCacheData {
  GradientCacheData(GradientStops* aStops, GradientCacheKey&& aKey)
      : mStops(aStops), mKey(std::move(aKey)) {}

  GradientCacheData(GradientCacheData&& aOther) = default;

  nsExpirationState* GetExpirationState() { return &mExpirationState; }

  nsExpirationState mExpirationState;
  const RefPtr<GradientStops> mStops;
  GradientCacheKey mKey;
};

/**
 * This class implements a cache, that retains the GradientStops used to draw
 * the gradients.
 *
 * An entry stays in the cache as long as it is used often and we don't exceed
 * the maximum, in which case the most recently used will be kept.
 */

class GradientCache;
using GradientCacheMutex = StaticDataMutex<UniquePtr<GradientCache>>;
class MOZ_RAII LockedInstance {
 public:
  explicit LockedInstance(GradientCacheMutex& aDataMutex)
      : mAutoLock(aDataMutex.Lock()) {}
  UniquePtr<GradientCache>& operator->() const& { return mAutoLock.ref(); }
  UniquePtr<GradientCache>& operator->() const&& = delete;
  UniquePtr<GradientCache>& operator*() const& { return mAutoLock.ref(); }
  UniquePtr<GradientCache>& operator*() const&& = delete;
  explicit operator bool() const { return !!mAutoLock.ref(); }

 private:
  GradientCacheMutex::AutoLock mAutoLock;
};

class GradientCache final
    : public ExpirationTrackerImpl<GradientCacheData, 4, GradientCacheMutex,
                                   LockedInstance> {
 public:
  GradientCache()
      : ExpirationTrackerImpl<GradientCacheData, 4, GradientCacheMutex,
                              LockedInstance>(MAX_GENERATION_MS,
                                              "GradientCache") {}
  static bool EnsureInstance() {
    LockedInstance lockedInstance(sInstanceMutex);
    return EnsureInstanceLocked(lockedInstance);
  }

  static void DestroyInstance() {
    LockedInstance lockedInstance(sInstanceMutex);
    if (lockedInstance) {
      *lockedInstance = nullptr;
    }
  }

  static void AgeAllGenerations() {
    LockedInstance lockedInstance(sInstanceMutex);
    if (!lockedInstance) {
      return;
    }
    lockedInstance->AgeAllGenerationsLocked(lockedInstance);
    lockedInstance->NotifyHandlerEndLocked(lockedInstance);
  }

  template <typename CreateFunc>
  static already_AddRefed<GradientStops> LookupOrInsert(
      const GradientCacheKey& aKey, CreateFunc aCreateFunc) {
    uint32_t numberOfEntries;
    RefPtr<GradientStops> stops;
    {
      LockedInstance lockedInstance(sInstanceMutex);
      if (!EnsureInstanceLocked(lockedInstance)) {
        return aCreateFunc();
      }

      GradientCacheData* gradientData = lockedInstance->mHashEntries.Get(aKey);
      if (gradientData) {
        if (gradientData->mStops && gradientData->mStops->IsValid()) {
          lockedInstance->MarkUsedLocked(gradientData, lockedInstance);
          return do_AddRef(gradientData->mStops);
        }

        lockedInstance->NotifyExpiredLocked(gradientData, lockedInstance);
        lockedInstance->NotifyHandlerEndLocked(lockedInstance);
      }

      stops = aCreateFunc();
      if (!stops) {
        return nullptr;
      }

      auto data = MakeUnique<GradientCacheData>(stops, GradientCacheKey(&aKey));
      nsresult rv = lockedInstance->AddObjectLocked(data.get(), lockedInstance);
      if (NS_FAILED(rv)) {
        // We are OOM, and we cannot track this object. We don't want to store
        // entries in the hash table (since the expiration tracker is
        // responsible for removing the cache entries), so we avoid putting that
        // entry in the table, which is a good thing considering we are short on
        // memory anyway, we probably don't want to retain things.
        return stops.forget();
      }
      lockedInstance->mHashEntries.InsertOrUpdate(aKey, std::move(data));
      numberOfEntries = lockedInstance->mHashEntries.Count();
    }

    if (numberOfEntries > MAX_ENTRIES) {
      // We have too many entries force the cache to age a generation.
      NS_DispatchToMainThread(
          NS_NewRunnableFunction("GradientCache::OnMaxEntriesBreached", [] {
            LockedInstance lockedInstance(sInstanceMutex);
            if (!lockedInstance) {
              return;
            }
            lockedInstance->AgeOneGenerationLocked(lockedInstance);
            lockedInstance->NotifyHandlerEndLocked(lockedInstance);
          }));
    }

    return stops.forget();
  }

  GradientCacheMutex& GetMutex() final { return sInstanceMutex; }

  void NotifyExpiredLocked(GradientCacheData* aObject,
                           const LockedInstance& aLockedInstance) final {
    // Remove the gradient from the tracker.
    RemoveObjectLocked(aObject, aLockedInstance);

    // If entry exists move the data to mRemovedGradientData because we want to
    // drop it outside of the lock.
    Maybe<UniquePtr<GradientCacheData>> gradientData =
        mHashEntries.Extract(aObject->mKey);
    if (gradientData.isSome()) {
      mRemovedGradientData.AppendElement(std::move(*gradientData));
    }
  }

  void NotifyHandlerEndLocked(const LockedInstance&) final {
    NS_DispatchToMainThread(
        NS_NewRunnableFunction("GradientCache::DestroyRemovedGradientStops",
                               [stops = std::move(mRemovedGradientData)] {}));
  }

 private:
  static const uint32_t MAX_GENERATION_MS = 10000;

  // On Windows some of the Direct2D objects associated with the gradient stops
  // can be quite large, so we limit the number of cache entries.
  static const uint32_t MAX_ENTRIES = 4000;
  static GradientCacheMutex sInstanceMutex;

  [[nodiscard]] static bool EnsureInstanceLocked(
      LockedInstance& aLockedInstance) {
    if (!aLockedInstance) {
      // GradientCache must be created on the main thread.
      if (!NS_IsMainThread()) {
        // This should only happen at shutdown, we fall back to not caching.
        return false;
      }
      *aLockedInstance = MakeUnique<GradientCache>();
    }
    return true;
  }

  /**
   * FIXME use nsTHashtable to avoid duplicating the GradientCacheKey.
   * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47
   */

  nsClassHashtable<GradientCacheKey, GradientCacheData> mHashEntries;
  nsTArray<UniquePtr<GradientCacheData>> mRemovedGradientData;
};

MOZ_RUNINIT GradientCacheMutex GradientCache::sInstanceMutex("GradientCache");

void gfxGradientCache::Init() {
  MOZ_RELEASE_ASSERT(GradientCache::EnsureInstance(),
                     "First call must be on main thread.");
}

already_AddRefed<GradientStops> gfxGradientCache::GetOrCreateGradientStops(
    const DrawTarget* aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend) {
  if (aDT->IsRecording()) {
    return aDT->CreateGradientStops(aStops.Elements(), aStops.Length(),
                                    aExtend);
  }

  return GradientCache::LookupOrInsert(
      GradientCacheKey(aStops, aExtend, aDT->GetBackendType()),
      [&]() -> already_AddRefed<GradientStops> {
        return aDT->CreateGradientStops(aStops.Elements(), aStops.Length(),
                                        aExtend);
      });
}

void gfxGradientCache::PurgeAllCaches() { GradientCache::AgeAllGenerations(); }

void gfxGradientCache::Shutdown() { GradientCache::DestroyInstance(); }

}  // namespace gfx
}  // namespace mozilla

97%


¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.