Bug 892169 - [WebGL 2.0] Bypass ANGLE shader compilation (GL_EXT_gpu_shader4). r=jgilbert
authorGuillaume Abadie <gabadie@mozilla.com>
Thu, 18 Jul 2013 10:31:01 -0400
changeset 152257 f57adec8f404fed0db383f9dff485d46535ff6d8
parent 152256 b96c56413b5e9426e4da3e7f63a45bd5902910cb
child 152258 3e23e51086bf4aac331ce0f091ea5befb2c41a6f
push id382
push userakeybl@mozilla.com
push dateMon, 21 Oct 2013 21:47:13 +0000
treeherdermozilla-release@5f1868ee45cb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs892169
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 892169 - [WebGL 2.0] Bypass ANGLE shader compilation (GL_EXT_gpu_shader4). r=jgilbert
content/canvas/src/WebGLContextGL.cpp
content/canvas/src/WebGLContextValidate.cpp
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -4260,16 +4260,67 @@ WebGLContext::CompileShader(WebGLShader 
             const uint32_t maxSourceLength = 0x3ffff;
             if (sourceCString.Length() > maxSourceLength)
                 return ErrorInvalidValue("compileShader: source has more than %d characters", 
                                          maxSourceLength);
         }
 
         const char *s = sourceCString.get();
 
+#define WEBGL2_BYPASS_ANGLE
+#ifdef WEBGL2_BYPASS_ANGLE
+        /*
+         * The bypass don't bring a full support for GLSL ES 3.0, but the main purpose
+         * is to natively bring gl_InstanceID (to do instanced rendering) and gl_FragData
+         *
+         * To remove the bypass code, just comment #define WEBGL2_BYPASS_ANGLE above
+         *
+         * To bypass angle, the context must be a WebGL 2 and the shader must have the
+         * following line at the very top :
+         *      #version proto-200
+         *
+         * In this case, byPassANGLE == true and here is what we do :
+         *  We create two shader source code:
+         *    - one for the driver, that enable GL_EXT_gpu_shader4
+         *    - one for the angle compilor, to get informations about vertex attributes
+         *      and uniforms
+         */
+        static const char *bypassPrefixSearch = "#version proto-200";
+        static const char *bypassANGLEPrefix[2] = {"precision mediump float;\n"
+                                                   "#define gl_VertexID 0\n"
+                                                   "#define gl_InstanceID 0\n",
+
+                                                   "precision mediump float;\n"
+                                                   "#extension GL_EXT_draw_buffers : enable\n"
+                                                   "#define gl_PrimitiveID 0\n"};
+
+        const bool bypassANGLE = IsWebGL2() && (strstr(s, bypassPrefixSearch) != 0);
+
+        const char *angleShaderCode = s;
+        nsTArray<char> bypassANGLEShaderCode;
+        nsTArray<char> bypassDriverShaderCode;
+
+        if (bypassANGLE) {
+            const int bypassStage = (shader->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) ? 1 : 0;
+            const char *originalShader = strstr(s, bypassPrefixSearch) + strlen(bypassPrefixSearch);
+            int originalShaderSize = strlen(s) - (originalShader - s);
+            int bypassShaderCodeSize = originalShaderSize + 4096 + 1;
+
+            bypassANGLEShaderCode.SetLength(bypassShaderCodeSize);
+            strcpy(bypassANGLEShaderCode.Elements(), bypassANGLEPrefix[bypassStage]);
+            strcat(bypassANGLEShaderCode.Elements(), originalShader);
+
+            bypassDriverShaderCode.SetLength(bypassShaderCodeSize);
+            strcpy(bypassDriverShaderCode.Elements(), "#extension GL_EXT_gpu_shader4 : enable\n");
+            strcat(bypassDriverShaderCode.Elements(), originalShader);
+
+            angleShaderCode = bypassANGLEShaderCode.Elements();
+        }
+#endif
+
         compiler = ShConstructCompiler((ShShaderType) shader->ShaderType(),
                                        SH_WEBGL_SPEC,
                                        targetShaderSourceLanguage,
                                        &resources);
 
         int compileOptions = SH_ATTRIBUTES_UNIFORMS |
                              SH_ENFORCE_PACKING_RESTRICTIONS;
 
