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/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 #include "tools/gpu/gl/command_buffer/GLTestContext_command_buffer.h"
20 #ifdef SK_VULKAN
21 #include "tools/gpu/vk/VkTestContext.h"
22 #endif
23 #ifdef SK_METAL
24 #include "tools/gpu/mtl/MtlTestContext.h"
25 #endif
26 #ifdef SK_DIRECT3D
27 #include "tools/gpu/d3d/D3DTestContext.h"
28 #endif
29 #ifdef SK_DAWN
30 #include "tools/gpu/dawn/DawnTestContext.h"
31 #endif
32 #include "src/gpu/GrCaps.h"
33 #include "tools/gpu/mock/MockTestContext.h"
34
35 #if defined(SK_BUILD_FOR_WIN) && defined(SK_ENABLE_DISCRETE_GPU)
36 extern "C" {
37 // NVIDIA documents that the presence and value of this symbol programmatically enable the high
38 // performance GPU in laptops with switchable graphics.
39 // https://docs.nvidia.com/gameworks/content/technologies/desktop/optimus.htm
40 // From testing, including this symbol, even if it is set to 0, we still get the NVIDIA GPU.
41 // _declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
42
43 // AMD has a similar mechanism, although I don't have an AMD laptop, so this is untested.
44 // https://community.amd.com/thread/169965
45 // __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
46 }
47 #endif
48
49 namespace sk_gpu_test {
GrContextFactory()50 GrContextFactory::GrContextFactory() { }
51
GrContextFactory(const GrContextOptions & opts)52 GrContextFactory::GrContextFactory(const GrContextOptions& opts)
53 : fGlobalOptions(opts) {
54 }
55
~GrContextFactory()56 GrContextFactory::~GrContextFactory() {
57 this->destroyContexts();
58 }
59
destroyContexts()60 void GrContextFactory::destroyContexts() {
61 // We must delete the test contexts in reverse order so that any child context is finished and
62 // deleted before a parent context. This relies on the fact that when we make a new context we
63 // append it to the end of fContexts array.
64 // TODO: Look into keeping a dependency dag for contexts and deletion order
65 for (int i = fContexts.count() - 1; i >= 0; --i) {
66 Context& context = fContexts[i];
67 SkScopeExit restore(nullptr);
68 if (context.fTestContext) {
69 restore = context.fTestContext->makeCurrentAndAutoRestore();
70 }
71 if (!context.fGrContext->unique()) {
72 context.fGrContext->releaseResourcesAndAbandonContext();
73 context.fAbandoned = true;
74 }
75 context.fGrContext->unref();
76 delete context.fTestContext;
77 }
78 fContexts.reset();
79 }
80
abandonContexts()81 void GrContextFactory::abandonContexts() {
82 // We must abandon the test contexts in reverse order so that any child context is finished and
83 // abandoned before a parent context. This relies on the fact that when we make a new context we
84 // append it to the end of fContexts array.
85 // TODO: Look into keeping a dependency dag for contexts and deletion order
86 for (int i = fContexts.count() - 1; i >= 0; --i) {
87 Context& context = fContexts[i];
88 if (!context.fAbandoned) {
89 if (context.fTestContext) {
90 auto restore = context.fTestContext->makeCurrentAndAutoRestore();
91 context.fTestContext->testAbandon();
92 }
93 GrBackendApi api = context.fGrContext->backend();
94 bool requiresEarlyAbandon = api == GrBackendApi::kVulkan || api == GrBackendApi::kDawn;
95 if (requiresEarlyAbandon) {
96 context.fGrContext->abandonContext();
97 }
98 if (context.fTestContext) {
99 delete(context.fTestContext);
100 context.fTestContext = nullptr;
101 }
102 if (!requiresEarlyAbandon) {
103 context.fGrContext->abandonContext();
104 }
105 context.fAbandoned = true;
106 }
107 }
108 }
109
releaseResourcesAndAbandonContexts()110 void GrContextFactory::releaseResourcesAndAbandonContexts() {
111 // We must abandon the test contexts in reverse order so that any child context is finished and
112 // abandoned before a parent context. This relies on the fact that when we make a new context we
113 // append it to the end of fContexts array.
114 // TODO: Look into keeping a dependency dag for contexts and deletion order
115 for (int i = fContexts.count() - 1; i >= 0; --i) {
116 Context& context = fContexts[i];
117 SkScopeExit restore(nullptr);
118 if (!context.fAbandoned) {
119 if (context.fTestContext) {
120 restore = context.fTestContext->makeCurrentAndAutoRestore();
121 }
122 context.fGrContext->releaseResourcesAndAbandonContext();
123 if (context.fTestContext) {
124 delete context.fTestContext;
125 context.fTestContext = nullptr;
126 }
127 context.fAbandoned = true;
128 }
129 }
130 }
131
get(ContextType type,ContextOverrides overrides)132 GrDirectContext* GrContextFactory::get(ContextType type, ContextOverrides overrides) {
133 return this->getContextInfo(type, overrides).directContext();
134 }
135
getContextInfoInternal(ContextType type,ContextOverrides overrides,GrDirectContext * shareContext,uint32_t shareIndex)136 ContextInfo GrContextFactory::getContextInfoInternal(ContextType type, ContextOverrides overrides,
137 GrDirectContext* shareContext,
138 uint32_t shareIndex) {
139 // (shareIndex != 0) -> (shareContext != nullptr)
140 SkASSERT((shareIndex == 0) || (shareContext != nullptr));
141
142 for (int i = 0; i < fContexts.count(); ++i) {
143 Context& context = fContexts[i];
144 if (context.fType == type &&
145 context.fOverrides == overrides &&
146 context.fShareContext == shareContext &&
147 context.fShareIndex == shareIndex &&
148 !context.fAbandoned) {
149 context.fTestContext->makeCurrent();
150 return ContextInfo(context.fType, context.fTestContext, context.fGrContext,
151 context.fOptions);
152 }
153 }
154
155 // If we're trying to create a context in a share group, find the primary context
156 Context* primaryContext = nullptr;
157 if (shareContext) {
158 for (int i = 0; i < fContexts.count(); ++i) {
159 if (!fContexts[i].fAbandoned && fContexts[i].fGrContext == shareContext) {
160 primaryContext = &fContexts[i];
161 break;
162 }
163 }
164 SkASSERT(primaryContext && primaryContext->fType == type);
165 }
166
167 std::unique_ptr<TestContext> testCtx;
168 GrBackendApi backend = ContextTypeBackend(type);
169 switch (backend) {
170 #ifdef SK_GL
171 case GrBackendApi::kOpenGL: {
172 GLTestContext* glShareContext = primaryContext
173 ? static_cast<GLTestContext*>(primaryContext->fTestContext) : nullptr;
174 GLTestContext* glCtx;
175 switch (type) {
176 case kGL_ContextType:
177 glCtx = CreatePlatformGLTestContext(kGL_GrGLStandard, glShareContext);
178 break;
179 case kGLES_ContextType:
180 glCtx = CreatePlatformGLTestContext(kGLES_GrGLStandard, glShareContext);
181 break;
182 #if SK_ANGLE
183 case kANGLE_D3D9_ES2_ContextType:
184 glCtx = MakeANGLETestContext(ANGLEBackend::kD3D9, ANGLEContextVersion::kES2,
185 glShareContext).release();
186 // Chrome will only run on D3D9 with NVIDIA for 2012 and earlier drivers.
187 // (<= 269.73). We get shader link failures when testing on recent drivers
188 // using this backend.
189 if (glCtx) {
190 GrGLDriverInfo info = GrGLGetDriverInfo(glCtx->gl());
191 if (info.fANGLEVendor == GrGLVendor::kNVIDIA) {
192 delete glCtx;
193 return ContextInfo();
194 }
195 }
196 break;
197 case kANGLE_D3D11_ES2_ContextType:
198 glCtx = MakeANGLETestContext(ANGLEBackend::kD3D11, ANGLEContextVersion::kES2,
199 glShareContext).release();
200 break;
201 case kANGLE_D3D11_ES3_ContextType:
202 glCtx = MakeANGLETestContext(ANGLEBackend::kD3D11, ANGLEContextVersion::kES3,
203 glShareContext).release();
204 break;
205 case kANGLE_GL_ES2_ContextType:
206 glCtx = MakeANGLETestContext(ANGLEBackend::kOpenGL, ANGLEContextVersion::kES2,
207 glShareContext).release();
208 break;
209 case kANGLE_GL_ES3_ContextType:
210 glCtx = MakeANGLETestContext(ANGLEBackend::kOpenGL, ANGLEContextVersion::kES3,
211 glShareContext).release();
212 break;
213 #endif
214 #ifndef SK_NO_COMMAND_BUFFER
215 case kCommandBuffer_ES2_ContextType:
216 glCtx = CommandBufferGLTestContext::Create(2, glShareContext);
217 break;
218 case kCommandBuffer_ES3_ContextType:
219 glCtx = CommandBufferGLTestContext::Create(3, glShareContext);
220 break;
221 #endif
222 default:
223 return ContextInfo();
224 }
225 if (!glCtx) {
226 return ContextInfo();
227 }
228 if (glCtx->gl()->fStandard == kGLES_GrGLStandard &&
229 (overrides & ContextOverrides::kFakeGLESVersionAs2)) {
230 glCtx->overrideVersion("OpenGL ES 2.0", "OpenGL ES GLSL ES 1.00");
231 }
232 testCtx.reset(glCtx);
233 break;
234 }
235 #endif // SK_GL
236 #ifdef SK_VULKAN
237 case GrBackendApi::kVulkan: {
238 VkTestContext* vkSharedContext = primaryContext
239 ? static_cast<VkTestContext*>(primaryContext->fTestContext) : nullptr;
240 SkASSERT(kVulkan_ContextType == type);
241 testCtx.reset(CreatePlatformVkTestContext(vkSharedContext));
242 if (!testCtx) {
243 return ContextInfo();
244 }
245
246 // We previously had an issue where the VkDevice destruction would occasionally hang
247 // on systems with NVIDIA GPUs and having an existing GL context fixed it. Now (March
248 // 2020) we still need the GL context to keep Vulkan/TSAN bots from running incredibly
249 // slow. Perhaps this prevents repeated driver loading/unloading? Note that keeping
250 // a persistent VkTestContext around instead was tried and did not work.
251 if (!fSentinelGLContext) {
252 fSentinelGLContext.reset(CreatePlatformGLTestContext(kGL_GrGLStandard));
253 if (!fSentinelGLContext) {
254 fSentinelGLContext.reset(CreatePlatformGLTestContext(kGLES_GrGLStandard));
255 }
256 }
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.count(); ++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