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) && !defined(_M_ARM64) 10 11 #include "SkWGL.h" 12 13 #include "SkOnce.h" 14 #include "SkTDArray.h" 15 #include "SkTSearch.h" 16 #include "SkTSort.h" 17 18 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 42 const char* SkWGLExtensions::getExtensionsString(HDC hdc) const { 43 return fGetExtensionsString(hdc); 44 } 45 46 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 56 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 66 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 } 75 HGLRC SkWGLExtensions::createContextAttribs(HDC hDC, 76 HGLRC hShareContext, 77 const int *attribList) const { 78 return fCreateContextAttribs(hDC, hShareContext, attribList); 79 } 80 81 BOOL SkWGLExtensions::swapInterval(int interval) const { 82 return fSwapInterval(interval); 83 } 84 85 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 93 HDC SkWGLExtensions::getPbufferDC(HPBUFFER hPbuffer) const { 94 return fGetPbufferDC(hPbuffer); 95 } 96 97 int SkWGLExtensions::releasePbufferDC(HPBUFFER hPbuffer, HDC hDC) const { 98 return fReleasePbufferDC(hPbuffer, hDC); 99 } 100 101 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 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 125 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 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 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 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 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_back(attr); 308 attrs.push_back(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 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 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 446 sk_sp<SkWGLPbufferContext> SkWGLPbufferContext::Create(HDC parentDC, 447 SkWGLContextRequest contextType, 448 HGLRC shareContext) { 449 SkWGLExtensions extensions; 450 if (!extensions.hasExtension(parentDC, "WGL_ARB_pixel_format") || 451 !extensions.hasExtension(parentDC, "WGL_ARB_pbuffer")) { 452 return nullptr; 453 } 454 455 // We cache the pixel formats once, and then reuse them. That's possibly incorrect if 456 // there are multiple GPUs, but this function is always called with a freshly made, 457 // identically constructed HDC (see WinGLTestContext). 458 // 459 // We only store two potential pixel formats, one for single buffer, one for double buffer. 460 // We never ask for MSAA, so we don't need the second pixel format for each buffering state. 461 static int gPixelFormats[2] = { -1, -1 }; 462 static SkOnce once; 463 once([=] { 464 { 465 // Single buffer 466 int pixelFormatsToTry[2] = { -1, -1 }; 467 get_pixel_formats_to_try(parentDC, extensions, false, 0, false, pixelFormatsToTry); 468 gPixelFormats[0] = pixelFormatsToTry[0]; 469 } 470 { 471 // Double buffer 472 int pixelFormatsToTry[2] = { -1, -1 }; 473 get_pixel_formats_to_try(parentDC, extensions, true, 0, false, pixelFormatsToTry); 474 gPixelFormats[1] = pixelFormatsToTry[0]; 475 } 476 }); 477 478 // try for single buffer first 479 for (int pixelFormat : gPixelFormats) { 480 if (-1 == pixelFormat) { 481 continue; 482 } 483 HPBUFFER pbuf = extensions.createPbuffer(parentDC, pixelFormat, 1, 1, nullptr); 484 if (0 != pbuf) { 485 HDC dc = extensions.getPbufferDC(pbuf); 486 if (dc) { 487 HGLRC glrc = create_gl_context(dc, extensions, contextType, shareContext); 488 if (glrc) { 489 return sk_sp<SkWGLPbufferContext>(new SkWGLPbufferContext(pbuf, dc, glrc)); 490 } 491 extensions.releasePbufferDC(pbuf, dc); 492 } 493 extensions.destroyPbuffer(pbuf); 494 } 495 } 496 return nullptr; 497 } 498 499 SkWGLPbufferContext::~SkWGLPbufferContext() { 500 SkASSERT(fExtensions.hasExtension(fDC, "WGL_ARB_pbuffer")); 501 wglDeleteContext(fGLRC); 502 fExtensions.releasePbufferDC(fPbuffer, fDC); 503 fExtensions.destroyPbuffer(fPbuffer); 504 } 505 506 SkWGLPbufferContext::SkWGLPbufferContext(HPBUFFER pbuffer, HDC dc, HGLRC glrc) 507 : fPbuffer(pbuffer) 508 , fDC(dc) 509 , fGLRC(glrc) { 510 } 511 512 #endif//defined(SK_BUILD_FOR_WIN) 513