diff --git a/AUTHORS b/AUTHORS
index a2ce91575..65c21aa24 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -12,6 +12,7 @@ TransGaming Inc.
3DLabs Inc. Ltd.
Adobe Systems Inc.
+Apple Inc.
Autodesk, Inc.
Cloud Party, Inc.
Intel Corporation
diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h
index 1b8e32d54..967462cfb 100644
--- a/include/GLSLANG/ShaderLang.h
+++ b/include/GLSLANG/ShaderLang.h
@@ -151,7 +151,13 @@ typedef enum {
SH_DEPENDENCY_GRAPH = 0x0400,
// Enforce the GLSL 1.017 Appendix A section 7 packing restrictions.
- SH_ENFORCE_PACKING_RESTRICTIONS = 0x0800
+ SH_ENFORCE_PACKING_RESTRICTIONS = 0x0800,
+
+ // 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 = 0x1000
} ShCompileOptions;
//
diff --git a/src/build_angle.gypi b/src/build_angle.gypi
index 0fd07968f..264048716 100644
--- a/src/build_angle.gypi
+++ b/src/build_angle.gypi
@@ -60,6 +60,8 @@
'COMPILER_IMPLEMENTATION',
],
'sources': [
+ 'compiler/ArrayBoundsClamper.cpp',
+ 'compiler/ArrayBoundsClamper.h',
'compiler/BaseTypes.h',
'compiler/BuiltInFunctionEmulator.cpp',
'compiler/BuiltInFunctionEmulator.h',
diff --git a/src/compiler/ArrayBoundsClamper.cpp b/src/compiler/ArrayBoundsClamper.cpp
new file mode 100644
index 000000000..6a317217c
--- /dev/null
+++ b/src/compiler/ArrayBoundsClamper.cpp
@@ -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/src/compiler/ArrayBoundsClamper.h b/src/compiler/ArrayBoundsClamper.h
new file mode 100644
index 000000000..d4a8d6994
--- /dev/null
+++ b/src/compiler/ArrayBoundsClamper.h
@@ -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_
diff --git a/src/compiler/Compiler.cpp b/src/compiler/Compiler.cpp
index ab12ac93e..dce8c6872 100644
--- a/src/compiler/Compiler.cpp
+++ b/src/compiler/Compiler.cpp
@@ -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"
@@ -192,6 +193,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.
@@ -237,6 +242,7 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources& resources)
void TCompiler::clearResults()
{
+ arrayBoundsClamper.Cleanup();
infoSink.info.erase();
infoSink.obj.erase();
infoSink.debug.erase();
@@ -353,3 +359,9 @@ const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
{
return builtInFunctionEmulator;
}
+
+const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const
+{
+ return arrayBoundsClamper;
+}
+
diff --git a/src/compiler/OutputGLSLBase.cpp b/src/compiler/OutputGLSLBase.cpp
index ed86c686d..3c3c9deb1 100644
--- a/src/compiler/OutputGLSLBase.cpp
+++ b/src/compiler/OutputGLSLBase.cpp
@@ -212,9 +212,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)
{
diff --git a/src/compiler/ShHandle.h b/src/compiler/ShHandle.h
index d6aa90b15..9a6f8ddf2 100644
--- a/src/compiler/ShHandle.h
+++ b/src/compiler/ShHandle.h
@@ -16,6 +16,7 @@
#include "GLSLANG/ShaderLang.h"
+#include "compiler/ArrayBoundsClamper.h"
#include "compiler/BuiltInFunctionEmulator.h"
#include "compiler/ExtensionBehavior.h"
#include "compiler/HashNames.h"
@@ -108,6 +109,7 @@ protected:
// Get built-in extensions with default behavior.
const TExtensionBehavior& getExtensionBehavior() const;
+ const ArrayBoundsClamper& getArrayBoundsClamper() const;
const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const;
private:
@@ -122,6 +124,7 @@ private:
// Built-in extensions with default behavior.
TExtensionBehavior extensionBehavior;
+ ArrayBoundsClamper arrayBoundsClamper;
BuiltInFunctionEmulator builtInFunctionEmulator;
// Results of compilation.
diff --git a/src/compiler/TranslatorESSL.cpp b/src/compiler/TranslatorESSL.cpp
index f718f1ef8..a4f047a3a 100644
--- a/src/compiler/TranslatorESSL.cpp
+++ b/src/compiler/TranslatorESSL.cpp
@@ -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, getHashFunction(), getNameMap(), getSymbolTable());
root->traverse(&outputESSL);
diff --git a/src/compiler/TranslatorGLSL.cpp b/src/compiler/TranslatorGLSL.cpp
index 1e3ff381e..5f3dbccb2 100644
--- a/src/compiler/TranslatorGLSL.cpp
+++ b/src/compiler/TranslatorGLSL.cpp
@@ -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, getHashFunction(), getNameMap(), getSymbolTable());
root->traverse(&outputGLSL);
diff --git a/src/compiler/intermOut.cpp b/src/compiler/intermOut.cpp
index e83c7b72f..f48a049c6 100644
--- a/src/compiler/intermOut.cpp
+++ b/src/compiler/intermOut.cpp
@@ -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)
diff --git a/src/compiler/intermediate.h b/src/compiler/intermediate.h
index 1dbeef01c..09454f6f5 100644
--- a/src/compiler/intermediate.h
+++ b/src/compiler/intermediate.h
@@ -395,7 +395,7 @@ protected:
//
class TIntermBinary : public TIntermOperator {
public:
- TIntermBinary(TOperator o) : TIntermOperator(o) {}
+ TIntermBinary(TOperator o) : TIntermOperator(o), addIndexClamp(false) {}
virtual TIntermBinary* getAsBinaryNode() { return this; }
virtual void traverse(TIntermTraverser*);
@@ -406,9 +406,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;
};
//
diff --git a/src/compiler/translator_common.vcxproj b/src/compiler/translator_common.vcxproj
index 0f383db84..19d7a11b8 100644
--- a/src/compiler/translator_common.vcxproj
+++ b/src/compiler/translator_common.vcxproj
@@ -138,6 +138,7 @@
+
@@ -225,6 +226,7 @@
+
@@ -279,4 +281,4 @@
-
\ No newline at end of file
+