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