Commit 966c940b by Geoff Lang Committed by Commit Bot

Fix validation for compressed texture functions.

* No validation that the format matched the texture level's format for SubImage calls. * WebGL does not allow the base level to be smaller than the block size. * ANGLE used to allow mips of size 3 when this is disallowed. * Don't early-exit validation when dimensions are 0, imageSize validation happens later. TEST=conformance/extensions/webgl-compressed-texture-s3tc BUG=angleproject:1998 Change-Id: I05f5a0b5180344d67b036fdecc17edd2256e85ab Reviewed-on: https://chromium-review.googlesource.com/480442Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Geoff Lang <geofflang@chromium.org>
parent e491578f
...@@ -2136,10 +2136,15 @@ bool CompressedTextureFormatRequiresExactSize(GLenum internalFormat) ...@@ -2136,10 +2136,15 @@ bool CompressedTextureFormatRequiresExactSize(GLenum internalFormat)
} }
} }
bool ValidCompressedDimension(GLsizei size, GLuint blockSize, bool smallerThanBlockSizeAllowed)
{
return (smallerThanBlockSizeAllowed && (size > 0) && (blockSize % size == 0)) ||
(size % blockSize == 0);
}
bool ValidCompressedImageSize(const ValidationContext *context, bool ValidCompressedImageSize(const ValidationContext *context,
GLenum internalFormat, GLenum internalFormat,
GLint xoffset, GLint level,
GLint yoffset,
GLsizei width, GLsizei width,
GLsizei height) GLsizei height)
{ {
...@@ -2149,6 +2154,45 @@ bool ValidCompressedImageSize(const ValidationContext *context, ...@@ -2149,6 +2154,45 @@ bool ValidCompressedImageSize(const ValidationContext *context,
return false; return false;
} }
if (width < 0 || height < 0)
{
return false;
}
if (CompressedTextureFormatRequiresExactSize(internalFormat))
{
// The ANGLE extensions allow specifying compressed textures with sizes smaller than the
// block size for level 0 but WebGL disallows this.
bool smallerThanBlockSizeAllowed =
level > 0 || !context->getExtensions().webglCompatibility;
if (!ValidCompressedDimension(width, formatInfo.compressedBlockWidth,
smallerThanBlockSizeAllowed) ||
!ValidCompressedDimension(height, formatInfo.compressedBlockHeight,
smallerThanBlockSizeAllowed))
{
return false;
}
}
return true;
}
bool ValidCompressedSubImageSize(const ValidationContext *context,
GLenum internalFormat,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
size_t textureWidth,
size_t textureHeight)
{
const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
if (!formatInfo.compressed)
{
return false;
}
if (xoffset < 0 || yoffset < 0 || width < 0 || height < 0) if (xoffset < 0 || yoffset < 0 || width < 0 || height < 0)
{ {
return false; return false;
...@@ -2157,11 +2201,19 @@ bool ValidCompressedImageSize(const ValidationContext *context, ...@@ -2157,11 +2201,19 @@ bool ValidCompressedImageSize(const ValidationContext *context,
if (CompressedTextureFormatRequiresExactSize(internalFormat)) if (CompressedTextureFormatRequiresExactSize(internalFormat))
{ {
if (xoffset % formatInfo.compressedBlockWidth != 0 || if (xoffset % formatInfo.compressedBlockWidth != 0 ||
yoffset % formatInfo.compressedBlockHeight != 0 || yoffset % formatInfo.compressedBlockHeight != 0)
(static_cast<GLuint>(width) > formatInfo.compressedBlockWidth && {
width % formatInfo.compressedBlockWidth != 0) || return false;
(static_cast<GLuint>(height) > formatInfo.compressedBlockHeight && }
height % formatInfo.compressedBlockHeight != 0))
// Allowed to either have data that is a multiple of block size or is smaller than the block
// size but fills the entire mip
bool fillsEntireMip = xoffset == 0 && yoffset == 0 &&
static_cast<size_t>(width) == textureWidth &&
static_cast<size_t>(height) == textureHeight;
bool sizeMultipleOfBlockSize = (width % formatInfo.compressedBlockWidth) == 0 &&
(height % formatInfo.compressedBlockHeight) == 0;
if (!sizeMultipleOfBlockSize && !fillsEntireMip)
{ {
return false; return false;
} }
...@@ -3598,14 +3650,7 @@ bool ValidateCopyTexImageParametersBase(ValidationContext *context, ...@@ -3598,14 +3650,7 @@ bool ValidateCopyTexImageParametersBase(ValidationContext *context,
const gl::InternalFormat &formatInfo = const gl::InternalFormat &formatInfo =
gl::GetInternalFormatInfo(internalformat, GL_UNSIGNED_BYTE); gl::GetInternalFormatInfo(internalformat, GL_UNSIGNED_BYTE);
if (formatInfo.depthBits > 0) if (formatInfo.depthBits > 0 || formatInfo.compressed)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
if (formatInfo.compressed &&
!ValidCompressedImageSize(context, internalformat, xoffset, yoffset, width, height))
{ {
context->handleError(Error(GL_INVALID_OPERATION)); context->handleError(Error(GL_INVALID_OPERATION));
return false; return false;
......
...@@ -49,10 +49,17 @@ bool ValidImageSizeParameters(const ValidationContext *context, ...@@ -49,10 +49,17 @@ bool ValidImageSizeParameters(const ValidationContext *context,
bool isSubImage); bool isSubImage);
bool ValidCompressedImageSize(const ValidationContext *context, bool ValidCompressedImageSize(const ValidationContext *context,
GLenum internalFormat, GLenum internalFormat,
GLint xoffset, GLint level,
GLint yoffset,
GLsizei width, GLsizei width,
GLsizei height); GLsizei height);
bool ValidCompressedSubImageSize(const ValidationContext *context,
GLenum internalFormat,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
size_t textureWidth,
size_t textureHeight);
bool ValidImageDataSize(ValidationContext *context, bool ValidImageDataSize(ValidationContext *context,
GLenum textureTarget, GLenum textureTarget,
GLsizei width, GLsizei width,
......
...@@ -524,11 +524,33 @@ bool ValidateES2TexImageParameters(Context *context, ...@@ -524,11 +524,33 @@ bool ValidateES2TexImageParameters(Context *context,
"internalformat is not a supported compressed internal format")); "internalformat is not a supported compressed internal format"));
return false; return false;
} }
if (!ValidCompressedImageSize(context, actualInternalFormat, xoffset, yoffset, width,
height)) if (isSubImage)
{ {
context->handleError(Error(GL_INVALID_OPERATION)); if (!ValidCompressedSubImageSize(context, actualInternalFormat, xoffset, yoffset, width,
return false; height, texture->getWidth(target, level),
texture->getHeight(target, level)))
{
context->handleError(
Error(GL_INVALID_OPERATION, "Invalid compressed format dimension."));
return false;
}
if (format != actualInternalFormat)
{
context->handleError(Error(
GL_INVALID_OPERATION, "Format must match the internal format of the texture."));
return false;
}
}
else
{
if (!ValidCompressedImageSize(context, actualInternalFormat, level, width, height))
{
context->handleError(
Error(GL_INVALID_OPERATION, "Invalid compressed format dimension."));
return false;
}
} }
} }
else else
...@@ -1970,7 +1992,7 @@ bool ValidateCompressedTexSubImage2D(Context *context, ...@@ -1970,7 +1992,7 @@ bool ValidateCompressedTexSubImage2D(Context *context,
if (context->getClientMajorVersion() < 3) if (context->getClientMajorVersion() < 3)
{ {
if (!ValidateES2TexImageParameters(context, target, level, GL_NONE, true, true, xoffset, if (!ValidateES2TexImageParameters(context, target, level, GL_NONE, true, true, xoffset,
yoffset, width, height, 0, GL_NONE, GL_NONE, -1, data)) yoffset, width, height, 0, format, GL_NONE, -1, data))
{ {
return false; return false;
} }
...@@ -1979,7 +2001,7 @@ bool ValidateCompressedTexSubImage2D(Context *context, ...@@ -1979,7 +2001,7 @@ bool ValidateCompressedTexSubImage2D(Context *context,
{ {
ASSERT(context->getClientMajorVersion() >= 3); ASSERT(context->getClientMajorVersion() >= 3);
if (!ValidateES3TexImage2DParameters(context, target, level, GL_NONE, true, true, xoffset, if (!ValidateES3TexImage2DParameters(context, target, level, GL_NONE, true, true, xoffset,
yoffset, 0, width, height, 1, 0, GL_NONE, GL_NONE, -1, yoffset, 0, width, height, 1, 0, format, GL_NONE, -1,
data)) data))
{ {
return false; return false;
......
...@@ -204,11 +204,32 @@ bool ValidateES3TexImageParametersBase(Context *context, ...@@ -204,11 +204,32 @@ bool ValidateES3TexImageParametersBase(Context *context,
return false; return false;
} }
if (!ValidCompressedImageSize(context, actualFormatInfo.internalFormat, xoffset, yoffset, if (isSubImage)
width, height))
{ {
context->handleError(Error(GL_INVALID_OPERATION)); if (!ValidCompressedSubImageSize(
return false; context, actualFormatInfo.internalFormat, xoffset, yoffset, width, height,
texture->getWidth(target, level), texture->getHeight(target, level)))
{
context->handleError(
Error(GL_INVALID_OPERATION, "Invalid compressed format dimension."));
return false;
}
if (format != actualInternalFormat)
{
context->handleError(Error(
GL_INVALID_OPERATION, "Format must match the internal format of the texture."));
return false;
}
}
else
{
if (!ValidCompressedImageSize(context, actualInternalFormat, level, width, height))
{
context->handleError(
Error(GL_INVALID_OPERATION, "Invalid compressed format dimension."));
return false;
}
} }
if (!actualFormatInfo.textureSupport(context->getClientVersion(), context->getExtensions())) if (!actualFormatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
...@@ -240,11 +261,6 @@ bool ValidateES3TexImageParametersBase(Context *context, ...@@ -240,11 +261,6 @@ bool ValidateES3TexImageParametersBase(Context *context,
return false; return false;
} }
if (width == 0 || height == 0 || depth == 0)
{
return false;
}
if (xoffset < 0 || yoffset < 0 || zoffset < 0) if (xoffset < 0 || yoffset < 0 || zoffset < 0)
{ {
context->handleError(Error(GL_INVALID_VALUE)); context->handleError(Error(GL_INVALID_VALUE));
...@@ -1831,7 +1847,7 @@ bool ValidateCompressedTexSubImage3D(Context *context, ...@@ -1831,7 +1847,7 @@ bool ValidateCompressedTexSubImage3D(Context *context,
} }
return ValidateES3TexImage3DParameters(context, target, level, GL_NONE, true, true, 0, 0, 0, return ValidateES3TexImage3DParameters(context, target, level, GL_NONE, true, true, 0, 0, 0,
width, height, depth, 0, GL_NONE, GL_NONE, -1, data); width, height, depth, 0, format, GL_NONE, -1, data);
} }
bool ValidateCompressedTexSubImage3DRobustANGLE(Context *context, bool ValidateCompressedTexSubImage3DRobustANGLE(Context *context,
GLenum target, GLenum target,
......
...@@ -1251,6 +1251,96 @@ TEST_P(WebGLCompatibilityTest, BuiltInInvariant) ...@@ -1251,6 +1251,96 @@ TEST_P(WebGLCompatibilityTest, BuiltInInvariant)
EXPECT_EQ(0u, program); EXPECT_EQ(0u, program);
} }
// Test dimension and image size validation of compressed textures
TEST_P(WebGLCompatibilityTest, CompressedTextureS3TC)
{
if (extensionRequestable("GL_EXT_texture_compression_dxt1"))
{
glRequestExtensionANGLE("GL_EXT_texture_compression_dxt1");
}
if (!extensionEnabled("GL_EXT_texture_compression_dxt1"))
{
std::cout << "Test skipped because GL_EXT_texture_compression_dxt1 is not available."
<< std::endl;
return;
}
constexpr uint8_t CompressedImageDXT1[] = {0x00, 0xf8, 0x00, 0xf8, 0xaa, 0xaa, 0xaa, 0xaa};
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
// Regular case, verify that it works
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
ASSERT_GL_NO_ERROR();
// Test various dimensions that are not valid
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 3, 4, 0,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 3, 0,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 2, 2, 0,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 1, 1, 0,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
// Test various image sizes that are not valid
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0,
sizeof(CompressedImageDXT1) - 1, CompressedImageDXT1);
ASSERT_GL_ERROR(GL_INVALID_VALUE);
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0,
sizeof(CompressedImageDXT1) + 1, CompressedImageDXT1);
ASSERT_GL_ERROR(GL_INVALID_VALUE);
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0, 0,
CompressedImageDXT1);
ASSERT_GL_ERROR(GL_INVALID_VALUE);
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 0, 0, 0,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
ASSERT_GL_ERROR(GL_INVALID_VALUE);
// Fill a full mip chain and verify that it works
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
glCompressedTexImage2D(GL_TEXTURE_2D, 1, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 2, 2, 0,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
glCompressedTexImage2D(GL_TEXTURE_2D, 2, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 1, 1, 0,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
ASSERT_GL_NO_ERROR();
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_COMPRESSED_RGB_S3TC_DXT1_EXT,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
ASSERT_GL_NO_ERROR();
// Test that non-block size sub-uploads are not valid for the 0 mip
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 2, 2, 2, 2, GL_COMPRESSED_RGB_S3TC_DXT1_EXT,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
// Test that non-block size sub-uploads are valid for if they fill the whole mip
glCompressedTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 2, 2, GL_COMPRESSED_RGB_S3TC_DXT1_EXT,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
glCompressedTexSubImage2D(GL_TEXTURE_2D, 2, 0, 0, 1, 1, GL_COMPRESSED_RGB_S3TC_DXT1_EXT,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
ASSERT_GL_NO_ERROR();
// Test that if the format miss-matches the texture, an error is generated
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 2, 2, 2, 2, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
}
// This tests that rendering feedback loops works as expected with WebGL 2. // This tests that rendering feedback loops works as expected with WebGL 2.
// Based on WebGL test conformance2/rendering/rendering-sampling-feedback-loop.html // Based on WebGL test conformance2/rendering/rendering-sampling-feedback-loop.html
TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDrawBuffers) TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDrawBuffers)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment