1 /* 2 * Copyright 2022 Google LLC 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef skgpu_graphite_GlobalCache_DEFINED 9 #define skgpu_graphite_GlobalCache_DEFINED 10 11 #include "include/core/SkRefCnt.h" 12 #include "include/private/SkSpinlock.h" 13 #include "src/core/SkLRUCache.h" 14 #include "src/gpu/ResourceKey.h" 15 16 17 namespace skgpu::graphite { 18 19 class ComputePipeline; 20 class GraphicsPipeline; 21 class Resource; 22 class ShaderCodeDictionary; 23 24 /** 25 * GlobalCache holds GPU resources that should be shared by every Recorder. The common requirement 26 * of these resources are they are static/read-only, have long lifetimes, and are likely to be used 27 * by multiple Recorders. The canonical example of this are pipelines. 28 * 29 * GlobalCache is thread safe, but intentionally splits queries and storing operations so that they 30 * are not atomic. The pattern is to query for a resource, which has a high likelihood of a cache 31 * hit. If it's not found, the Recorder creates the resource on its own, without locking the 32 * GlobalCache. After the resource is created, it is added to the GlobalCache, atomically returning 33 * the winning Resource in the event of a race between Recorders for the same UniqueKey. 34 */ 35 class GlobalCache { 36 public: 37 GlobalCache(); 38 ~GlobalCache(); 39 40 // Find a cached GraphicsPipeline that matches the associated key. 41 sk_sp<GraphicsPipeline> findGraphicsPipeline(const UniqueKey&) SK_EXCLUDES(fSpinLock); 42 // Associate the given pipeline with the key. If the key has already had a separate pipeline 43 // associated with the key, that pipeline is returned and the passed-in pipeline is discarded. 44 // Otherwise, the passed-in pipeline is held by the GlobalCache and also returned back. 45 sk_sp<GraphicsPipeline> addGraphicsPipeline(const UniqueKey&, 46 sk_sp<GraphicsPipeline>) SK_EXCLUDES(fSpinLock); 47 48 #if GRAPHITE_TEST_UTILS 49 int numGraphicsPipelines() const SK_EXCLUDES(fSpinLock); 50 void resetGraphicsPipelines() SK_EXCLUDES(fSpinLock); 51 #endif 52 53 // Find amd add operations for ComputePipelines, with the same pattern as GraphicsPipelines. 54 sk_sp<ComputePipeline> findComputePipeline(const UniqueKey&) SK_EXCLUDES(fSpinLock); 55 sk_sp<ComputePipeline> addComputePipeline(const UniqueKey&, 56 sk_sp<ComputePipeline>) SK_EXCLUDES(fSpinLock); 57 58 // The GlobalCache holds a ref on the given Resource until the cache is destroyed, keeping it 59 // alive for the lifetime of the SharedContext. This should be used only for Resources that are 60 // immutable after initialization so that anyone can use the resource without synchronization 61 // or reference tracking. 62 void addStaticResource(sk_sp<Resource>) SK_EXCLUDES(fSpinLock); 63 64 private: 65 struct KeyHash { operatorKeyHash66 uint32_t operator()(const UniqueKey& key) const { return key.hash(); } 67 }; 68 69 using GraphicsPipelineCache = SkLRUCache<UniqueKey, sk_sp<GraphicsPipeline>, KeyHash>; 70 using ComputePipelineCache = SkLRUCache<UniqueKey, sk_sp<ComputePipeline>, KeyHash>; 71 72 // TODO: can we do something better given this should have write-seldom/read-often behavior? 73 mutable SkSpinlock fSpinLock; 74 75 // GraphicsPipelines and ComputePipelines are expensive to create, likely to be used by multiple 76 // Recorders, and are ideally pre-compiled on process startup so thread write-contention is 77 // expected to be low. For these reasons we store pipelines globally instead of per-Recorder. 78 GraphicsPipelineCache fGraphicsPipelineCache SK_GUARDED_BY(fSpinLock); 79 ComputePipelineCache fComputePipelineCache SK_GUARDED_BY(fSpinLock); 80 81 SkTArray<sk_sp<Resource>> fStaticResource SK_GUARDED_BY(fSpinLock); 82 }; 83 84 } // namespace skgpu::graphite 85 86 #endif // skgpu_graphite_GlobalCache_DEFINED 87