• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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