Reland "Make GPU cache invalidation SkMessageBus messages go to one GrContext."

This is a reland of f4c5bb9aba485aa47c27b15905d81992b7cf4707

Original change's description:
> Make GPU cache invalidation SkMessageBus messages go to one GrContext.
> 
> Makes it so the template param to SkMessageBus must implement:
> bool shouldSend(uint32_t inboxID) const
> 
> Updates all GPU backend message types to only go to the GrContext that
> is adding a cache entry.
> 
> Bug: skia:
> Change-Id: I3e8a4eb90654b7b8ac57cac9fb508c0ef1d51058
> Reviewed-on: https://skia-review.googlesource.com/140220
> Reviewed-by: Robert Phillips <robertphillips@google.com>
> Reviewed-by: Jim Van Verth <jvanverth@google.com>

Bug: skia:
Change-Id: I8402bfe3ed0170c99936d47050458817030b473b
Reviewed-on: https://skia-review.googlesource.com/140801
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/include/gpu/GrResourceKey.h b/include/gpu/GrResourceKey.h
index 90b84d7..5e9bf1f 100644
--- a/include/gpu/GrResourceKey.h
+++ b/include/gpu/GrResourceKey.h
@@ -8,13 +8,13 @@
 #ifndef GrResourceKey_DEFINED
 #define GrResourceKey_DEFINED
 
+#include <new>
 #include "../private/SkOnce.h"
 #include "../private/SkTemplates.h"
 #include "../private/SkTo.h"
 #include "GrTypes.h"
 #include "SkData.h"
 #include "SkString.h"
-#include <new>
 
 uint32_t GrResourceKeyHash(const uint32_t* data, size_t size);
 
