Bug 1289655 - Add primitive restart for WebGL 2. - r=mtseng a=ritu
authorJeff Gilbert <jgilbert@mozilla.com>
Thu, 28 Jul 2016 17:15:32 -0700
changeset 428755 2c0795d4b1b0e83b31136fd217f9d237dd4d7031
parent 422556 5dff69685ff080094513b59dda01c23d488d6639
child 428756 1b1c16064e3ec621e0d07668918f7d7e48d8e347
push id33416
push userpaul@paul.cx
push dateMon, 24 Oct 2016 16:26:20 +0000
reviewersmtseng, ritu
bugs1289655
milestone50.0
Bug 1289655 - Add primitive restart for WebGL 2. - r=mtseng a=ritu MozReview-Commit-ID: 6SfI8yfROGI
dom/canvas/WebGL2Context.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLContextValidate.cpp
dom/canvas/test/webgl-conf/generated-mochitest.ini
dom/canvas/test/webgl-conf/mochitest-errata.ini
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/gl/GLContextFeatures.cpp
gfx/gl/GLContextSymbols.h
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -96,44 +96,53 @@ static const gl::GLFeature kRequiredFeat
     gl::GLFeature::vertex_array_object
 };
 
 bool
 WebGLContext::InitWebGL2(FailureReason* const out_failReason)
 {
     MOZ_ASSERT(IsWebGL2(), "WebGLContext is not a WebGL 2 context!");
 
-    // check OpenGL features
-    if (!gl->IsSupported(gl::GLFeature::occlusion_query) &&
-        !gl->IsSupported(gl::GLFeature::occlusion_query_boolean))
-    {
-        // On desktop, we fake occlusion_query_boolean with occlusion_query if
-        // necessary. (See WebGL2ContextQueries.cpp)
-        *out_failReason = FailureReason("FEATURE_FAILURE_WEBGL2_OCCL",
-                                        "WebGL 2 requires occlusion query support.");
-        return false;
+    std::vector<gl::GLFeature> missingList;
+
+    const auto fnGatherMissing = [&](gl::GLFeature cur) {
+        if (!gl->IsSupported(cur)) {
+            missingList.push_back(cur);
+        }
+    };
+
+    const auto fnGatherMissing2 = [&](gl::GLFeature main, gl::GLFeature alt) {
+        if (!gl->IsSupported(main) && !gl->IsSupported(alt)) {
+            missingList.push_back(main);
+        }
+    };
+
+    ////
+
+    for (const auto& cur : kRequiredFeatures) {
+        fnGatherMissing(cur);
     }
 
-    std::vector<gl::GLFeature> missingList;
-
-    for (size_t i = 0; i < ArrayLength(kRequiredFeatures); i++) {
-        if (!gl->IsSupported(kRequiredFeatures[i])) {
-            missingList.push_back(kRequiredFeatures[i]);
-        }
-    }
+    // On desktop, we fake occlusion_query_boolean with occlusion_query if
+    // necessary. (See WebGL2ContextQueries.cpp)
+    fnGatherMissing2(gl::GLFeature::occlusion_query_boolean,
+                     gl::GLFeature::occlusion_query);
 
 #ifdef XP_MACOSX
     // On OSX, GL core profile is used. This requires texture swizzle
     // support to emulate legacy texture formats: ALPHA, LUMINANCE,
     // and LUMINANCE_ALPHA.
-    if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) {
-        missingList.push_back(gl::GLFeature::texture_swizzle);
-    }
+    fnGatherMissing(gl::GLFeature::texture_swizzle);
 #endif
 
+    fnGatherMissing2(gl::GLFeature::prim_restart_fixed,
+                     gl::GLFeature::prim_restart);
+
+    ////
+
     if (missingList.size()) {
         nsAutoCString exts;
         for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) {
             exts.AppendLiteral("\n  ");
             exts.Append(gl::GLContext::GetFeatureName(*itr));
         }
 
         const nsPrintfCString reason("WebGL 2 requires support for the following"
@@ -150,23 +159,31 @@ WebGLContext::InitWebGL2(FailureReason* 
                      &mGLMaxUniformBufferBindings);
 
     mBoundTransformFeedbackBuffers.SetLength(mGLMaxTransformFeedbackSeparateAttribs);
     mBoundUniformBuffers.SetLength(mGLMaxUniformBufferBindings);
 
     mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0);
     mBoundTransformFeedback = mDefaultTransformFeedback;
 
+    ////
+
     if (!gl->IsGLES()) {
         // Desktop OpenGL requires the following to be enabled in order to
         // support sRGB operations on framebuffers.
-        gl->MakeCurrent();
         gl->fEnable(LOCAL_GL_FRAMEBUFFER_SRGB_EXT);
     }
 
+    if (gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
+        gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART_FIXED_INDEX);
+    } else {
+        MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
+        gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART);
+    }
+
     //////
 
     static const GLenum kWebGL2_CompressedFormats[] = {
         LOCAL_GL_COMPRESSED_R11_EAC,
         LOCAL_GL_COMPRESSED_SIGNED_R11_EAC,
         LOCAL_GL_COMPRESSED_RG11_EAC,
         LOCAL_GL_COMPRESSED_SIGNED_RG11_EAC,
         LOCAL_GL_COMPRESSED_RGB8_ETC2,
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -953,16 +953,18 @@ protected:
                                    WebGLTexture** const out_texture,
                                    WebGLTexture::ImageInfo** const out_imageInfo);
 
     bool ValidateUnpackInfo(const char* funcName, bool usePBOs, GLenum format,
                             GLenum type, webgl::PackingInfo* const out);
 
 // -----------------------------------------------------------------------------
 // Vertices Feature (WebGLContextVertices.cpp)
