Shader validate draft
authorMorris Tseng <mtseng@mozilla.com>
Fri, 27 Nov 2015 15:37:37 +0800
changeset 311637 1a9e8f76def84adb9edbf1a2f9a5d6fe4ce90062
parent 311572 74c7941a9e22d50057800771ebae07f69deecc9f
child 311638 c8aa4acae92dd7bc20c3021172fb2c5c2d08e376
push id7904
push usermtseng@mozilla.com
push dateFri, 27 Nov 2015 07:38:22 +0000
milestone45.0a1
Shader validate
dom/canvas/WebGLShader.cpp
dom/canvas/WebGLShaderValidator.cpp
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
--- a/dom/canvas/WebGLShader.cpp
+++ b/dom/canvas/WebGLShader.cpp
@@ -88,21 +88,19 @@ TranslateWithoutValidation(const nsACStr
         glesslVersion = 100;
     }
 
     std::string reversionedSource = source;
     reversionedSource.erase(versionStrStart, versionStrLen);
 
     switch (glesslVersion) {
     case 100:
-        if (!versionStrLen) {
-            /* According to ARB_ES2_compatibility extension glsl
-             * should accept #version 100 for ES 2 shaders. */
-            reversionedSource.insert(versionStrStart, "#version 100\n");
-        }
+        /* According to ARB_ES2_compatibility extension glsl
+         * should accept #version 100 for ES 2 shaders. */
+        reversionedSource.insert(versionStrStart, "#version 100\n");
         break;
     case 300:
         reversionedSource.insert(versionStrStart, "#version 330\n");
         break;
     default:
         MOZ_CRASH("Bad `glesslVersion`.");
     }
 
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -88,25 +88,51 @@ ChooseValidatorCompileOptions(const ShBu
 
     return options;
 }
 
 } // namespace webgl
 
 ////////////////////////////////////////
 
