xref: /aosp_15_r20/external/skia/src/gpu/graphite/GlobalCache.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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