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 "GrContextFactory.h"
10 #include "gl/GLTestContext.h"
11
12 #if SK_ANGLE
13 #include "gl/angle/GLTestContext_angle.h"
14 #endif
15 #include "gl/command_buffer/GLTestContext_command_buffer.h"
16 #include "gl/debug/DebugGLTestContext.h"
17 #ifdef SK_VULKAN
18 #include "vk/VkTestContext.h"
19 #endif
20 #ifdef SK_METAL
21 #include "mtl/MtlTestContext.h"
22 #endif
23 #include "gl/null/NullGLTestContext.h"
24 #include "gl/GrGLGpu.h"
25 #include "mock/MockTestContext.h"
26 #include "GrCaps.h"
27
28 #if defined(SK_BUILD_FOR_WIN) && defined(SK_ENABLE_DISCRETE_GPU)
29 extern "C" {
30 // NVIDIA documents that the presence and value of this symbol programmatically enable the high
31 // performance GPU in laptops with switchable graphics.
32 // https://docs.nvidia.com/gameworks/content/technologies/desktop/optimus.htm
33 // From testing, including this symbol, even if it is set to 0, we still get the NVIDIA GPU.
34 _declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
35
36 // AMD has a similar mechanism, although I don't have an AMD laptop, so this is untested.
37 // https://community.amd.com/thread/169965
38 __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
39 }
40 #endif
41
42 namespace sk_gpu_test {
GrContextFactory()43 GrContextFactory::GrContextFactory() { }
44
GrContextFactory(const GrContextOptions & opts)45 GrContextFactory::GrContextFactory(const GrContextOptions& opts)
46 : fGlobalOptions(opts) {
47 }
48
~GrContextFactory()49 GrContextFactory::~GrContextFactory() {
50 this->destroyContexts();
51 }
52
destroyContexts()53 void GrContextFactory::destroyContexts() {
54 // We must delete the test contexts in reverse order so that any child context is finished and
55 // deleted before a parent context. This relies on the fact that when we make a new context we
56 // append it to the end of fContexts array.
57 // TODO: Look into keeping a dependency dag for contexts and deletion order
58 for (int i = fContexts.count() - 1; i >= 0; --i) {
59 Context& context = fContexts[i];
60 SkScopeExit restore(nullptr);
61 if (context.fTestContext) {
62 restore = context.fTestContext->makeCurrentAndAutoRestore();
63 }
64 if (!context.fGrContext->unique()) {
65 context.fGrContext->releaseResourcesAndAbandonContext();
66 context.fAbandoned = true;
67 }
68 context.fGrContext->unref();
69 delete context.fTestContext;
70 }
71 fContexts.reset();
72 }
73
abandonContexts()74 void GrContextFactory::abandonContexts() {
75 // We must abandon the test contexts in reverse order so that any child context is finished and
76 // abandoned before a parent context. This relies on the fact that when we make a new context we
77 // append it to the end of fContexts array.
78 // TODO: Look into keeping a dependency dag for contexts and deletion order
79 for (int i = fContexts.count() - 1; i >= 0; --i) {
80 Context& context = fContexts[i];
81 if (!context.fAbandoned) {
82 if (context.fTestContext) {
83 auto restore = context.fTestContext->makeCurrentAndAutoRestore();
84 context.fTestContext->testAbandon();
85 delete(context.fTestContext);
86 context.fTestContext = nullptr;
87 }
88 context.fGrContext->abandonContext();
89 context.fAbandoned = true;
90 }
91 }
92 }
93
releaseResourcesAndAbandonContexts()94 void GrContextFactory::releaseResourcesAndAbandonContexts() {
95 // We must abandon the test contexts in reverse order so that any child context is finished and
96 // abandoned before a parent context. This relies on the fact that when we make a new context we
97 // append it to the end of fContexts array.
98 // TODO: Look into keeping a dependency dag for contexts and deletion order
99 for (int i = fContexts.count() - 1; i >= 0; --i) {
100 Context& context = fContexts[i];
101 SkScopeExit restore(nullptr);
102 if (!context.fAbandoned) {
103 if (context.fTestContext) {
104 restore = context.fTestContext->makeCurrentAndAutoRestore();
105 }
106 context.fGrContext->releaseResourcesAndAbandonContext();
107 context.fAbandoned = true;
108 if (context.fTestContext) {
109 delete context.fTestContext;
110 context.fTestContext = nullptr;
111 }
112 }
113 }
114 }
115
get(ContextType type,ContextOverrides overrides)116 GrContext* GrContextFactory::get(ContextType type, ContextOverrides overrides) {
117 return this->getContextInfo(type, overrides).grContext();
118 }
119
getContextInfoInternal(ContextType type,ContextOverrides overrides,GrContext * shareContext,uint32_t shareIndex)120 ContextInfo GrContextFactory::getContextInfoInternal(ContextType type, ContextOverrides overrides,
121 GrContext* shareContext, uint32_t shareIndex) {
122 // (shareIndex != 0) -> (shareContext != nullptr)
123 SkASSERT((shareIndex == 0) || (shareContext != nullptr));
124
125 for (int i = 0; i < fContexts.count(); ++i) {
126 Context& context = fContexts[i];
127 if (context.fType == type &&
128 context.fOverrides == overrides &&
129 context.fShareContext == shareContext &&
130 context.fShareIndex == shareIndex &&
131 !context.fAbandoned) {
132 context.fTestContext->makeCurrent();
133 return ContextInfo(context.fType, context.fTestContext, context.fGrContext,
134 context.fOptions);
135 }
136 }
137
138 // If we're trying to create a context in a share group, find the master context
139 Context* masterContext = nullptr;
140 if (shareContext) {
141 for (int i = 0; i < fContexts.count(); ++i) {
142 if (!fContexts[i].fAbandoned && fContexts[i].fGrContext == shareContext) {
143 masterContext = &fContexts[i];
144 break;
145 }
146 }
147 SkASSERT(masterContext && masterContext->fType == type);
148 }
149
150 std::unique_ptr<TestContext> testCtx;
151 GrBackend backend = ContextTypeBackend(type);
152 switch (backend) {
153 case kOpenGL_GrBackend: {
154 GLTestContext* glShareContext = masterContext
155 ? static_cast<GLTestContext*>(masterContext->fTestContext) : nullptr;
156 GLTestContext* glCtx;
157 switch (type) {
158 case kGL_ContextType:
159 glCtx = CreatePlatformGLTestContext(kGL_GrGLStandard, glShareContext);
160 break;
161 case kGLES_ContextType:
162 glCtx = CreatePlatformGLTestContext(kGLES_GrGLStandard, glShareContext);
163 break;
164 #if SK_ANGLE
165 case kANGLE_D3D9_ES2_ContextType:
166 glCtx = MakeANGLETestContext(ANGLEBackend::kD3D9, ANGLEContextVersion::kES2,
167 glShareContext).release();
168 break;
169 case kANGLE_D3D11_ES2_ContextType:
170 glCtx = MakeANGLETestContext(ANGLEBackend::kD3D11, ANGLEContextVersion::kES2,
171 glShareContext).release();
172 break;
173 case kANGLE_D3D11_ES3_ContextType:
174 glCtx = MakeANGLETestContext(ANGLEBackend::kD3D11, ANGLEContextVersion::kES3,
175 glShareContext).release();
176 break;
177 case kANGLE_GL_ES2_ContextType:
178 glCtx = MakeANGLETestContext(ANGLEBackend::kOpenGL, ANGLEContextVersion::kES2,
179 glShareContext).release();
180 break;
181 case kANGLE_GL_ES3_ContextType:
182 glCtx = MakeANGLETestContext(ANGLEBackend::kOpenGL, ANGLEContextVersion::kES3,
183 glShareContext).release();
184 break;
185 #endif
186 #ifndef SK_NO_COMMAND_BUFFER
187 case kCommandBuffer_ContextType:
188 glCtx = CommandBufferGLTestContext::Create(glShareContext);
189 break;
190 #endif
191 case kNullGL_ContextType:
192 glCtx = CreateNullGLTestContext(
193 ContextOverrides::kRequireNVPRSupport & overrides, glShareContext);
194 break;
195 case kDebugGL_ContextType:
196 glCtx = CreateDebugGLTestContext(glShareContext);
197 break;
198 default:
199 return ContextInfo();
200 }
201 if (!glCtx) {
202 return ContextInfo();
203 }
204 testCtx.reset(glCtx);
205 break;
206 }
207 #ifdef SK_VULKAN
208 case kVulkan_GrBackend: {
209 VkTestContext* vkSharedContext = masterContext
210 ? static_cast<VkTestContext*>(masterContext->fTestContext) : nullptr;
211 SkASSERT(kVulkan_ContextType == type);
212 if (ContextOverrides::kRequireNVPRSupport & overrides) {
213 return ContextInfo();
214 }
215 testCtx.reset(CreatePlatformVkTestContext(vkSharedContext));
216 if (!testCtx) {
217 return ContextInfo();
218 }
219
220 // There is some bug (either in Skia or the NV Vulkan driver) where VkDevice
221 // destruction will hang occaisonally. For some reason having an existing GL
222 // context fixes this.
223 if (!fSentinelGLContext) {
224 fSentinelGLContext.reset(CreatePlatformGLTestContext(kGL_GrGLStandard));
225 if (!fSentinelGLContext) {
226 fSentinelGLContext.reset(CreatePlatformGLTestContext(kGLES_GrGLStandard));
227 }
228 }
229 break;
230 }
231 #endif
232 #ifdef SK_METAL
233 case kMetal_GrBackend: {
234 SkASSERT(!masterContext);
235 testCtx.reset(CreatePlatformMtlTestContext(nullptr));
236 if (!testCtx) {
237 return ContextInfo();
238 }
239 break;
240 }
241 #endif
242 case kMock_GrBackend: {
243 TestContext* sharedContext = masterContext ? masterContext->fTestContext : nullptr;
244 SkASSERT(kMock_ContextType == type);
245 if (ContextOverrides::kRequireNVPRSupport & overrides) {
246 return ContextInfo();
247 }
248 testCtx.reset(CreateMockTestContext(sharedContext));
249 if (!testCtx) {
250 return ContextInfo();
251 }
252 break;
253 }
254 default:
255 return ContextInfo();
256 }
257
258 SkASSERT(testCtx && testCtx->backend() == backend);
259 GrContextOptions grOptions = fGlobalOptions;
260 if (ContextOverrides::kDisableNVPR & overrides) {
261 grOptions.fSuppressPathRendering = true;
262 }
263 if (ContextOverrides::kAllowSRGBWithoutDecodeControl & overrides) {
264 grOptions.fRequireDecodeDisableForSRGB = false;
265 }
266 if (ContextOverrides::kAvoidStencilBuffers & overrides) {
267 grOptions.fAvoidStencilBuffers = true;
268 }
269 sk_sp<GrContext> grCtx;
270 {
271 auto restore = testCtx->makeCurrentAndAutoRestore();
272 grCtx = testCtx->makeGrContext(grOptions);
273 }
274 if (!grCtx.get()) {
275 return ContextInfo();
276 }
277 if (ContextOverrides::kRequireNVPRSupport & overrides) {
278 if (!grCtx->caps()->shaderCaps()->pathRenderingSupport()) {
279 return ContextInfo();
280 }
281 }
282 if (ContextOverrides::kRequireSRGBSupport & overrides) {
283 if (!grCtx->caps()->srgbSupport()) {
284 return ContextInfo();
285 }
286 }
287
288 // We must always add new contexts by pushing to the back so that when we delete them we delete
289 // them in reverse order in which they were made.
290 Context& context = fContexts.push_back();
291 context.fBackend = backend;
292 context.fTestContext = testCtx.release();
293 context.fGrContext = SkRef(grCtx.get());
294 context.fType = type;
295 context.fOverrides = overrides;
296 context.fAbandoned = false;
297 context.fShareContext = shareContext;
298 context.fShareIndex = shareIndex;
299 context.fOptions = grOptions;
300 context.fTestContext->makeCurrent();
301 return ContextInfo(context.fType, context.fTestContext, context.fGrContext, context.fOptions);
302 }
303
getContextInfo(ContextType type,ContextOverrides overrides)304 ContextInfo GrContextFactory::getContextInfo(ContextType type, ContextOverrides overrides) {
305 return this->getContextInfoInternal(type, overrides, nullptr, 0);
306 }
307
getSharedContextInfo(GrContext * shareContext,uint32_t shareIndex)308 ContextInfo GrContextFactory::getSharedContextInfo(GrContext* shareContext, uint32_t shareIndex) {
309 SkASSERT(shareContext);
310 for (int i = 0; i < fContexts.count(); ++i) {
311 if (!fContexts[i].fAbandoned && fContexts[i].fGrContext == shareContext) {
312 return this->getContextInfoInternal(fContexts[i].fType, fContexts[i].fOverrides,
313 shareContext, shareIndex);
314 }
315 }
316
317 return ContextInfo();
318 }
319
320 } // namespace sk_gpu_test
321