Shader translator needs option to clamp uniform array accesses in vertex shaders
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 19 Oct 2012 19:45:03 +0000 (19:45 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 19 Oct 2012 19:45:03 +0000 (19:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=98977
https://code.google.com/p/angleproject/issues/detail?id=49

Reviewed by Alok Priyadarshi and Ken Russell.

Source/ThirdParty/ANGLE:

WebGL does not allow GLSL code to index a uniform array outside its bounds. Add a
flag to the ANGLE compiler to insert clamp statements around such indexing.
Since it is possible to access vec2/3/4 and mat2/3/4 components by array indexing,
they must be similarly clamped.

Unfortunately, it is currently not possible to always determine that the indexing is
operating on a uniform variable. For example, suppose we have "uniform mat4 a". ANGLE
is currently not able to tell us that the rvalue of "a[0]" is a uniform, just that
it has a size of 4. Therefore, the clamping is done on all indirect array indexing.

This will have a performance impact. Future enhancements may be able to determine
cases where the clamping is not necessary. Currently only direct indexing is skipped
(i.e. looking up a value using a constant index).

The clamp insertion is only performed on the GLSL output. Direct3D already guarantees
that out-of-bounds uniform array access returns a zero value.

* ANGLE.xcodeproj/project.pbxproj:
* Target.pri:
* include/GLSLANG/ShaderLang.h: New compiler option.
* src/compiler/ArrayBoundsClamper.cpp: Added.
(ArrayBoundsClamper::ArrayBoundsClamper):
(ArrayBoundsClamper::OutputClampingFunctionDefinition): Injects a clamping function for integers into GLSL source.
(ArrayBoundsClamper::MarkIndirectArrayBoundsForClamping): Examines the AST looking for non-direct array indexing.
* src/compiler/ArrayBoundsClamper.h: Added.
(ArrayBoundsClamper):
(ArrayBoundsClamper::GetArrayBoundsClampDefinitionNeeded):
(ArrayBoundsClamper::SetArrayBoundsClampDefinitionNeeded): Marks the object as needing to output the clamping function.
(ArrayBoundsClamper::Cleanup): Resets the state so that subsequent runs start fresh.
* src/compiler/Compiler.cpp:
(TCompiler::compile): Run the clamping code if the compile option was set.
(TCompiler::clearResults):
(TCompiler::getArrayBoundsClamper):
* src/compiler/OutputGLSLBase.cpp:
(TOutputGLSLBase::visitBinary): If the expression has been flagged, insert an appropriate "clamp" statement.
* src/compiler/ShHandle.h:
(TCompiler):
* src/compiler/TranslatorESSL.cpp:
(TranslatorESSL::translate):
* src/compiler/TranslatorGLSL.cpp:
(TranslatorGLSL::translate):
* src/compiler/intermOut.cpp:
(TType::getCompleteString): Add array size to intermediate tree output.
* src/compiler/intermediate.h:
(TIntermBinary::setAddIndexClamp): New flag for indicating a binary expression needs clamping.
(TIntermBinary::getAddIndexClamp):
(TIntermBinary):

Source/WebCore:

WebGL forbids out-of-bounds array access in shaders. Rewrite any shaders to
ensure that non-direct array indexing is clamped to the bounds of the array.

Test: fast/canvas/webgl/array-bounds-clamping.html

* platform/graphics/ANGLEWebKitBridge.cpp:
(WebCore::ANGLEWebKitBridge::compileShaderSource): Pass new compiler option SH_CLAMP_INDIRECT_ARRAY_BOUNDS

LayoutTests:

* fast/canvas/webgl/array-bounds-clamping-expected.txt: Added.
* fast/canvas/webgl/array-bounds-clamping.html: Added.
* platform/chromium/TestExpectations: Won't pass on Chromium until this patch is upstreamed.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@131933 268f45cc-cd09-0410-ab3c-d52691b4dbfc

19 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/canvas/webgl/array-bounds-clamping-expected.txt [new file with mode: 0644]
LayoutTests/fast/canvas/webgl/array-bounds-clamping.html [new file with mode: 0644]
LayoutTests/platform/chromium/TestExpectations
Source/ThirdParty/ANGLE/ANGLE.xcodeproj/project.pbxproj
Source/ThirdParty/ANGLE/ChangeLog
Source/ThirdParty/ANGLE/Target.pri
Source/ThirdParty/ANGLE/include/GLSLANG/ShaderLang.h
Source/ThirdParty/ANGLE/src/compiler/ArrayBoundsClamper.cpp [new file with mode: 0644]
Source/ThirdParty/ANGLE/src/compiler/ArrayBoundsClamper.h [new file with mode: 0644]
Source/ThirdParty/ANGLE/src/compiler/Compiler.cpp
Source/ThirdParty/ANGLE/src/compiler/OutputGLSLBase.cpp
Source/ThirdParty/ANGLE/src/compiler/ShHandle.h
Source/ThirdParty/ANGLE/src/compiler/TranslatorESSL.cpp
Source/ThirdParty/ANGLE/src/compiler/TranslatorGLSL.cpp
Source/ThirdParty/ANGLE/src/compiler/intermOut.cpp
Source/ThirdParty/ANGLE/src/compiler/intermediate.h
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp

index f3e308b..448bcbd 100644 (file)
@@ -1,3 +1,15 @@
+2012-10-18  Dean Jackson  <dino@apple.com>
+
+        Shader translator needs option to clamp uniform array accesses in vertex shaders
+        https://bugs.webkit.org/show_bug.cgi?id=98977
+        https://code.google.com/p/angleproject/issues/detail?id=49
+
+        Reviewed by Alok Priyadarshi and Ken Russell.
+
+        * fast/canvas/webgl/array-bounds-clamping-expected.txt: Added.
+        * fast/canvas/webgl/array-bounds-clamping.html: Added.
+        * platform/chromium/TestExpectations: Won't pass on Chromium until this patch is upstreamed.
+
 2012-09-08  Alpha Lam  <hclam@chromium.org>
 
         [chromium] Implement deferred image decoding
diff --git a/LayoutTests/fast/canvas/webgl/array-bounds-clamping-expected.txt b/LayoutTests/fast/canvas/webgl/array-bounds-clamping-expected.txt
new file mode 100644 (file)
index 0000000..f9c8d8f
--- /dev/null
@@ -0,0 +1,12 @@
+ Checks that array access in a shader can not read out of bounds
+
+PASS Top left corner should clamp to index 0
+PASS Inside top right corner should clamp to index 0
+PASS Inside bottom left corner should clamp to index 0
+PASS Bottom right corner should clamp to index 7
+PASS Outside bottom left corner should clamp to index 7
+PASS Outside top right corner should clamp to index 7
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/canvas/webgl/array-bounds-clamping.html b/LayoutTests/fast/canvas/webgl/array-bounds-clamping.html
new file mode 100644 (file)
index 0000000..a90dee7
--- /dev/null
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+  "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<title>WebGL array bounds clamping conformance test.</title>
+<script src="../../js/resources/js-test-pre.js"></script>
+<script src="resources/webgl-test.js"> </script>
+</head>
+<body>
+<canvas id="example" width="40" height="40" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script id="vshader" type="x-shader/x-vertex">
+#ifdef GL_ES
+precision highp float;
+#endif
+attribute vec4 vPosition;
+attribute float index;
+uniform float shades[8];
+varying vec4 texColor;
+void main()
+{
+    gl_Position = vPosition;
+    texColor = vec4(shades[int(index)], 0, 0, 1.0);
+}
+</script>
+
+<script id="fshader" type="x-shader/x-fragment">
+#ifdef GL_ES
+precision highp float;
+#endif
+varying vec4 texColor;
+void main()
+{
+    gl_FragColor = texColor;
+}
+</script>
+
+<script>
+function init()
+{
+    if (window.initNonKhronosFramework)
+        window.initNonKhronosFramework(false);
+
+    debug("Checks that array access in a shader can not read out of bounds");
+    debug("");
+
+    gl = initWebGL("example", "vshader", "fshader", [ "vPosition", "index" ],
+                   [ 1, 1, 1, 1 ], 1);
+
+    gl.disable(gl.DEPTH_TEST);
+    gl.disable(gl.BLEND);
+
+    var vertexObject = gl.createBuffer();
+    gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
+    gl.bufferData(gl.ARRAY_BUFFER,
+                  new Float32Array([ -1,1,0, 1,1,0, -1,-1,0,
+                                     -1,-1,0, 1,1,0, 1,-1,0 ]),
+                  gl.STATIC_DRAW);
+    gl.enableVertexAttribArray(0);
+    gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
+
+    var vertexObject = gl.createBuffer();
+    gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
+    gl.bufferData(gl.ARRAY_BUFFER,
+                  // Create an array that exercises well outside the
+                  // limits on each side, near the limits, and the
+                  // exact limits.
+                  // This should be clamped to [0, 0, 0, 7, 7, 7]
+                  new Float32Array([ -Number.MAX_VALUE, -1, 0, 7, 8, Number.MAX_VALUE]),
+                  gl.STATIC_DRAW);
+    gl.enableVertexAttribArray(1);
+    gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0);
+
+    var loc = gl.getUniformLocation(gl.program, "shades");
+    gl.uniform1fv(loc, [0.25, 0.5, 0, 0, 0, 0, 0.75, 1]);
+
+    checkRedValue(0, 38, 64, "Top left corner should clamp to index 0");
+    checkRedValue(37, 38, 64, "Inside top right corner should clamp to index 0");
+    checkRedValue(0, 1, 64, "Inside bottom left corner should clamp to index 0");
+
+    checkRedValue(38, 0, 255, "Bottom right corner should clamp to index 7");
+    checkRedValue(3, 1, 255, "Outside bottom left corner should clamp to index 7");
+    checkRedValue(38, 37, 255, "Outside top right corner should clamp to index 7");
+
+    function checkRedValue(x, y, value, msg) {
+        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+        gl.drawArrays(gl.TRIANGLES, 0, 6);
+        gl.flush();
+        var buf = new Uint8Array(4);
+        gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, buf);
+        if (buf[0] != value || buf[1] != 0 || buf[2] != 0 || buf[3] != 255) {
+            debug('expected: rgb(' + value + ', 0, 0, 255) was rgb(' + buf[0] + ', ' + buf[1] + ', ' + buf[2] + ', ' + buf[3] + ')');
+            testFailed(msg);
+            return;
+        }
+        testPassed(msg);
+    }
+}
+
+init();
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
+
index 4cc416b..361f1ab 100644 (file)
@@ -2363,6 +2363,9 @@ crbug.com/60651 [ Win ] platform/chromium/virtual/gpu/fast/canvas/webgl/gl-objec
 crbug.com/60651 fast/canvas/webgl/uniform-location.html [ Failure Timeout ]
 crbug.com/60651 platform/chromium/virtual/gpu/fast/canvas/webgl/uniform-location.html [ Failure Timeout ]
 
