WebGL: Validate texture copying feedback loops.
This adds basic validation for catching CopyTex{Sub}Image calls
whose source and destination textures overlap. It does not yet
implement full support for ES3 types (3D textures, array textures).
BUG=angleproject:1685
Change-Id: I83e7b1998df5575057fed8f99f7ee9970fb38df0
Reviewed-on: https://chromium-review.googlesource.com/425491
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Framebuffer.cpp b/src/libANGLE/Framebuffer.cpp
index ae884a9..61c6bed 100644
--- a/src/libANGLE/Framebuffer.cpp
+++ b/src/libANGLE/Framebuffer.cpp
@@ -1018,4 +1018,26 @@
return false;
}
+bool Framebuffer::formsCopyingFeedbackLoopWith(GLuint copyTextureID, GLint copyTextureLevel) const
+{
+ if (mId == 0)
+ {
+ // It seems impossible to form a texture copying feedback loop with the default FBO.
+ return false;
+ }
+
+ const FramebufferAttachment *readAttachment = getReadColorbuffer();
+ ASSERT(readAttachment);
+
+ if (readAttachment->isTextureWithId(copyTextureID))
+ {
+ // TODO(jmadill): 3D/Array texture layers.
+ if (readAttachment->getTextureImageIndex().mipIndex == copyTextureLevel)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace gl
diff --git a/src/libANGLE/Framebuffer.h b/src/libANGLE/Framebuffer.h
index d7909f5..e54fa3a 100644
--- a/src/libANGLE/Framebuffer.h
+++ b/src/libANGLE/Framebuffer.h
@@ -214,6 +214,7 @@
void signal(angle::SignalToken token) override;
bool formsRenderingFeedbackLoopWith(const State &state) const;
+ bool formsCopyingFeedbackLoopWith(GLuint copyTextureID, GLint copyTextureLevel) const;
private:
void detachResourceById(GLenum resourceType, GLuint resourceId);
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index ad604c4..106923c 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -3124,6 +3124,19 @@
{
*textureFormatOut = texture->getFormat(target, level);
}
+
+ // Detect texture copying feedback loops for WebGL.
+ if (context->getExtensions().webglCompatibility)
+ {
+ if (readFramebuffer->formsCopyingFeedbackLoopWith(texture->id(), level))
+ {
+ context->handleError(Error(GL_INVALID_OPERATION,
+ "Texture copying feedback loop formed between Framebuffer "
+ "and specified Texture level."));
+ return false;
+ }
+ }
+
return true;
}
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
index f43c193..f1ce3ca 100644
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
@@ -528,6 +528,67 @@
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
}
+// Test tests that texture copying feedback loops are properly rejected in WebGL.
+// Based on the WebGL test conformance/textures/misc/texture-copying-feedback-loops.html
+TEST_P(WebGLCompatibilityTest, TextureCopyingFeedbackLoops)
+{
+ GLTexture texture;
+ glBindTexture(GL_TEXTURE_2D, texture.get());
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ GLTexture texture2;
+ glBindTexture(GL_TEXTURE_2D, texture2.get());
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ GLFramebuffer framebuffer;
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
+
+ // framebuffer should be FRAMEBUFFER_COMPLETE.
+ ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+ ASSERT_GL_NO_ERROR();
+
+ // testing copyTexImage2D
+
+ // copyTexImage2D to same texture but different level
+ glBindTexture(GL_TEXTURE_2D, texture.get());
+ glCopyTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 0, 0, 2, 2, 0);
+ EXPECT_GL_NO_ERROR();
+
+ // copyTexImage2D to same texture same level, invalid feedback loop
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+ // copyTexImage2D to different texture
+ glBindTexture(GL_TEXTURE_2D, texture2.get());
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
+ EXPECT_GL_NO_ERROR();
+
+ // testing copyTexSubImage2D
+
+ // copyTexSubImage2D to same texture but different level
+ glBindTexture(GL_TEXTURE_2D, texture.get());
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 0, 0, 1, 1);
+ EXPECT_GL_NO_ERROR();
+
+ // copyTexSubImage2D to same texture same level, invalid feedback loop
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+ // copyTexSubImage2D to different texture
+ glBindTexture(GL_TEXTURE_2D, texture2.get());
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
+ 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,