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