@@ -315,18 +315,22 @@
 // The cache listens for these messages to purge junk resources proactively.
 class GrUniqueKeyInvalidatedMessage {
 public:
-    explicit GrUniqueKeyInvalidatedMessage(const GrUniqueKey& key) : fKey(key) {}
-
-    GrUniqueKeyInvalidatedMessage(const GrUniqueKeyInvalidatedMessage& that) : fKey(that.fKey) {}
-
-    GrUniqueKeyInvalidatedMessage& operator=(const GrUniqueKeyInvalidatedMessage& that) {
-        fKey = that.fKey;
-        return *this;
+    GrUniqueKeyInvalidatedMessage(const GrUniqueKey& key, uint32_t contextUniqueID)
+            : fKey(key), fContextID(contextUniqueID) {
+        SkASSERT(SK_InvalidUniqueID != contextUniqueID);
     }
 
+    GrUniqueKeyInvalidatedMessage(const GrUniqueKeyInvalidatedMessage&) = default;
+
+    GrUniqueKeyInvalidatedMessage& operator=(const GrUniqueKeyInvalidatedMessage&) = default;
+
     const GrUniqueKey& key() const { return fKey; }
 
+    bool shouldSend(uint32_t inboxID) const { return fContextID == inboxID; }
+
 private:
     GrUniqueKey fKey;
+    uint32_t fContextID;
 };
+
 #endif
diff --git a/include/private/SkMessageBus.h b/include/private/SkMessageBus.h
index 19e9375..418235d 100644
--- a/include/private/SkMessageBus.h
+++ b/include/private/SkMessageBus.h
@@ -14,13 +14,17 @@
 #include "SkTDArray.h"
 #include "SkTypes.h"
 
+/**
+ * Message must implement bool Message::shouldSend(uint32_t inboxID) const. Perhaps someday we
+ * can use std::experimental::is_detected to avoid this requirement by sending to all inboxes when
+ * the method is not detected on Message.
+ */
 template <typename Message>
 class SkMessageBus : SkNoncopyable {
 public:
-    // Post a message to be received by Inboxes for this Message type.  Threadsafe.
-    // If id is SK_InvalidUniqueID then it will be sent to all inboxes.
-    // Otherwise it will be sent to the inbox with that id.
-    static void Post(const Message& m, uint32_t destID = SK_InvalidUniqueID);
+    // Post a message to be received by Inboxes for this Message type. Checks
+    // Message::shouldSend() for each inbox. Threadsafe.
+    static void Post(const Message& m);
 
     class Inbox {
     public:
@@ -102,11 +106,11 @@
 SkMessageBus<Message>::SkMessageBus() {}
 
 template <typename Message>
-/*static*/ void SkMessageBus<Message>::Post(const Message& m, uint32_t destID) {
+/*static*/ void SkMessageBus<Message>::Post(const Message& m) {
     SkMessageBus<Message>* bus = SkMessageBus<Message>::Get();
     SkAutoMutexAcquire lock(bus->fInboxesMutex);
     for (int i = 0; i < bus->fInboxes.count(); i++) {
-        if (SK_InvalidUniqueID == destID || bus->fInboxes[i]->fUniqueID == destID) {
+        if (m.shouldSend(bus->fInboxes[i]->fUniqueID)) {
             bus->fInboxes[i]->receive(m);
         }
     }
diff --git a/src/core/SkResourceCache.h b/src/core/SkResourceCache.h
index 6087be7..4794669 100644
--- a/src/core/SkResourceCache.h
+++ b/src/core/SkResourceCache.h
@@ -112,7 +112,9 @@
     // Used with SkMessageBus
     struct PurgeSharedIDMessage {
         PurgeSharedIDMessage(uint64_t sharedID) : fSharedID(sharedID) {}
-
+        // SkResourceCache is typically used as a singleton and we don't label Inboxes so all
+        // messages go to all inboxes.
+        bool shouldSend(uint32_t inboxID) const { return true; }
         uint64_t    fSharedID;
     };
 
diff --git a/src/gpu/GrBitmapTextureMaker.cpp b/src/gpu/GrBitmapTextureMaker.cpp
index d2a1fbe..7a70587 100644
--- a/src/gpu/GrBitmapTextureMaker.cpp
+++ b/src/gpu/GrBitmapTextureMaker.cpp
@@ -61,7 +61,8 @@
             if (!willBeMipped || GrMipMapped::kYes == proxy->mipMapped()) {
                 SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin);
                 if (fOriginalKey.isValid()) {
-                    GrInstallBitmapUniqueKeyInvalidator(fOriginalKey, fBitmap.pixelRef());
+                    GrInstallBitmapUniqueKeyInvalidator(
+                            fOriginalKey, proxyProvider->contextUniqueID(), fBitmap.pixelRef());
                 }
                 return proxy;
             }
@@ -85,7 +86,8 @@
                 // time it too will be deleted or recycled.
                 proxyProvider->removeUniqueKeyFromProxy(fOriginalKey, proxy.get());
                 proxyProvider->assignUniqueKeyToProxy(fOriginalKey, mippedProxy.get());
-                GrInstallBitmapUniqueKeyInvalidator(fOriginalKey, fBitmap.pixelRef());
+                GrInstallBitmapUniqueKeyInvalidator(fOriginalKey, proxyProvider->contextUniqueID(),
+                                                    fBitmap.pixelRef());
             }
             return mippedProxy;
         }
@@ -105,8 +107,8 @@
     }
 }
 
-void GrBitmapTextureMaker::didCacheCopy(const GrUniqueKey& copyKey) {
-    GrInstallBitmapUniqueKeyInvalidator(copyKey, fBitmap.pixelRef());
+void GrBitmapTextureMaker::didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) {
+    GrInstallBitmapUniqueKeyInvalidator(copyKey, contextUniqueID, fBitmap.pixelRef());
 }
 
 SkAlphaType GrBitmapTextureMaker::alphaType() const {
diff --git a/src/gpu/GrBitmapTextureMaker.h b/src/gpu/GrBitmapTextureMaker.h
index f945cbc..22d8ca3 100644
--- a/src/gpu/GrBitmapTextureMaker.h
+++ b/src/gpu/GrBitmapTextureMaker.h
@@ -26,7 +26,7 @@
     void makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey,
                      SkColorSpace* dstColorSpace) override;
 
-    void didCacheCopy(const GrUniqueKey& copyKey) override;
+    void didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) override;
 
     SkAlphaType alphaType() const override;
     sk_sp<SkColorSpace> getColorSpace(SkColorSpace* dstColorSpace) override;
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
index 8095286..7704d92 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -313,12 +313,13 @@
 }
 
 static void add_invalidate_on_pop_message(const SkClipStack& stack, uint32_t clipGenID,
-                                          const GrUniqueKey& clipMaskKey) {
+                                          const GrUniqueKey& clipMaskKey,
+                                          uint32_t contextUniqueID) {
     SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
     while (const Element* element = iter.prev()) {
         if (element->getGenID() == clipGenID) {
             std::unique_ptr<GrUniqueKeyInvalidatedMessage> msg(
-                    new GrUniqueKeyInvalidatedMessage(clipMaskKey));
+                    new GrUniqueKeyInvalidatedMessage(clipMaskKey, contextUniqueID));
             element->addResourceInvalidationMessage(std::move(msg));
             return;
         }
@@ -363,7 +364,7 @@
 
     SkASSERT(result->origin() == kTopLeft_GrSurfaceOrigin);
     proxyProvider->assignUniqueKeyToProxy(key, result.get());
-    add_invalidate_on_pop_message(*fStack, reducedClip.maskGenID(), key);
+    add_invalidate_on_pop_message(*fStack, reducedClip.maskGenID(), key, context->uniqueID());
 
     return result;
 }
@@ -504,6 +505,6 @@
 
     SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin);
     proxyProvider->assignUniqueKeyToProxy(key, proxy.get());
-    add_invalidate_on_pop_message(*fStack, reducedClip.maskGenID(), key);
+    add_invalidate_on_pop_message(*fStack, reducedClip.maskGenID(), key, context->uniqueID());
     return proxy;
 }
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 6cf5d05..dd4f321 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -85,10 +85,12 @@
         fResourceCache = new GrResourceCache(fCaps.get(), fUniqueID);
         fResourceProvider = new GrResourceProvider(fGpu.get(), fResourceCache, &fSingleOwner,
                                                    options.fExplicitlyAllocateGPUResources);
