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