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 #if SK_MESA
18     #include "gl/mesa/GLTestContext_mesa.h"
19 #endif
20 #ifdef SK_VULKAN
21 #include "vk/VkTestContext.h"
22 #endif
23 #include "gl/null/NullGLTestContext.h"
24 #include "gl/GrGLGpu.h"
25 #include "GrCaps.h"
26 
27 #if defined(SK_BUILD_FOR_WIN32) && defined(SK_ENABLE_DISCRETE_GPU)
28 extern "C" {
29     // NVIDIA documents that the presence and value of this symbol programmatically enable the high
30     // performance GPU in laptops with switchable graphics.
31     //   https://docs.nvidia.com/gameworks/content/technologies/desktop/optimus.htm
32     // From testing, including this symbol, even if it is set to 0, we still get the NVIDIA GPU.
33     _declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
34 
35     // AMD has a similar mechanism, although I don't have an AMD laptop, so this is untested.
36     //   https://community.amd.com/thread/169965
37     __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
38 }
39 #endif
40 
41 namespace sk_gpu_test {
GrContextFactory()42 GrContextFactory::GrContextFactory() { }
43 
GrContextFactory(const GrContextOptions & opts)44 GrContextFactory::GrContextFactory(const GrContextOptions& opts)
45     : fGlobalOptions(opts) {
46 }
47 
~GrContextFactory()48 GrContextFactory::~GrContextFactory() {
49     this->destroyContexts();
50 }
51 
destroyContexts()52 void GrContextFactory::destroyContexts() {
53     for (Context& context : fContexts) {
54         if (context.fTestContext) {
55             context.fTestContext->makeCurrent();
56         }
57         if (!context.fGrContext->unique()) {
58             context.fGrContext->releaseResourcesAndAbandonContext();
59             context.fAbandoned = true;
60         }
61         context.fGrContext->unref();
62         delete context.fTestContext;
63     }
64     fContexts.reset();
65 }
66 
abandonContexts()67 void GrContextFactory::abandonContexts() {
68     for (Context& context : fContexts) {
69         if (!context.fAbandoned) {
70             if (context.fTestContext) {
71                 context.fTestContext->makeCurrent();
72                 context.fTestContext->testAbandon();
73                 delete(context.fTestContext);
74                 context.fTestContext = nullptr;
75             }
76             context.fGrContext->abandonContext();
77             context.fAbandoned = true;
78         }
79     }
80 }
81 
releaseResourcesAndAbandonContexts()82 void GrContextFactory::releaseResourcesAndAbandonContexts() {
83     for (Context& context : fContexts) {
84         if (!context.fAbandoned) {
85             if (context.fTestContext) {
86                 context.fTestContext->makeCurrent();
87             }
88             context.fGrContext->releaseResourcesAndAbandonContext();
89             context.fAbandoned = true;
90             if (context.fTestContext) {
91                 delete context.fTestContext;
92                 context.fTestContext = nullptr;
93             }
94         }
95     }
96 }
97 
getContextInfoInternal(ContextType type,ContextOverrides overrides,GrContext * shareContext,uint32_t shareIndex)98 ContextInfo GrContextFactory::getContextInfoInternal(ContextType type, ContextOverrides overrides,
99                                                      GrContext* shareContext, uint32_t shareIndex) {
100     // (shareIndex != 0) -> (shareContext != nullptr)
101     SkASSERT((shareIndex == 0) || (shareContext != nullptr));
102 
103     for (int i = 0; i < fContexts.count(); ++i) {
104         Context& context = fContexts[i];
105         if (context.fType == type &&
106             context.fOverrides == overrides &&
107             context.fShareContext == shareContext &&
108             context.fShareIndex == shareIndex &&
109             !context.fAbandoned) {
110             context.fTestContext->makeCurrent();
111             return ContextInfo(context.fBackend, context.fTestContext, context.fGrContext);
112         }
113     }
114 
115     // If we're trying to create a context in a share group, find the master context
116     Context* masterContext = nullptr;
117     if (shareContext) {
118         for (int i = 0; i < fContexts.count(); ++i) {
119             if (!fContexts[i].fAbandoned && fContexts[i].fGrContext == shareContext) {
120                 masterContext = &fContexts[i];
121                 break;
122             }
123         }
124         SkASSERT(masterContext && masterContext->fType == type);
125     }
126 
127     std::unique_ptr<TestContext> testCtx;
128     GrBackendContext backendContext = 0;
129     sk_sp<const GrGLInterface> glInterface;
130     GrBackend backend = ContextTypeBackend(type);
131     switch (backend) {
132         case kOpenGL_GrBackend: {
133             GLTestContext* glShareContext = masterContext
134                     ? static_cast<GLTestContext*>(masterContext->fTestContext) : nullptr;
135             GLTestContext* glCtx;
136             switch (type) {
137                 case kGL_ContextType:
138                     glCtx = CreatePlatformGLTestContext(kGL_GrGLStandard, glShareContext);
139                     break;
140                 case kGLES_ContextType:
141                     glCtx = CreatePlatformGLTestContext(kGLES_GrGLStandard, glShareContext);
142                     break;
143 #if SK_ANGLE
144                 case kANGLE_D3D9_ES2_ContextType:
145                     glCtx = MakeANGLETestContext(ANGLEBackend::kD3D9, ANGLEContextVersion::kES2,
146                                                  glShareContext).release();
147                     break;
148                 case kANGLE_D3D11_ES2_ContextType:
149                     glCtx = MakeANGLETestContext(ANGLEBackend::kD3D11, ANGLEContextVersion::kES2,
150                                                  glShareContext).release();
151                     break;
152                 case kANGLE_D3D11_ES3_ContextType:
153                     glCtx = MakeANGLETestContext(ANGLEBackend::kD3D11, ANGLEContextVersion::kES3,
154                                                  glShareContext).release();
155                     break;
156                 case kANGLE_GL_ES2_ContextType:
157                     glCtx = MakeANGLETestContext(ANGLEBackend::kOpenGL, ANGLEContextVersion::kES2,
158                                                  glShareContext).release();
159                     break;
160                 case kANGLE_GL_ES3_ContextType:
161                     glCtx = MakeANGLETestContext(ANGLEBackend::kOpenGL, ANGLEContextVersion::kES3,
162                                                  glShareContext).release();
163                     break;
164 #endif
165                 case kCommandBuffer_ContextType:
166                     glCtx = CommandBufferGLTestContext::Create(glShareContext);
167                     break;
168 #if SK_MESA
169                 case kMESA_ContextType:
170                     glCtx = CreateMesaGLTestContext(glShareContext);
171                     break;
172 #endif
173                 case kNullGL_ContextType:
174                     glCtx = CreateNullGLTestContext(
175                             ContextOverrides::kRequireNVPRSupport & overrides, glShareContext);
176                     break;
177                 case kDebugGL_ContextType:
178                     glCtx = CreateDebugGLTestContext(glShareContext);
179                     break;
180                 default:
181                     return ContextInfo();
182             }
183             if (!glCtx) {
184                 return ContextInfo();
185             }
186             testCtx.reset(glCtx);
187             glInterface.reset(SkRef(glCtx->gl()));
188             backendContext = reinterpret_cast<GrBackendContext>(glInterface.get());
189             break;
190         }
191 #ifdef SK_VULKAN
192         case kVulkan_GrBackend:
193             if (masterContext) {
194                 // Shared contexts not supported yet
195                 return ContextInfo();
196             }
197             SkASSERT(kVulkan_ContextType == type);
198             if (ContextOverrides::kRequireNVPRSupport & overrides) {
199                 return ContextInfo();
200             }
201             testCtx.reset(CreatePlatformVkTestContext());
202             if (!testCtx) {
203                 return ContextInfo();
204             }
205 
206             // There is some bug (either in Skia or the NV Vulkan driver) where VkDevice
207             // destruction will hang occaisonally. For some reason having an existing GL
208             // context fixes this.
209             if (!fSentinelGLContext) {
210                 fSentinelGLContext.reset(CreatePlatformGLTestContext(kGL_GrGLStandard));
211                 if (!fSentinelGLContext) {
212                     fSentinelGLContext.reset(CreatePlatformGLTestContext(kGLES_GrGLStandard));
213                 }
214             }
215             backendContext = testCtx->backendContext();
216             break;
217 #endif
218         default:
219             return ContextInfo();
220     }
221     testCtx->makeCurrent();
222     SkASSERT(testCtx && testCtx->backend() == backend);
223     GrContextOptions grOptions = fGlobalOptions;
224     if (ContextOverrides::kDisableNVPR & overrides) {
225         grOptions.fSuppressPathRendering = true;
226     }
227     if (ContextOverrides::kUseInstanced & overrides) {
228         grOptions.fEnableInstancedRendering = true;
229     }
230     if (ContextOverrides::kAllowSRGBWithoutDecodeControl & overrides) {
231         grOptions.fRequireDecodeDisableForSRGB = false;
232     }
233     sk_sp<GrContext> grCtx(GrContext::Create(backend, backendContext, grOptions));
234     if (!grCtx.get()) {
235         return ContextInfo();
236     }
237     if (ContextOverrides::kRequireNVPRSupport & overrides) {
238         if (!grCtx->caps()->shaderCaps()->pathRenderingSupport()) {
239             return ContextInfo();
240         }
241     }
242     if (ContextOverrides::kUseInstanced & overrides) {
243         if (GrCaps::InstancedSupport::kNone == grCtx->caps()->instancedSupport()) {
244             return ContextInfo();
245         }
246     }
247     if (ContextOverrides::kRequireSRGBSupport & overrides) {
248         if (!grCtx->caps()->srgbSupport()) {
249             return ContextInfo();
250         }
251     }
252 
253     Context& context = fContexts.push_back();
254     context.fBackend = backend;
255     context.fTestContext = testCtx.release();
256     context.fGrContext = SkRef(grCtx.get());
257     context.fType = type;
258     context.fOverrides = overrides;
259     context.fAbandoned = false;
260     context.fShareContext = shareContext;
261     context.fShareIndex = shareIndex;
262     return ContextInfo(context.fBackend, context.fTestContext, context.fGrContext);
263 }
264 
getContextInfo(ContextType type,ContextOverrides overrides)265 ContextInfo GrContextFactory::getContextInfo(ContextType type, ContextOverrides overrides) {
266     return this->getContextInfoInternal(type, overrides, nullptr, 0);
267 }
268 
getSharedContextInfo(GrContext * shareContext,uint32_t shareIndex)269 ContextInfo GrContextFactory::getSharedContextInfo(GrContext* shareContext, uint32_t shareIndex) {
270     SkASSERT(shareContext);
271     for (int i = 0; i < fContexts.count(); ++i) {
272         if (!fContexts[i].fAbandoned && fContexts[i].fGrContext == shareContext) {
273             return this->getContextInfoInternal(fContexts[i].fType, fContexts[i].fOverrides,
274                                                 shareContext, shareIndex);
275         }
276     }
277 
278     return ContextInfo();
279 }
280 
281 }  // namespace sk_gpu_test
282