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 { 43 GrContextFactory::GrContextFactory() { } 44 45 GrContextFactory::GrContextFactory(const GrContextOptions& opts) 46 : fGlobalOptions(opts) { 47 } 48 49 GrContextFactory::~GrContextFactory() { 50 this->destroyContexts(); 51 } 52 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 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 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 116 GrContext* GrContextFactory::get(ContextType type, ContextOverrides overrides) { 117 return this->getContextInfo(type, overrides).grContext(); 118 } 119 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 304 ContextInfo GrContextFactory::getContextInfo(ContextType type, ContextOverrides overrides) { 305 return this->getContextInfoInternal(type, overrides, nullptr, 0); 306 } 307 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