1 | /* |
2 | * Copyright 2018 Google Inc. |
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 | #include "include/gpu/GrContext.h" |
9 | #include "src/core/SkLRUCache.h" |
10 | #include "src/gpu/GrCaps.h" |
11 | #include "src/gpu/GrContextPriv.h" |
12 | #include "src/gpu/GrContextThreadSafeProxyPriv.h" |
13 | #include "src/gpu/GrProgramDesc.h" |
14 | #include "src/gpu/GrProgramInfo.h" |
15 | #include "src/gpu/effects/GrSkSLFP.h" |
16 | |
17 | /** |
18 | * The DDL Context is the one in effect during DDL Recording. It isn't backed by a GrGPU and |
19 | * cannot allocate any GPU resources. |
20 | */ |
21 | class GrDDLContext final : public GrContext { |
22 | public: |
23 | GrDDLContext(sk_sp<GrContextThreadSafeProxy> proxy) |
24 | : INHERITED(proxy->backend(), proxy->priv().options(), proxy->priv().contextID()) { |
25 | fThreadSafeProxy = std::move(proxy); |
26 | } |
27 | |
28 | ~GrDDLContext() override {} |
29 | |
30 | void abandonContext() override { |
31 | SkASSERT(0); // abandoning in a DDL Recorder doesn't make a whole lot of sense |
32 | INHERITED::abandonContext(); |
33 | } |
34 | |
35 | void releaseResourcesAndAbandonContext() override { |
36 | SkASSERT(0); // abandoning in a DDL Recorder doesn't make a whole lot of sense |
37 | INHERITED::releaseResourcesAndAbandonContext(); |
38 | } |
39 | |
40 | void freeGpuResources() override { |
41 | SkASSERT(0); // freeing resources in a DDL Recorder doesn't make a whole lot of sense |
42 | INHERITED::freeGpuResources(); |
43 | } |
44 | |
45 | private: |
46 | // TODO: Here we're pretending this isn't derived from GrContext. Switch this to be derived from |
47 | // GrRecordingContext! |
48 | GrContext* asDirectContext() override { return nullptr; } |
49 | |
50 | bool init(sk_sp<const GrCaps> caps) override { |
51 | SkASSERT(caps); |
52 | SkASSERT(fThreadSafeProxy); // should've been set in the ctor |
53 | |
54 | if (!INHERITED::init(std::move(caps))) { |
55 | return false; |
56 | } |
57 | |
58 | // DDL contexts/drawing managers always sort the oplists and attempt to reduce opsTask |
59 | // splitting. |
60 | this->setupDrawingManager(true, true); |
61 | |
62 | SkASSERT(this->caps()); |
63 | |
64 | return true; |
65 | } |
66 | |
67 | GrAtlasManager* onGetAtlasManager() override { |
68 | SkASSERT(0); // the DDL Recorders should never invoke this |
69 | return nullptr; |
70 | } |
71 | |
72 | // Add to the set of unique program infos required by this DDL |
73 | void recordProgramInfo(const GrProgramInfo* programInfo) final { |
74 | if (!programInfo) { |
75 | return; |
76 | } |
77 | |
78 | const GrCaps* caps = this->caps(); |
79 | |
80 | if (this->backend() == GrBackendApi::kVulkan || |
81 | this->backend() == GrBackendApi::kMetal || |
82 | this->backend() == GrBackendApi::kDawn) { |
83 | // Currently, Vulkan, Metal and Dawn require a live renderTarget to |
84 | // compute the key |
85 | return; |
86 | } |
87 | |
88 | if (programInfo->requestedFeatures() & GrProcessor::CustomFeatures::kSampleLocations) { |
89 | // Sample locations require a live renderTarget to compute the key |
90 | return; |
91 | } |
92 | |
93 | GrProgramDesc desc = caps->makeDesc(nullptr, *programInfo); |
94 | if (!desc.isValid()) { |
95 | return; |
96 | } |
97 | |
98 | fProgramInfoMap.add(desc, programInfo); |
99 | } |
100 | |
101 | void detachProgramData(SkTArray<ProgramData>* dst) final { |
102 | SkASSERT(dst->empty()); |
103 | |
104 | fProgramInfoMap.toArray(dst); |
105 | } |
106 | |
107 | |
108 | private: |
109 | class ProgramInfoMap : public ::SkNoncopyable { |
110 | typedef const GrProgramDesc CacheKey; |
111 | typedef const GrProgramInfo* CacheValue; |
112 | |
113 | public: |
114 | // All the programInfo data should be stored in the record-time arena so there is no |
115 | // need to ref them here or to delete them in the destructor. |
116 | ProgramInfoMap() : fMap(10) {} |
117 | ~ProgramInfoMap() {} |
118 | |
119 | // TODO: this is doing a lot of reallocating of the ProgramDesc! Once the program descs |
120 | // are allocated in the record-time area there won't be a problem. |
121 | void add(CacheKey& desc, const GrProgramInfo* programInfo) { |
122 | SkASSERT(desc.isValid()); |
123 | |
124 | const CacheValue* preExisting = fMap.find(desc); |
125 | if (preExisting) { |
126 | return; |
127 | } |
128 | |
129 | fMap.insert(desc, programInfo); |
130 | } |
131 | |
132 | void toArray(SkTArray<ProgramData>* dst) { |
133 | fMap.foreach([dst](CacheKey* programDesc, CacheValue* programInfo) { |
134 | // TODO: remove this allocation once the program descs are stored |
135 | // in the record-time arena. |
136 | dst->emplace_back(std::make_unique<const GrProgramDesc>(*programDesc), |
137 | *programInfo); |
138 | }); |
139 | } |
140 | |
141 | private: |
142 | struct DescHash { |
143 | uint32_t operator()(CacheKey& desc) const { |
144 | return SkOpts::hash_fn(desc.asKey(), desc.keyLength(), 0); |
145 | } |
146 | }; |
147 | |
148 | SkLRUCache<CacheKey, CacheValue, DescHash> fMap; |
149 | }; |
150 | |
151 | ProgramInfoMap fProgramInfoMap; |
152 | |
153 | typedef GrContext INHERITED; |
154 | }; |
155 | |
156 | sk_sp<GrContext> GrContextPriv::MakeDDL(const sk_sp<GrContextThreadSafeProxy>& proxy) { |
157 | sk_sp<GrContext> context(new GrDDLContext(proxy)); |
158 | |
159 | if (!context->init(proxy->priv().refCaps())) { |
160 | return nullptr; |
161 | } |
162 | return context; |
163 | } |
164 | |