Validate clear attachment formats match color clear types.

TEST=conformance2/rendering/clear-func-buffer-type-match.html

BUG=angleproject:1954

Change-Id: Iefeb38041608f11781f87aadb8611737ba2ee96f
Reviewed-on: https://chromium-review.googlesource.com/461270
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 8f93a05..ab47a90 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -5883,4 +5883,31 @@
     return true;
 }
 
+// Perform validation from WebGL 2 section 5.10 "Invalid Clears":
+// In the WebGL 2 API, trying to perform a clear when there is a mismatch between the type of the
+// specified clear value and the type of a buffer that is being cleared generates an
+// INVALID_OPERATION error instead of producing undefined results
+bool ValidateWebGLFramebufferAttachmentClearType(ValidationContext *context,
+                                                 GLint drawbuffer,
+                                                 const GLenum *validComponentTypes,
+                                                 size_t validComponentTypeCount)
+{
+    const FramebufferAttachment *attachment =
+        context->getGLState().getDrawFramebuffer()->getDrawBuffer(drawbuffer);
+    if (attachment)
+    {
+        GLenum componentType = attachment->getFormat().info->componentType;
+        const GLenum *end    = validComponentTypes + validComponentTypeCount;
+        if (std::find(validComponentTypes, end, componentType) == end)
+        {
+            context->handleError(
+                Error(GL_INVALID_OPERATION,
+                      "No defined conversion between clear value and attachment format."));
+            return false;
+        }
+    }
+
+    return true;
+}
+
 }  // namespace gl
diff --git a/src/libANGLE/validationES.h b/src/libANGLE/validationES.h
index cc4bd43..56ccfe6 100644
--- a/src/libANGLE/validationES.h
+++ b/src/libANGLE/validationES.h
@@ -624,6 +624,11 @@
                               GLenum type,
                               GLboolean pureInteger);
 
+bool ValidateWebGLFramebufferAttachmentClearType(ValidationContext *context,
+                                                 GLint drawbuffer,
+                                                 const GLenum *validComponentTypes,
+                                                 size_t validComponentTypeCount);
+
 // Error messages shared here for use in testing.
 extern const char *g_ExceedsMaxElementErrorMessage;
 }  // namespace gl
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index c17e0f8..3b084d4 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -1965,6 +1965,22 @@
         return false;
     }
 
+    if (context->getExtensions().webglCompatibility && (mask & GL_COLOR_BUFFER_BIT) != 0)
+    {
+        constexpr GLenum validComponentTypes[] = {GL_FLOAT, GL_UNSIGNED_NORMALIZED,
+                                                  GL_SIGNED_NORMALIZED};
+
+        for (GLuint drawBufferIdx = 0; drawBufferIdx < context->getCaps().maxDrawBuffers;
+             drawBufferIdx++)
+        {
+            if (!ValidateWebGLFramebufferAttachmentClearType(
+                    context, drawBufferIdx, validComponentTypes, ArraySize(validComponentTypes)))
+            {
+                return false;
+            }
+        }
+    }
+
     return true;
 }
 
diff --git a/src/libANGLE/validationES3.cpp b/src/libANGLE/validationES3.cpp
index 9c7d03d..2197ddd 100644
--- a/src/libANGLE/validationES3.cpp
+++ b/src/libANGLE/validationES3.cpp
@@ -1481,6 +1481,15 @@
                 context->handleError(Error(GL_INVALID_VALUE));
                 return false;
             }
+            if (context->getExtensions().webglCompatibility)
+            {
+                constexpr GLenum validComponentTypes[] = {GL_INT};
+                if (ValidateWebGLFramebufferAttachmentClearType(
+                        context, drawbuffer, validComponentTypes, ArraySize(validComponentTypes)))
+                {
+                    return false;
+                }
+            }
             break;
 
         case GL_STENCIL:
@@ -1513,6 +1522,15 @@
                 context->handleError(Error(GL_INVALID_VALUE));
                 return false;
             }