@@ -4292,17 +4343,21 @@ WebGLContext::CompileShader(WebGLShader 
                 // Work around bug 735560
                 if (gl->Vendor() == gl::GLContext::VendorIntel) {
                     compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS;
                 }
             }
 #endif
         }
 
+#ifdef WEBGL2_BYPASS_ANGLE
+        if (!ShCompile(compiler, &angleShaderCode, 1, compileOptions)) {
+#else
         if (!ShCompile(compiler, &s, 1, compileOptions)) {
+#endif
             size_t len = 0;
             ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &len);
 
             if (len) {
                 nsAutoCString info;
                 info.SetLength(len);
                 ShGetInfoLog(compiler, info.BeginWriting());
                 shader->SetTranslationFailure(info);
@@ -4381,17 +4436,27 @@ WebGLContext::CompileShader(WebGLShader 
             ShGetInfo(compiler, SH_OBJECT_CODE_LENGTH, &len);
 
             nsAutoCString translatedSrc;
             translatedSrc.SetLength(len);
             ShGetObjectCode(compiler, translatedSrc.BeginWriting());
 
             const char *ts = translatedSrc.get();
 
+#ifdef WEBGL2_BYPASS_ANGLE
+            if (bypassANGLE) {
+                const char* driverShaderCode = bypassDriverShaderCode.Elements();
+                gl->fShaderSource(shadername, 1, (const GLchar**) &driverShaderCode, nullptr);
+            }
+            else {
+                gl->fShaderSource(shadername, 1, &ts, nullptr);
+            }
+#else
             gl->fShaderSource(shadername, 1, &ts, nullptr);
+#endif
         } else { // not useShaderSourceTranslation
             // we just pass the raw untranslated shader source. We then can't use ANGLE idenfier mapping.
             // that's really bad, as that means we can't be 100% conformant. We should work towards always
             // using ANGLE identifier mapping.
             gl->fShaderSource(shadername, 1, &s, nullptr);
         }
 
         shader->SetTranslationSuccess();
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -1046,17 +1046,18 @@ WebGLContext::InitAndValidateGL()
     error = gl->GetAndClearError();
     if (error != LOCAL_GL_NO_ERROR) {
         GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error);
         return false;
     }
 
     if (IsWebGL2() &&
         (!IsExtensionSupported(OES_vertex_array_object) ||
-         !IsExtensionSupported(WEBGL_draw_buffers)
+         !IsExtensionSupported(WEBGL_draw_buffers) ||
+         !gl->IsExtensionSupported(gl::GLContext::EXT_gpu_shader4)
         ))
     {
         return false;
     }
 
     mMemoryPressureObserver
         = new WebGLMemoryPressureObserver(this);
     nsCOMPtr<nsIObserverService> observerService
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -86,16 +86,17 @@ static const char *sExtensionNames[] = {
     "GL_OES_EGL_image_external",
     "GL_EXT_packed_depth_stencil",
     "GL_OES_element_index_uint",
     "GL_OES_vertex_array_object",
     "GL_ARB_vertex_array_object",
     "GL_APPLE_vertex_array_object",
     "GL_ARB_draw_buffers",
     "GL_EXT_draw_buffers",
+    "GL_EXT_gpu_shader4",
     nullptr
 };
 
 static int64_t sTextureMemoryUsage = 0;
 
 static int64_t
 GetTextureMemoryUsage()
 {
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -1074,16 +1074,17 @@ public:
         OES_EGL_image_external,
         EXT_packed_depth_stencil,
         OES_element_index_uint,
         OES_vertex_array_object,
         ARB_vertex_array_object,
         APPLE_vertex_array_object,
         ARB_draw_buffers,
         EXT_draw_buffers,
+        EXT_gpu_shader4,
         Extensions_Max
     };
 
     bool IsExtensionSupported(GLExtensions aKnownExtension) const {
         return mAvailableExtensions[aKnownExtension];
     }
 
     void MarkExtensionUnsupported(GLExtensions aKnownExtension) {