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/ganesh/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 {
GrContextFactory()48 GrContextFactory::GrContextFactory() {}
49
GrContextFactory(const GrContextOptions & opts)50 GrContextFactory::GrContextFactory(const GrContextOptions& opts)
51 : fGlobalOptions(opts) {}
52
~GrContextFactory()53 GrContextFactory::~GrContextFactory() {
54 this->destroyContexts();
55 }
56
destroyContexts()57 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 for (int i = fContexts.size() - 1; i >= 0; --i) {
63 Context& context = fContexts[i];
64 SkScopeExit restore(nullptr);
65 if (context.fTestContext) {
66 restore = context.fTestContext->makeCurrentAndAutoRestore();
67 }
68 if (!context.fGrContext->unique()) {
69 context.fGrContext->releaseResourcesAndAbandonContext();
70 context.fAbandoned = true;
71 }
72 context.fGrContext->unref();
73 delete context.fTestContext;
74 }
75 fContexts.clear();
76 }
77
abandonContexts()78 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 for (int i = fContexts.size() - 1; i >= 0; --i) {
84 Context& context = fContexts[i];
85 if (!context.fAbandoned) {
86 if (context.fTestContext) {
87 auto restore = context.fTestContext->makeCurrentAndAutoRestore();
88 context.fTestContext->testAbandon();
89 }
90 GrBackendApi api = context.fGrContext->backend();
91 bool requiresEarlyAbandon = api == GrBackendApi::kVulkan;
92 if (requiresEarlyAbandon) {
93 context.fGrContext->abandonContext();
94 }
95 if (context.fTestContext) {
96 delete(context.fTestContext);
97 context.fTestContext = nullptr;
98 }
99 if (!requiresEarlyAbandon) {
100 context.fGrContext->abandonContext();
101 }
102 context.fAbandoned = true;
103 }
104 }
105 }
106
releaseResourcesAndAbandonContexts()107 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 for (int i = fContexts.size() - 1; i >= 0; --i) {
113 Context& context = fContexts[i];
114 SkScopeExit restore(nullptr);
115 if (!context.fAbandoned) {
116 if (context.fTestContext) {
117 restore = context.fTestContext->makeCurrentAndAutoRestore();
118 }
119 context.fGrContext->releaseResourcesAndAbandonContext();
120 if (context.fTestContext) {
121 delete context.fTestContext;
122 context.fTestContext = nullptr;
123 }
124 context.fAbandoned = true;
125 }
126 }
127 }
128
get(ContextType type,ContextOverrides overrides)129 GrDirectContext* GrContextFactory::get(ContextType type, ContextOverrides overrides) {
130 return this->getContextInfo(type, overrides).directContext();
131 }
132
getContextInfoInternal(ContextType type,ContextOverrides overrides,GrDirectContext * shareContext,uint32_t shareIndex)133 ContextInfo GrContextFactory::getContextInfoInternal(ContextType type, ContextOverrides overrides,
134 GrDirectContext* shareContext,
135 uint32_t shareIndex) {
136 // (shareIndex != 0) -> (shareContext != nullptr)
137 SkASSERT((shareIndex == 0) || (shareContext != nullptr));
138
139 for (int i = 0; i < fContexts.size(); ++i) {
140 Context& context = fContexts[i];
141 if (context.fType == type &&
142 context.fOverrides == overrides &&
143 context.fShareContext == shareContext &&
144 context.fShareIndex == shareIndex &&
145 !context.fAbandoned) {
146 context.fTestContext->makeCurrent();
147 return ContextInfo(context.fType, context.fTestContext, context.fGrContext,
148 context.fOptions);
149 }
150 }
151
152 // If we're trying to create a context in a share group, find the primary context
153 Context* primaryContext = nullptr;
154 if (shareContext) {
155 for (int i = 0; i < fContexts.size(); ++i) {
156 if (!fContexts[i].fAbandoned && fContexts[i].fGrContext == shareContext) {
157 primaryContext = &fContexts[i];
158 break;
159 }
160 }
161 SkASSERT(primaryContext && primaryContext->fType == type);
162 }
163
164 std::unique_ptr<TestContext> testCtx;
165 GrBackendApi backend = skgpu::ganesh::ContextTypeBackend(type);
166 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 #ifdef SK_VULKAN
234 case GrBackendApi::kVulkan: {
235 VkTestContext* vkSharedContext = primaryContext
236 ? static_cast<VkTestContext*>(primaryContext->fTestContext) : nullptr;
237 SkASSERT(ContextType::kVulkan == type);
238 testCtx.reset(CreatePlatformVkTestContext(vkSharedContext));
239 if (!testCtx) {
240 return ContextInfo();
241 }
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 break;
256 }
257 #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 case GrBackendApi::kMock: {
283 TestContext* sharedContext = primaryContext ? primaryContext->fTestContext : nullptr;
284 SkASSERT(ContextType::kMock == type);
285 testCtx.reset(CreateMockTestContext(sharedContext));
286 if (!testCtx) {
287 return ContextInfo();
288 }
289 break;
290 }
291 default:
292 return ContextInfo();
293 }
294
295 SkASSERT(testCtx && testCtx->backend() == backend);
296 GrContextOptions grOptions = fGlobalOptions;
297 if (ContextOverrides::kAvoidStencilBuffers & overrides) {
298 grOptions.fAvoidStencilBuffers = true;
299 }
300 if (ContextOverrides::kReducedShaders & overrides) {
301 grOptions.fReducedShaderVariations = true;
302 }
303 sk_sp<GrDirectContext> grCtx;
304 {
305 auto restore = testCtx->makeCurrentAndAutoRestore();
306 grCtx = testCtx->makeContext(grOptions);
307 }
308 if (!grCtx) {
309 return ContextInfo();
310 }
311
312 if (shareContext) {
313 SkASSERT(grCtx->directContextID() != shareContext->directContextID());
314 }
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 Context& context = fContexts.push_back();
319 context.fBackend = backend;
320 context.fTestContext = testCtx.release();
321 context.fGrContext = SkRef(grCtx.get());
322 context.fType = type;
323 context.fOverrides = overrides;
324 context.fAbandoned = false;
325 context.fShareContext = shareContext;
326 context.fShareIndex = shareIndex;
327 context.fOptions = grOptions;
328 context.fTestContext->makeCurrent();
329 return ContextInfo(context.fType, context.fTestContext, context.fGrContext, context.fOptions);
330 }
331
getContextInfo(ContextType type,ContextOverrides overrides)332 ContextInfo GrContextFactory::getContextInfo(ContextType type, ContextOverrides overrides) {
333 return this->getContextInfoInternal(type, overrides, nullptr, 0);
334 }
335
getSharedContextInfo(GrDirectContext * shareContext,uint32_t shareIndex)336 ContextInfo GrContextFactory::getSharedContextInfo(GrDirectContext* shareContext,
337 uint32_t shareIndex) {
338 SkASSERT(shareContext);
339 for (int i = 0; i < fContexts.size(); ++i) {
340 if (!fContexts[i].fAbandoned && fContexts[i].fGrContext == shareContext) {
341 return this->getContextInfoInternal(fContexts[i].fType, fContexts[i].fOverrides,
342 shareContext, shareIndex);
343 }
344 }
345
346 return ContextInfo();
347 }
348
349 } // namespace sk_gpu_test
350