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
78namespace {
79
80struct PixelFormat {
81    int fFormat;
82    int fSampleCnt;
83    int fChoosePixelFormatRank;
84};
85
86bool pf_less(const PixelFormat& a, const PixelFormat& b) {
87    if (a.fSampleCnt < b.fSampleCnt) {
88        return true;
89    } else if (b.fSampleCnt < a.fSampleCnt) {
90        return false;
91    } else if (a.fChoosePixelFormatRank < b.fChoosePixelFormatRank) {
92        return true;
93    }
94    return false;
95}
96}
97
98int SkWGLExtensions::selectFormat(const int formats[],
99                                  int formatCount,
100                                  HDC dc,
101                                  int desiredSampleCount) {
102    PixelFormat desiredFormat = {
103        0,
104        desiredSampleCount,
105        0,
106    };
107    SkTDArray<PixelFormat> rankedFormats;
108    rankedFormats.setCount(formatCount);
109    for (int i = 0; i < formatCount; ++i) {
110        static const int kQueryAttr = SK_WGL_SAMPLES;
111        int numSamples;
112        this->getPixelFormatAttribiv(dc,
113                                     formats[i],
114                                     0,
115                                     1,
116                                     &kQueryAttr,
117                                     &numSamples);
118        rankedFormats[i].fFormat =  formats[i];
119        rankedFormats[i].fSampleCnt = numSamples;
120        rankedFormats[i].fChoosePixelFormatRank = i;
121    }
122    SkTQSort(rankedFormats.begin(),
123             rankedFormats.begin() + rankedFormats.count() - 1,
124             SkTLessFunctionToFunctorAdaptor<PixelFormat, pf_less>());
125    int idx = SkTSearch<PixelFormat, pf_less>(rankedFormats.begin(),
126                                              rankedFormats.count(),
127                                              desiredFormat,
128                                              sizeof(PixelFormat));
129    if (idx < 0) {
130        idx = ~idx;
131    }
132    return rankedFormats[idx].fFormat;
133}
134
135
136namespace {
137
138#if defined(UNICODE)
139    #define STR_LIT(X) L## #X
140#else
141    #define STR_LIT(X) #X
142#endif
143
144#define DUMMY_CLASS STR_LIT("DummyClass")
145
146HWND create_dummy_window() {
147    HMODULE module = GetModuleHandle(NULL);
148    HWND dummy;
149    RECT windowRect;
150    windowRect.left = 0;
151    windowRect.right = 8;
152    windowRect.top = 0;
153    windowRect.bottom = 8;
154
155    WNDCLASS wc;
156
157    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
158    wc.lpfnWndProc = (WNDPROC) DefWindowProc;
159    wc.cbClsExtra = 0;
160    wc.cbWndExtra = 0;
161    wc.hInstance = module;
162    wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
163    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
164    wc.hbrBackground = NULL;
165    wc.lpszMenuName = NULL;
166    wc.lpszClassName = DUMMY_CLASS;
167
168    if(!RegisterClass(&wc)) {
169        return 0;
170    }
171
172    DWORD style, exStyle;
173    exStyle = WS_EX_CLIENTEDGE;
174    style = WS_SYSMENU;
175
176    AdjustWindowRectEx(&windowRect, style, false, exStyle);
177    if(!(dummy = CreateWindowEx(exStyle,
178                                DUMMY_CLASS,
179                                STR_LIT("DummyWindow"),
180                                WS_CLIPSIBLINGS | WS_CLIPCHILDREN | style,
181                                0, 0,
182                                windowRect.right-windowRect.left,
183                                windowRect.bottom-windowRect.top,
184                                NULL, NULL,
185                                module,
186                                NULL))) {
187        UnregisterClass(DUMMY_CLASS, module);
188        return NULL;
189    }
190    ShowWindow(dummy, SW_HIDE);
191
192    return dummy;
193}
194
195void destroy_dummy_window(HWND dummy) {
196    DestroyWindow(dummy);
197    HMODULE module = GetModuleHandle(NULL);
198    UnregisterClass(DUMMY_CLASS, module);
199}
200}
201
202#define GET_PROC(NAME, SUFFIX) f##NAME = \
203                     (##NAME##Proc) wglGetProcAddress("wgl" #NAME #SUFFIX)
204
205SkWGLExtensions::SkWGLExtensions()
206    : fGetExtensionsString(NULL)
207    , fChoosePixelFormat(NULL)
208    , fGetPixelFormatAttribfv(NULL)
209    , fGetPixelFormatAttribiv(NULL)
210    , fCreateContextAttribs(NULL) {
211    HDC prevDC = wglGetCurrentDC();
212    HGLRC prevGLRC = wglGetCurrentContext();
213
214    PIXELFORMATDESCRIPTOR dummyPFD;
215
216    ZeroMemory(&dummyPFD, sizeof(dummyPFD));
217    dummyPFD.nSize = sizeof(dummyPFD);
218    dummyPFD.nVersion = 1;
219    dummyPFD.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
220    dummyPFD.iPixelType = PFD_TYPE_RGBA;
221    dummyPFD.cColorBits  = 32;
222    dummyPFD.cDepthBits  = 0;
223    dummyPFD.cStencilBits = 8;
224    dummyPFD.iLayerType = PFD_MAIN_PLANE;
225    HWND dummyWND = create_dummy_window();
226    if (dummyWND) {
227        HDC dummyDC = GetDC(dummyWND);
228        int dummyFormat = ChoosePixelFormat(dummyDC, &dummyPFD);
229        SetPixelFormat(dummyDC, dummyFormat, &dummyPFD);
230        HGLRC dummyGLRC = wglCreateContext(dummyDC);
231        SkASSERT(dummyGLRC);
232        wglMakeCurrent(dummyDC, dummyGLRC);
233
234        GET_PROC(GetExtensionsString, ARB);
235        GET_PROC(ChoosePixelFormat, ARB);
236        GET_PROC(GetPixelFormatAttribiv, ARB);
237        GET_PROC(GetPixelFormatAttribfv, ARB);
238        GET_PROC(CreateContextAttribs, ARB);
239
240        wglMakeCurrent(dummyDC, NULL);
241        wglDeleteContext(dummyGLRC);
242        destroy_dummy_window(dummyWND);
243    }
244
245    wglMakeCurrent(prevDC, prevGLRC);
246}
247
248HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, bool preferCoreProfile) {
249    SkWGLExtensions extensions;
250    if (!extensions.hasExtension(dc, "WGL_ARB_pixel_format")) {
251        return NULL;
252    }
253
254    HDC prevDC = wglGetCurrentDC();
255    HGLRC prevGLRC = wglGetCurrentContext();
256    PIXELFORMATDESCRIPTOR pfd;
257
258    int format = 0;
259
260    static const int iAttrs[] = {
261        SK_WGL_DRAW_TO_WINDOW, TRUE,
262        SK_WGL_DOUBLE_BUFFER, TRUE,
263        SK_WGL_ACCELERATION, SK_WGL_FULL_ACCELERATION,
264        SK_WGL_SUPPORT_OPENGL, TRUE,
265        SK_WGL_COLOR_BITS, 24,
266        SK_WGL_ALPHA_BITS, 8,
267        SK_WGL_STENCIL_BITS, 8,
268        0, 0
269    };
270
271    float fAttrs[] = {0, 0};
272
273    if (msaaSampleCount > 0 &&
274        extensions.hasExtension(dc, "WGL_ARB_multisample")) {
275        static const int kIAttrsCount = SK_ARRAY_COUNT(iAttrs);
276        int msaaIAttrs[kIAttrsCount + 4];
277        memcpy(msaaIAttrs, iAttrs, sizeof(int) * kIAttrsCount);
278        SkASSERT(0 == msaaIAttrs[kIAttrsCount - 2] &&
279                 0 == msaaIAttrs[kIAttrsCount - 1]);
280        msaaIAttrs[kIAttrsCount - 2] = SK_WGL_SAMPLE_BUFFERS;
281        msaaIAttrs[kIAttrsCount - 1] = TRUE;
282        msaaIAttrs[kIAttrsCount + 0] = SK_WGL_SAMPLES;
283        msaaIAttrs[kIAttrsCount + 1] = msaaSampleCount;
284        msaaIAttrs[kIAttrsCount + 2] = 0;
285        msaaIAttrs[kIAttrsCount + 3] = 0;
286        unsigned int num;
287        int formats[64];
288        extensions.choosePixelFormat(dc, msaaIAttrs, fAttrs, 64, formats, &num);
289        num = SkTMin(num, 64U);
290        int formatToTry = extensions.selectFormat(formats,
291                                                  num,
292                                                  dc,
293                                                  msaaSampleCount);
294        DescribePixelFormat(dc, formatToTry, sizeof(pfd), &pfd);
295        if (SetPixelFormat(dc, formatToTry, &pfd)) {
296            format = formatToTry;
297        }
298    }
299
300    if (0 == format) {
301        // Either MSAA wasn't requested or creation failed
302        unsigned int num;
303        extensions.choosePixelFormat(dc, iAttrs, fAttrs, 1, &format, &num);
304        DescribePixelFormat(dc, format, sizeof(pfd), &pfd);
305        SkDEBUGCODE(BOOL set =) SetPixelFormat(dc, format, &pfd);
306        SkASSERT(TRUE == set);
307    }
308
309    HGLRC glrc = NULL;
310    if (preferCoreProfile && extensions.hasExtension(dc, "WGL_ARB_create_context")) {
311        static const int kCoreGLVersions[] = {
312            4, 3,
313            4, 2,
314            4, 1,
315            4, 0,
316            3, 3,
317            3, 2,
318        };
319        int coreProfileAttribs[] = {
320            SK_WGL_CONTEXT_MAJOR_VERSION, -1,
321            SK_WGL_CONTEXT_MINOR_VERSION, -1,
322            SK_WGL_CONTEXT_PROFILE_MASK,  SK_WGL_CONTEXT_CORE_PROFILE_BIT,
323            0,
324        };
325        for (int v = 0; v < SK_ARRAY_COUNT(kCoreGLVersions) / 2; ++v) {
326            coreProfileAttribs[1] = kCoreGLVersions[2 * v];
327            coreProfileAttribs[3] = kCoreGLVersions[2 * v + 1];
328            glrc = extensions.createContextAttribs(dc, NULL, coreProfileAttribs);
329            if (NULL != glrc) {
330                break;
331            }
332        }
333    }
334
335    if (NULL == glrc) {
336        glrc = wglCreateContext(dc);
337    }
338    SkASSERT(glrc);
339
340    wglMakeCurrent(prevDC, prevGLRC);
341    return glrc;
342}
343