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