+        fProxyProvider =
+                new GrProxyProvider(fResourceProvider, fResourceCache, fCaps, &fSingleOwner);
+    } else {
+        fProxyProvider = new GrProxyProvider(this->uniqueID(), fCaps, &fSingleOwner);
     }
 
-    fProxyProvider = new GrProxyProvider(fResourceProvider, fResourceCache, fCaps, &fSingleOwner);
-
     if (fResourceCache) {
         fResourceCache->setProxyProvider(fProxyProvider);
     }
diff --git a/src/gpu/GrImageTextureMaker.cpp b/src/gpu/GrImageTextureMaker.cpp
index 7328116..a8e6216 100644
--- a/src/gpu/GrImageTextureMaker.cpp
+++ b/src/gpu/GrImageTextureMaker.cpp
@@ -42,7 +42,7 @@
     }
 }
 
-void GrImageTextureMaker::didCacheCopy(const GrUniqueKey& copyKey) {
+void GrImageTextureMaker::didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) {
     as_IB(fClient)->notifyAddedToCache();
 }
 
diff --git a/src/gpu/GrImageTextureMaker.h b/src/gpu/GrImageTextureMaker.h
index 1a72f68..1fe882a 100644
--- a/src/gpu/GrImageTextureMaker.h
+++ b/src/gpu/GrImageTextureMaker.h
@@ -29,7 +29,7 @@
 
     void makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey,
                      SkColorSpace* dstColorSpace) override;
-    void didCacheCopy(const GrUniqueKey& copyKey) override;
+    void didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) override;
 
     SkAlphaType alphaType() const override;
     sk_sp<SkColorSpace> getColorSpace(SkColorSpace* dstColorSpace) override;
diff --git a/src/gpu/GrOnFlushResourceProvider.cpp b/src/gpu/GrOnFlushResourceProvider.cpp
index ddf6602..26b59f9 100644
--- a/src/gpu/GrOnFlushResourceProvider.cpp
+++ b/src/gpu/GrOnFlushResourceProvider.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "GrOnFlushResourceProvider.h"
+#include "GrContext.h"
 #include "GrContextPriv.h"
 #include "GrDrawingManager.h"
 #include "GrProxyProvider.h"
@@ -130,6 +131,10 @@
     return buffer;
 }
 
+uint32_t GrOnFlushResourceProvider::contextUniqueID() const {
+    return fDrawingMgr->getContext()->uniqueID();
+}
+
 const GrCaps* GrOnFlushResourceProvider::caps() const {
     return fDrawingMgr->getContext()->contextPriv().caps();
 }
diff --git a/src/gpu/GrOnFlushResourceProvider.h b/src/gpu/GrOnFlushResourceProvider.h
index d01f134..c30ec61 100644
--- a/src/gpu/GrOnFlushResourceProvider.h
+++ b/src/gpu/GrOnFlushResourceProvider.h
@@ -8,7 +8,6 @@
 #ifndef GrOnFlushResourceProvider_DEFINED
 #define GrOnFlushResourceProvider_DEFINED
 
-#include "GrTypes.h"
 #include "GrDeferredUpload.h"
 #include "GrOpFlushState.h"
 #include "GrResourceProvider.h"
@@ -21,7 +20,6 @@
 class GrRenderTargetOpList;
 class GrRenderTargetContext;
 class GrSurfaceProxy;
-
 class SkColorSpace;
 class SkSurfaceProps;
 