+# This will fail in Chromium until the patch is upstreamed to ANGLE
+webkit.org/b/98977 fast/canvas/webgl/array-bounds-clamping.html [ Failure ]
+
 # Flaky since r68438:r68445 or before that.
 webkit.org/b/46693 [ Mac ] fast/repaint/repaint-svg-after-style-change.html [ Failure ]
 
index 8f2be60..3207eca 100644 (file)
@@ -9,6 +9,8 @@
 /* Begin PBXBuildFile section */
                312BDB0C15FECAC90097EBC7 /* ANGLE.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 312BDB0915FEC91E0097EBC7 /* ANGLE.plist */; };
                312BDB0E15FECAE50097EBC7 /* ANGLE.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = 312BDB0A15FECA3A0097EBC7 /* ANGLE.txt */; };
+               3158EA0E1630968D006BE5EE /* ArrayBoundsClamper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3158EA0C1630968D006BE5EE /* ArrayBoundsClamper.cpp */; };
+               3158EA0F1630968D006BE5EE /* ArrayBoundsClamper.h in Headers */ = {isa = PBXBuildFile; fileRef = 3158EA0D1630968D006BE5EE /* ArrayBoundsClamper.h */; };
                49951C0314B7AAB30060E96E /* length_limits.h in Headers */ = {isa = PBXBuildFile; fileRef = 49951C0214B7AAB30060E96E /* length_limits.h */; };
                49951C0914B7AAD80060E96E /* BuiltInFunctionEmulator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49951C0514B7AAD70060E96E /* BuiltInFunctionEmulator.cpp */; };
                49951C0A14B7AAD80060E96E /* BuiltInFunctionEmulator.h in Headers */ = {isa = PBXBuildFile; fileRef = 49951C0614B7AAD80060E96E /* BuiltInFunctionEmulator.h */; };
 /* Begin PBXFileReference section */
                312BDB0915FEC91E0097EBC7 /* ANGLE.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ANGLE.plist; sourceTree = "<group>"; };
                312BDB0A15FECA3A0097EBC7 /* ANGLE.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ANGLE.txt; sourceTree = "<group>"; };
+               3158EA0C1630968D006BE5EE /* ArrayBoundsClamper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayBoundsClamper.cpp; sourceTree = "<group>"; };
+               3158EA0D1630968D006BE5EE /* ArrayBoundsClamper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayBoundsClamper.h; sourceTree = "<group>"; };
                49951C0214B7AAB30060E96E /* length_limits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = length_limits.h; sourceTree = "<group>"; };
                49951C0514B7AAD70060E96E /* BuiltInFunctionEmulator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BuiltInFunctionEmulator.cpp; sourceTree = "<group>"; };
                49951C0614B7AAD80060E96E /* BuiltInFunctionEmulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BuiltInFunctionEmulator.h; sourceTree = "<group>"; };
                FB39D2201200F35A00088E69 /* compiler */ = {
                        isa = PBXGroup;
                        children = (
+                               3158EA0C1630968D006BE5EE /* ArrayBoundsClamper.cpp */,
+                               3158EA0D1630968D006BE5EE /* ArrayBoundsClamper.h */,
                                FB39D2211200F35A00088E69 /* BaseTypes.h */,
                                49951C0514B7AAD70060E96E /* BuiltInFunctionEmulator.cpp */,
                                49951C0614B7AAD80060E96E /* BuiltInFunctionEmulator.h */,
                                A265683E159C23E100398539 /* DependencyGraphOutput.h in Headers */,
                                A2656841159C23E100398539 /* RestrictFragmentShaderTiming.h in Headers */,
                                A2656843159C23E100398539 /* RestrictVertexShaderTiming.h in Headers */,
+                               3158EA0F1630968D006BE5EE /* ArrayBoundsClamper.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                A265683F159C23E100398539 /* DependencyGraphTraverse.cpp in Sources */,
                                A2656840159C23E100398539 /* RestrictFragmentShaderTiming.cpp in Sources */,
                                A2656842159C23E100398539 /* RestrictVertexShaderTiming.cpp in Sources */,
+                               3158EA0E1630968D006BE5EE /* ArrayBoundsClamper.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index dbb1521..bc892b5 100644 (file)
@@ -1,3 +1,59 @@
+2012-10-18  Dean Jackson  <dino@apple.com>
+
+        Shader translator needs option to clamp uniform array accesses in vertex shaders
+        https://bugs.webkit.org/show_bug.cgi?id=98977
+        https://code.google.com/p/angleproject/issues/detail?id=49
+
+        Reviewed by Alok Priyadarshi and Ken Russell.
+
+        WebGL does not allow GLSL code to index a uniform array outside its bounds. Add a
+        flag to the ANGLE compiler to insert clamp statements around such indexing.
+        Since it is possible to access vec2/3/4 and mat2/3/4 components by array indexing,
+        they must be similarly clamped.
+
+        Unfortunately, it is currently not possible to always determine that the indexing is
+        operating on a uniform variable. For example, suppose we have "uniform mat4 a". ANGLE
+        is currently not able to tell us that the rvalue of "a[0]" is a uniform, just that
+        it has a size of 4. Therefore, the clamping is done on all indirect array indexing.
+
+        This will have a performance impact. Future enhancements may be able to determine
+        cases where the clamping is not necessary. Currently only direct indexing is skipped
+        (i.e. looking up a value using a constant index).
+
+        The clamp insertion is only performed on the GLSL output. Direct3D already guarantees
+        that out-of-bounds uniform array access returns a zero value.
+
+        * ANGLE.xcodeproj/project.pbxproj:
+        * Target.pri:
+        * include/GLSLANG/ShaderLang.h: New compiler option.
+        * src/compiler/ArrayBoundsClamper.cpp: Added.
+        (ArrayBoundsClamper::ArrayBoundsClamper):
+        (ArrayBoundsClamper::OutputClampingFunctionDefinition): Injects a clamping function for integers into GLSL source.
+        (ArrayBoundsClamper::MarkIndirectArrayBoundsForClamping): Examines the AST looking for non-direct array indexing.
+        * src/compiler/ArrayBoundsClamper.h: Added.
+        (ArrayBoundsClamper):
+        (ArrayBoundsClamper::GetArrayBoundsClampDefinitionNeeded):
+        (ArrayBoundsClamper::SetArrayBoundsClampDefinitionNeeded): Marks the object as needing to output the clamping function.
+        (ArrayBoundsClamper::Cleanup): Resets the state so that subsequent runs start fresh.
+        * src/compiler/Compiler.cpp:
+        (TCompiler::compile): Run the clamping code if the compile option was set.
+        (TCompiler::clearResults):
+        (TCompiler::getArrayBoundsClamper):
+        * src/compiler/OutputGLSLBase.cpp:
+        (TOutputGLSLBase::visitBinary): If the expression has been flagged, insert an appropriate "clamp" statement.
+        * src/compiler/ShHandle.h:
+        (TCompiler):
+        * src/compiler/TranslatorESSL.cpp:
+        (TranslatorESSL::translate):
+        * src/compiler/TranslatorGLSL.cpp:
+        (TranslatorGLSL::translate):
+        * src/compiler/intermOut.cpp:
+        (TType::getCompleteString): Add array size to intermediate tree output.
+        * src/compiler/intermediate.h:
+        (TIntermBinary::setAddIndexClamp): New flag for indicating a binary expression needs clamping.
+        (TIntermBinary::getAddIndexClamp):
+        (TIntermBinary):
+
 2012-09-13  Mark Rowe  <mrowe@apple.com>
 
         <rdar://problem/12255720> Fix the build with newer Clang
index 4e9303a..eaa936d 100644 (file)
@@ -17,6 +17,7 @@ INCLUDEPATH += \
     $$SOURCE_DIR/include
 
 HEADERS += \
+    src/compiler/ArrayBoundsClamper.h \
     src/compiler/BaseTypes.h \
     src/compiler/BuiltInFunctionEmulator.h \
     src/compiler/Common.h \
@@ -90,6 +91,7 @@ HEADERS += \
     src/compiler/VersionGLSL.h
 
 SOURCES += \
+    src/compiler/ArrayBoundsClamper.cpp \
     src/compiler/BuiltInFunctionEmulator.cpp \
     src/compiler/CodeGenGLSL.cpp \
     src/compiler/Compiler.cpp \
index 9a4a36d..d82931e 100644 (file)
@@ -142,7 +142,13 @@ typedef enum {
   // - The shader spec is SH_WEBGL_SPEC.
   // - The compile options contain the SH_TIMING_RESTRICTIONS flag.
   // - The shader type is SH_FRAGMENT_SHADER.
-  SH_DEPENDENCY_GRAPH = 0x0400
+  SH_DEPENDENCY_GRAPH = 0x0400,
+
+  // This flag ensures all indirect (expression-based) array indexing
+  // is clamped to the bounds of the array. This ensures, for example,
+  // that you cannot read off the end of a uniform, whether an array
+  // vec234, or mat234 type.
+  SH_CLAMP_INDIRECT_ARRAY_BOUNDS = 0x0800
 } ShCompileOptions;
 
 //
diff --git a/Source/ThirdParty/ANGLE/src/compiler/ArrayBoundsClamper.cpp b/Source/ThirdParty/ANGLE/src/compiler/ArrayBoundsClamper.cpp
new file mode 100644 (file)
index 0000000..6a31721
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "compiler/ArrayBoundsClamper.h"
+
+const char* kIntClampBegin = "// BEGIN: Generated code for array bounds clamping\n\n";
+const char* kIntClampEnd = "// END: Generated code for array bounds clamping\n\n";
+const char* kIntClampDefinition = "int webgl_int_clamp(int value, int minValue, int maxValue) { return ((value < minValue) ? minValue : ((value > maxValue) ? maxValue : value)); }\n\n";
+
+namespace {
+
+class ArrayBoundsClamperMarker : public TIntermTraverser {
+public:
+    ArrayBoundsClamperMarker()
+        : mNeedsClamp(false)
+   {
+   }
+
+   virtual bool visitBinary(Visit visit, TIntermBinary* node)
+   {
+       if (node->getOp() == EOpIndexIndirect)
+       {
+           TIntermTyped* left = node->getLeft();
+           if (left->isArray() || left->isVector() || left->isMatrix())
+           {
+               node->setAddIndexClamp();
+               mNeedsClamp = true;
+           }
+       }
+       return true;
+   }
+
+    bool GetNeedsClamp() { return mNeedsClamp; }
+
+private:
+    bool mNeedsClamp;
+};
+
+}  // anonymous namespace
+
+ArrayBoundsClamper::ArrayBoundsClamper()
+    : mArrayBoundsClampDefinitionNeeded(false)
+{
+}
+
+void ArrayBoundsClamper::OutputClampingFunctionDefinition(TInfoSinkBase& out) const
+{
+    if (!mArrayBoundsClampDefinitionNeeded)
+    {
+        return;
+    }
+    out << kIntClampBegin << kIntClampDefinition << kIntClampEnd;
+}
+
+void ArrayBoundsClamper::MarkIndirectArrayBoundsForClamping(TIntermNode* root)
+{
+    ASSERT(root);
+
+    ArrayBoundsClamperMarker clamper;
+    root->traverse(&clamper);
+    if (clamper.GetNeedsClamp())
+    {
+        SetArrayBoundsClampDefinitionNeeded();
+    }
+}
+
diff --git a/Source/ThirdParty/ANGLE/src/compiler/ArrayBoundsClamper.h b/Source/ThirdParty/ANGLE/src/compiler/ArrayBoundsClamper.h
new file mode 100644 (file)
index 0000000..d4a8d69
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef COMPILER_ARRAY_BOUNDS_CLAMPER_H_
+#define COMPILER_ARRAY_BOUNDS_CLAMPER_H_
+
+#include "GLSLANG/ShaderLang.h"
+
+#include "compiler/InfoSink.h"
+#include "compiler/intermediate.h"
+
+class ArrayBoundsClamper {
+public:
+    ArrayBoundsClamper();
+
+    // Output array clamp function source into the shader source.
+    void OutputClampingFunctionDefinition(TInfoSinkBase& out) const;
+
+    // Marks nodes in the tree that index arrays indirectly as
+    // requiring clamping.
+    void MarkIndirectArrayBoundsForClamping(TIntermNode* root);
+
+    void Cleanup()
+    {
+        mArrayBoundsClampDefinitionNeeded = false;
+    }
+
+private:
+    bool GetArrayBoundsClampDefinitionNeeded() const { return mArrayBoundsClampDefinitionNeeded; }
+    void SetArrayBoundsClampDefinitionNeeded() { mArrayBoundsClampDefinitionNeeded = true; }
+    
+    bool mArrayBoundsClampDefinitionNeeded;
+};
+
+#endif // COMPILER_ARRAY_BOUNDS_CLAMPER_H_
index cab8056..db5f729 100644 (file)
@@ -4,6 +4,7 @@
 // found in the LICENSE file.
 //
 
+#include "compiler/ArrayBoundsClamper.h"
 #include "compiler/BuiltInFunctionEmulator.h"
 #include "compiler/DetectRecursion.h"
 #include "compiler/ForLoopUnroll.h"
@@ -186,6 +187,10 @@ bool TCompiler::compile(const char* const shaderStrings[],
         if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
             builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
 
+        // Clamping uniform array bounds needs to happen after validateLimitations pass.
+        if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
+            arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
+
         // Call mapLongVariableNames() before collectAttribsUniforms() so in
         // collectAttribsUniforms() we already have the mapped symbol names and
         // we could composite mapped and original variable names.
@@ -231,6 +236,7 @@ void TCompiler::clearResults()
     uniforms.clear();
 
     builtInFunctionEmulator.Cleanup();
+    arrayBoundsClamper.Cleanup();
 }
 
 bool TCompiler::detectRecursion(TIntermNode* root)
@@ -331,3 +337,9 @@ const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
 {
     return builtInFunctionEmulator;
 }
+
+const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const
+{
+    return arrayBoundsClamper;
+}
+
index 552fa50..2100eaa 100644 (file)
@@ -235,9 +235,38 @@ bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary* node)
             break;
 
         case EOpIndexDirect:
-        case EOpIndexIndirect:
             writeTriplet(visit, NULL, "[", "]");
             break;
+        case EOpIndexIndirect:
+            if (node->getAddIndexClamp())
+            {
+                if (visit == InVisit)
+                {
+                    out << "[webgl_int_clamp(";
+                }
+                else if (visit == PostVisit)
+                {
+                    int maxSize;
+                    TIntermTyped *left = node->getLeft();
+                    TType leftType = left->getType();
+
+                    if (left->isArray())
+                    {
+                        // The shader will fail validation if the array length is not > 0.
+                        maxSize = leftType.getArraySize() - 1;
+                    }
+                    else
+                    {
+                        maxSize = leftType.getNominalSize() - 1;
+                    }
+                    out << ", 0, " << maxSize << ")]";
+                }
+            }
+            else
+            {
+                writeTriplet(visit, NULL, "[", "]");
+            }
+            break;
         case EOpIndexDirectStruct:
             if (visit == InVisit)
             {
index 26528b8..3eaf19f 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "GLSLANG/ShaderLang.h"
 
+#include "compiler/ArrayBoundsClamper.h"
 #include "compiler/BuiltInFunctionEmulator.h"
 #include "compiler/ExtensionBehavior.h"
 #include "compiler/InfoSink.h"
@@ -98,6 +99,7 @@ protected:
     // Get built-in extensions with default behavior.
     const TExtensionBehavior& getExtensionBehavior() const;
 
+    const ArrayBoundsClamper& getArrayBoundsClamper() const;
     const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const;
 
 private:
@@ -110,6 +112,7 @@ private:
     // Built-in extensions with default behavior.
     TExtensionBehavior extensionBehavior;
 
+    ArrayBoundsClamper arrayBoundsClamper;
     BuiltInFunctionEmulator builtInFunctionEmulator;
 
     // Results of compilation.
index e3a2c2a..d90c70c 100644 (file)
@@ -22,6 +22,9 @@ void TranslatorESSL::translate(TIntermNode* root) {
     getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition(
         sink, getShaderType() == SH_FRAGMENT_SHADER);
 
+    // Write array bounds clamping emulation if needed.
+    getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
+
     // Write translated shader.
     TOutputESSL outputESSL(sink);
     root->traverse(&outputESSL);
index bb07a1e..ba38112 100644 (file)
@@ -35,6 +35,9 @@ void TranslatorGLSL::translate(TIntermNode* root) {
     getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition(
         sink, false);
 
+    // Write array bounds clamping emulation if needed.
+    getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
+
     // Write translated shader.
     TOutputGLSL outputGLSL(sink);
     root->traverse(&outputGLSL);
index e83c7b7..f48a049 100644 (file)
@@ -42,7 +42,7 @@ TString TType::getCompleteString() const
     if (qualifier != EvqTemporary && qualifier != EvqGlobal)
         stream << getQualifierString() << " " << getPrecisionString() << " ";
     if (array)
-        stream << "array of ";
+        stream << "array[" << getArraySize() << "] of ";
     if (matrix)
         stream << size << "X" << size << " matrix of ";
     else if (size > 1)
index 843c40e..9e9abbc 100644 (file)
@@ -400,9 +400,15 @@ public:
     TIntermTyped* getRight() const { return right; }
     bool promote(TInfoSink&);
 
+    void setAddIndexClamp() { addIndexClamp = true; }
+    bool getAddIndexClamp() { return addIndexClamp; }
+
 protected:
     TIntermTyped* left;
     TIntermTyped* right;
+
+    // If set to true, wrap any EOpIndexIndirect with a clamp to bounds.
+    bool addIndexClamp;
 };
 
 //
index b74ef63..115959d 100644 (file)
@@ -1,3 +1,19 @@
+2012-10-18  Dean Jackson  <dino@apple.com>
+
+        Shader translator needs option to clamp uniform array accesses in vertex shaders
+        https://bugs.webkit.org/show_bug.cgi?id=98977
+        https://code.google.com/p/angleproject/issues/detail?id=49
+
+        Reviewed by Alok Priyadarshi and Ken Russell.
+
+        WebGL forbids out-of-bounds array access in shaders. Rewrite any shaders to
+        ensure that non-direct array indexing is clamped to the bounds of the array.
+
+        Test: fast/canvas/webgl/array-bounds-clamping.html
+
+        * platform/graphics/ANGLEWebKitBridge.cpp:
+        (WebCore::ANGLEWebKitBridge::compileShaderSource): Pass new compiler option SH_CLAMP_INDIRECT_ARRAY_BOUNDS
+
 2012-10-19  Justin Novosad  <junov@chromium.org>
 
         [Chromium] Reduce memory footprint of canvas pattern object with deferred rendering
index 22ffac0..21a9796 100644 (file)
@@ -173,6 +173,12 @@ bool ANGLEWebKitBridge::compileShaderSource(const char* shaderSource, ANGLEShade
 
     const char* const shaderSourceStrings[] = { shaderSource };
 
+#if !PLATFORM(CHROMIUM)
+    // Chromium does not use the ANGLE bundled in WebKit source, and thus
+    // does not yet have the symbol SH_CLAMP_INDIRECT_ARRAY_BOUNDS.
+    extraCompileOptions |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
+#endif
+
     bool validateSuccess = ShCompile(compiler, shaderSourceStrings, 1, SH_OBJECT_CODE | SH_ATTRIBUTES_UNIFORMS | extraCompileOptions);
     if (!validateSuccess) {
         int logSize = getValidationResultValue(compiler, SH_INFO_LOG_LENGTH);