From 9245b7ee764b61222ffba409a7d2d7e62fde01a6 Mon Sep 17 00:00:00 2001 From: bsalomon Date: Tue, 1 Jul 2014 07:20:11 -0700 Subject: [PATCH] When performing offscreen rendering on windows, attempt to use a pbuffer context. A pbuffer context is less likely to have a blocking SwapBuffers (due to vsync). R=robertphillips@google.com Author: bsalomon@google.com Review URL: https://codereview.chromium.org/336863009 --- include/gpu/gl/SkGLContextHelper.h | 4 +- include/gpu/gl/SkNativeGLContext.h | 2 + include/utils/SkWGL.h | 53 +++++++++- src/gpu/gl/win/SkNativeGLContext_win.cpp | 58 +++++++++-- src/utils/win/SkWGL_win.cpp | 160 +++++++++++++++++++++++++------ 5 files changed, 230 insertions(+), 47 deletions(-) diff --git a/include/gpu/gl/SkGLContextHelper.h b/include/gpu/gl/SkGLContextHelper.h index 9da8741..a06c6e1 100644 --- a/include/gpu/gl/SkGLContextHelper.h +++ b/include/gpu/gl/SkGLContextHelper.h @@ -78,9 +78,9 @@ private: * SK_GL(glCtx, GenTextures(1, &texID)); */ #define SK_GL(ctx, X) (ctx).gl()->fFunctions.f ## X; \ - SkASSERT(GR_GL_NO_ERROR == (ctx).gl()->fFunctions.fGetError()) + SkASSERT(0 == (ctx).gl()->fFunctions.fGetError()) #define SK_GL_RET(ctx, RET, X) (RET) = (ctx).gl()->fFunctions.f ## X; \ - SkASSERT(GR_GL_NO_ERROR == (ctx).gl()->fFunctions.fGetError()) + SkASSERT(0 == (ctx).gl()->fFunctions.fGetError()) #define SK_GL_NOERRCHECK(ctx, X) (ctx).gl()->fFunctions.f ## X #define SK_GL_RET_NOERRCHECK(ctx, RET, X) (RET) = (ctx).gl()->fFunctions.f ## X diff --git a/include/gpu/gl/SkNativeGLContext.h b/include/gpu/gl/SkNativeGLContext.h index fac52b3..01b7309 100644 --- a/include/gpu/gl/SkNativeGLContext.h +++ b/include/gpu/gl/SkNativeGLContext.h @@ -21,6 +21,7 @@ #elif defined(SK_BUILD_FOR_WIN32) #include #include + #include "SkWGL.h" #endif class SkNativeGLContext : public SkGLContextHelper { @@ -78,6 +79,7 @@ private: HDC fDeviceContext; HGLRC fGlRenderContext; static ATOM gWC; + SkWGLPbufferContext* fPbufferContext; #elif defined(SK_BUILD_FOR_IOS) void* fEAGLContext; #endif diff --git a/include/utils/SkWGL.h b/include/utils/SkWGL.h index 5272f17..d502eb0 100644 --- a/include/utils/SkWGL.h +++ b/include/utils/SkWGL.h @@ -56,6 +56,8 @@ #define SK_ERROR_INVALID_VERSION 0x2095 #define SK_ERROR_INVALID_PROFILE 0x2096 +DECLARE_HANDLE(HPBUFFER); + class SkWGLExtensions { public: SkWGLExtensions(); @@ -73,6 +75,13 @@ public: BOOL getPixelFormatAttribfv(HDC hdc, int, int, UINT, const int*, FLOAT*) const; HGLRC createContextAttribs(HDC, HGLRC, const int *) const; + BOOL swapInterval(int interval) const; + + HPBUFFER createPbuffer(HDC, int , int, int, const int*) const; + HDC getPbufferDC(HPBUFFER) const; + int releasePbufferDC(HPBUFFER, HDC) const; + BOOL destroyPbuffer(HPBUFFER) const; + /** * WGL doesn't have precise rules for the ordering of formats returned * by wglChoosePixelFormat. This function helps choose among the set of @@ -89,19 +98,29 @@ public: int selectFormat(const int formats[], int formatCount, HDC dc, - int desiredSampleCount); + int desiredSampleCount) const; private: - typedef const char* (WINAPI *GetExtensionsStringProc)(HDC hdc); - typedef BOOL (WINAPI *ChoosePixelFormatProc)(HDC hdc, const int *, const FLOAT *, UINT, int *, UINT *); + typedef const char* (WINAPI *GetExtensionsStringProc)(HDC); + typedef BOOL (WINAPI *ChoosePixelFormatProc)(HDC, const int *, const FLOAT *, UINT, int *, UINT *); typedef BOOL (WINAPI *GetPixelFormatAttribivProc)(HDC, int, int, UINT, const int*, int*); - typedef BOOL (WINAPI *GetPixelFormatAttribfvProc)(HDC hdc, int, int, UINT, const int*, FLOAT*); - typedef HGLRC (WINAPI *CreateContextAttribsProc)(HDC hDC, HGLRC, const int *); + typedef BOOL (WINAPI *GetPixelFormatAttribfvProc)(HDC, int, int, UINT, const int*, FLOAT*); + typedef HGLRC (WINAPI *CreateContextAttribsProc)(HDC, HGLRC, const int *); + typedef BOOL (WINAPI* SwapIntervalProc)(int); + typedef HPBUFFER (WINAPI* CreatePbufferProc)(HDC, int , int, int, const int*); + typedef HDC (WINAPI* GetPbufferDCProc)(HPBUFFER); + typedef int (WINAPI* ReleasePbufferDCProc)(HPBUFFER, HDC); + typedef BOOL (WINAPI* DestroyPbufferProc)(HPBUFFER); GetExtensionsStringProc fGetExtensionsString; ChoosePixelFormatProc fChoosePixelFormat; GetPixelFormatAttribfvProc fGetPixelFormatAttribfv; GetPixelFormatAttribivProc fGetPixelFormatAttribiv; CreateContextAttribsProc fCreateContextAttribs; + SwapIntervalProc fSwapInterval; + CreatePbufferProc fCreatePbuffer; + GetPbufferDCProc fGetPbufferDC; + ReleasePbufferDCProc fReleasePbufferDC; + DestroyPbufferProc fDestroyPbuffer; }; enum SkWGLContextRequest { @@ -122,4 +141,28 @@ enum SkWGLContextRequest { */ HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, SkWGLContextRequest context); +/** + * Helper class for creating a pbuffer context and deleting all the handles when finished. This + * requires that a device context has been created. However, the pbuffer gets its own device + * context. The original device context can be released once the pbuffer context is created. + */ +class SkWGLPbufferContext : public SkRefCnt { +public: + static SkWGLPbufferContext* Create(HDC parentDC, int msaaSampleCount, + SkWGLContextRequest contextType); + + virtual ~SkWGLPbufferContext(); + + HDC getDC() const { return fDC; } + HGLRC getGLRC() const { return fGLRC; } + +private: + SkWGLPbufferContext(HPBUFFER pbuffer, HDC dc, HGLRC glrc); + + HPBUFFER fPbuffer; + HDC fDC; + HGLRC fGLRC; + SkWGLExtensions fExtensions; +}; + #endif diff --git a/src/gpu/gl/win/SkNativeGLContext_win.cpp b/src/gpu/gl/win/SkNativeGLContext_win.cpp index f085fdc..ab66ba4 100644 --- a/src/gpu/gl/win/SkNativeGLContext_win.cpp +++ b/src/gpu/gl/win/SkNativeGLContext_win.cpp @@ -7,7 +7,6 @@ */ #include "gl/SkNativeGLContext.h" -#include "SkWGL.h" #define WIN32_LEAN_AND_MEAN #include @@ -28,7 +27,8 @@ ATOM SkNativeGLContext::gWC = 0; SkNativeGLContext::SkNativeGLContext() : fWindow(NULL) , fDeviceContext(NULL) - , fGlRenderContext(0) { + , fGlRenderContext(0) + , fPbufferContext(NULL) { } SkNativeGLContext::~SkNativeGLContext() { @@ -36,14 +36,18 @@ SkNativeGLContext::~SkNativeGLContext() { } void SkNativeGLContext::destroyGLContext() { + SkSafeSetNull(fPbufferContext); if (fGlRenderContext) { wglDeleteContext(fGlRenderContext); + fGlRenderContext = 0; } if (fWindow && fDeviceContext) { ReleaseDC(fWindow, fDeviceContext); + fDeviceContext = 0; } if (fWindow) { DestroyWindow(fWindow); + fWindow = 0; } } @@ -91,17 +95,35 @@ const GrGLInterface* SkNativeGLContext::createGLContext(GrGLStandard forcedGpuAP kGLES_GrGLStandard == forcedGpuAPI ? kGLES_SkWGLContextRequest : kGLPreferCompatibilityProfile_SkWGLContextRequest; - if (!(fGlRenderContext = SkCreateWGLContext(fDeviceContext, 0, contextType))) { - SkDebugf("Could not create rendering context.\n"); - this->destroyGLContext(); - return NULL; + fPbufferContext = SkWGLPbufferContext::Create(fDeviceContext, 0, contextType); + + HDC dc; + HGLRC glrc; + + if (NULL == fPbufferContext) { + if (!(fGlRenderContext = SkCreateWGLContext(fDeviceContext, 0, contextType))) { + SkDebugf("Could not create rendering context.\n"); + this->destroyGLContext(); + return NULL; + } + dc = fDeviceContext; + glrc = fGlRenderContext; + } else { + ReleaseDC(fWindow, fDeviceContext); + fDeviceContext = 0; + DestroyWindow(fWindow); + fWindow = 0; + + dc = fPbufferContext->getDC(); + glrc = fPbufferContext->getGLRC(); } - if (!(wglMakeCurrent(fDeviceContext, fGlRenderContext))) { + if (!(wglMakeCurrent(dc, glrc))) { SkDebugf("Could not set the context.\n"); this->destroyGLContext(); return NULL; } + const GrGLInterface* interface = GrGLCreateNativeInterface(); if (NULL == interface) { SkDebugf("Could not create GL interface.\n"); @@ -113,13 +135,31 @@ const GrGLInterface* SkNativeGLContext::createGLContext(GrGLStandard forcedGpuAP } void SkNativeGLContext::makeCurrent() const { - if (!wglMakeCurrent(fDeviceContext, fGlRenderContext)) { + HDC dc; + HGLRC glrc; + + if (NULL == fPbufferContext) { + dc = fDeviceContext; + glrc = fGlRenderContext; + } else { + dc = fPbufferContext->getDC(); + glrc = fPbufferContext->getGLRC(); + } + + if (!wglMakeCurrent(dc, glrc)) { SkDebugf("Could not create rendering context.\n"); } } void SkNativeGLContext::swapBuffers() const { - if (!SwapBuffers(fDeviceContext)) { + HDC dc; + + if (NULL == fPbufferContext) { + dc = fDeviceContext; + } else { + dc = fPbufferContext->getDC(); + } + if (!SwapBuffers(dc)) { SkDebugf("Could not complete SwapBuffers.\n"); } } diff --git a/src/utils/win/SkWGL_win.cpp b/src/utils/win/SkWGL_win.cpp index a8552a8..9c0c2d4 100644 --- a/src/utils/win/SkWGL_win.cpp +++ b/src/utils/win/SkWGL_win.cpp @@ -75,6 +75,30 @@ HGLRC SkWGLExtensions::createContextAttribs(HDC hDC, 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 { @@ -98,7 +122,7 @@ bool pf_less(const PixelFormat& a, const PixelFormat& b) { int SkWGLExtensions::selectFormat(const int formats[], int formatCount, HDC dc, - int desiredSampleCount) { + int desiredSampleCount) const { PixelFormat desiredFormat = { 0, desiredSampleCount, @@ -207,7 +231,13 @@ SkWGLExtensions::SkWGLExtensions() , fChoosePixelFormat(NULL) , fGetPixelFormatAttribfv(NULL) , fGetPixelFormatAttribiv(NULL) - , fCreateContextAttribs(NULL) { + , fCreateContextAttribs(NULL) + , fSwapInterval(NULL) + , fCreatePbuffer(NULL) + , fGetPbufferDC(NULL) + , fReleasePbufferDC(NULL) + , fDestroyPbuffer(NULL) + { HDC prevDC = wglGetCurrentDC(); HGLRC prevGLRC = wglGetCurrentContext(); @@ -236,6 +266,11 @@ SkWGLExtensions::SkWGLExtensions() 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, NULL); wglDeleteContext(dummyGLRC); @@ -245,21 +280,14 @@ SkWGLExtensions::SkWGLExtensions() wglMakeCurrent(prevDC, prevGLRC); } -HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, SkWGLContextRequest contextType) { - SkWGLExtensions extensions; - if (!extensions.hasExtension(dc, "WGL_ARB_pixel_format")) { - return NULL; - } - - HDC prevDC = wglGetCurrentDC(); - HGLRC prevGLRC = wglGetCurrentContext(); - PIXELFORMATDESCRIPTOR pfd; +/////////////////////////////////////////////////////////////////////////////// - int format = 0; - - static const int iAttrs[] = { +static void get_pixel_formats_to_try(HDC dc, const SkWGLExtensions& extensions, + bool doubleBuffered, int msaaSampleCount, + int formatsToTry[2]) { + int iAttrs[] = { SK_WGL_DRAW_TO_WINDOW, TRUE, - SK_WGL_DOUBLE_BUFFER, TRUE, + SK_WGL_DOUBLE_BUFFER, (doubleBuffered ? TRUE : FALSE), SK_WGL_ACCELERATION, SK_WGL_FULL_ACCELERATION, SK_WGL_SUPPORT_OPENGL, TRUE, SK_WGL_COLOR_BITS, 24, @@ -270,6 +298,7 @@ HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, SkWGLContextRequest contex float fAttrs[] = {0, 0}; + // Get a MSAA format if requested and possible. if (msaaSampleCount > 0 && extensions.hasExtension(dc, "WGL_ARB_multisample")) { static const int kIAttrsCount = SK_ARRAY_COUNT(iAttrs); @@ -287,28 +316,23 @@ HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, SkWGLContextRequest contex int formats[64]; extensions.choosePixelFormat(dc, msaaIAttrs, fAttrs, 64, formats, &num); num = SkTMin(num, 64U); - int formatToTry = extensions.selectFormat(formats, - num, - dc, - msaaSampleCount); - DescribePixelFormat(dc, formatToTry, sizeof(pfd), &pfd); - if (SetPixelFormat(dc, formatToTry, &pfd)) { - format = formatToTry; - } + formatsToTry[0] = extensions.selectFormat(formats, num, dc, msaaSampleCount); } - if (0 == format) { - // Either MSAA wasn't requested or creation failed - unsigned int num; - extensions.choosePixelFormat(dc, iAttrs, fAttrs, 1, &format, &num); - DescribePixelFormat(dc, format, sizeof(pfd), &pfd); - SkDEBUGCODE(BOOL set =) SetPixelFormat(dc, format, &pfd); - SkASSERT(TRUE == set); - } + // Get a non-MSAA format + int* format = -1 == formatsToTry[0] ? &formatsToTry[0] : &formatsToTry[1]; + unsigned int num; + extensions.choosePixelFormat(dc, iAttrs, fAttrs, 1, format, &num); +} + +static HGLRC create_gl_context(HDC dc, SkWGLExtensions extensions, SkWGLContextRequest contextType) { + HDC prevDC = wglGetCurrentDC(); + HGLRC prevGLRC = wglGetCurrentContext(); HGLRC glrc = NULL; if (kGLES_SkWGLContextRequest == contextType) { if (!extensions.hasExtension(dc, "WGL_EXT_create_context_es2_profile")) { + wglMakeCurrent(prevDC, prevGLRC); return NULL; } static const int glesAttribs[] = { @@ -319,6 +343,7 @@ HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, SkWGLContextRequest contex }; glrc = extensions.createContextAttribs(dc, NULL, glesAttribs); if (NULL == glrc) { + wglMakeCurrent(prevDC, prevGLRC); return NULL; } } else { @@ -355,5 +380,78 @@ HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, SkWGLContextRequest contex 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, SkWGLContextRequest contextType) { + SkWGLExtensions extensions; + if (!extensions.hasExtension(dc, "WGL_ARB_pixel_format")) { + return NULL; + } + + BOOL set = FALSE; + + int pixelFormatsToTry[] = { -1, -1 }; + get_pixel_formats_to_try(dc, extensions, true, msaaSampleCount, pixelFormatsToTry); + for (int 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 NULL; + } + + return create_gl_context(dc, extensions, contextType);} + +SkWGLPbufferContext* SkWGLPbufferContext::Create(HDC parentDC, int msaaSampleCount, + SkWGLContextRequest contextType) { + SkWGLExtensions extensions; + if (!extensions.hasExtension(parentDC, "WGL_ARB_pixel_format") || + !extensions.hasExtension(parentDC, "WGL_ARB_pbuffer")) { + return NULL; + } + + // try for single buffer first + for (int dblBuffer = 0; dblBuffer < 2; ++dblBuffer) { + int pixelFormatsToTry[] = { -1, -1 }; + get_pixel_formats_to_try(parentDC, extensions, (0 != dblBuffer), msaaSampleCount, + pixelFormatsToTry); + for (int f = 0; -1 != pixelFormatsToTry[f] && f < SK_ARRAY_COUNT(pixelFormatsToTry); ++f) { + HPBUFFER pbuf = extensions.createPbuffer(parentDC, pixelFormatsToTry[f], 1, 1, NULL); + if (0 != pbuf) { + HDC dc = extensions.getPbufferDC(pbuf); + if (NULL != dc) { + HGLRC glrc = create_gl_context(dc, extensions, contextType); + if (NULL != glrc) { + return SkNEW_ARGS(SkWGLPbufferContext, (pbuf, dc, glrc)); + } + extensions.releasePbufferDC(pbuf, dc); + } + extensions.destroyPbuffer(pbuf); + } + } + } + return NULL; +} + +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) { +} -- 2.7.4