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
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 @@
  * 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 <windows.h>
     #include <GL/GL.h>
+    #include "SkWGL.h"
 #endif
 
 class SkNativeGLContext : public SkGLContextHelper {
@@ -78,6 +79,7 @@
     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 @@
     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 @@
     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 @@
  */
 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 <windows.h>
@@ -28,7 +27,8 @@
 SkNativeGLContext::SkNativeGLContext()
     : fWindow(NULL)
     , fDeviceContext(NULL)
-    , fGlRenderContext(0) {
+    , fGlRenderContext(0)
+    , fPbufferContext(NULL) {
 }
 
 SkNativeGLContext::~SkNativeGLContext() {
@@ -36,14 +36,18 @@
 }
 
 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 @@
         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 @@
 }
 
 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 @@
     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 @@
 int SkWGLExtensions::selectFormat(const int formats[],
                                   int formatCount,
                                   HDC dc,
-                                  int desiredSampleCount) {
+                                  int desiredSampleCount) const {
     PixelFormat desiredFormat = {
         0,
         desiredSampleCount,
@@ -207,7 +231,13 @@
     , 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 @@
         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 @@
     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 @@
 
     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 @@
         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 @@
         };
         glrc = extensions.createContextAttribs(dc, NULL, glesAttribs);
         if (NULL == glrc) {
+            wglMakeCurrent(prevDC, prevGLRC);
             return NULL;
         }
     } else {
@@ -355,5 +380,78 @@
     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) {
+}