xref: /aosp_15_r20/external/skia/tools/gpu/GrContextFactory.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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