@@ -31,7 +29,7 @@
  */
 class GrOnFlushCallbackObject {
 public:
-    virtual ~GrOnFlushCallbackObject() { }
+    virtual ~GrOnFlushCallbackObject() {}
 
     /*
      * The onFlush callback allows subsystems (e.g., text, path renderers) to create atlases
@@ -91,6 +89,7 @@
     sk_sp<const GrBuffer> findOrMakeStaticBuffer(GrBufferType, size_t, const void* data,
                                                  const GrUniqueKey&);
 
+    uint32_t contextUniqueID() const;
     const GrCaps* caps() const;
 
 private:
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index e259e34..df7b61e 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -37,11 +37,32 @@
         , fResourceCache(resourceCache)
         , fAbandoned(false)
         , fCaps(caps)
+        , fContextUniqueID(resourceCache->contextUniqueID())
 #ifdef SK_DEBUG
         , fSingleOwner(owner)
 #endif
 {
+    SkASSERT(fResourceProvider);
+    SkASSERT(fResourceCache);
+    SkASSERT(fCaps);
+    SkASSERT(fSingleOwner);
+}
 
+GrProxyProvider::GrProxyProvider(uint32_t contextUniqueID,
+                                 sk_sp<const GrCaps> caps,
+                                 GrSingleOwner* owner)
+        : fResourceProvider(nullptr)
+        , fResourceCache(nullptr)
+        , fAbandoned(false)
+        , fCaps(caps)
+        , fContextUniqueID(contextUniqueID)
+#ifdef SK_DEBUG
+        , fSingleOwner(owner)
+#endif
+{
+    SkASSERT(fContextUniqueID != SK_InvalidUniqueID);
+    SkASSERT(fCaps);
+    SkASSERT(fSingleOwner);
 }
 
 GrProxyProvider::~GrProxyProvider() {
diff --git a/src/gpu/GrProxyProvider.h b/src/gpu/GrProxyProvider.h
index 64b9ac4..89f9518 100644
--- a/src/gpu/GrProxyProvider.h
+++ b/src/gpu/GrProxyProvider.h
@@ -27,6 +27,7 @@
 class GrProxyProvider {
 public:
     GrProxyProvider(GrResourceProvider*, GrResourceCache*, sk_sp<const GrCaps>, GrSingleOwner*);
+    GrProxyProvider(uint32_t contextUniqueID, sk_sp<const GrCaps>, GrSingleOwner*);
 
     ~GrProxyProvider();
 
@@ -211,6 +212,7 @@
      */
     void processInvalidProxyUniqueKey(const GrUniqueKey&, GrTextureProxy*, bool invalidateSurface);
 
+    uint32_t contextUniqueID() const { return fContextUniqueID; }
     const GrCaps* caps() const { return fCaps.get(); }
     sk_sp<const GrCaps> refCaps() const { return fCaps; }
 
@@ -258,6 +260,8 @@
     GrResourceCache*       fResourceCache;
     bool                   fAbandoned;
     sk_sp<const GrCaps>    fCaps;
+    // If this provider is owned by a DDLContext then this is the DirectContext's ID.
+    uint32_t               fContextUniqueID;
 
     // In debug builds we guard against improper thread handling
     SkDEBUGCODE(mutable GrSingleOwner* fSingleOwner;)
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index ee0d183..376fe10 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -16,6 +16,7 @@
 #include "GrSurfaceContext.h"
 #include "GrTypesPriv.h"
 #include "GrXferProcessor.h"
+#include "SkCanvas.h"
 #include "SkRefCnt.h"
 #include "SkSurfaceProps.h"
 #include "text/GrTextUtils.h"
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index c4e94098..23c8e7c 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -63,27 +63,29 @@
 
  //////////////////////////////////////////////////////////////////////////////
 
-
 GrResourceCache::GrResourceCache(const GrCaps* caps, uint32_t contextUniqueID)
-    : fProxyProvider(nullptr)
-    , fTimestamp(0)
-    , fMaxCount(kDefaultMaxCount)
-    , fMaxBytes(kDefaultMaxSize)
-    , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
+        : fProxyProvider(nullptr)
+        , fTimestamp(0)
+        , fMaxCount(kDefaultMaxCount)
+        , fMaxBytes(kDefaultMaxSize)
+        , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
 #if GR_CACHE_STATS
-    , fHighWaterCount(0)
-    , fHighWaterBytes(0)
-    , fBudgetedHighWaterCount(0)
-    , fBudgetedHighWaterBytes(0)
+        , fHighWaterCount(0)
+        , fHighWaterBytes(0)
+        , fBudgetedHighWaterCount(0)
+        , fBudgetedHighWaterBytes(0)
 #endif
-    , fBytes(0)
-    , fBudgetedCount(0)
-    , fBudgetedBytes(0)
-    , fPurgeableBytes(0)
-    , fRequestFlush(false)
-    , fExternalFlushCnt(0)
-    , fContextUniqueID(contextUniqueID)
-    , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
+        , fBytes(0)
+        , fBudgetedCount(0)
+        , fBudgetedBytes(0)
+        , fPurgeableBytes(0)
+        , fRequestFlush(false)
+        , fExternalFlushCnt(0)
+        , fInvalidUniqueKeyInbox(contextUniqueID)
+        , fFreedGpuResourceInbox(contextUniqueID)
+        , fContextUniqueID(contextUniqueID)
+        , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
+    SkASSERT(contextUniqueID != SK_InvalidUniqueID);
     SkDEBUGCODE(fCount = 0;)
     SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
 }
