Add support for GL_NV_framebuffer_multisample_coverage

Review URL: http://codereview.appspot.com/6005043


git-svn-id: http://skia.googlecode.com/svn/trunk@3651 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/gpu/gl/GrGLDefines.h b/include/gpu/gl/GrGLDefines.h
index 2268510..0e03b55 100644
--- a/include/gpu/gl/GrGLDefines.h
+++ b/include/gpu/gl/GrGLDefines.h
@@ -194,6 +194,10 @@
 #define GR_GL_SAMPLES                        0x80A9
 #define GR_GL_SAMPLE_COVERAGE_VALUE          0x80AA
 #define GR_GL_SAMPLE_COVERAGE_INVERT         0x80AB
+#define GR_GL_RENDERBUFFER_COVERAGE_SAMPLES  0x8CAB
+#define GR_GL_RENDERBUFFER_COLOR_SAMPLES     0x8E10
+#define GR_GL_MAX_MULTISAMPLE_COVERAGE_MODES 0x8E11
+#define GR_GL_MULTISAMPLE_COVERAGE_MODES     0x8E12
 
 /* GetTextureParameter */
 /*      GL_TEXTURE_MAG_FILTER */
diff --git a/include/gpu/gl/GrGLInterface.h b/include/gpu/gl/GrGLInterface.h
index a16b9b1..876c072 100644
--- a/include/gpu/gl/GrGLInterface.h
+++ b/include/gpu/gl/GrGLInterface.h
@@ -223,6 +223,7 @@
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLReadPixelsProc)(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLvoid* pixels);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLRenderbufferStorageProc)(GrGLenum target, GrGLenum internalformat, GrGLsizei width, GrGLsizei height);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLRenderbufferStorageMultisampleProc)(GrGLenum target, GrGLsizei samples, GrGLenum internalformat, GrGLsizei width, GrGLsizei height);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLRenderbufferStorageMultisampleCoverageProc)(GrGLenum target, GrGLsizei coverageSamples, GrGLsizei colorSamples, GrGLenum internalformat, GrGLsizei width, GrGLsizei height);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLResolveMultisampleFramebufferProc)();
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLScissorProc)(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLShaderSourceProc)(GrGLuint shader, GrGLsizei count, const char** str, const GrGLint* length);
@@ -384,6 +385,7 @@
     GLPtr<GrGLReadPixelsProc> fReadPixels;
     GLPtr<GrGLRenderbufferStorageProc> fRenderbufferStorage;
     GLPtr<GrGLRenderbufferStorageMultisampleProc> fRenderbufferStorageMultisample;
+    GLPtr<GrGLRenderbufferStorageMultisampleCoverageProc> fRenderbufferStorageMultisampleCoverage;
     GLPtr<GrGLResolveMultisampleFramebufferProc> fResolveMultisampleFramebuffer;
     GLPtr<GrGLScissorProc> fScissor;
     GLPtr<GrGLShaderSourceProc> fShaderSource;
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 5361967..c7e9380 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -9,6 +9,7 @@
 
 #include "GrGLCaps.h"
 #include "GrGLContextInfo.h"
+#include "SkTSearch.h"
 
 GrGLCaps::GrGLCaps() {
     this->reset();
@@ -19,6 +20,7 @@
     fStencilFormats.reset();
     fStencilVerifiedColorConfigs.reset();
     fMSFBOType = kNone_MSFBOType;
+    fCoverageAAType = kNone_CoverageAAType;
     fMaxFragmentUniformVectors = 0;
     fRGBA8RenderbufferSupport = false;
     fBGRAFormatSupport = false;
@@ -43,6 +45,8 @@
     fStencilVerifiedColorConfigs = caps.fStencilVerifiedColorConfigs;
     fMaxFragmentUniformVectors = caps.fMaxFragmentUniformVectors;
     fMSFBOType = caps.fMSFBOType;
+    fCoverageAAType = caps.fCoverageAAType;
+    fMSAACoverageModes = caps.fMSAACoverageModes;
     fRGBA8RenderbufferSupport = caps.fRGBA8RenderbufferSupport;
     fBGRAFormatSupport = caps.fBGRAFormatSupport;
     fBGRAIsInternalFormat = caps.fBGRAIsInternalFormat;
@@ -143,6 +147,22 @@
     this->initStencilFormats(ctxInfo);
 }
 
