/src/skia/tools/gpu/GrContextFactory.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | /* |
3 | | * Copyright 2014 Google Inc. |
4 | | * |
5 | | * Use of this source code is governed by a BSD-style license that can be |
6 | | * found in the LICENSE file. |
7 | | */ |
8 | | |
9 | | #include "include/gpu/GrDirectContext.h" |
10 | | #include "src/gpu/ganesh/GrDirectContextPriv.h" |
11 | | #include "tools/gpu/GrContextFactory.h" |
12 | | #ifdef SK_GL |
13 | | #include "tools/gpu/gl/GLTestContext.h" |
14 | | #endif |
15 | | |
16 | | #if SK_ANGLE |
17 | | #include "tools/gpu/gl/angle/GLTestContext_angle.h" |
18 | | #endif |
19 | | #ifdef SK_VULKAN |
20 | | #include "tools/gpu/vk/VkTestContext.h" |
21 | | #endif |
22 | | #ifdef SK_METAL |
23 | | #include "tools/gpu/mtl/MtlTestContext.h" |
24 | | #endif |
25 | | #ifdef SK_DIRECT3D |
26 | | #include "tools/gpu/d3d/D3DTestContext.h" |
27 | | #endif |
28 | | #include "src/gpu/ganesh/GrCaps.h" |
29 | | #include "tools/gpu/mock/MockTestContext.h" |
30 | | |
31 | | #if defined(SK_BUILD_FOR_WIN) && defined(SK_ENABLE_DISCRETE_GPU) |
32 | | extern "C" { |
33 | | // NVIDIA documents that the presence and value of this symbol programmatically enable the high |
34 | | // performance GPU in laptops with switchable graphics. |
35 | | // https://docs.nvidia.com/gameworks/content/technologies/desktop/optimus.htm |
36 | | // From testing, including this symbol, even if it is set to 0, we still get the NVIDIA GPU. |
37 | | _declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; |
38 | | |
39 | | // AMD has a similar mechanism, although I don't have an AMD laptop, so this is untested. |
40 | | // https://community.amd.com/thread/169965 |
41 | | __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; |
42 | | } |
43 | | #endif |
44 | | |
45 | | bool gCreateProtectedContext = false; |
46 | | |
47 | | namespace sk_gpu_test { |
48 | 2.76k | GrContextFactory::GrContextFactory() {} |
49 | | |
50 | | GrContextFactory::GrContextFactory(const GrContextOptions& opts) |
51 | 0 | : fGlobalOptions(opts) {} |
52 | | |
53 | 2.76k | GrContextFactory::~GrContextFactory() { |
54 | 2.76k | this->destroyContexts(); |
55 | 2.76k | } |
56 | | |
57 | 2.76k | void GrContextFactory::destroyContexts() { |
58 | | // We must delete the test contexts in reverse order so that any child context is finished and |
59 | | // deleted before a parent context. This relies on the fact that when we make a new context we |
60 | | // append it to the end of fContexts array. |
61 | | // TODO: Look into keeping a dependency dag for contexts and deletion order |
62 | 5.34k | for (int i = fContexts.size() - 1; i >= 0; --i) { |
63 | 2.58k | Context& context = fContexts[i]; |
64 | 2.58k | SkScopeExit restore(nullptr); |
65 | 2.58k | if (context.fTestContext) { |
66 | 2.58k | restore = context.fTestContext->makeCurrentAndAutoRestore(); |
67 | 2.58k | } |
68 | 2.58k | if (!context.fGrContext->unique()) { |
69 | 0 | context.fGrContext->releaseResourcesAndAbandonContext(); |
70 | 0 | context.fAbandoned = true; |
71 | 0 | } |
72 | 2.58k | context.fGrContext->unref(); |
73 | 2.58k | delete context.fTestContext; |
74 | 2.58k | } |
75 | 2.76k | fContexts.clear(); |
76 | 2.76k | } |
77 | | |
78 | 0 | void GrContextFactory::abandonContexts() { |
79 | | // We must abandon the test contexts in reverse order so that any child context is finished and |
80 | | // abandoned before a parent context. This relies on the fact that when we make a new context we |
81 | | // append it to the end of fContexts array. |
82 | | // TODO: Look into keeping a dependency dag for contexts and deletion order |
83 | 0 | for (int i = fContexts.size() - 1; i >= 0; --i) { |
84 | 0 | Context& context = fContexts[i]; |
85 | 0 | if (!context.fAbandoned) { |
86 | 0 | if (context.fTestContext) { |
87 | 0 | auto restore = context.fTestContext->makeCurrentAndAutoRestore(); |
88 | 0 | context.fTestContext->testAbandon(); |
89 | 0 | } |
90 | 0 | GrBackendApi api = context.fGrContext->backend(); |
91 | 0 | bool requiresEarlyAbandon = api == GrBackendApi::kVulkan; |
92 | 0 | if (requiresEarlyAbandon) { |
93 | 0 | context.fGrContext->abandonContext(); |
94 | 0 | } |
95 | 0 | if (context.fTestContext) { |
96 | 0 | delete(context.fTestContext); |
97 | 0 | context.fTestContext = nullptr; |
98 | 0 | } |
99 | 0 | if (!requiresEarlyAbandon) { |
100 | 0 | context.fGrContext->abandonContext(); |
101 | 0 | } |
102 | 0 | context.fAbandoned = true; |
103 | 0 | } |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | 0 | void GrContextFactory::releaseResourcesAndAbandonContexts() { |
108 | | // We must abandon the test contexts in reverse order so that any child context is finished and |
109 | | // abandoned before a parent context. This relies on the fact that when we make a new context we |
110 | | // append it to the end of fContexts array. |
111 | | // TODO: Look into keeping a dependency dag for contexts and deletion order |
112 | 0 | for (int i = fContexts.size() - 1; i >= 0; --i) { |
113 | 0 | Context& context = fContexts[i]; |
114 | 0 | SkScopeExit restore(nullptr); |
115 | 0 | if (!context.fAbandoned) { |
116 | 0 | if (context.fTestContext) { |
117 | 0 | restore = context.fTestContext->makeCurrentAndAutoRestore(); |
118 | 0 | } |
119 | 0 | context.fGrContext->releaseResourcesAndAbandonContext(); |
120 | 0 | if (context.fTestContext) { |
121 | 0 | delete context.fTestContext; |
122 | 0 | context.fTestContext = nullptr; |
123 | 0 | } |
124 | 0 | context.fAbandoned = true; |
125 | 0 | } |
126 | 0 | } |
127 | 0 | } |
128 | | |
129 | 2.58k | GrDirectContext* GrContextFactory::get(ContextType type, ContextOverrides overrides) { |
130 | 2.58k | return this->getContextInfo(type, overrides).directContext(); |
131 | 2.58k | } |
132 | | |
133 | | ContextInfo GrContextFactory::getContextInfoInternal(ContextType type, ContextOverrides overrides, |
134 | | GrDirectContext* shareContext, |
135 | 2.76k | uint32_t shareIndex) { |
136 | | // (shareIndex != 0) -> (shareContext != nullptr) |
137 | 2.76k | SkASSERT((shareIndex == 0) || (shareContext != nullptr)); |
138 | | |
139 | 2.76k | for (int i = 0; i < fContexts.size(); ++i) { |
140 | 0 | Context& context = fContexts[i]; |
141 | 0 | if (context.fType == type && |
142 | 0 | context.fOverrides == overrides && |
143 | 0 | context.fShareContext == shareContext && |
144 | 0 | context.fShareIndex == shareIndex && |
145 | 0 | !context.fAbandoned) { |
146 | 0 | context.fTestContext->makeCurrent(); |
147 | 0 | return ContextInfo(context.fType, context.fTestContext, context.fGrContext, |
148 | 0 | context.fOptions); |
149 | 0 | } |
150 | 0 | } |
151 | | |
152 | | // If we're trying to create a context in a share group, find the primary context |
153 | 2.76k | Context* primaryContext = nullptr; |
154 | 2.76k | if (shareContext) { |
155 | 0 | for (int i = 0; i < fContexts.size(); ++i) { |
156 | 0 | if (!fContexts[i].fAbandoned && fContexts[i].fGrContext == shareContext) { |
157 | 0 | primaryContext = &fContexts[i]; |
158 | 0 | break; |
159 | 0 | } |
160 | 0 | } |
161 | 0 | SkASSERT(primaryContext && primaryContext->fType == type); |
162 | 0 | } |
163 | | |
164 | 2.76k | std::unique_ptr<TestContext> testCtx; |
165 | 2.76k | GrBackendApi backend = skgpu::ganesh::ContextTypeBackend(type); |
166 | 2.76k | switch (backend) { |
167 | | #ifdef SK_GL |
168 | | case GrBackendApi::kOpenGL: { |
169 | | GLTestContext* glShareContext = primaryContext |
170 | | ? static_cast<GLTestContext*>(primaryContext->fTestContext) : nullptr; |
171 | | GLTestContext* glCtx; |
172 | | switch (type) { |
173 | | case ContextType::kGL: |
174 | | glCtx = CreatePlatformGLTestContext(kGL_GrGLStandard, glShareContext); |
175 | | break; |
176 | | case ContextType::kGLES: |
177 | | glCtx = CreatePlatformGLTestContext(kGLES_GrGLStandard, glShareContext); |
178 | | break; |
179 | | #if SK_ANGLE |
180 | | case ContextType::kANGLE_D3D9_ES2: |
181 | | glCtx = MakeANGLETestContext(ANGLEBackend::kD3D9, ANGLEContextVersion::kES2, |
182 | | glShareContext).release(); |
183 | | // Chrome will only run on D3D9 with NVIDIA for 2012 and earlier drivers. |
184 | | // (<= 269.73). We get shader link failures when testing on recent drivers |
185 | | // using this backend. |
186 | | if (glCtx) { |
187 | | GrGLDriverInfo info = GrGLGetDriverInfo(glCtx->gl()); |
188 | | if (info.fANGLEVendor == GrGLVendor::kNVIDIA) { |
189 | | delete glCtx; |
190 | | return ContextInfo(); |
191 | | } |
192 | | } |
193 | | break; |
194 | | case ContextType::kANGLE_D3D11_ES2: |
195 | | glCtx = MakeANGLETestContext(ANGLEBackend::kD3D11, ANGLEContextVersion::kES2, |
196 | | glShareContext).release(); |
197 | | break; |
198 | | case ContextType::kANGLE_D3D11_ES3: |
199 | | glCtx = MakeANGLETestContext(ANGLEBackend::kD3D11, ANGLEContextVersion::kES3, |
200 | | glShareContext).release(); |
201 | | break; |
202 | | case ContextType::kANGLE_GL_ES2: |
203 | | glCtx = MakeANGLETestContext(ANGLEBackend::kOpenGL, ANGLEContextVersion::kES2, |
204 | | glShareContext).release(); |
205 | | break; |
206 | | case ContextType::kANGLE_GL_ES3: |
207 | | glCtx = MakeANGLETestContext(ANGLEBackend::kOpenGL, ANGLEContextVersion::kES3, |
208 | | glShareContext).release(); |
209 | | break; |
210 | | case ContextType::kANGLE_Metal_ES2: |
211 | | glCtx = MakeANGLETestContext(ANGLEBackend::kMetal, ANGLEContextVersion::kES2, |
212 | | glShareContext).release(); |
213 | | break; |
214 | | case ContextType::kANGLE_Metal_ES3: |
215 | | glCtx = MakeANGLETestContext(ANGLEBackend::kMetal, ANGLEContextVersion::kES3, |
216 | | glShareContext).release(); |
217 | | break; |
218 | | #endif |
219 | | default: |
220 | | return ContextInfo(); |
221 | | } |
222 | | if (!glCtx) { |
223 | | return ContextInfo(); |
224 | | } |
225 | | if (glCtx->gl()->fStandard == kGLES_GrGLStandard && |
226 | | (overrides & ContextOverrides::kFakeGLESVersionAs2)) { |
227 | | glCtx->overrideVersion("OpenGL ES 2.0", "OpenGL ES GLSL ES 1.00"); |
228 | | } |
229 | | testCtx.reset(glCtx); |
230 | | break; |
231 | | } |
232 | | #endif // SK_GL |
233 | 0 | #ifdef SK_VULKAN |
234 | 0 | case GrBackendApi::kVulkan: { |
235 | 0 | VkTestContext* vkSharedContext = primaryContext |
236 | 0 | ? static_cast<VkTestContext*>(primaryContext->fTestContext) : nullptr; |
237 | 0 | SkASSERT(ContextType::kVulkan == type); |
238 | 0 | testCtx.reset(CreatePlatformVkTestContext(vkSharedContext)); |
239 | 0 | if (!testCtx) { |
240 | 0 | return ContextInfo(); |
241 | 0 | } |
242 | | #ifdef SK_GL |
243 | | // We previously had an issue where the VkDevice destruction would occasionally hang |
244 | | // on systems with NVIDIA GPUs and having an existing GL context fixed it. Now (Feb |
245 | | // 2022) we still need the GL context to keep Vulkan/TSAN bots from running incredibly |
246 | | // slow. Perhaps this prevents repeated driver loading/unloading? Note that keeping |
247 | | // a persistent VkTestContext around instead was tried and did not work. |
248 | | if (!fSentinelGLContext) { |
249 | | fSentinelGLContext.reset(CreatePlatformGLTestContext(kGL_GrGLStandard)); |
250 | | if (!fSentinelGLContext) { |
251 | | fSentinelGLContext.reset(CreatePlatformGLTestContext(kGLES_GrGLStandard)); |
252 | | } |
253 | | } |
254 | | #endif |
255 | 0 | break; |
256 | 0 | } |
257 | 0 | #endif |
258 | | #ifdef SK_METAL |
259 | | case GrBackendApi::kMetal: { |
260 | | MtlTestContext* mtlSharedContext = primaryContext |
261 | | ? static_cast<MtlTestContext*>(primaryContext->fTestContext) : nullptr; |
262 | | SkASSERT(ContextType::kMetal == type); |
263 | | testCtx.reset(CreatePlatformMtlTestContext(mtlSharedContext)); |
264 | | if (!testCtx) { |
265 | | return ContextInfo(); |
266 | | } |
267 | | break; |
268 | | } |
269 | | #endif |
270 | | #ifdef SK_DIRECT3D |
271 | | case GrBackendApi::kDirect3D: { |
272 | | D3DTestContext* d3dSharedContext = primaryContext |
273 | | ? static_cast<D3DTestContext*>(primaryContext->fTestContext) : nullptr; |
274 | | SkASSERT(ContextType::kDirect3D == type); |
275 | | testCtx.reset(CreatePlatformD3DTestContext(d3dSharedContext)); |
276 | | if (!testCtx) { |
277 | | return ContextInfo(); |
278 | | } |
279 | | break; |
280 | | } |
281 | | #endif |
282 | 2.58k | case GrBackendApi::kMock: { |
283 | 2.58k | TestContext* sharedContext = primaryContext ? primaryContext->fTestContext : nullptr; |
284 | 2.58k | SkASSERT(ContextType::kMock == type); |
285 | 2.58k | testCtx.reset(CreateMockTestContext(sharedContext)); |
286 | 2.58k | if (!testCtx) { |
287 | 0 | return ContextInfo(); |
288 | 0 | } |
289 | 2.58k | break; |
290 | 2.58k | } |
291 | 2.58k | default: |
292 | 184 | return ContextInfo(); |
293 | 2.76k | } |
294 | | |
295 | 2.58k | SkASSERT(testCtx && testCtx->backend() == backend); |
296 | 2.58k | GrContextOptions grOptions = fGlobalOptions; |
297 | 2.58k | if (ContextOverrides::kAvoidStencilBuffers & overrides) { |
298 | 0 | grOptions.fAvoidStencilBuffers = true; |
299 | 0 | } |
300 | 2.58k | if (ContextOverrides::kReducedShaders & overrides) { |
301 | 0 | grOptions.fReducedShaderVariations = true; |
302 | 0 | } |
303 | 2.58k | sk_sp<GrDirectContext> grCtx; |
304 | 2.58k | { |
305 | 2.58k | auto restore = testCtx->makeCurrentAndAutoRestore(); |
306 | 2.58k | grCtx = testCtx->makeContext(grOptions); |
307 | 2.58k | } |
308 | 2.58k | if (!grCtx) { |
309 | 0 | return ContextInfo(); |
310 | 0 | } |
311 | | |
312 | 2.58k | if (shareContext) { |
313 | 0 | SkASSERT(grCtx->directContextID() != shareContext->directContextID()); |
314 | 0 | } |
315 | | |
316 | | // We must always add new contexts by pushing to the back so that when we delete them we delete |
317 | | // them in reverse order in which they were made. |
318 | 2.58k | Context& context = fContexts.push_back(); |
319 | 2.58k | context.fBackend = backend; |
320 | 2.58k | context.fTestContext = testCtx.release(); |
321 | 2.58k | context.fGrContext = SkRef(grCtx.get()); |
322 | 2.58k | context.fType = type; |
323 | 2.58k | context.fOverrides = overrides; |
324 | 2.58k | context.fAbandoned = false; |
325 | 2.58k | context.fShareContext = shareContext; |
326 | 2.58k | context.fShareIndex = shareIndex; |
327 | 2.58k | context.fOptions = grOptions; |
328 | 2.58k | context.fTestContext->makeCurrent(); |
329 | 2.58k | return ContextInfo(context.fType, context.fTestContext, context.fGrContext, context.fOptions); |
330 | 2.58k | } |
331 | | |
332 | 2.76k | ContextInfo GrContextFactory::getContextInfo(ContextType type, ContextOverrides overrides) { |
333 | 2.76k | return this->getContextInfoInternal(type, overrides, nullptr, 0); |
334 | 2.76k | } |
335 | | |
336 | | ContextInfo GrContextFactory::getSharedContextInfo(GrDirectContext* shareContext, |
337 | 0 | uint32_t shareIndex) { |
338 | 0 | SkASSERT(shareContext); |
339 | 0 | for (int i = 0; i < fContexts.size(); ++i) { |
340 | 0 | if (!fContexts[i].fAbandoned && fContexts[i].fGrContext == shareContext) { |
341 | 0 | return this->getContextInfoInternal(fContexts[i].fType, fContexts[i].fOverrides, |
342 | 0 | shareContext, shareIndex); |
343 | 0 | } |
344 | 0 | } |
345 | | |
346 | 0 | return ContextInfo(); |
347 | 0 | } Unexecuted instantiation: sk_gpu_test::GrContextFactory::getSharedContextInfo(GrDirectContext*, unsigned int) Unexecuted instantiation: sk_gpu_test::GrContextFactory::getSharedContextInfo(GrDirectContext*, unsigned int) |
348 | | |
349 | | } // namespace sk_gpu_test |