@@ -604,9 +606,8 @@
     SkTArray<GrGpuResourceFreedMessage> msgs;
     fFreedGpuResourceInbox.poll(&msgs);
     for (int i = 0; i < msgs.count(); ++i) {
-        if (msgs[i].fOwningUniqueID == fContextUniqueID) {
-            msgs[i].fResource->unref();
-        }
+        SkASSERT(msgs[i].fOwningUniqueID == fContextUniqueID);
+        msgs[i].fResource->unref();
     }
 }
 
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index 771b13f..507c192 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -27,6 +27,10 @@
 struct GrGpuResourceFreedMessage {
     GrGpuResource* fResource;
     uint32_t fOwningUniqueID;
+    bool shouldSend(uint32_t inboxID) const {
+        // The inbox's ID is the unique ID of the owning GrContext.
+        return inboxID == fOwningUniqueID;
+    }
 };
 
 /**
@@ -67,6 +71,9 @@
     class ResourceAccess;
     ResourceAccess resourceAccess();
 
+    /** Unique ID of the owning GrContext. */
+    uint32_t contextUniqueID() const { return fContextUniqueID; }
+
     /**
      * Sets the cache limits in terms of number of resources, max gpu memory byte size, and number
      * of external GrContext flushes that a resource can be unused before it is evicted. The latter
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index 35c3682..347a5de 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -251,6 +251,7 @@
         fGpu = nullptr;
     }
 
+    uint32_t contextUniqueID() const { return fCache->contextUniqueID(); }
     const GrCaps* caps() const { return fCaps.get(); }
     bool overBudget() const { return fCache->overBudget(); }
 
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
index ee52122..213dbdb 100644
--- a/src/gpu/GrSoftwarePathRenderer.cpp
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -216,7 +216,9 @@
 // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
 class PathInvalidator : public SkPathRef::GenIDChangeListener {
 public:
-    explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
+    PathInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID)
+            : fMsg(key, contextUniqueID) {}
+
 private:
     GrUniqueKeyInvalidatedMessage fMsg;
 
@@ -366,7 +368,8 @@
         if (useCache) {
             SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin);
             fProxyProvider->assignUniqueKeyToProxy(maskKey, proxy.get());
-            args.fShape->addGenIDChangeListener(sk_make_sp<PathInvalidator>(maskKey));
+            args.fShape->addGenIDChangeListener(
+                    sk_make_sp<PathInvalidator>(maskKey, args.fContext->uniqueID()));
         }
     }
     if (inverseFilled) {
diff --git a/src/gpu/GrTextureAdjuster.cpp b/src/gpu/GrTextureAdjuster.cpp
index 4b21bac..d016896 100644
--- a/src/gpu/GrTextureAdjuster.cpp
+++ b/src/gpu/GrTextureAdjuster.cpp
@@ -33,7 +33,7 @@
     MakeCopyKeyFromOrigKey(baseKey, params, copyKey);
 }
 
-void GrTextureAdjuster::didCacheCopy(const GrUniqueKey& copyKey) {
+void GrTextureAdjuster::didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) {
     // We don't currently have a mechanism for notifications on Images!
 }
 
@@ -67,7 +67,7 @@
                 proxyProvider->removeUniqueKeyFromProxy(key, cachedCopy.get());
             }
             proxyProvider->assignUniqueKeyToProxy(key, copy.get());
-            this->didCacheCopy(key);
+            this->didCacheCopy(key, proxyProvider->contextUniqueID());
         }
     }
     return copy;
diff --git a/src/gpu/GrTextureAdjuster.h b/src/gpu/GrTextureAdjuster.h
index 885e390..e43e87b 100644
--- a/src/gpu/GrTextureAdjuster.h
+++ b/src/gpu/GrTextureAdjuster.h
@@ -37,7 +37,7 @@
     SkAlphaType alphaType() const override { return fAlphaType; }
     void makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey,
                      SkColorSpace* dstColorSpace) override;
-    void didCacheCopy(const GrUniqueKey& copyKey) override;
+    void didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) override;
 
     GrTextureProxy* originalProxy() const { return fOriginal.get(); }
     sk_sp<GrTextureProxy> originalProxyRef() const { return fOriginal; }
diff --git a/src/gpu/GrTextureMaker.cpp b/src/gpu/GrTextureMaker.cpp
index f9ee758..22870cc 100644
--- a/src/gpu/GrTextureMaker.cpp
+++ b/src/gpu/GrTextureMaker.cpp
@@ -109,7 +109,7 @@
             proxyProvider->removeUniqueKeyFromProxy(copyKey, cachedProxy.get());
         }
         proxyProvider->assignUniqueKeyToProxy(copyKey, result.get());
-        this->didCacheCopy(copyKey);
+        this->didCacheCopy(copyKey, proxyProvider->contextUniqueID());
     }
     return result;
 }
diff --git a/src/gpu/GrTextureProducer.h b/src/gpu/GrTextureProducer.h
index c458d4f..176247b 100644
--- a/src/gpu/GrTextureProducer.h
+++ b/src/gpu/GrTextureProducer.h
@@ -158,8 +158,7 @@
     *  makeCopyKey() returns true). In that case, the maker is notified in case it
     *  wants to note that for when the maker is destroyed.
     */
-    virtual void didCacheCopy(const GrUniqueKey& copyKey) = 0;
-
+    virtual void didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) = 0;
 
     enum DomainMode {
         kNoDomain_DomainMode,
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index ff2b984..5baf0d4 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -88,17 +88,20 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void GrInstallBitmapUniqueKeyInvalidator(const GrUniqueKey& key, SkPixelRef* pixelRef) {
+void GrInstallBitmapUniqueKeyInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID,
+                                         SkPixelRef* pixelRef) {
     class Invalidator : public SkPixelRef::GenIDChangeListener {
     public:
-        explicit Invalidator(const GrUniqueKey& key) : fMsg(key) {}
+        explicit Invalidator(const GrUniqueKey& key, uint32_t contextUniqueID)
+                : fMsg(key, contextUniqueID) {}
+
     private:
         GrUniqueKeyInvalidatedMessage fMsg;
 
         void onChange() override { SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg); }
     };
 
-    pixelRef->addGenIDChangeListener(new Invalidator(key));
+    pixelRef->addGenIDChangeListener(new Invalidator(key, contextUniqueID));
 }
 
 sk_sp<GrTextureProxy> GrCopyBaseMipMapToTextureProxy(GrContext* ctx, GrTextureProxy* baseProxy) {
@@ -199,7 +202,8 @@
             // When recording DDLs we do not want to install change listeners because doing
             // so isn't threadsafe.
             if (bm && !proxyProvider->recordingDDL()) {
-                GrInstallBitmapUniqueKeyInvalidator(originalKey, bm->pixelRef());
+                GrInstallBitmapUniqueKeyInvalidator(originalKey, proxyProvider->contextUniqueID(),
+                                                    bm->pixelRef());
             }
         }
     }