+static ShShaderOutput
+ShaderOutput(gl::GLContext* gl)
+{
+    if (gl->IsGLES())
+        return SH_ESSL_OUTPUT;
+
+    uint32_t version = gl->ShadingLanguageVersion();
+    switch (version) {
+    case 100: return SH_ESSL_OUTPUT;
+    case 120: return SH_GLSL_OUTPUT;
+    case 130: return SH_GLSL_130_OUTPUT;
+    case 140: return SH_GLSL_140_OUTPUT;
+    case 150: return SH_GLSL_150_CORE_OUTPUT;
+    case 330: return SH_GLSL_330_CORE_OUTPUT;
+    case 400: return SH_GLSL_400_CORE_OUTPUT;
+    case 410: return SH_GLSL_410_CORE_OUTPUT;
+    case 420: return SH_GLSL_420_CORE_OUTPUT;
+    case 430: return SH_GLSL_430_CORE_OUTPUT;
+    case 440: return SH_GLSL_440_CORE_OUTPUT;
+    case 450: return SH_GLSL_450_CORE_OUTPUT;
+    default:
+        MOZ_CRASH("Unexpected GLSL version.");
+    }
+
+    return SH_GLSL_OUTPUT;
+}
+
 webgl::ShaderValidator*
 WebGLContext::CreateShaderValidator(GLenum shaderType) const
 {
     if (mBypassShaderValidation)
         return nullptr;
 
     ShShaderSpec spec = IsWebGL2() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC;
-    ShShaderOutput outputLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT
-                                                 : SH_GLSL_OUTPUT;
+    ShShaderOutput outputLanguage = ShaderOutput(gl);
 
     ShBuiltInResources resources;
     memset(&resources, 0, sizeof(resources));
     ShInitBuiltInResources(&resources);
 
     resources.HashFunction = webgl::IdentifierHashFunc;
 
     resources.MaxVertexAttribs = mGLMaxVertexAttribs;
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -175,16 +175,105 @@ static const char *sExtensionNames[] = {
     "GL_OES_texture_float_linear",
     "GL_OES_texture_half_float",
     "GL_OES_texture_half_float_linear",
     "GL_OES_texture_npot",
     "GL_OES_vertex_array_object"
 };
 
 static bool
+ParseGLSLVersion(GLContext* gl, uint32_t* out_version)
+{
+    if (gl->fGetError() != LOCAL_GL_NO_ERROR) {
+        MOZ_ASSERT(false, "An OpenGL error has been triggered before.");
+        return false;
+    }
+
+    /**
+     * OpenGL 2.x, 3.x, 4.x specifications:
+     *  The VERSION and SHADING_LANGUAGE_VERSION strings are laid out as follows:
+     *
+     *    <version number><space><vendor-specific information>
+     *
+     *  The version number is either of the form major_number.minor_number or
+     *  major_number.minor_number.release_number, where the numbers all have
+     *  one or more digits.
+     *
+     * SHADING_LANGUAGE_VERSION is *almost* identical to VERSION. The
+     * difference is that the minor version always has two digits.
+     *
+     *
+     * OpenGL ES 2.0, 3.0 specifications:
+     *  The VERSION string is laid out as follows:
+     *
+     *     "OpenGL ES N.M vendor-specific information"
+     *
+     *  The version number is either of the form major_number.minor_number or
+     *  major_number.minor_number.release_number, where the numbers all have
+     *  one or more digits.
+     *
+     *
+     * Note:
+     *  We don't care about release_number.
+     */
+    const char* versionString = (const char*) gl->fGetString(LOCAL_GL_SHADING_LANGUAGE_VERSION);
+
+    if (gl->fGetError() != LOCAL_GL_NO_ERROR) {
+        MOZ_ASSERT(false, "glGetString(GL_SHADING_LANGUAGE_VERSION) has generated an error");
+        return false;
+    }
+
+    if (!versionString) {
+        MOZ_ASSERT(false, "glGetString(GL_SHADING_LANGUAGE_VERSION) has returned 0");
+        return false;
+    }
+
+    const char kGLESVersionPrefix[] = "OpenGL ES ";
+    if (strncmp(versionString, kGLESVersionPrefix, strlen(kGLESVersionPrefix)) == 0)
+        versionString += strlen(kGLESVersionPrefix);
+
+    const char* itr = versionString;
+    char* end = nullptr;
+    auto majorVersion = strtol(itr, &end, 10);
+
+    if (!end) {
+        MOZ_ASSERT(false, "Failed to parse the GL major version number.");
+        return false;
+    }
+
+    if (*end != '.') {
+        MOZ_ASSERT(false, "Failed to parse GL's major-minor version number separator.");
+        return false;
+    }
+
+    // we skip the '.' between the major and the minor version
+    itr = end + 1;
+    end = nullptr;
+
+    auto minorVersion = strtol(itr, &end, 10);
+    if (!end) {
+        MOZ_ASSERT(false, "Failed to parse GL's minor version number.");
+        return false;
+    }
+
+    if (majorVersion <= 0 || majorVersion >= 100) {
+        MOZ_ASSERT(false, "Invalid major version.");
+        return false;
+    }
+
+    if (minorVersion < 0 || minorVersion >= 100) {
+        MOZ_ASSERT(false, "Invalid minor version.");
+        return false;
+    }
+
+    *out_version = (uint32_t) majorVersion * 100 + (uint32_t) minorVersion;
+    return true;
+}
+
+static bool
 ParseGLVersion(GLContext* gl, uint32_t* out_version)
 {
     if (gl->fGetError() != LOCAL_GL_NO_ERROR) {
         MOZ_ASSERT(false, "An OpenGL error has been triggered before.");
         return false;
     }
 
     /**
@@ -298,16 +387,17 @@ ParseGLVersion(GLContext* gl, uint32_t* 
 GLContext::GLContext(const SurfaceCaps& caps,
           GLContext* sharedContext,
           bool isOffscreen)
   : mInitialized(false),
     mIsOffscreen(isOffscreen),
     mContextLost(false),
     mVersion(0),
     mProfile(ContextProfile::Unknown),
+    mShadingLanguageVersion(0),
     mVendor(GLVendor::Other),
     mRenderer(GLRenderer::Other),
     mHasRobustness(false),
     mTopError(LOCAL_GL_NO_ERROR),
     mSharedContext(sharedContext),
     mCaps(caps),
     mScreen(nullptr),
     mLockedSurface(nullptr),
@@ -510,25 +600,34 @@ GLContext::InitWithPrefix(const char *pr
     mInitialized = LoadSymbols(&symbols[0], trygl, prefix);
     MakeCurrent();
     if (mInitialized) {
         MOZ_ASSERT(mProfile != ContextProfile::Unknown);
 
         uint32_t version = 0;
         ParseGLVersion(this, &version);
 
+        uint32_t shadingLangVersion = 100;
+        ParseGLSLVersion(this, &shadingLangVersion);
+
         if (ShouldSpew()) {
             printf_stderr("OpenGL version detected: %u\n", version);
+            printf_stderr("OpenGL shading language version detected: %u\n", shadingLangVersion);
             printf_stderr("OpenGL vendor: %s\n", fGetString(LOCAL_GL_VENDOR));
             printf_stderr("OpenGL renderer: %s\n", fGetString(LOCAL_GL_RENDERER));
         }
 
         if (version >= mVersion) {
             mVersion = version;
         }
+
+        if (shadingLangVersion >= mShadingLanguageVersion) {
+            mShadingLanguageVersion = shadingLangVersion;
+        }
+
         // Don't fail if version < mVersion, see bug 999445,
         // Mac OSX 10.6/10.7 machines with Intel GPUs claim only OpenGL 1.4 but
         // have all the GL2+ extensions that we need.
     }
 
     // Load OpenGL ES 2.0 symbols, or desktop if we aren't using ES 2.
     if (mInitialized) {
         if (IsGLES()) {
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -297,16 +297,20 @@ public:
     inline uint32_t Version() const {
         return mVersion;
     }
 
     const char* VersionString() const {
         return mVersionString.get();
     }
 
+    inline uint32_t ShadingLanguageVersion() const {
+        return mShadingLanguageVersion;
+    }
+
     GLVendor Vendor() const {
         return mVendor;
     }
 
     GLRenderer Renderer() const {
         return mRenderer;
     }
 
@@ -341,16 +345,18 @@ protected:
     /**
      * mVersion store the OpenGL's version, multiplied by 100. For example, if
      * the context is an OpenGL 2.1 context, mVersion value will be 210.
      */
     uint32_t mVersion;
     nsCString mVersionString;
     ContextProfile mProfile;
 
+    uint32_t mShadingLanguageVersion;
+
     GLVendor mVendor;
     GLRenderer mRenderer;
 
     void SetProfileVersion(ContextProfile profile, uint32_t version) {
         MOZ_ASSERT(!mInitialized, "SetProfileVersion can only be called before"
                                   " initialization!");
         MOZ_ASSERT(profile != ContextProfile::Unknown &&
                    profile != ContextProfile::OpenGL,