From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shahbaz Youssefi <syoussefi@chromium.org>
Date: Tue, 28 Mar 2023 11:43:23 -0400
Subject: Translator: Limit the size of private variables in WebGL shaders

As a follow up to
https://chromium-review.googlesource.com/c/angle/angle/+/3023033, the
limit to shader-private variables (locals and globals) is further
reduced to 1MB.  A variable that large will not fit in GPU registers and
will spill to memory, killing performance.

Bug: chromium:1427865
Change-Id: I77314d4b891c591cd9a83ad2aebb77d7256f3ada
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4377639
Reviewed-by: Kenneth Russell <kbr@chromium.org>

diff --git a/src/compiler/translator/ValidateTypeSizeLimitations.cpp b/src/compiler/translator/ValidateTypeSizeLimitations.cpp
index c9607db74b53487950d31f6a56d55f3e834556a0..6097b6d236b547710aeaf37a6fb45df97d621ca0 100644
--- a/src/compiler/translator/ValidateTypeSizeLimitations.cpp
+++ b/src/compiler/translator/ValidateTypeSizeLimitations.cpp
@@ -20,9 +20,13 @@ namespace sh
 namespace
 {
 
-// Arbitrarily enforce that types - even local variables' - declared
-// with a size in bytes of over 2 GB will cause compilation failure.
-constexpr size_t kMaxTypeSizeInBytes = static_cast<size_t>(2) * 1024 * 1024 * 1024;
+// Arbitrarily enforce that all types declared with a size in bytes of over 2 GB will cause
+// compilation failure.
+//
+// For local and global variables, the limit is much lower (1MB) as that much memory won't fit in
+// the GPU registers anyway.
+constexpr size_t kMaxVariableSizeInBytes        = static_cast<size_t>(2) * 1024 * 1024 * 1024;
+constexpr size_t kMaxPrivateVariableSizeInBytes = static_cast<size_t>(1) * 1024 * 1024;
 
 // Traverses intermediate tree to ensure that the shader does not
 // exceed certain implementation-defined limits on the sizes of types.
@@ -78,13 +82,24 @@ class ValidateTypeSizeLimitationsTraverser : public TIntermTraverser
             // whether the row-major layout is correctly determined.
             bool isRowMajorLayout = false;
             TraverseShaderVariable(shaderVar, isRowMajorLayout, &visitor);
-            if (layoutEncoder.getCurrentOffset() > kMaxTypeSizeInBytes)
+            if (layoutEncoder.getCurrentOffset() > kMaxVariableSizeInBytes)
             {
                 error(asSymbol->getLine(),
                       "Size of declared variable exceeds implementation-defined limit",
                       asSymbol->getName());
                 return false;
             }
+
+            const bool isPrivate = variableType.getQualifier() == EvqTemporary ||
+                                   variableType.getQualifier() == EvqGlobal ||
+                                   variableType.getQualifier() == EvqConst;
+            if (layoutEncoder.getCurrentOffset() > kMaxPrivateVariableSizeInBytes && isPrivate)
+            {
+                error(asSymbol->getLine(),
+                      "Size of declared private variable exceeds implementation-defined limit",
+                      asSymbol->getName());
+                return false;
+            }
         }
 
         return true;
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
index 7dc56cddbc63add1aca6fca3bfd031f3da8d04fc..f4bd19baf3582c0b4a840d73a57ea6fc385159a6 100644
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
@@ -5283,8 +5283,8 @@ void main()
 
     constexpr char kVSArrayTooLarge[] =
         R"(varying vec4 color;
-// 2 GB / 32 aligned bytes per mat2 = 67108864
-const int array_size = 67108865;
+// 1 MB / 32 aligned bytes per mat2 = 32768
+const int array_size = 32769;
 void main()
 {
     mat2 array[array_size];
@@ -5296,7 +5296,7 @@ void main()
 
     constexpr char kVSArrayMuchTooLarge[] =
         R"(varying vec4 color;
-const int array_size = 556007917;
+const int array_size = 55600;
 void main()
 {
     mat2 array[array_size];