diff --git a/src/gpu/SkGr.h b/src/gpu/SkGr.h
index a2ac670..ec9f30a 100644
--- a/src/gpu/SkGr.h
+++ b/src/gpu/SkGr.h
@@ -243,6 +243,7 @@
 
 /** Call this after installing a GrUniqueKey on texture. It will cause the texture's key to be
     removed should the bitmap's contents change or be destroyed. */
-void GrInstallBitmapUniqueKeyInvalidator(const GrUniqueKey& key, SkPixelRef* pixelRef);
+void GrInstallBitmapUniqueKeyInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID,
+                                         SkPixelRef* pixelRef);
 
 #endif
diff --git a/src/gpu/ccpr/GrCCDrawPathsOp.cpp b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
index 2d250de..016756a 100644
--- a/src/gpu/ccpr/GrCCDrawPathsOp.cpp
+++ b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
@@ -251,7 +251,8 @@
                 GrCCAtlas* atlas =
                         resources->copyPathToCachedAtlas(*cacheEntry, doEvenOddFill, &newOffset);
                 cacheEntry->updateToCachedAtlas(atlas->getOrAssignUniqueKey(onFlushRP),
-                                                newOffset, atlas->refOrMakeCachedAtlasInfo());
+                                                onFlushRP->contextUniqueID(), newOffset,
+                                                atlas->refOrMakeCachedAtlasInfo());
                 this->recordInstance(atlas->textureProxy(), resources->nextPathInstanceIdx());
                 resources->appendDrawPathInstance().set(*cacheEntry, draw.fCachedMaskShift,
                                                         draw.fColor);
@@ -296,8 +297,9 @@
 
                 const GrUniqueKey& atlasKey =
                         resources->nextAtlasToStash()->getOrAssignUniqueKey(onFlushRP);
-                cacheEntry->initAsStashedAtlas(atlasKey, devToAtlasOffset, devBounds, devBounds45,
-                                               devIBounds, draw.fCachedMaskShift);
+                cacheEntry->initAsStashedAtlas(atlasKey, onFlushRP->contextUniqueID(),
+                                               devToAtlasOffset, devBounds, devBounds45, devIBounds,
+                                               draw.fCachedMaskShift);
                 // Remember this atlas in case we encounter the path again during the same flush.
                 cacheEntry->setCurrFlushAtlas(atlas);
             }
diff --git a/src/gpu/ccpr/GrCCPathCache.cpp b/src/gpu/ccpr/GrCCPathCache.cpp
index b22b1dd..d7dc714 100644
--- a/src/gpu/ccpr/GrCCPathCache.cpp
+++ b/src/gpu/ccpr/GrCCPathCache.cpp
@@ -149,13 +149,16 @@
     this->invalidateAtlas();
 }
 
-void GrCCPathCacheEntry::initAsStashedAtlas(const GrUniqueKey& atlasKey,
+void GrCCPathCacheEntry::initAsStashedAtlas(const GrUniqueKey& atlasKey, uint32_t contextUniqueID,
                                             const SkIVector& atlasOffset, const SkRect& devBounds,
                                             const SkRect& devBounds45, const SkIRect& devIBounds,
                                             const SkIVector& maskShift) {
+    SkASSERT(contextUniqueID != SK_InvalidUniqueID);
     SkASSERT(atlasKey.isValid());
     SkASSERT(!fCurrFlushAtlas);  // Otherwise we should reuse the atlas from last time.
 
+    fContextUniqueID = contextUniqueID;
+
     fAtlasKey = atlasKey;
     fAtlasOffset = atlasOffset + maskShift;
     SkASSERT(!fCachedAtlasInfo);  // Otherwise they should have reused the cached atlas instead.
@@ -166,12 +169,15 @@
     fDevIBounds = devIBounds.makeOffset(-maskShift.fX, -maskShift.fY);
 }
 
-void GrCCPathCacheEntry::updateToCachedAtlas(const GrUniqueKey& atlasKey,
+void GrCCPathCacheEntry::updateToCachedAtlas(const GrUniqueKey& atlasKey, uint32_t contextUniqueID,
                                              const SkIVector& newAtlasOffset,
                                              sk_sp<GrCCAtlas::CachedAtlasInfo> info) {
+    SkASSERT(contextUniqueID != SK_InvalidUniqueID);
     SkASSERT(atlasKey.isValid());
     SkASSERT(!fCurrFlushAtlas);  // Otherwise we should reuse the atlas from last time.
 
+    fContextUniqueID = contextUniqueID;
+
     fAtlasKey = atlasKey;
     fAtlasOffset = newAtlasOffset;
 
@@ -188,7 +194,7 @@
             fCachedAtlasInfo->fNumInvalidatedPathPixels >= fCachedAtlasInfo->fNumPathPixels / 2) {
             // Too many invalidated pixels: purge the atlas texture from the resource cache.
             SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(
-                    GrUniqueKeyInvalidatedMessage(fAtlasKey));
+                    GrUniqueKeyInvalidatedMessage(fAtlasKey, fContextUniqueID));
             fCachedAtlasInfo->fIsPurgedFromResourceCache = true;
         }
     }
