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