Home | History | Annotate | Download | only in gpu
      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