| /* |
| * Copyright 2011 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkTypes.h" |
| #if defined(SK_BUILD_FOR_WIN) |
| |
| #include "SkWGL.h" |
| |
| #include "SkOnce.h" |
| #include "SkTDArray.h" |
| #include "SkTSearch.h" |
| #include "SkTSort.h" |
| |
| bool SkWGLExtensions::hasExtension(HDC dc, const char* ext) const { |
| if (nullptr == this->fGetExtensionsString) { |
| return false; |
| } |
| if (!strcmp("WGL_ARB_extensions_string", ext)) { |
| return true; |
| } |
| const char* extensionString = this->getExtensionsString(dc); |
| size_t extLength = strlen(ext); |
| |
| while (true) { |
| size_t n = strcspn(extensionString, " "); |
| if (n == extLength && 0 == strncmp(ext, extensionString, n)) { |
| return true; |
| } |
| if (0 == extensionString[n]) { |
| return false; |
| } |
| extensionString += n+1; |
| } |
| |
| return false; |
| } |
| |
| const char* SkWGLExtensions::getExtensionsString(HDC hdc) const { |
| return fGetExtensionsString(hdc); |
| } |
| |
| BOOL SkWGLExtensions::choosePixelFormat(HDC hdc, |
| const int* piAttribIList, |
| const FLOAT* pfAttribFList, |
| UINT nMaxFormats, |
| int* piFormats, |
| UINT* nNumFormats) const { |
| return fChoosePixelFormat(hdc, piAttribIList, pfAttribFList, |
| nMaxFormats, piFormats, nNumFormats); |
| } |
| |
| BOOL SkWGLExtensions::getPixelFormatAttribiv(HDC hdc, |
| int iPixelFormat, |
| int iLayerPlane, |
| UINT nAttributes, |
| const int *piAttributes, |
| int *piValues) const { |
| return fGetPixelFormatAttribiv(hdc, iPixelFormat, iLayerPlane, |
| nAttributes, piAttributes, piValues); |
| } |
| |
| BOOL SkWGLExtensions::getPixelFormatAttribfv(HDC hdc, |
| int iPixelFormat, |
| int iLayerPlane, |
| UINT nAttributes, |
| const int *piAttributes, |
| float *pfValues) const { |
| return fGetPixelFormatAttribfv(hdc, iPixelFormat, iLayerPlane, |
| nAttributes, piAttributes, pfValues); |
| } |
| HGLRC SkWGLExtensions::createContextAttribs(HDC hDC, |
| HGLRC hShareContext, |
| const int *attribList) const { |
| return fCreateContextAttribs(hDC, hShareContext, attribList); |
| } |
| |
| BOOL SkWGLExtensions::swapInterval(int interval) const { |
| return fSwapInterval(interval); |
| } |
| |
| HPBUFFER SkWGLExtensions::createPbuffer(HDC hDC, |
| int iPixelFormat, |
| int iWidth, |
| int iHeight, |
| const int *piAttribList) const { |
| return fCreatePbuffer(hDC, iPixelFormat, iWidth, iHeight, piAttribList); |
| } |
| |
| HDC SkWGLExtensions::getPbufferDC(HPBUFFER hPbuffer) const { |
| return fGetPbufferDC(hPbuffer); |
| } |
| |
| int SkWGLExtensions::releasePbufferDC(HPBUFFER hPbuffer, HDC hDC) const { |
| return fReleasePbufferDC(hPbuffer, hDC); |
| } |
| |
| BOOL SkWGLExtensions::destroyPbuffer(HPBUFFER hPbuffer) const { |
| return fDestroyPbuffer(hPbuffer); |
| } |
| |
| namespace { |
| |
| struct PixelFormat { |
| int fFormat; |
| int fSampleCnt; |
| int fChoosePixelFormatRank; |
| }; |
| |
| bool pf_less(const PixelFormat& a, const PixelFormat& b) { |
| if (a.fSampleCnt < b.fSampleCnt) { |
| return true; |
| } else if (b.fSampleCnt < a.fSampleCnt) { |
| return false; |
| } else if (a.fChoosePixelFormatRank < b.fChoosePixelFormatRank) { |
| return true; |
| } |
| return false; |
| } |
| } |
| |
| int SkWGLExtensions::selectFormat(const int formats[], |
| int formatCount, |
| HDC dc, |
| int desiredSampleCount) const { |
| SkASSERT(desiredSampleCount >= 1); |
| if (formatCount <= 0) { |
| return -1; |
| } |
| PixelFormat desiredFormat = { |
| 0, |
| desiredSampleCount, |
| 0, |
| }; |
| SkTDArray<PixelFormat> rankedFormats; |
| rankedFormats.setCount(formatCount); |
| for (int i = 0; i < formatCount; ++i) { |
| static const int kQueryAttr = SK_WGL_SAMPLES; |
| int numSamples; |
| this->getPixelFormatAttribiv(dc, |
| formats[i], |
| 0, |
| 1, |
| &kQueryAttr, |
| &numSamples); |
| rankedFormats[i].fFormat = formats[i]; |
| rankedFormats[i].fSampleCnt = SkTMax(1, numSamples); |
| rankedFormats[i].fChoosePixelFormatRank = i; |
| } |
| SkTQSort(rankedFormats.begin(), |
| rankedFormats.begin() + rankedFormats.count() - 1, |
| SkTLessFunctionToFunctorAdaptor<PixelFormat, pf_less>()); |
| int idx = SkTSearch<PixelFormat, pf_less>(rankedFormats.begin(), |
| rankedFormats.count(), |
| desiredFormat, |
| sizeof(PixelFormat)); |
| if (idx < 0) { |
| idx = ~idx; |
| } |
| // If the caller asked for non-MSAA fail if the closest format has MSAA. |
| if (desiredSampleCount == 1 && rankedFormats[idx].fSampleCnt != 1) { |
| return -1; |
| } |
| return rankedFormats[idx].fFormat; |
| } |
| |
| |
| namespace { |
| |
| #if defined(UNICODE) |
| #define STR_LIT(X) L## #X |
| #else |
| #define STR_LIT(X) #X |
| #endif |
| |
| #define DUMMY_CLASS STR_LIT("DummyClass") |
| |
| HWND create_dummy_window() { |
| HMODULE module = GetModuleHandle(nullptr); |
| HWND dummy; |
| RECT windowRect; |
| windowRect.left = 0; |
| windowRect.right = 8; |
| windowRect.top = 0; |
| windowRect.bottom = 8; |
| |
| WNDCLASS wc; |
| |
| wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; |
| wc.lpfnWndProc = (WNDPROC) DefWindowProc; |
| wc.cbClsExtra = 0; |
| wc.cbWndExtra = 0; |
| wc.hInstance = module; |
| wc.hIcon = LoadIcon(nullptr, IDI_WINLOGO); |
| wc.hCursor = LoadCursor(nullptr, IDC_ARROW); |
| wc.hbrBackground = nullptr; |
| wc.lpszMenuName = nullptr; |
| wc.lpszClassName = DUMMY_CLASS; |
| |
| if(!RegisterClass(&wc)) { |
| return 0; |
| } |
| |
| DWORD style, exStyle; |
| exStyle = WS_EX_CLIENTEDGE; |
| style = WS_SYSMENU; |
| |
| AdjustWindowRectEx(&windowRect, style, false, exStyle); |
| if(!(dummy = CreateWindowEx(exStyle, |
| DUMMY_CLASS, |
| STR_LIT("DummyWindow"), |
| WS_CLIPSIBLINGS | WS_CLIPCHILDREN | style, |
| 0, 0, |
| windowRect.right-windowRect.left, |
| windowRect.bottom-windowRect.top, |
| nullptr, nullptr, |
| module, |
| nullptr))) { |
| UnregisterClass(DUMMY_CLASS, module); |
| return nullptr; |
| } |
| ShowWindow(dummy, SW_HIDE); |
| |
| return dummy; |
| } |
| |
| void destroy_dummy_window(HWND dummy) { |
| DestroyWindow(dummy); |
| HMODULE module = GetModuleHandle(nullptr); |
| UnregisterClass(DUMMY_CLASS, module); |
| } |
| } |
| |
| #define GET_PROC(NAME, SUFFIX) f##NAME = \ |
| (NAME##Proc) wglGetProcAddress("wgl" #NAME #SUFFIX) |
| |
| |
| SkWGLExtensions::GetExtensionsStringProc SkWGLExtensions::fGetExtensionsString = nullptr; |
| SkWGLExtensions::ChoosePixelFormatProc SkWGLExtensions::fChoosePixelFormat = nullptr; |
| SkWGLExtensions::GetPixelFormatAttribfvProc SkWGLExtensions::fGetPixelFormatAttribfv = nullptr; |
| SkWGLExtensions::GetPixelFormatAttribivProc SkWGLExtensions::fGetPixelFormatAttribiv = nullptr; |
| SkWGLExtensions::CreateContextAttribsProc SkWGLExtensions::fCreateContextAttribs = nullptr; |
| SkWGLExtensions::SwapIntervalProc SkWGLExtensions::fSwapInterval = nullptr; |
| SkWGLExtensions::CreatePbufferProc SkWGLExtensions::fCreatePbuffer = nullptr; |
| SkWGLExtensions::GetPbufferDCProc SkWGLExtensions::fGetPbufferDC = nullptr; |
| SkWGLExtensions::ReleasePbufferDCProc SkWGLExtensions::fReleasePbufferDC = nullptr; |
| SkWGLExtensions::DestroyPbufferProc SkWGLExtensions::fDestroyPbuffer = nullptr; |
| |
| SkWGLExtensions::SkWGLExtensions() { |
| // We cache these function pointers once, and then reuse them. That's possibly incorrect if |
| // there are multiple GPUs, or if we intend to use these for rendering contexts of different |
| // pixel formats (where wglGetProcAddress is not guaranteed to return the same pointer). |
| static SkOnce once; |
| once([] { |
| HDC prevDC = wglGetCurrentDC(); |
| HGLRC prevGLRC = wglGetCurrentContext(); |
| |
| PIXELFORMATDESCRIPTOR dummyPFD; |
| |
| ZeroMemory(&dummyPFD, sizeof(dummyPFD)); |
| dummyPFD.nSize = sizeof(dummyPFD); |
| dummyPFD.nVersion = 1; |
| dummyPFD.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; |
| dummyPFD.iPixelType = PFD_TYPE_RGBA; |
| dummyPFD.cColorBits = 32; |
| dummyPFD.cDepthBits = 0; |
| dummyPFD.cStencilBits = 8; |
| dummyPFD.iLayerType = PFD_MAIN_PLANE; |
| HWND dummyWND = create_dummy_window(); |
| if (dummyWND) { |
| HDC dummyDC = GetDC(dummyWND); |
| int dummyFormat = ChoosePixelFormat(dummyDC, &dummyPFD); |
| SetPixelFormat(dummyDC, dummyFormat, &dummyPFD); |
| HGLRC dummyGLRC = wglCreateContext(dummyDC); |
| SkASSERT(dummyGLRC); |
| wglMakeCurrent(dummyDC, dummyGLRC); |
| |
| GET_PROC(GetExtensionsString, ARB); |
| GET_PROC(ChoosePixelFormat, ARB); |
| GET_PROC(GetPixelFormatAttribiv, ARB); |
| GET_PROC(GetPixelFormatAttribfv, ARB); |
| GET_PROC(CreateContextAttribs, ARB); |
| GET_PROC(SwapInterval, EXT); |
| GET_PROC(CreatePbuffer, ARB); |
| GET_PROC(GetPbufferDC, ARB); |
| GET_PROC(ReleasePbufferDC, ARB); |
| GET_PROC(DestroyPbuffer, ARB); |
| |
| wglMakeCurrent(dummyDC, nullptr); |
| wglDeleteContext(dummyGLRC); |
| destroy_dummy_window(dummyWND); |
| } |
| |
| wglMakeCurrent(prevDC, prevGLRC); |
| }); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| static void get_pixel_formats_to_try(HDC dc, const SkWGLExtensions& extensions, |
| bool doubleBuffered, int msaaSampleCount, bool deepColor, |
| int formatsToTry[2]) { |
| auto appendAttr = [](SkTDArray<int>& attrs, int attr, int value) { |
| attrs.push(attr); |
| attrs.push(value); |
| }; |
| |
| SkTDArray<int> iAttrs; |
| appendAttr(iAttrs, SK_WGL_DRAW_TO_WINDOW, TRUE); |
| appendAttr(iAttrs, SK_WGL_DOUBLE_BUFFER, (doubleBuffered ? TRUE : FALSE)); |
| appendAttr(iAttrs, SK_WGL_ACCELERATION, SK_WGL_FULL_ACCELERATION); |
| appendAttr(iAttrs, SK_WGL_SUPPORT_OPENGL, TRUE); |
| if (deepColor) { |
| appendAttr(iAttrs, SK_WGL_RED_BITS, 10); |
| appendAttr(iAttrs, SK_WGL_GREEN_BITS, 10); |
| appendAttr(iAttrs, SK_WGL_BLUE_BITS, 10); |
| appendAttr(iAttrs, SK_WGL_ALPHA_BITS, 2); |
| } else { |
| appendAttr(iAttrs, SK_WGL_COLOR_BITS, 24); |
| appendAttr(iAttrs, SK_WGL_ALPHA_BITS, 8); |
| } |
| appendAttr(iAttrs, SK_WGL_STENCIL_BITS, 8); |
| |
| float fAttrs[] = {0, 0}; |
| |
| // Get a MSAA format if requested and possible. |
| if (msaaSampleCount > 0 && |
| extensions.hasExtension(dc, "WGL_ARB_multisample")) { |
| SkTDArray<int> msaaIAttrs = iAttrs; |
| appendAttr(msaaIAttrs, SK_WGL_SAMPLE_BUFFERS, TRUE); |
| appendAttr(msaaIAttrs, SK_WGL_SAMPLES, msaaSampleCount); |
| appendAttr(msaaIAttrs, 0, 0); |
| unsigned int num; |
| int formats[64]; |
| extensions.choosePixelFormat(dc, msaaIAttrs.begin(), fAttrs, 64, formats, &num); |
| num = SkTMin(num, 64U); |
| formatsToTry[0] = extensions.selectFormat(formats, num, dc, msaaSampleCount); |
| } |
| |
| // Get a non-MSAA format |
| int* format = -1 == formatsToTry[0] ? &formatsToTry[0] : &formatsToTry[1]; |
| unsigned int num; |
| appendAttr(iAttrs, 0, 0); |
| extensions.choosePixelFormat(dc, iAttrs.begin(), fAttrs, 1, format, &num); |
| } |
| |
| static HGLRC create_gl_context(HDC dc, const SkWGLExtensions& extensions, |
| SkWGLContextRequest contextType, HGLRC shareContext) { |
| HDC prevDC = wglGetCurrentDC(); |
| HGLRC prevGLRC = wglGetCurrentContext(); |
| |
| HGLRC glrc = nullptr; |
| if (kGLES_SkWGLContextRequest == contextType) { |
| if (!extensions.hasExtension(dc, "WGL_EXT_create_context_es2_profile")) { |
| wglMakeCurrent(prevDC, prevGLRC); |
| return nullptr; |
| } |
| static const int glesAttribs[] = { |
| SK_WGL_CONTEXT_MAJOR_VERSION, 3, |
| SK_WGL_CONTEXT_MINOR_VERSION, 0, |
| SK_WGL_CONTEXT_PROFILE_MASK, SK_WGL_CONTEXT_ES2_PROFILE_BIT, |
| 0, |
| }; |
| glrc = extensions.createContextAttribs(dc, shareContext, glesAttribs); |
| if (nullptr == glrc) { |
| wglMakeCurrent(prevDC, prevGLRC); |
| return nullptr; |
| } |
| } else { |
| if (kGLPreferCoreProfile_SkWGLContextRequest == contextType && |
| extensions.hasExtension(dc, "WGL_ARB_create_context")) { |
| static const int kCoreGLVersions[] = { |
| 4, 3, |
| 4, 2, |
| 4, 1, |
| 4, 0, |
| 3, 3, |
| 3, 2, |
| }; |
| int coreProfileAttribs[] = { |
| SK_WGL_CONTEXT_MAJOR_VERSION, -1, |
| SK_WGL_CONTEXT_MINOR_VERSION, -1, |
| SK_WGL_CONTEXT_PROFILE_MASK, SK_WGL_CONTEXT_CORE_PROFILE_BIT, |
| 0, |
| }; |
| for (size_t v = 0; v < SK_ARRAY_COUNT(kCoreGLVersions) / 2; ++v) { |
| coreProfileAttribs[1] = kCoreGLVersions[2 * v]; |
| coreProfileAttribs[3] = kCoreGLVersions[2 * v + 1]; |
| glrc = extensions.createContextAttribs(dc, shareContext, coreProfileAttribs); |
| if (glrc) { |
| break; |
| } |
| } |
| } |
| } |
| |
| if (nullptr == glrc) { |
| glrc = wglCreateContext(dc); |
| if (shareContext) { |
| if (!wglShareLists(shareContext, glrc)) { |
| wglDeleteContext(glrc); |
| return nullptr; |
| } |
| } |
| } |
| SkASSERT(glrc); |
| |
| wglMakeCurrent(prevDC, prevGLRC); |
| |
| // This might help make the context non-vsynced. |
| if (extensions.hasExtension(dc, "WGL_EXT_swap_control")) { |
| extensions.swapInterval(-1); |
| } |
| return glrc; |
| } |
| |
| HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, bool deepColor, |
| SkWGLContextRequest contextType, HGLRC shareContext) { |
| SkWGLExtensions extensions; |
| if (!extensions.hasExtension(dc, "WGL_ARB_pixel_format")) { |
| return nullptr; |
| } |
| |
| BOOL set = FALSE; |
| |
| int pixelFormatsToTry[] = { -1, -1 }; |
| get_pixel_formats_to_try(dc, extensions, true, msaaSampleCount, deepColor, pixelFormatsToTry); |
| for (size_t f = 0; |
| !set && -1 != pixelFormatsToTry[f] && f < SK_ARRAY_COUNT(pixelFormatsToTry); |
| ++f) { |
| PIXELFORMATDESCRIPTOR pfd; |
| DescribePixelFormat(dc, pixelFormatsToTry[f], sizeof(pfd), &pfd); |
| set = SetPixelFormat(dc, pixelFormatsToTry[f], &pfd); |
| } |
| |
| if (!set) { |
| return nullptr; |
| } |
| |
| return create_gl_context(dc, extensions, contextType, shareContext); |
| } |
| |
| SkWGLPbufferContext* SkWGLPbufferContext::Create(HDC parentDC, SkWGLContextRequest contextType, |
| HGLRC shareContext) { |
| SkWGLExtensions extensions; |
| if (!extensions.hasExtension(parentDC, "WGL_ARB_pixel_format") || |
| !extensions.hasExtension(parentDC, "WGL_ARB_pbuffer")) { |
| return nullptr; |
| } |
| |
| // We cache the pixel formats once, and then reuse them. That's possibly incorrect if |
| // there are multiple GPUs, but this function is always called with a freshly made, |
| // identically constructed HDC (see WinGLTestContext). |
| // |
| // We only store two potential pixel formats, one for single buffer, one for double buffer. |
| // We never ask for MSAA, so we don't need the second pixel format for each buffering state. |
| static int gPixelFormats[2] = { -1, -1 }; |
| static SkOnce once; |
| once([=] { |
| { |
| // Single buffer |
| int pixelFormatsToTry[2] = { -1, -1 }; |
| get_pixel_formats_to_try(parentDC, extensions, false, 0, false, pixelFormatsToTry); |
| gPixelFormats[0] = pixelFormatsToTry[0]; |
| } |
| { |
| // Double buffer |
| int pixelFormatsToTry[2] = { -1, -1 }; |
| get_pixel_formats_to_try(parentDC, extensions, true, 0, false, pixelFormatsToTry); |
| gPixelFormats[1] = pixelFormatsToTry[0]; |
| } |
| }); |
| |
| // try for single buffer first |
| for (int pixelFormat : gPixelFormats) { |
| if (-1 == pixelFormat) { |
| continue; |
| } |
| HPBUFFER pbuf = extensions.createPbuffer(parentDC, pixelFormat, 1, 1, nullptr); |
| if (0 != pbuf) { |
| HDC dc = extensions.getPbufferDC(pbuf); |
| if (dc) { |
| HGLRC glrc = create_gl_context(dc, extensions, contextType, shareContext); |
| if (glrc) { |
| return new SkWGLPbufferContext(pbuf, dc, glrc); |
| } |
| extensions.releasePbufferDC(pbuf, dc); |
| } |
| extensions.destroyPbuffer(pbuf); |
| } |
| } |
| return nullptr; |
| } |
| |
| SkWGLPbufferContext::~SkWGLPbufferContext() { |
| SkASSERT(fExtensions.hasExtension(fDC, "WGL_ARB_pbuffer")); |
| wglDeleteContext(fGLRC); |
| fExtensions.releasePbufferDC(fPbuffer, fDC); |
| fExtensions.destroyPbuffer(fPbuffer); |
| } |
| |
| SkWGLPbufferContext::SkWGLPbufferContext(HPBUFFER pbuffer, HDC dc, HGLRC glrc) |
| : fPbuffer(pbuffer) |
| , fDC(dc) |
| , fGLRC(glrc) { |
| } |
| |
| #endif//defined(SK_BUILD_FOR_WIN) |