diff --git a/src/gpu/ccpr/GrCCPathCache.h b/src/gpu/ccpr/GrCCPathCache.h
index 67e7d9f..7a552bb 100644
--- a/src/gpu/ccpr/GrCCPathCache.h
+++ b/src/gpu/ccpr/GrCCPathCache.h
@@ -121,14 +121,15 @@
     // Called once our path has been rendered into the mainline CCPR (fp16, coverage count) atlas.
     // The caller will stash this atlas texture away after drawing, and during the next flush,
     // recover it and attempt to copy any paths that got reused into permanent 8-bit atlases.
-    void initAsStashedAtlas(const GrUniqueKey& atlasKey, const SkIVector& atlasOffset,
-                            const SkRect& devBounds, const SkRect& devBounds45,
-                            const SkIRect& devIBounds, const SkIVector& maskShift);
+    void initAsStashedAtlas(const GrUniqueKey& atlasKey, uint32_t contextUniqueID,
+                            const SkIVector& atlasOffset, const SkRect& devBounds,
+                            const SkRect& devBounds45, const SkIRect& devIBounds,
+                            const SkIVector& maskShift);
 
     // Called once our path mask has been copied into a permanent, 8-bit atlas. This method points
     // the entry at the new atlas and updates the CachedAtlasInfo data.
-    void updateToCachedAtlas(const GrUniqueKey& atlasKey, const SkIVector& newAtlasOffset,
-                             sk_sp<GrCCAtlas::CachedAtlasInfo>);
+    void updateToCachedAtlas(const GrUniqueKey& atlasKey, uint32_t contextUniqueID,
+                             const SkIVector& newAtlasOffset, sk_sp<GrCCAtlas::CachedAtlasInfo>);
 
     const GrUniqueKey& atlasKey() const { return fAtlasKey; }
 
@@ -162,6 +163,7 @@
     // Called when our corresponding path is modified or deleted.
     void onChange() override;
 
+    uint32_t fContextUniqueID;
     GrCCPathCache* fCacheWeakPtr;  // Gets manually reset to null by the path cache upon eviction.
     MaskTransform fMaskTransform;
     int fHitCount = 1;
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index ddc29f1..449fd53 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -170,6 +170,7 @@
     virtual const GrXferProcessor::DstProxy& dstProxy() const = 0;
 
     virtual GrResourceProvider* resourceProvider() const = 0;
+    uint32_t contextUniqueID() const { return this->resourceProvider()->contextUniqueID(); }
 
     virtual GrGlyphCache* glyphCache() const = 0;
     virtual GrAtlasManager* atlasManager() const = 0;
diff --git a/src/gpu/ops/GrTessellatingPathRenderer.cpp b/src/gpu/ops/GrTessellatingPathRenderer.cpp
index 1c0a7f5..475197e 100644
--- a/src/gpu/ops/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/ops/GrTessellatingPathRenderer.cpp
@@ -42,7 +42,9 @@
 // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
 class PathInvalidator : public SkPathRef::GenIDChangeListener {
 public:
-    explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
+    PathInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID)
+            : fMsg(key, contextUniqueID) {}
+
 private:
     GrUniqueKeyInvalidatedMessage fMsg;
 
@@ -284,7 +286,7 @@
         info.fCount = count;
         key.setCustomData(SkData::MakeWithCopy(&info, sizeof(info)));
         rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
-        fShape.addGenIDChangeListener(sk_make_sp<PathInvalidator>(key));
+        fShape.addGenIDChangeListener(sk_make_sp<PathInvalidator>(key, target->contextUniqueID()));
     }
 
     void drawAA(Target* target, const GrGeometryProcessor* gp, size_t vertexStride) {
diff --git a/src/gpu/text/GrTextBlobCache.cpp b/src/gpu/text/GrTextBlobCache.cpp
index 71d9052..a60903f 100644
--- a/src/gpu/text/GrTextBlobCache.cpp
+++ b/src/gpu/text/GrTextBlobCache.cpp
@@ -30,7 +30,7 @@
 
 void GrTextBlobCache::PostPurgeBlobMessage(uint32_t blobID, uint32_t cacheID) {
     SkASSERT(blobID != SK_InvalidGenID);
-    SkMessageBus<PurgeBlobMessage>::Post(PurgeBlobMessage({blobID}), cacheID);
+    SkMessageBus<PurgeBlobMessage>::Post(PurgeBlobMessage(blobID, cacheID));
 }
 
 void GrTextBlobCache::purgeStaleBlobs() {
@@ -38,7 +38,7 @@
     fPurgeBlobInbox.poll(&msgs);
 
     for (const auto& msg : msgs) {
-        auto* idEntry = fBlobIDCache.find(msg.fID);
+        auto* idEntry = fBlobIDCache.find(msg.fBlobID);
         if (!idEntry) {
             // no cache entries for id
             continue;
@@ -51,7 +51,7 @@
         }
 
         // drop the idEntry itself (unrefs all blobs)
-        fBlobIDCache.remove(msg.fID);
+        fBlobIDCache.remove(msg.fBlobID);
     }
 }
 
diff --git a/src/gpu/text/GrTextBlobCache.h b/src/gpu/text/GrTextBlobCache.h
index b41d401..a174c9c 100644
--- a/src/gpu/text/GrTextBlobCache.h
+++ b/src/gpu/text/GrTextBlobCache.h
@@ -99,7 +99,12 @@
     }
 
     struct PurgeBlobMessage {
-        uint32_t fID;
+        PurgeBlobMessage(uint32_t blobID, uint32_t contextUniqueID)
+                : fBlobID(blobID), fContextID(contextUniqueID) {}
+        bool shouldSend(uint32_t inboxID) const { return fContextID == inboxID; }
+
+        uint32_t fBlobID;
+        uint32_t fContextID;
     };
 
     static void PostPurgeBlobMessage(uint32_t blobID, uint32_t cacheID);