+            if (context->getExtensions().webglCompatibility)
+            {
+                constexpr GLenum validComponentTypes[] = {GL_UNSIGNED_INT};
+                if (ValidateWebGLFramebufferAttachmentClearType(
+                        context, drawbuffer, validComponentTypes, ArraySize(validComponentTypes)))
+                {
+                    return false;
+                }
+            }
             break;
 
         default:
@@ -1537,6 +1555,16 @@
                 context->handleError(Error(GL_INVALID_VALUE));
                 return false;
             }
+            if (context->getExtensions().webglCompatibility)
+            {
+                constexpr GLenum validComponentTypes[] = {GL_FLOAT, GL_UNSIGNED_NORMALIZED,
+                                                          GL_SIGNED_NORMALIZED};
+                if (ValidateWebGLFramebufferAttachmentClearType(
+                        context, drawbuffer, validComponentTypes, ArraySize(validComponentTypes)))
+                {
+                    return false;
+                }
+            }
             break;
 
         case GL_DEPTH:
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
index d99c51d..ca56989 100644
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
@@ -1288,6 +1288,94 @@
     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
 }
 
+// Verify that errors are generated when there isn not a defined conversion between the clear type
+// and the buffer type.
+TEST_P(WebGL2CompatibilityTest, ClearBufferTypeCompatibity)
+{
+    if (IsD3D11())
+    {
+        std::cout << "Test skipped because it generates D3D11 runtime warnings." << std::endl;
+        return;
+    }
+
+    constexpr float clearFloat[]       = {0.0f, 0.0f, 0.0f, 0.0f};
+    constexpr int clearInt[]           = {0, 0, 0, 0};
+    constexpr unsigned int clearUint[] = {0, 0, 0, 0};
+
+    GLTexture texture;
+    GLFramebuffer framebuffer;
+
+    glBindTexture(GL_TEXTURE_2D, texture.get());
+    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
+
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
+    ASSERT_GL_NO_ERROR();
+
+    // Unsigned integer buffer
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32UI, 1, 1, 0, GL_RGBA_INTEGER, GL_UNSIGNED_INT, nullptr);
+    ASSERT_GL_NO_ERROR();
+
+    glClearBufferfv(GL_COLOR, 0, clearFloat);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    glClearBufferiv(GL_COLOR, 0, clearInt);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    glClearBufferuiv(GL_COLOR, 0, clearUint);
+    EXPECT_GL_NO_ERROR();
+
+    glClear(GL_COLOR_BUFFER_BIT);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    // Integer buffer
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32I, 1, 1, 0, GL_RGBA_INTEGER, GL_INT, nullptr);
+    ASSERT_GL_NO_ERROR();
+
+    glClearBufferfv(GL_COLOR, 0, clearFloat);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    glClearBufferiv(GL_COLOR, 0, clearInt);
+    EXPECT_GL_NO_ERROR();
+
+    glClearBufferuiv(GL_COLOR, 0, clearUint);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    glClear(GL_COLOR_BUFFER_BIT);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    // Float buffer
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1, 1, 0, GL_RGBA, GL_FLOAT, nullptr);
+    ASSERT_GL_NO_ERROR();
+
+    glClearBufferfv(GL_COLOR, 0, clearFloat);
+    EXPECT_GL_NO_ERROR();
+
+    glClearBufferiv(GL_COLOR, 0, clearInt);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    glClearBufferuiv(GL_COLOR, 0, clearUint);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    glClear(GL_COLOR_BUFFER_BIT);
+    EXPECT_GL_NO_ERROR();
+
+    // Normalized uint buffer
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    ASSERT_GL_NO_ERROR();
+
+    glClearBufferfv(GL_COLOR, 0, clearFloat);
+    EXPECT_GL_NO_ERROR();
+
+    glClearBufferiv(GL_COLOR, 0, clearInt);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    glClearBufferuiv(GL_COLOR, 0, clearUint);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    glClear(GL_COLOR_BUFFER_BIT);
+    EXPECT_GL_NO_ERROR();
+}
+
 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
 // tests should be run against.
 ANGLE_INSTANTIATE_TEST(WebGLCompatibilityTest,