+namespace {
+int coverage_mode_compare(const GrGLCaps::MSAACoverageMode* left,
+                          const GrGLCaps::MSAACoverageMode* right) {
+    if (left->fCoverageSampleCnt < right->fCoverageSampleCnt) {
+        return -1;
+    } else if (right->fCoverageSampleCnt < left->fCoverageSampleCnt) {
+        return 1;
+    } else if (left->fColorSampleCnt < right->fColorSampleCnt) {
+        return -1;
+    } else if (right->fColorSampleCnt < left->fColorSampleCnt) {
+        return 1;
+    }
+    return 0;
+}
+}
+
 void GrGLCaps::initFSAASupport(const GrGLContextInfo& ctxInfo) {
 
     fMSFBOType = kNone_MSFBOType;
@@ -152,8 +172,8 @@
            // and fbo_blit extensions.
            fMSFBOType = kDesktopEXT_MSFBOType;
        } else if (ctxInfo.hasExtension("GL_APPLE_framebuffer_multisample")) {
-            fMSFBOType = kAppleES_MSFBOType;
-        }
+           fMSFBOType = kAppleES_MSFBOType;
+       }
     } else {
         if ((ctxInfo.version() >= GR_GL_VER(3,0)) ||
             ctxInfo.hasExtension("GL_ARB_framebuffer_object")) {
@@ -162,6 +182,52 @@
                    ctxInfo.hasExtension("GL_EXT_framebuffer_blit")) {
             fMSFBOType = GrGLCaps::kDesktopEXT_MSFBOType;
         }
+        // TODO: We could populate fMSAACoverageModes using GetInternalformativ
+        // on GL 4.2+. It's format-specific, though. See also
+        // http://code.google.com/p/skia/issues/detail?id=470 about using actual
+        // rather than requested sample counts in cache key.
+        if (ctxInfo.hasExtension("GL_NV_framebuffer_multisample_coverage")) {
+            fCoverageAAType = kNVDesktop_CoverageAAType;
+            GrGLint count;
+            GR_GL_GetIntegerv(ctxInfo.interface(),
+                              GR_GL_MAX_MULTISAMPLE_COVERAGE_MODES,
+                              &count);
+            fMSAACoverageModes.setCount(count);
+            GR_GL_GetIntegerv(ctxInfo.interface(),
+                              GR_GL_MULTISAMPLE_COVERAGE_MODES,
+                              (int*)&fMSAACoverageModes[0]);
+            // The NV driver seems to return the modes already sorted but the
+            // spec doesn't require this. So we sort.
+            SkQSortCompareProc compareProc =
+                reinterpret_cast<SkQSortCompareProc>(&coverage_mode_compare);
+            SkQSort(&fMSAACoverageModes[0],
+                    count,
+                    sizeof(MSAACoverageMode),
+                    compareProc);
+        }
+    }
+}
+
+const GrGLCaps::MSAACoverageMode& GrGLCaps::getMSAACoverageMode(
+                                            int desiredSampleCount) const {
+    static const MSAACoverageMode kNoneMode = {0, 0};
+    if (0 == fMSAACoverageModes.count()) {
+        return kNoneMode;
+    } else {
+        GrAssert(kNone_CoverageAAType != fCoverageAAType);
+        int max = (fMSAACoverageModes.end() - 1)->fCoverageSampleCnt;
+        desiredSampleCount = GrMin(desiredSampleCount, max);
+        MSAACoverageMode desiredMode = {desiredSampleCount, 0};
+        int idx = SkTSearch<MSAACoverageMode>(&fMSAACoverageModes[0],
+                                              fMSAACoverageModes.count(),
+                                              desiredMode,
+                                              sizeof(MSAACoverageMode),
+                                              &coverage_mode_compare);
+        if (idx < 0) {
+            idx = ~idx;
+        }
+        GrAssert(idx >= 0 && idx < fMSAACoverageModes.count());
+        return fMSAACoverageModes[idx];
     }
 }
 
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index 8b3f9e7..31c4392 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -24,6 +24,23 @@
     typedef GrGLStencilBuffer::Format StencilFormat;
 
     /**
+     * Represents a supported multisampling/coverage-sampling mode.
+     */
+    struct MSAACoverageMode {
+        // "Coverage samples" includes samples that actually have color, depth,
+        // stencil, ... as well as those that don't (coverage only). All samples
+        // are coverage samples. (We're using the word "coverage sample" to
+        // match the NV extension language.)
+        int fCoverageSampleCnt;
+
+        // Color samples are samples that store data values (color, stencil,
+        // depth) rather than just representing coverage. They are a subset
+        // of coverage samples. (Again the wording was chosen to match the
+        // extension.)
+        int fColorSampleCnt;
+    };
+
+    /**
      * The type of MSAA for FBOs supported. Different extensions have different
      * semantics of how / when a resolve is performed.
      */