diff --git a/tests/MessageBusTest.cpp b/tests/MessageBusTest.cpp
index 163addf..145176b 100644
--- a/tests/MessageBusTest.cpp
+++ b/tests/MessageBusTest.cpp
@@ -8,10 +8,15 @@
 #include "SkMessageBus.h"
 #include "Test.h"
 
+namespace {
 struct TestMessage {
+    bool shouldSend(uint32_t inboxID) const { return true; }
+    TestMessage(int i, float f) : x(i), y(f) {}
+
     int x;
     float y;
 };
+}
 DECLARE_SKMESSAGEBUS_MESSAGE(TestMessage)
 
 DEF_TEST(MessageBus, r) {
@@ -50,4 +55,42 @@
     REPORTER_ASSERT(r, 1 == messages[2].x);
 }
 
+namespace {
+struct AddressedMessage {
+    uint32_t fInboxID;
+
+    bool shouldSend(uint32_t inboxID) const {
+        SkASSERT(inboxID);
+        if (!fInboxID) {
+            return true;
+        }
+        return inboxID == fInboxID;
+    }
+};
+}
+DECLARE_SKMESSAGEBUS_MESSAGE(AddressedMessage)
+
+DEF_TEST(MessageBus_shouldSend, r) {
+    SkMessageBus<AddressedMessage>::Inbox inbox1(1), inbox2(2);
+
+    SkMessageBus<AddressedMessage>::Post({0});  // Should go to both
+    SkMessageBus<AddressedMessage>::Post({1});  // Should go to inbox1
+    SkMessageBus<AddressedMessage>::Post({2});  // Should go to inbox2
+    SkMessageBus<AddressedMessage>::Post({3});  // Should go nowhere
+
+    SkTArray<AddressedMessage> messages;
+    inbox1.poll(&messages);
+    REPORTER_ASSERT(r, messages.count() == 2);
+    if (messages.count() == 2) {
+        REPORTER_ASSERT(r, messages[0].fInboxID == 0);
+        REPORTER_ASSERT(r, messages[1].fInboxID == 1);
+    }
+    inbox2.poll(&messages);
+    REPORTER_ASSERT(r, messages.count() == 2);
+    if (messages.count() == 2) {
+        REPORTER_ASSERT(r, messages[0].fInboxID == 0);
+        REPORTER_ASSERT(r, messages[1].fInboxID == 2);
+    }
+}
+
 // Multithreaded tests tbd.
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index dceb3e7..5b7a67a 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -1003,8 +1003,8 @@
     typedef SkMessageBus<GrUniqueKeyInvalidatedMessage> Bus;
 
     // Invalidate two of the three, they should be purged and no longer accessible via their keys.
-    Bus::Post(Msg(key1));
-    Bus::Post(Msg(key2));
+    Bus::Post(Msg(key1, context->uniqueID()));
+    Bus::Post(Msg(key2, context->uniqueID()));
     cache->purgeAsNeeded();
     // a should be deleted now, but we still have a ref on b.
     REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1));
@@ -1013,7 +1013,7 @@
     REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3));
 
     // Invalidate the third.
-    Bus::Post(Msg(key3));
+    Bus::Post(Msg(key3, context->uniqueID()));
     cache->purgeAsNeeded();
     // we still have a ref on b, c should be recycled as scratch.
     REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
diff --git a/tests/TextureProxyTest.cpp b/tests/TextureProxyTest.cpp
index 15e3ef6..da072b5 100644
--- a/tests/TextureProxyTest.cpp
+++ b/tests/TextureProxyTest.cpp
@@ -255,7 +255,8 @@
     SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
 
     // Send an invalidation message, which will be sitting in the cache's inbox
-    SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(GrUniqueKeyInvalidatedMessage(key));
+    SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(
+            GrUniqueKeyInvalidatedMessage(key, context->uniqueID()));
 
     REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
     REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());