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