+    GLenum mPrimRestartTypeBytes;
+
 public:
     void DrawArrays(GLenum mode, GLint first, GLsizei count);
     void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count,
                              GLsizei primcount);
     void DrawElements(GLenum mode, GLsizei count, GLenum type,
                       WebGLintptr byteOffset);
     void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
                                WebGLintptr byteOffset, GLsizei primcount);
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -253,16 +253,26 @@ WebGLContext::DrawArrays_check(GLint fir
         ErrorInvalidValue("%s: negative primcount", info);
         return false;
     }
 
     if (!ValidateStencilParamsForDrawCall()) {
         return false;
     }
 
+    if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
+        MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
+        if (mPrimRestartTypeBytes != 4) {
+            mPrimRestartTypeBytes = 4;
+
+            // OSX has issues leaving this as 0.
+            gl->fPrimitiveRestartIndex(UINT32_MAX);
+        }
+    }
+
     // If count is 0, there's nothing to do.
     if (count == 0 || primcount == 0) {
         return false;
     }
 
     if (!ValidateBufferFetching(info)) {
         return false;
     }
@@ -409,16 +419,30 @@ WebGLContext::DrawElements_check(GLsizei
     }
 
     if (byteOffset % bytesPerElem != 0) {
         ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`",
                               info);
         return false;
     }
 
+    ////
+
+    if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
+        MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
+        if (mPrimRestartTypeBytes != bytesPerElem) {
+            mPrimRestartTypeBytes = bytesPerElem;
+
+            const uint32_t ones = UINT32_MAX >> (4 - mPrimRestartTypeBytes);
+            gl->fPrimitiveRestartIndex(ones);
+        }
+    }
+
+    ////
+
     const GLsizei first = byteOffset / bytesPerElem;
     const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(count);
 
     if (!checked_byteCount.isValid()) {
         ErrorInvalidValue("%s: overflow in byteCount", info);
         return false;
     }
 
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -996,16 +996,18 @@ WebGLContext::InitAndValidateGL(FailureR
     mPixelStore_UnpackSkipRows = 0;
     mPixelStore_UnpackSkipPixels = 0;
     mPixelStore_UnpackAlignment = 4;
     mPixelStore_PackRowLength = 0;
     mPixelStore_PackSkipRows = 0;
     mPixelStore_PackSkipPixels = 0;
     mPixelStore_PackAlignment = 4;
 
+    mPrimRestartTypeBytes = 0;
+
     return true;
 }
 
 bool
 WebGLContext::ValidateFramebufferTarget(GLenum target,
                                         const char* const info)
 {
     bool isValid = true;
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -5771,17 +5771,17 @@ skip-if = (os == 'android' || os == 'lin
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__rendering__many-draw-calls.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__rendering__more-than-65536-indices.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__rendering__multisample-corruption.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__rendering__negative-one-index.html]
-fail-if = (os == 'mac') || (os == 'win')
+fail-if = (os == 'mac')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__rendering__point-no-attributes.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__rendering__point-size.html]
 fail-if = (os == 'mac')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__rendering__point-specific-shader-variables.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -144,17 +144,17 @@ fail-if = (os == 'mac') || (os == 'win')
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance__textures__misc__tex-image-with-format-and-type.html]
 fail-if = (os == 'mac')
 [generated/test_2_conformance__attribs__gl-vertexattribpointer.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__vertex_arrays__vertex-array-object.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance__rendering__negative-one-index.html]
-fail-if = (os == 'mac') || (os == 'win')
+fail-if = (os == 'mac')
 [generated/test_conformance__extensions__oes-texture-half-float.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_2_conformance2__reading__read-pixels-pack-parameters.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__attribs__gl-vertexattribpointer.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_conformance__ogles__GL__biuDepthRange__biuDepthRange_001_to_002.html]
 fail-if = (os == 'android') || (os == 'linux')
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -158,16 +158,17 @@ static const char* const sExtensionNames
     "GL_IMG_texture_npot",
     "GL_KHR_debug",
     "GL_NV_draw_instanced",
     "GL_NV_fence",
     "GL_NV_framebuffer_blit",
     "GL_NV_geometry_program4",
     "GL_NV_half_float",
     "GL_NV_instanced_arrays",
+    "GL_NV_primitive_restart",
     "GL_NV_texture_barrier",
     "GL_NV_transform_feedback",
     "GL_NV_transform_feedback2",
     "GL_OES_EGL_image",
     "GL_OES_EGL_image_external",
     "GL_OES_EGL_sync",
     "GL_OES_compressed_ETC1_RGB8_texture",
     "GL_OES_depth24",
@@ -1582,16 +1583,24 @@ GLContext::LoadMoreSymbols(const char* p
         const SymLoadStruct symbols[] = {
             { (PRFuncPtr*) &mSymbols.fInvalidateFramebuffer,    { "InvalidateFramebuffer", nullptr } },
             { (PRFuncPtr*) &mSymbols.fInvalidateSubFramebuffer, { "InvalidateSubFramebuffer", nullptr } },
             END_SYMBOLS
         };
         fnLoadForFeature(symbols, GLFeature::invalidate_framebuffer);
     }
 
+    if (IsSupported(GLFeature::prim_restart)) {
+        const SymLoadStruct symbols[] = {
+            { (PRFuncPtr*) &mSymbols.fPrimitiveRestartIndex,    { "PrimitiveRestartIndex", "PrimitiveRestartIndexNV", nullptr } },
+            END_SYMBOLS
+        };
+        fnLoadForFeature(symbols, GLFeature::prim_restart);
+    }
+
     if (IsExtensionSupported(KHR_debug)) {
         const SymLoadStruct symbols[] = {
             { (PRFuncPtr*) &mSymbols.fDebugMessageControl,  { "DebugMessageControl",  "DebugMessageControlKHR",  nullptr } },
             { (PRFuncPtr*) &mSymbols.fDebugMessageInsert,   { "DebugMessageInsert",   "DebugMessageInsertKHR",   nullptr } },
             { (PRFuncPtr*) &mSymbols.fDebugMessageCallback, { "DebugMessageCallback", "DebugMessageCallbackKHR", nullptr } },
             { (PRFuncPtr*) &mSymbols.fGetDebugMessageLog,   { "GetDebugMessageLog",   "GetDebugMessageLogKHR",   nullptr } },
             { (PRFuncPtr*) &mSymbols.fGetPointerv,          { "GetPointerv",          "GetPointervKHR",          nullptr } },
             { (PRFuncPtr*) &mSymbols.fPushDebugGroup,       { "PushDebugGroup",       "PushDebugGroupKHR",       nullptr } },
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -105,16 +105,18 @@ enum class GLFeature {
     instanced_non_arrays,
     internalformat_query,
     invalidate_framebuffer,
     map_buffer_range,
     occlusion_query,
     occlusion_query_boolean,
     occlusion_query2,
     packed_depth_stencil,
+    prim_restart,
+    prim_restart_fixed,
     query_counter,
     query_objects,
     query_time_elapsed,
     read_buffer,
     renderbuffer_color_float,
     renderbuffer_color_half_float,
     robustness,
     sRGB_framebuffer,
@@ -473,16 +475,17 @@ public:
         IMG_texture_npot,
         KHR_debug,
         NV_draw_instanced,
         NV_fence,
         NV_framebuffer_blit,
         NV_geometry_program4,
         NV_half_float,
         NV_instanced_arrays,
+        NV_primitive_restart,
         NV_texture_barrier,
         NV_transform_feedback,
         NV_transform_feedback2,
         OES_EGL_image,
         OES_EGL_image_external,
         OES_EGL_sync,
         OES_compressed_ETC1_RGB8_texture,
         OES_depth24,
@@ -3171,16 +3174,26 @@ public:
     void fResolveMultisampleFramebufferAPPLE() {
         BEFORE_GL_CALL;
         ASSERT_SYMBOL_PRESENT(fResolveMultisampleFramebufferAPPLE);
         mSymbols.fResolveMultisampleFramebufferAPPLE();
         AFTER_GL_CALL;
     }
 
 // -----------------------------------------------------------------------------
+// prim_restart
+
+    void fPrimitiveRestartIndex(GLuint index) {
+        BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fPrimitiveRestartIndex);
+        mSymbols.fPrimitiveRestartIndex(index);
+        AFTER_GL_CALL;
+    }
+
+// -----------------------------------------------------------------------------
 // Constructor
 protected:
     explicit GLContext(CreateContextFlags flags, const SurfaceCaps& caps,
                        GLContext* sharedContext = nullptr,
                        bool isOffscreen = false);
 
 
 // -----------------------------------------------------------------------------
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -435,16 +435,35 @@ static const FeatureInfo sFeatureInfoArr
         GLContext::Extension_None,
         {
             GLContext::EXT_packed_depth_stencil,
             GLContext::OES_packed_depth_stencil,
             GLContext::Extensions_End
         }
     },
     {
+        "prim_restart",
+        GLVersion::GL3_1,
+        GLESVersion::NONE,
+        GLContext::Extension_None,
+        {
+            GLContext::NV_primitive_restart,
+            GLContext::Extensions_End
+        }
+    },
+    {
+        "prim_restart_fixed",
+        kGLCoreVersionForES3Compat,
+        GLESVersion::ES3,
+        GLContext::ARB_ES3_compatibility,
+        {
+            GLContext::Extensions_End
+        }
+    },
+    {
         "query_counter",
         GLVersion::GL3_3,
         GLESVersion::NONE,
         GLContext::ARB_timer_query,
         {
             GLContext::ANGLE_timer_query,
             GLContext::EXT_disjoint_timer_query,
             // EXT_timer_query does NOT support GL_TIMESTAMP retrieval with
--- a/gfx/gl/GLContextSymbols.h
+++ b/gfx/gl/GLContextSymbols.h
@@ -689,14 +689,17 @@ struct GLContextSymbols
 
     // APPLE_framebuffer_multisample
     typedef void (GLAPIENTRY * PFNRESOLVEMULTISAMPLEFRAMEBUFFERAPPLE) (void);
     PFNRESOLVEMULTISAMPLEFRAMEBUFFERAPPLE fResolveMultisampleFramebufferAPPLE;
 
     // NV_texture_barrier
     typedef void (GLAPIENTRY * PFNTEXTUREBARRIERPROC) (void);
     PFNTEXTUREBARRIERPROC fTextureBarrier;
+
+    // NV_primitive_restart
+    void (GLAPIENTRY * fPrimitiveRestartIndex) (GLuint index);
 };
 
 } // namespace gl
 } // namespace mozilla
 
 #endif /* GLCONTEXTSYMBOLS_H_ */