Home | History | Annotate | Download | only in win
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkTypes.h"
      9 #if defined(SK_BUILD_FOR_WIN) && !defined(_M_ARM64)
     10 
     11 #include "SkWGL.h"
     12 
     13 #include "SkOnce.h"
     14 #include "SkTDArray.h"
     15 #include "SkTSearch.h"
     16 #include "SkTSort.h"
     17 
     18 bool SkWGLExtensions::hasExtension(HDC dc, const char* ext) const {
     19     if (nullptr == this->fGetExtensionsString) {
     20         return false;
     21     }
     22     if (!strcmp("WGL_ARB_extensions_string", ext)) {
     23         return true;
     24     }
     25     const char* extensionString = this->getExtensionsString(dc);
     26     size_t extLength = strlen(ext);
     27 
     28     while (true) {
     29         size_t n = strcspn(extensionString, " ");
     30         if (n == extLength && 0 == strncmp(ext, extensionString, n)) {
     31             return true;
     32         }
     33         if (0 == extensionString[n]) {
     34             return false;
     35         }
     36         extensionString += n+1;
     37     }
     38 
     39     return false;
     40 }
     41 
     42 const char* SkWGLExtensions::getExtensionsString(HDC hdc) const {
     43     return fGetExtensionsString(hdc);
     44 }
     45 
     46 BOOL SkWGLExtensions::choosePixelFormat(HDC hdc,
     47                                         const int* piAttribIList,
     48                                         const FLOAT* pfAttribFList,
     49                                         UINT nMaxFormats,
     50                                         int* piFormats,
     51                                         UINT* nNumFormats) const {
     52     return fChoosePixelFormat(hdc, piAttribIList, pfAttribFList,
     53                               nMaxFormats, piFormats, nNumFormats);
     54 }
     55 
     56 BOOL SkWGLExtensions::getPixelFormatAttribiv(HDC hdc,
     57                                              int iPixelFormat,
     58                                              int iLayerPlane,
     59                                              UINT nAttributes,
     60                                              const int *piAttributes,
     61                                              int *piValues) const {
     62     return fGetPixelFormatAttribiv(hdc, iPixelFormat, iLayerPlane,
     63                                    nAttributes, piAttributes, piValues);
     64 }
     65 
     66 BOOL SkWGLExtensions::getPixelFormatAttribfv(HDC hdc,
     67                                              int iPixelFormat,
     68                                              int iLayerPlane,
     69                                              UINT nAttributes,
     70                                              const int *piAttributes,
     71                                              float *pfValues) const {
     72     return fGetPixelFormatAttribfv(hdc, iPixelFormat, iLayerPlane,
     73                                    nAttributes, piAttributes, pfValues);
     74 }
     75 HGLRC SkWGLExtensions::createContextAttribs(HDC hDC,
     76                                             HGLRC hShareContext,
     77                                             const int *attribList) const {
     78     return fCreateContextAttribs(hDC, hShareContext, attribList);
     79 }
     80 
     81 BOOL SkWGLExtensions::swapInterval(int interval) const {
     82     return fSwapInterval(interval);
     83 }
     84 
     85 HPBUFFER SkWGLExtensions::createPbuffer(HDC hDC,
     86                                         int iPixelFormat,
     87                                         int iWidth,
     88                                         int iHeight,
     89                                         const int *piAttribList) const {
     90     return fCreatePbuffer(hDC, iPixelFormat, iWidth, iHeight, piAttribList);
     91 }
     92 
     93 HDC SkWGLExtensions::getPbufferDC(HPBUFFER hPbuffer) const {
     94     return fGetPbufferDC(hPbuffer);
     95 }
     96 
     97 int SkWGLExtensions::releasePbufferDC(HPBUFFER hPbuffer, HDC hDC) const {
     98     return fReleasePbufferDC(hPbuffer, hDC);
     99 }
    100 
    101 BOOL SkWGLExtensions::destroyPbuffer(HPBUFFER hPbuffer) const {
    102     return fDestroyPbuffer(hPbuffer);
    103 }
    104 
    105 namespace {
    106 
    107 struct PixelFormat {
    108     int fFormat;
    109     int fSampleCnt;
    110     int fChoosePixelFormatRank;
    111 };
    112 
    113 bool pf_less(const PixelFormat& a, const PixelFormat& b) {
    114     if (a.fSampleCnt < b.fSampleCnt) {
    115         return true;
    116     } else if (b.fSampleCnt < a.fSampleCnt) {
    117         return false;
    118     } else if (a.fChoosePixelFormatRank < b.fChoosePixelFormatRank) {
    119         return true;
    120     }
    121     return false;
    122 }
    123 }
    124 
    125 int SkWGLExtensions::selectFormat(const int formats[],
    126                                   int formatCount,
    127                                   HDC dc,
    128                                   int desiredSampleCount) const {
    129     SkASSERT(desiredSampleCount >= 1);
    130     if (formatCount <= 0) {
    131         return -1;
    132     }
    133     PixelFormat desiredFormat = {
    134         0,
    135         desiredSampleCount,
    136         0,
    137     };
    138     SkTDArray<PixelFormat> rankedFormats;
    139     rankedFormats.setCount(formatCount);
    140     for (int i = 0; i < formatCount; ++i) {
    141         static const int kQueryAttr = SK_WGL_SAMPLES;
    142         int numSamples;
    143         this->getPixelFormatAttribiv(dc,
    144                                      formats[i],
    145                                      0,
    146                                      1,
    147                                      &kQueryAttr,
    148                                      &numSamples);
    149         rankedFormats[i].fFormat =  formats[i];
    150         rankedFormats[i].fSampleCnt = SkTMax(1, numSamples);
    151         rankedFormats[i].fChoosePixelFormatRank = i;
    152     }
    153     SkTQSort(rankedFormats.begin(),
    154              rankedFormats.begin() + rankedFormats.count() - 1,
    155              SkTLessFunctionToFunctorAdaptor<PixelFormat, pf_less>());
    156     int idx = SkTSearch<PixelFormat, pf_less>(rankedFormats.begin(),
    157                                               rankedFormats.count(),
    158                                               desiredFormat,
    159                                               sizeof(PixelFormat));
    160     if (idx < 0) {
    161         idx = ~idx;
    162     }
    163     // If the caller asked for non-MSAA fail if the closest format has MSAA.
    164     if (desiredSampleCount == 1 && rankedFormats[idx].fSampleCnt != 1) {
    165         return -1;
    166     }
    167     return rankedFormats[idx].fFormat;
    168 }
    169 
    170 
    171 namespace {
    172 
    173 #if defined(UNICODE)
    174     #define STR_LIT(X) L## #X
    175 #else
    176     #define STR_LIT(X) #X
    177 #endif
    178 
    179 #define DUMMY_CLASS STR_LIT("DummyClass")
    180 
    181 HWND create_dummy_window() {
    182     HMODULE module = GetModuleHandle(nullptr);
    183     HWND dummy;
    184     RECT windowRect;
    185     windowRect.left = 0;
    186     windowRect.right = 8;
    187     windowRect.top = 0;
    188     windowRect.bottom = 8;
    189 
    190     WNDCLASS wc;
    191 
    192     wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    193     wc.lpfnWndProc = (WNDPROC) DefWindowProc;
    194     wc.cbClsExtra = 0;
    195     wc.cbWndExtra = 0;
    196     wc.hInstance = module;
    197     wc.hIcon = LoadIcon(nullptr, IDI_WINLOGO);
    198     wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    199     wc.hbrBackground = nullptr;
    200     wc.lpszMenuName = nullptr;
    201     wc.lpszClassName = DUMMY_CLASS;
    202 
    203     if(!RegisterClass(&wc)) {
    204         return 0;
    205     }
    206 
    207     DWORD style, exStyle;
    208     exStyle = WS_EX_CLIENTEDGE;
    209     style = WS_SYSMENU;
    210 
    211     AdjustWindowRectEx(&windowRect, style, false, exStyle);
    212     if(!(dummy = CreateWindowEx(exStyle,
    213                                 DUMMY_CLASS,
    214                                 STR_LIT("DummyWindow"),
    215                                 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | style,
    216                                 0, 0,
    217                                 windowRect.right-windowRect.left,
    218                                 windowRect.bottom-windowRect.top,
    219                                 nullptr, nullptr,
    220                                 module,
    221                                 nullptr))) {
    222         UnregisterClass(DUMMY_CLASS, module);
    223         return nullptr;
    224     }
    225     ShowWindow(dummy, SW_HIDE);
    226 
    227     return dummy;
    228 }
    229 
    230 void destroy_dummy_window(HWND dummy) {
    231     DestroyWindow(dummy);
    232     HMODULE module = GetModuleHandle(nullptr);
    233     UnregisterClass(DUMMY_CLASS, module);
    234 }
    235 }
    236 
    237 #define GET_PROC(NAME, SUFFIX) f##NAME = \
    238                      (NAME##Proc) wglGetProcAddress("wgl" #NAME #SUFFIX)
    239 
    240 
    241 SkWGLExtensions::GetExtensionsStringProc SkWGLExtensions::fGetExtensionsString = nullptr;
    242 SkWGLExtensions::ChoosePixelFormatProc SkWGLExtensions::fChoosePixelFormat = nullptr;
    243 SkWGLExtensions::GetPixelFormatAttribfvProc SkWGLExtensions::fGetPixelFormatAttribfv = nullptr;
    244 SkWGLExtensions::GetPixelFormatAttribivProc SkWGLExtensions::fGetPixelFormatAttribiv = nullptr;
    245 SkWGLExtensions::CreateContextAttribsProc SkWGLExtensions::fCreateContextAttribs = nullptr;
    246 SkWGLExtensions::SwapIntervalProc SkWGLExtensions::fSwapInterval = nullptr;
    247 SkWGLExtensions::CreatePbufferProc SkWGLExtensions::fCreatePbuffer = nullptr;
    248 SkWGLExtensions::GetPbufferDCProc SkWGLExtensions::fGetPbufferDC = nullptr;
    249 SkWGLExtensions::ReleasePbufferDCProc SkWGLExtensions::fReleasePbufferDC = nullptr;
    250 SkWGLExtensions::DestroyPbufferProc SkWGLExtensions::fDestroyPbuffer = nullptr;
    251 
    252 SkWGLExtensions::SkWGLExtensions() {
    253     // We cache these function pointers once, and then reuse them. That's possibly incorrect if
    254     // there are multiple GPUs, or if we intend to use these for rendering contexts of different
    255     // pixel formats (where wglGetProcAddress is not guaranteed to return the same pointer).
    256     static SkOnce once;
    257     once([] {
    258         HDC prevDC = wglGetCurrentDC();
    259         HGLRC prevGLRC = wglGetCurrentContext();
    260 
    261         PIXELFORMATDESCRIPTOR dummyPFD;
    262 
    263         ZeroMemory(&dummyPFD, sizeof(dummyPFD));
    264         dummyPFD.nSize = sizeof(dummyPFD);
    265         dummyPFD.nVersion = 1;
    266         dummyPFD.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
    267         dummyPFD.iPixelType = PFD_TYPE_RGBA;
    268         dummyPFD.cColorBits  = 32;
    269         dummyPFD.cDepthBits  = 0;
    270         dummyPFD.cStencilBits = 8;
    271         dummyPFD.iLayerType = PFD_MAIN_PLANE;
    272         HWND dummyWND = create_dummy_window();
    273         if (dummyWND) {
    274             HDC dummyDC = GetDC(dummyWND);
    275             int dummyFormat = ChoosePixelFormat(dummyDC, &dummyPFD);
    276             SetPixelFormat(dummyDC, dummyFormat, &dummyPFD);
    277             HGLRC dummyGLRC = wglCreateContext(dummyDC);
    278             SkASSERT(dummyGLRC);
    279             wglMakeCurrent(dummyDC, dummyGLRC);
    280 
    281             GET_PROC(GetExtensionsString, ARB);
    282             GET_PROC(ChoosePixelFormat, ARB);
    283             GET_PROC(GetPixelFormatAttribiv, ARB);
    284             GET_PROC(GetPixelFormatAttribfv, ARB);
    285             GET_PROC(CreateContextAttribs, ARB);
    286             GET_PROC(SwapInterval, EXT);
    287             GET_PROC(CreatePbuffer, ARB);
    288             GET_PROC(GetPbufferDC, ARB);
    289             GET_PROC(ReleasePbufferDC, ARB);
    290             GET_PROC(DestroyPbuffer, ARB);
    291 
    292             wglMakeCurrent(dummyDC, nullptr);
    293             wglDeleteContext(dummyGLRC);
    294             destroy_dummy_window(dummyWND);
    295         }
    296 
    297         wglMakeCurrent(prevDC, prevGLRC);
    298     });
    299 }
    300 
    301 ///////////////////////////////////////////////////////////////////////////////
    302 
    303 static void get_pixel_formats_to_try(HDC dc, const SkWGLExtensions& extensions,
    304                                      bool doubleBuffered, int msaaSampleCount, bool deepColor,
    305                                      int formatsToTry[2]) {
    306     auto appendAttr = [](SkTDArray<int>& attrs, int attr, int value) {
    307         attrs.push_back(attr);
    308         attrs.push_back(value);
    309     };
    310 
    311     SkTDArray<int> iAttrs;
    312     appendAttr(iAttrs, SK_WGL_DRAW_TO_WINDOW, TRUE);
    313     appendAttr(iAttrs, SK_WGL_DOUBLE_BUFFER, (doubleBuffered ? TRUE : FALSE));
    314     appendAttr(iAttrs, SK_WGL_ACCELERATION, SK_WGL_FULL_ACCELERATION);
    315     appendAttr(iAttrs, SK_WGL_SUPPORT_OPENGL, TRUE);
    316     if (deepColor) {
    317         appendAttr(iAttrs, SK_WGL_RED_BITS, 10);
    318         appendAttr(iAttrs, SK_WGL_GREEN_BITS, 10);
    319         appendAttr(iAttrs, SK_WGL_BLUE_BITS, 10);
    320         appendAttr(iAttrs, SK_WGL_ALPHA_BITS, 2);
    321     } else {
    322         appendAttr(iAttrs, SK_WGL_COLOR_BITS, 24);
    323         appendAttr(iAttrs, SK_WGL_ALPHA_BITS, 8);
    324     }
    325     appendAttr(iAttrs, SK_WGL_STENCIL_BITS, 8);
    326 
    327     float fAttrs[] = {0, 0};
    328 
    329     // Get a MSAA format if requested and possible.
    330     if (msaaSampleCount > 0 &&
    331         extensions.hasExtension(dc, "WGL_ARB_multisample")) {
    332         SkTDArray<int> msaaIAttrs = iAttrs;
    333         appendAttr(msaaIAttrs, SK_WGL_SAMPLE_BUFFERS, TRUE);
    334         appendAttr(msaaIAttrs, SK_WGL_SAMPLES, msaaSampleCount);
    335         appendAttr(msaaIAttrs, 0, 0);
    336         unsigned int num;
    337         int formats[64];
    338         extensions.choosePixelFormat(dc, msaaIAttrs.begin(), fAttrs, 64, formats, &num);
    339         num = SkTMin(num, 64U);
    340         formatsToTry[0] = extensions.selectFormat(formats, num, dc, msaaSampleCount);
    341     }
    342 
    343     // Get a non-MSAA format
    344     int* format = -1 == formatsToTry[0] ? &formatsToTry[0] : &formatsToTry[1];
    345     unsigned int num;
    346     appendAttr(iAttrs, 0, 0);
    347     extensions.choosePixelFormat(dc, iAttrs.begin(), fAttrs, 1, format, &num);
    348 }
    349 
    350 static HGLRC create_gl_context(HDC dc, const SkWGLExtensions& extensions,
    351                                SkWGLContextRequest contextType, HGLRC shareContext) {
    352     HDC prevDC = wglGetCurrentDC();
    353     HGLRC prevGLRC = wglGetCurrentContext();
    354 
    355     HGLRC glrc = nullptr;
    356     if (kGLES_SkWGLContextRequest == contextType) {
    357         if (!extensions.hasExtension(dc, "WGL_EXT_create_context_es2_profile")) {
    358             wglMakeCurrent(prevDC, prevGLRC);
    359             return nullptr;
    360         }
    361         static const int glesAttribs[] = {
    362             SK_WGL_CONTEXT_MAJOR_VERSION, 3,
    363             SK_WGL_CONTEXT_MINOR_VERSION, 0,
    364             SK_WGL_CONTEXT_PROFILE_MASK,  SK_WGL_CONTEXT_ES2_PROFILE_BIT,
    365             0,
    366         };
    367         glrc = extensions.createContextAttribs(dc, shareContext, glesAttribs);
    368         if (nullptr == glrc) {
    369             wglMakeCurrent(prevDC, prevGLRC);
    370             return nullptr;
    371         }
    372     } else {
    373         if (kGLPreferCoreProfile_SkWGLContextRequest == contextType &&
    374             extensions.hasExtension(dc, "WGL_ARB_create_context")) {
    375             static const int kCoreGLVersions[] = {
    376                 4, 3,
    377                 4, 2,
    378                 4, 1,
    379                 4, 0,
    380                 3, 3,
    381                 3, 2,
    382             };
    383             int coreProfileAttribs[] = {
    384                 SK_WGL_CONTEXT_MAJOR_VERSION, -1,
    385                 SK_WGL_CONTEXT_MINOR_VERSION, -1,
    386                 SK_WGL_CONTEXT_PROFILE_MASK,  SK_WGL_CONTEXT_CORE_PROFILE_BIT,
    387                 0,
    388             };
    389             for (size_t v = 0; v < SK_ARRAY_COUNT(kCoreGLVersions) / 2; ++v) {
    390                 coreProfileAttribs[1] = kCoreGLVersions[2 * v];
    391                 coreProfileAttribs[3] = kCoreGLVersions[2 * v + 1];
    392                 glrc = extensions.createContextAttribs(dc, shareContext, coreProfileAttribs);
    393                 if (glrc) {
    394                     break;
    395                 }
    396             }
    397         }
    398     }
    399 
    400     if (nullptr == glrc) {
    401         glrc = wglCreateContext(dc);
    402         if (shareContext) {
    403             if (!wglShareLists(shareContext, glrc)) {
    404                 wglDeleteContext(glrc);
    405                 return nullptr;
    406             }
    407         }
    408     }
    409     SkASSERT(glrc);
    410 
    411     wglMakeCurrent(prevDC, prevGLRC);
    412 
    413     // This might help make the context non-vsynced.
    414     if (extensions.hasExtension(dc, "WGL_EXT_swap_control")) {
    415         extensions.swapInterval(-1);
    416     }
    417     return glrc;
    418 }
    419 
    420 HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, bool deepColor,
    421                          SkWGLContextRequest contextType, HGLRC shareContext) {
    422     SkWGLExtensions extensions;
    423     if (!extensions.hasExtension(dc, "WGL_ARB_pixel_format")) {
    424         return nullptr;
    425     }
    426 
    427     BOOL set = FALSE;
    428 
    429     int pixelFormatsToTry[] = { -1, -1 };
    430     get_pixel_formats_to_try(dc, extensions, true, msaaSampleCount, deepColor, pixelFormatsToTry);
    431     for (size_t f = 0;
    432          !set && -1 != pixelFormatsToTry[f] && f < SK_ARRAY_COUNT(pixelFormatsToTry);
    433          ++f) {
    434         PIXELFORMATDESCRIPTOR pfd;
    435         DescribePixelFormat(dc, pixelFormatsToTry[f], sizeof(pfd), &pfd);
    436         set = SetPixelFormat(dc, pixelFormatsToTry[f], &pfd);
    437     }
    438 
    439     if (!set) {
    440         return nullptr;
    441     }
    442 
    443     return create_gl_context(dc, extensions, contextType, shareContext);
    444 }
    445 
    446 sk_sp<SkWGLPbufferContext> SkWGLPbufferContext::Create(HDC parentDC,
    447                                                        SkWGLContextRequest contextType,
    448                                                        HGLRC shareContext) {
    449     SkWGLExtensions extensions;
    450     if (!extensions.hasExtension(parentDC, "WGL_ARB_pixel_format") ||
    451         !extensions.hasExtension(parentDC, "WGL_ARB_pbuffer")) {
    452         return nullptr;
    453     }
    454 
    455     // We cache the pixel formats once, and then reuse them. That's possibly incorrect if
    456     // there are multiple GPUs, but this function is always called with a freshly made,
    457     // identically constructed HDC (see WinGLTestContext).
    458     //
    459     // We only store two potential pixel formats, one for single buffer, one for double buffer.
    460     // We never ask for MSAA, so we don't need the second pixel format for each buffering state.
    461     static int gPixelFormats[2] = { -1, -1 };
    462     static SkOnce once;
    463     once([=] {
    464         {
    465             // Single buffer
    466             int pixelFormatsToTry[2] = { -1, -1 };
    467             get_pixel_formats_to_try(parentDC, extensions, false, 0, false, pixelFormatsToTry);
    468             gPixelFormats[0] = pixelFormatsToTry[0];
    469         }
    470         {
    471             // Double buffer
    472             int pixelFormatsToTry[2] = { -1, -1 };
    473             get_pixel_formats_to_try(parentDC, extensions, true, 0, false, pixelFormatsToTry);
    474             gPixelFormats[1] = pixelFormatsToTry[0];
    475         }
    476     });
    477 
    478     // try for single buffer first
    479     for (int pixelFormat : gPixelFormats) {
    480         if (-1 == pixelFormat) {
    481             continue;
    482         }
    483         HPBUFFER pbuf = extensions.createPbuffer(parentDC, pixelFormat, 1, 1, nullptr);
    484         if (0 != pbuf) {
    485             HDC dc = extensions.getPbufferDC(pbuf);
    486             if (dc) {
    487                 HGLRC glrc = create_gl_context(dc, extensions, contextType, shareContext);
    488                 if (glrc) {
    489                     return sk_sp<SkWGLPbufferContext>(new SkWGLPbufferContext(pbuf, dc, glrc));
    490                 }
    491                 extensions.releasePbufferDC(pbuf, dc);
    492             }
    493             extensions.destroyPbuffer(pbuf);
    494         }
    495     }
    496     return nullptr;
    497 }
    498 
    499 SkWGLPbufferContext::~SkWGLPbufferContext() {
    500     SkASSERT(fExtensions.hasExtension(fDC, "WGL_ARB_pbuffer"));
    501     wglDeleteContext(fGLRC);
    502     fExtensions.releasePbufferDC(fPbuffer, fDC);
    503     fExtensions.destroyPbuffer(fPbuffer);
    504 }
    505 
    506 SkWGLPbufferContext::SkWGLPbufferContext(HPBUFFER pbuffer, HDC dc, HGLRC glrc)
    507     : fPbuffer(pbuffer)
    508     , fDC(dc)
    509     , fGLRC(glrc) {
    510 }
    511 
    512 #endif//defined(SK_BUILD_FOR_WIN)
    513