@@ -46,6 +63,18 @@
         kAppleES_MSFBOType,
     };
 
+    enum CoverageAAType {
+        /**
+         * No coverage sample support
+         */
+        kNone_CoverageAAType,
+
+        /**
+         * GL_NV_framebuffer_multisample_coverage
+         */
+        kNVDesktop_CoverageAAType,
+    };
+
     /**
      * Creates a GrGLCaps that advertises no support for any extensions,
      * formats, etc. Call init to initialize from a GrGLContextInfo.
@@ -108,6 +137,20 @@
     MSFBOType msFBOType() const { return fMSFBOType; }
 
     /**
+     * Reports the type of coverage sample AA support.
+     */
+    CoverageAAType coverageAAType() const { return fCoverageAAType; }
+
+    /**
+     * Chooses a supported coverage mode based on a desired sample count. The
+     * desired sample count is rounded up the next supported coverage sample
+     * count unless a it is larger than the max in which case it is rounded
+     * down. Once a coverage sample count is decided, the supported mode with
+     * the fewest color samples is chosen.
+     */
+    const MSAACoverageMode& getMSAACoverageMode(int desiredSampleCount) const;
+
+    /**
      * Prints the caps info using GrPrintf.
      */
     void print() const;
@@ -214,6 +257,8 @@
 
     int fMaxFragmentUniformVectors;
     MSFBOType fMSFBOType;
+    CoverageAAType fCoverageAAType;
+    SkTDArray<MSAACoverageMode> fMSAACoverageModes;
 
     bool fRGBA8RenderbufferSupport : 1;
     bool fBGRAFormatSupport : 1;
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index dcde84e..9114a0e 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -845,6 +845,38 @@
     return succeeded;
 }
 
+namespace {
+bool renderbuffer_storage_msaa(GrGLContextInfo& ctxInfo,
+                               int sampleCount,
+                               GrGLenum format,
+                               int width, int height) {
+    CLEAR_ERROR_BEFORE_ALLOC(ctxInfo.interface());
+    GrAssert(GrGLCaps::kNone_MSFBOType != ctxInfo.caps().msFBOType());
+    bool created = false;
+    if (GrGLCaps::kNVDesktop_CoverageAAType ==
+        ctxInfo.caps().coverageAAType()) {
+        const GrGLCaps::MSAACoverageMode& mode =
+            ctxInfo.caps().getMSAACoverageMode(sampleCount);
+        GL_ALLOC_CALL(ctxInfo.interface(),
+                      RenderbufferStorageMultisampleCoverage(GR_GL_RENDERBUFFER,
+                                                        mode.fCoverageSampleCnt,
+                                                        mode.fColorSampleCnt,
+                                                        format,
+                                                        width, height));
+        created = (GR_GL_NO_ERROR == CHECK_ALLOC_ERROR(ctxInfo.interface()));
+    }
+    if (!created) {
+        GL_ALLOC_CALL(ctxInfo.interface(),
+                      RenderbufferStorageMultisample(GR_GL_RENDERBUFFER,
+                                                     sampleCount,
+                                                     format,
+                                                     width, height));
+        created = (GR_GL_NO_ERROR == CHECK_ALLOC_ERROR(ctxInfo.interface()));
+    }
+    return created;
+}
+}
+
 bool GrGpuGL::createRenderTargetObjects(int width, int height,
                                         GrGLuint texID,
                                         GrGLRenderTarget::Desc* desc) {
@@ -854,7 +886,6 @@
     desc->fOwnIDs = true;
 
     GrGLenum status;
-    GrGLint err;
 
     GrGLenum msColorFormat = 0; // suppress warning
 
@@ -890,14 +921,10 @@
         GrAssert(desc->fSampleCnt > 1);
         GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER,
                                desc->fMSColorRenderbufferID));
