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)
10
11 #include "SkWGL.h"
12
13 #include "SkOnce.h"
14 #include "SkTDArray.h"
15 #include "SkTSearch.h"
16 #include "SkTSort.h"
17
hasExtension(HDC dc,const char * ext) const18 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
getExtensionsString(HDC hdc) const42 const char* SkWGLExtensions::getExtensionsString(HDC hdc) const {
43 return fGetExtensionsString(hdc);
44 }
45
choosePixelFormat(HDC hdc,const int * piAttribIList,const FLOAT * pfAttribFList,UINT nMaxFormats,int * piFormats,UINT * nNumFormats) const46 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
getPixelFormatAttribiv(HDC hdc,int iPixelFormat,int iLayerPlane,UINT nAttributes,const int * piAttributes,int * piValues) const56 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
getPixelFormatAttribfv(HDC hdc,int iPixelFormat,int iLayerPlane,UINT nAttributes,const int * piAttributes,float * pfValues) const66 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 }
createContextAttribs(HDC hDC,HGLRC hShareContext,const int * attribList) const75 HGLRC SkWGLExtensions::createContextAttribs(HDC hDC,
76 HGLRC hShareContext,
77 const int *attribList) const {
78 return fCreateContextAttribs(hDC, hShareContext, attribList);
79 }
80
swapInterval(int interval) const81 BOOL SkWGLExtensions::swapInterval(int interval) const {
82 return fSwapInterval(interval);
83 }
84
createPbuffer(HDC hDC,int iPixelFormat,int iWidth,int iHeight,const int * piAttribList) const85 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
getPbufferDC(HPBUFFER hPbuffer) const93 HDC SkWGLExtensions::getPbufferDC(HPBUFFER hPbuffer) const {
94 return fGetPbufferDC(hPbuffer);
95 }
96
releasePbufferDC(HPBUFFER hPbuffer,HDC hDC) const97 int SkWGLExtensions::releasePbufferDC(HPBUFFER hPbuffer, HDC hDC) const {
98 return fReleasePbufferDC(hPbuffer, hDC);
99 }
100
destroyPbuffer(HPBUFFER hPbuffer) const101 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
pf_less(const PixelFormat & a,const PixelFormat & b)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
selectFormat(const int formats[],int formatCount,HDC dc,int desiredSampleCount) const125 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
create_dummy_window()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
destroy_dummy_window(HWND dummy)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
SkWGLExtensions()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
get_pixel_formats_to_try(HDC dc,const SkWGLExtensions & extensions,bool doubleBuffered,int msaaSampleCount,bool deepColor,int formatsToTry[2])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(attr);
308 attrs.push(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
create_gl_context(HDC dc,const SkWGLExtensions & extensions,SkWGLContextRequest contextType,HGLRC shareContext)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
SkCreateWGLContext(HDC dc,int msaaSampleCount,bool deepColor,SkWGLContextRequest contextType,HGLRC shareContext)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
Create(HDC parentDC,SkWGLContextRequest contextType,HGLRC shareContext)446 SkWGLPbufferContext* SkWGLPbufferContext::Create(HDC parentDC, SkWGLContextRequest contextType,
447 HGLRC shareContext) {
448 SkWGLExtensions extensions;
449 if (!extensions.hasExtension(parentDC, "WGL_ARB_pixel_format") ||
450 !extensions.hasExtension(parentDC, "WGL_ARB_pbuffer")) {
451 return nullptr;
452 }
453
454 // We cache the pixel formats once, and then reuse them. That's possibly incorrect if
455 // there are multiple GPUs, but this function is always called with a freshly made,
456 // identically constructed HDC (see WinGLTestContext).
457 //
458 // We only store two potential pixel formats, one for single buffer, one for double buffer.
459 // We never ask for MSAA, so we don't need the second pixel format for each buffering state.
460 static int gPixelFormats[2] = { -1, -1 };
461 static SkOnce once;
462 once([=] {
463 {
464 // Single buffer
465 int pixelFormatsToTry[2] = { -1, -1 };
466 get_pixel_formats_to_try(parentDC, extensions, false, 0, false, pixelFormatsToTry);
467 gPixelFormats[0] = pixelFormatsToTry[0];
468 }
469 {
470 // Double buffer
471 int pixelFormatsToTry[2] = { -1, -1 };
472 get_pixel_formats_to_try(parentDC, extensions, true, 0, false, pixelFormatsToTry);
473 gPixelFormats[1] = pixelFormatsToTry[0];
474 }
475 });
476
477 // try for single buffer first
478 for (int pixelFormat : gPixelFormats) {
479 if (-1 == pixelFormat) {
480 continue;
481 }
482 HPBUFFER pbuf = extensions.createPbuffer(parentDC, pixelFormat, 1, 1, nullptr);
483 if (0 != pbuf) {
484 HDC dc = extensions.getPbufferDC(pbuf);
485 if (dc) {
486 HGLRC glrc = create_gl_context(dc, extensions, contextType, shareContext);
487 if (glrc) {
488 return new SkWGLPbufferContext(pbuf, dc, glrc);
489 }
490 extensions.releasePbufferDC(pbuf, dc);
491 }
492 extensions.destroyPbuffer(pbuf);
493 }
494 }
495 return nullptr;
496 }
497
~SkWGLPbufferContext()498 SkWGLPbufferContext::~SkWGLPbufferContext() {
499 SkASSERT(fExtensions.hasExtension(fDC, "WGL_ARB_pbuffer"));
500 wglDeleteContext(fGLRC);
501 fExtensions.releasePbufferDC(fPbuffer, fDC);
502 fExtensions.destroyPbuffer(fPbuffer);
503 }
504
SkWGLPbufferContext(HPBUFFER pbuffer,HDC dc,HGLRC glrc)505 SkWGLPbufferContext::SkWGLPbufferContext(HPBUFFER pbuffer, HDC dc, HGLRC glrc)
506 : fPbuffer(pbuffer)
507 , fDC(dc)
508 , fGLRC(glrc) {
509 }
510
511 #endif//defined(SK_BUILD_FOR_WIN)
512