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/base/SkTArray.h" 13 #include "src/base/SkSpinlock.h" 14 #include "src/core/SkLRUCache.h" 15 #include "src/gpu/ResourceKey.h" 16 17 #include <functional> 18 19 namespace skgpu::graphite { 20 21 class ComputePipeline; 22 class GraphicsPipeline; 23 class Resource; 24 class ShaderCodeDictionary; 25 26 /** 27 * GlobalCache holds GPU resources that should be shared by every Recorder. The common requirement 28 * of these resources are they are static/read-only, have long lifetimes, and are likely to be used 29 * by multiple Recorders. The canonical example of this are pipelines. 30 * 31 * GlobalCache is thread safe, but intentionally splits queries and storing operations so that they 32 * are not atomic. The pattern is to query for a resource, which has a high likelihood of a cache 33 * hit. If it's not found, the Recorder creates the resource on its own, without locking the 34 * GlobalCache. After the resource is created, it is added to the GlobalCache, atomically returning 35 * the winning Resource in the event of a race between Recorders for the same UniqueKey. 36 */ 37 class GlobalCache { 38 public: 39 GlobalCache(); 40 ~GlobalCache(); 41 42 void deleteResources(); 43 44 // Find a cached GraphicsPipeline that matches the associated key. 45 sk_sp<GraphicsPipeline> findGraphicsPipeline(const UniqueKey&) SK_EXCLUDES(fSpinLock); 46 47 // Associate the given pipeline with the key. If the key has already had a separate pipeline 48 // associated with the key, that pipeline is returned and the passed-in pipeline is discarded. 49 // Otherwise, the passed-in pipeline is held by the GlobalCache and also returned back. 50 sk_sp<GraphicsPipeline> addGraphicsPipeline(const UniqueKey&, 51 sk_sp<GraphicsPipeline>) SK_EXCLUDES(fSpinLock); 52 53 #if defined(GPU_TEST_UTILS) 54 int numGraphicsPipelines() const SK_EXCLUDES(fSpinLock); 55 void resetGraphicsPipelines() SK_EXCLUDES(fSpinLock); 56 void forEachGraphicsPipeline( 57 const std::function<void(const UniqueKey&, const GraphicsPipeline*)>& fn) 58 SK_EXCLUDES(fSpinLock); 59 60 struct PipelineStats { 61 int fGraphicsCacheHits = 0; 62 int fGraphicsCacheMisses = 0; 63 int fGraphicsCacheAdditions = 0; 64 int fGraphicsRaces = 0; 65 }; 66 67 PipelineStats getStats() const SK_EXCLUDES(fSpinLock); 68 #endif 69 70 // Find and add operations for ComputePipelines, with the same pattern as GraphicsPipelines. 71 sk_sp<ComputePipeline> findComputePipeline(const UniqueKey&) SK_EXCLUDES(fSpinLock); 72 sk_sp<ComputePipeline> addComputePipeline(const UniqueKey&, 73 sk_sp<ComputePipeline>) SK_EXCLUDES(fSpinLock); 74 75 // The GlobalCache holds a ref on the given Resource until the cache is destroyed, keeping it 76 // alive for the lifetime of the SharedContext. This should be used only for Resources that are 77 // immutable after initialization so that anyone can use the resource without synchronization 78 // or reference tracking. 79 void addStaticResource(sk_sp<Resource>) SK_EXCLUDES(fSpinLock); 80 81 private: 82 struct KeyHash { operatorKeyHash83 uint32_t operator()(const UniqueKey& key) const { return key.hash(); } 84 }; 85 86 using GraphicsPipelineCache = SkLRUCache<UniqueKey, sk_sp<GraphicsPipeline>, KeyHash>; 87 using ComputePipelineCache = SkLRUCache<UniqueKey, sk_sp<ComputePipeline>, KeyHash>; 88 89 // TODO: can we do something better given this should have write-seldom/read-often behavior? 90 mutable SkSpinlock fSpinLock; 91 92 // GraphicsPipelines and ComputePipelines are expensive to create, likely to be used by multiple 93 // Recorders, and are ideally pre-compiled on process startup so thread write-contention is 94 // expected to be low. For these reasons we store pipelines globally instead of per-Recorder. 95 GraphicsPipelineCache fGraphicsPipelineCache SK_GUARDED_BY(fSpinLock); 96 ComputePipelineCache fComputePipelineCache SK_GUARDED_BY(fSpinLock); 97 98 skia_private::TArray<sk_sp<Resource>> fStaticResource SK_GUARDED_BY(fSpinLock); 99 100 #if defined(GPU_TEST_UTILS) 101 PipelineStats fStats SK_GUARDED_BY(fSpinLock); 102 #endif 103 }; 104 105 } // namespace skgpu::graphite 106 107 #endif // skgpu_graphite_GlobalCache_DEFINED 108