-        CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
-        GL_ALLOC_CALL(this->glInterface(),
-                      RenderbufferStorageMultisample(GR_GL_RENDERBUFFER, 
-                                                     desc->fSampleCnt,
-                                                     msColorFormat,
-                                                     width, height));
-        err = CHECK_ALLOC_ERROR(this->glInterface());
-        if (err != GR_GL_NO_ERROR) {
+        if (!renderbuffer_storage_msaa(fGLContextInfo,
+                                       desc->fSampleCnt,
+                                       msColorFormat,
+                                       width, height)) {
             goto FAILED;
         }
         GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, desc->fRTFBOID));
@@ -1128,23 +1155,23 @@
         CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
         // we do this "if" so that we don't call the multisample
         // version on a GL that doesn't have an MSAA extension.
-        if (samples > 1) {
-            GL_ALLOC_CALL(this->glInterface(),
-                          RenderbufferStorageMultisample(GR_GL_RENDERBUFFER,
-                                                         samples,
-                                                         sFmt.fInternalFormat,
-                                                         width, height));
+        bool created;
+        if (samples > 0) {
+            created = renderbuffer_storage_msaa(fGLContextInfo,
+                                                samples,
+                                                sFmt.fInternalFormat,
+                                                width, height);
         } else {
             GL_ALLOC_CALL(this->glInterface(),
                           RenderbufferStorage(GR_GL_RENDERBUFFER,
                                               sFmt.fInternalFormat,
                                               width, height));
+            created =
+                (GR_GL_NO_ERROR == CHECK_ALLOC_ERROR(this->glInterface()));
         }
-
-        GrGLenum err = CHECK_ALLOC_ERROR(this->glInterface());
-        if (err == GR_GL_NO_ERROR) {
-            // After sized formats we attempt an unsized format and take whatever
-            // sizes GL gives us. In that case we query for the size.
+        if (created) {
+            // After sized formats we attempt an unsized format and take
+            // whatever sizes GL gives us. In that case we query for the size.
             GrGLStencilBuffer::Format format = sFmt;
             get_stencil_rb_sizes(this->glInterface(), sbID, &format);
             sb = new GrGLStencilBuffer(this, sbID, width, height, 
diff --git a/src/gpu/gl/unix/GrGLCreateNativeInterface_unix.cpp b/src/gpu/gl/unix/GrGLCreateNativeInterface_unix.cpp
index 1e9f2e0..be915ac 100644
--- a/src/gpu/gl/unix/GrGLCreateNativeInterface_unix.cpp
+++ b/src/gpu/gl/unix/GrGLCreateNativeInterface_unix.cpp
@@ -102,6 +102,10 @@
         interface->fPixelStorei = glPixelStorei;
         interface->fReadBuffer = glReadBuffer;
         interface->fReadPixels = glReadPixels;
+        if (GrGLHasExtensionFromString("GL_NV_framebuffer_multisample_coverage",
+                                       extString)) {
+            GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisampleCoverage, NV);
+        }
         interface->fScissor = glScissor;
         GR_GL_GET_PROC(ShaderSource);
         interface->fStencilFunc = glStencilFunc;
diff --git a/src/gpu/gl/win/GrGLCreateNativeInterface_win.cpp b/src/gpu/gl/win/GrGLCreateNativeInterface_win.cpp
index f050a6f..2048974 100644
--- a/src/gpu/gl/win/GrGLCreateNativeInterface_win.cpp
+++ b/src/gpu/gl/win/GrGLCreateNativeInterface_win.cpp
@@ -123,6 +123,9 @@
         GR_GL_GET_PROC(GetShaderiv);
         GR_GL_GET_PROC(GetUniformLocation);
         GR_GL_GET_PROC(LinkProgram);
+        if (GrGLHasExtensionFromString("GL_NV_framebuffer_multisample_coverage", extString)) {
+            GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisampleCoverage, NV);
+        }
         GR_GL_GET_PROC(ShaderSource);
         GR_GL_GET_PROC(StencilFuncSeparate);
         GR_GL_GET_PROC(StencilMaskSeparate);