/* * 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)