blob: 76e1881c65c1dbc8ce86247230079101bdabac54 [file]
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RobustClientMemoryTest.cpp : Tests of the GL_ANGLE_robust_client_memory extension.
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
namespace angle
{
constexpr GLsizei kWindowSize = 128;
class RobustClientMemoryTest : public ANGLETest<>
{
protected:
RobustClientMemoryTest()
{
setWindowWidth(kWindowSize);
setWindowHeight(kWindowSize);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
};
// Test basic usage and validation of glGetIntegervRobustANGLE
TEST_P(RobustClientMemoryTest, GetInteger)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_robust_client_memory"));
// Verify that the robust and regular entry points return the same values
GLint resultRobust;
GLsizei length;
glGetIntegervRobustANGLE(GL_MAX_VERTEX_ATTRIBS, 1, &length, &resultRobust);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(1, length);
GLint resultRegular;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &resultRegular);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(resultRegular, resultRobust);
// Query a dynamic value
GLint numCompressedFormats;
glGetIntegervRobustANGLE(GL_NUM_COMPRESSED_TEXTURE_FORMATS, 1, &length, &numCompressedFormats);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(1, length);
if (numCompressedFormats > 0)
{
std::vector<GLint> resultBuf(numCompressedFormats * 2, 0);
// Test when the bufSize is too low
glGetIntegervRobustANGLE(GL_COMPRESSED_TEXTURE_FORMATS, numCompressedFormats - 1, &length,
resultBuf.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
EXPECT_TRUE(std::all_of(resultBuf.begin(), resultBuf.end(),
[](GLint value) { return value == 0; }));
// Make sure the GL doesn't touch the end of the buffer
glGetIntegervRobustANGLE(GL_COMPRESSED_TEXTURE_FORMATS,
static_cast<GLsizei>(resultBuf.size()), &length, resultBuf.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(numCompressedFormats, length);
EXPECT_TRUE(std::none_of(resultBuf.begin(), resultBuf.begin() + length,
[](GLint value) { return value == 0; }));
EXPECT_TRUE(std::all_of(resultBuf.begin() + length, resultBuf.end(),
[](GLint value) { return value == 0; }));
}
// Test with null length
glGetIntegervRobustANGLE(GL_MAX_VARYING_VECTORS, 1, nullptr, &resultRobust);
EXPECT_GL_NO_ERROR();
glGetIntegervRobustANGLE(GL_MAX_VIEWPORT_DIMS, 1, nullptr, &resultRobust);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
GLint maxViewportDims[2];
glGetIntegervRobustANGLE(GL_MAX_VIEWPORT_DIMS, 2, nullptr, maxViewportDims);
EXPECT_GL_NO_ERROR();
}
// Test basic usage and validation of glGetInteger64vRobustANGLE
TEST_P(RobustClientMemoryTest, GetInteger64)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_robust_client_memory"));
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
constexpr GLint64 kMinRequiredMaxElementIndex = 16777215;
// Verify that the regular and robust entry points return the same values
GLint64 resultRegular;
glGetInteger64v(GL_MAX_ELEMENT_INDEX, &resultRegular);
ASSERT_GL_NO_ERROR();
ASSERT_GE(resultRegular, kMinRequiredMaxElementIndex);
GLsizei length;
GLint64 resultRobust;
glGetInteger64vRobustANGLE(GL_MAX_ELEMENT_INDEX, 1, &length, &resultRobust);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(1, length);
EXPECT_EQ(resultRegular, resultRobust);
}
// Test basic usage and validation of glTexImage2DRobustANGLE and glTexSubImage2DRobustANGLE
TEST_P(RobustClientMemoryTest, TexImage2D)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_robust_client_memory"));
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
GLsizei dataDimension = 1024;
std::vector<GLubyte> rgbaData(dataDimension * dataDimension * 4);
// Test the regular case
glTexImage2DRobustANGLE(GL_TEXTURE_2D, 0, GL_RGBA, dataDimension, dataDimension, 0, GL_RGBA,
GL_UNSIGNED_BYTE, static_cast<GLsizei>(rgbaData.size()),
rgbaData.data());
EXPECT_GL_NO_ERROR();
glTexSubImage2DRobustANGLE(GL_TEXTURE_2D, 0, 0, 0, dataDimension, dataDimension, GL_RGBA,
GL_UNSIGNED_BYTE, static_cast<GLsizei>(rgbaData.size()),
rgbaData.data());
EXPECT_GL_NO_ERROR();
// Test with no data and zero size
glTexImage2DRobustANGLE(GL_TEXTURE_2D, 0, GL_RGBA, dataDimension, dataDimension, 0, GL_RGBA,
GL_UNSIGNED_BYTE, 0, nullptr);
EXPECT_GL_NO_ERROR();
// Test with data and negative size
glTexImage2DRobustANGLE(GL_TEXTURE_2D, 0, GL_RGBA, dataDimension, dataDimension, 0, GL_RGBA,
GL_UNSIGNED_BYTE, -1, rgbaData.data());
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Test with no data and negative size
glTexImage2DRobustANGLE(GL_TEXTURE_2D, 0, GL_RGBA, dataDimension, dataDimension, 0, GL_RGBA,
GL_UNSIGNED_BYTE, -1, nullptr);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Test with no data and positive size
glTexImage2DRobustANGLE(GL_TEXTURE_2D, 0, GL_RGBA, dataDimension, dataDimension, 0, GL_RGBA,
GL_UNSIGNED_BYTE, 1, nullptr);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
// Test with a data size that is too small
glTexImage2DRobustANGLE(GL_TEXTURE_2D, 0, GL_RGBA, dataDimension, dataDimension, 0, GL_RGBA,
GL_UNSIGNED_BYTE, static_cast<GLsizei>(rgbaData.size()) / 2,
rgbaData.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glTexSubImage2DRobustANGLE(GL_TEXTURE_2D, 0, 0, 0, dataDimension, dataDimension, GL_RGBA,
GL_UNSIGNED_BYTE, static_cast<GLsizei>(rgbaData.size()) / 2,
rgbaData.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
if (getClientMajorVersion() >= 3)
{
{
// Test bufSize when a PBO is bound
GLBuffer buffer;
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
glBufferData(GL_PIXEL_UNPACK_BUFFER, rgbaData.size(), nullptr, GL_STATIC_COPY);
// Zero bufSize must be accepted
glTexImage2DRobustANGLE(GL_TEXTURE_2D, 0, GL_RGBA, dataDimension, dataDimension, 0,
GL_RGBA, GL_UNSIGNED_BYTE, 0, reinterpret_cast<void *>(0));
EXPECT_GL_NO_ERROR();
// Positive bufSize must be rejected
glTexImage2DRobustANGLE(GL_TEXTURE_2D, 0, GL_RGBA, dataDimension, dataDimension, 0,
GL_RGBA, GL_UNSIGNED_BYTE, 1, reinterpret_cast<void *>(0));
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
// Negative bufSize is invalid
glTexImage2DRobustANGLE(GL_TEXTURE_2D, 0, GL_RGBA, dataDimension, dataDimension, 0,
GL_RGBA, GL_UNSIGNED_BYTE, -1, reinterpret_cast<void *>(0));
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
// Set an unpack parameter that would cause the driver to read past the end of the buffer
glPixelStorei(GL_UNPACK_ROW_LENGTH, dataDimension + 1);
glTexImage2DRobustANGLE(GL_TEXTURE_2D, 0, GL_RGBA, dataDimension, dataDimension, 0, GL_RGBA,
GL_UNSIGNED_BYTE, static_cast<GLsizei>(rgbaData.size()),
rgbaData.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
}
// Test basic usage and validation of glReadPixelsRobustANGLE
TEST_P(RobustClientMemoryTest, ReadPixels)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_robust_client_memory"));
// TODO(ynovikov): Looks like a driver bug on Intel HD 530 http://anglebug.com/42260689
ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsDesktopOpenGL());
GLsizei dataDimension = 16;
std::vector<GLubyte> rgbaData(dataDimension * dataDimension * 4);
// Test the regular case
GLsizei length = -1;
GLsizei columns = -1;
GLsizei rows = -1;
glReadPixelsRobustANGLE(0, 0, dataDimension, dataDimension, GL_RGBA, GL_UNSIGNED_BYTE,
static_cast<GLsizei>(rgbaData.size()), &length, &columns, &rows,
rgbaData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(static_cast<GLsizei>(rgbaData.size()), length);
EXPECT_EQ(dataDimension, columns);
EXPECT_EQ(dataDimension, rows);
// Test a case that would be partially clipped
length = -1;
columns = -1;
rows = -1;
glReadPixelsRobustANGLE(-1, kWindowSize - dataDimension + 3, dataDimension, dataDimension,
GL_RGBA, GL_UNSIGNED_BYTE, static_cast<GLsizei>(rgbaData.size()),
&length, &columns, &rows, rgbaData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(static_cast<GLsizei>(rgbaData.size()), length);
EXPECT_EQ(dataDimension - 1, columns);
EXPECT_EQ(dataDimension - 3, rows);
length = 123;
columns = 456;
rows = 789;
// Test with a data size that is too small, the output params must not be updated
glReadPixelsRobustANGLE(0, 0, dataDimension, dataDimension, GL_RGBA, GL_UNSIGNED_BYTE,
static_cast<GLsizei>(rgbaData.size()) - 1, &length, &columns, &rows,
rgbaData.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
EXPECT_EQ(length, 123);
EXPECT_EQ(columns, 456);
EXPECT_EQ(rows, 789);
// Test that all output parameters may be null
glReadPixelsRobustANGLE(0, 0, dataDimension, dataDimension, GL_RGBA, GL_UNSIGNED_BYTE,
static_cast<GLsizei>(rgbaData.size()), nullptr, nullptr, nullptr,
rgbaData.data());
EXPECT_GL_NO_ERROR();
if (getClientMajorVersion() >= 3)
{
{
// Test that non-negative bufSize is ignored when a PBO is bound
GLBuffer buffer;
glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer);
glBufferData(GL_PIXEL_PACK_BUFFER, rgbaData.size(), nullptr, GL_STATIC_COPY);
// Zero bufSize must be accepted
glReadPixelsRobustANGLE(0, 0, dataDimension, dataDimension, GL_RGBA, GL_UNSIGNED_BYTE,
0, nullptr, nullptr, nullptr, reinterpret_cast<void *>(0));
EXPECT_GL_NO_ERROR();
// Small bufSize must be accepted
glReadPixelsRobustANGLE(0, 0, dataDimension, dataDimension, GL_RGBA, GL_UNSIGNED_BYTE,
16, nullptr, nullptr, nullptr, reinterpret_cast<void *>(0));
EXPECT_GL_NO_ERROR();
// Test that negative bufSize is still invalid when a PBO is bound
glReadPixelsRobustANGLE(0, 0, dataDimension, dataDimension, GL_RGBA, GL_UNSIGNED_BYTE,
-1, nullptr, nullptr, nullptr, reinterpret_cast<void *>(0));
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
// Set a pack parameter that would cause the driver to write past the end of the buffer
glPixelStorei(GL_PACK_ROW_LENGTH, dataDimension + 1);
glReadPixelsRobustANGLE(0, 0, dataDimension, dataDimension, GL_RGBA, GL_UNSIGNED_BYTE,
static_cast<GLsizei>(rgbaData.size()), &length, nullptr, nullptr,
rgbaData.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
}
// Test the output length parameter
TEST_P(RobustClientMemoryTest, ReadPixelsLengthOutput)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_robust_client_memory"));
// Buffer twice as large as the window
std::vector<GLubyte> rgbaData(2 * kWindowSize * kWindowSize * 4);
// Test that output length is computed based on the input dimensions
{
// Input x, y, width, height, output length
const int testValues[7][5] = {
{0, 0, 0, 0, 0}, // Zero input area
{0, 0, 128, 0, 0}, // Zero input height
{0, 0, 0, 128, 0}, // Zero input width
{0, 0, 128, 128, 65536}, // Full size
{64, 0, 64, 128, 32768}, // Half width with positive offset
{0, -128, 128, 256, 131072}, // Full width, double height with negative offset
{128, 128, 128, 256, 131072}, // Origin OOB, zero intersection
};
for (auto &test : testValues)
{
GLsizei length = -1;
glReadPixelsRobustANGLE(test[0], test[1], test[2], test[3], GL_RGBA, GL_UNSIGNED_BYTE,
static_cast<GLsizei>(rgbaData.size()), &length, nullptr,
nullptr, rgbaData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(length, test[4]);
}
}
// Test that output length is zero when reading to PBO
if (getClientMajorVersion() >= 3)
{
GLsizei length = -1;
GLBuffer buffer;
glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer);
glBufferData(GL_PIXEL_PACK_BUFFER, rgbaData.size(), nullptr, GL_STATIC_COPY);
glReadPixelsRobustANGLE(0, 0, 128, 128, GL_RGBA, GL_UNSIGNED_BYTE,
static_cast<GLsizei>(rgbaData.size()), &length, nullptr, nullptr,
reinterpret_cast<void *>(0));
EXPECT_GL_NO_ERROR();
EXPECT_EQ(length, 0);
// Unbind and repeat
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glReadPixelsRobustANGLE(0, 0, 128, 128, GL_RGBA, GL_UNSIGNED_BYTE,
static_cast<GLsizei>(rgbaData.size()), &length, nullptr, nullptr,
rgbaData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(length, 128 * 128 * 4);
}
}
// Test edge cases of the returned image dimensions
TEST_P(RobustClientMemoryTest, ReadPixelsClipping)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_robust_client_memory"));
// Buffer twice as large as the window
std::vector<GLubyte> rgbaData(2 * kWindowSize * kWindowSize * 4);
// Origin, input dimension, output dimension
const int testValues[16][3] = {
{-15, 0, 0}, // Negative origin with zero length
{-15, 16, 1}, // Negative origin with overlapping region
{-16, 16, 0}, // Negative origin with adjacent region
{-17, 16, 0}, // Negative origin with non-overlapping region
{-16, 144, 128}, // Negative origin with exact length
{-16, 160, 128}, // Negative origin with large length
{0, 0, 0}, // Zero origin with zero length
{0, 128, 128}, // Zero origin with exact length
{0, 16, 16}, // Zero origin with small length
{0, 144, 128}, // Zero origin with large length
{16, 0, 0}, // Positive origin with zero length
{16, 112, 112}, // Positive origin with exact length
{16, 32, 32}, // Positive origin with small length
{16, 128, 112}, // Positive origin with large width
{144, 0, 0}, // Out of range origin with zero length
{144, 16, 0}, // Out of range origin with non-zero length
};
for (auto &test : testValues)
{
// Test columns and rows separately
{
GLsizei columns = -1;
glReadPixelsRobustANGLE(test[0], 0, test[1], 128, GL_RGBA, GL_UNSIGNED_BYTE,
static_cast<GLsizei>(rgbaData.size()), nullptr, &columns,
nullptr, rgbaData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(columns, test[2]);
GLsizei rows = -1;
glReadPixelsRobustANGLE(0, test[0], 128, test[1], GL_RGBA, GL_UNSIGNED_BYTE,
static_cast<GLsizei>(rgbaData.size()), nullptr, nullptr, &rows,
rgbaData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(rows, test[2]);
}
// Test that if one output is zero, then the other output is also zero
if (test[2] == 0)
{
GLsizei columns = -1;
GLsizei rows = -1;
glReadPixelsRobustANGLE(test[0], 0, test[1], 128, GL_RGBA, GL_UNSIGNED_BYTE,
static_cast<GLsizei>(rgbaData.size()), nullptr, &columns, &rows,
rgbaData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(columns, 0);
EXPECT_EQ(rows, 0);
columns = -1;
rows = -1;
glReadPixelsRobustANGLE(0, test[0], 128, test[1], GL_RGBA, GL_UNSIGNED_BYTE,
static_cast<GLsizei>(rgbaData.size()), nullptr, &columns, &rows,
rgbaData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(columns, 0);
EXPECT_EQ(rows, 0);
}
}
}
class RobustClientMemoryNoExtensionsTest : public RobustClientMemoryTest
{
protected:
RobustClientMemoryNoExtensionsTest() : RobustClientMemoryTest() { setExtensionsEnabled(false); }
};
// Test with empty result
TEST_P(RobustClientMemoryNoExtensionsTest, GetEmpty)
{
GLint numFormats = 10;
glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats);
ASSERT_GL_NO_ERROR();
ASSERT_EQ(numFormats, 0); // Must be zero on unextended OpenGL ES 2.0
GLsizei length = 1;
std::vector<GLint> resultBuf(2, 3);
// Test non-robust with empty response
{
glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, resultBuf.data());
EXPECT_GL_NO_ERROR();
// Must not touch the buffer
EXPECT_TRUE(std::all_of(resultBuf.begin(), resultBuf.end(),
[](GLint value) { return value == 3; }));
}
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_robust_client_memory"));
// Test robust with empty response
{
glGetIntegervRobustANGLE(GL_COMPRESSED_TEXTURE_FORMATS, resultBuf.size(), &length,
resultBuf.data());
EXPECT_GL_NO_ERROR();
// Must update length
EXPECT_EQ(length, 0);
// Must not touch the buffer
EXPECT_TRUE(std::all_of(resultBuf.begin(), resultBuf.end(),
[](GLint value) { return value == 3; }));
}
// Test robust with empty response and null length
{
glGetIntegervRobustANGLE(GL_COMPRESSED_TEXTURE_FORMATS, resultBuf.size(), nullptr,
resultBuf.data());
EXPECT_GL_NO_ERROR();
// Must not touch the buffer
EXPECT_TRUE(std::all_of(resultBuf.begin(), resultBuf.end(),
[](GLint value) { return value == 3; }));
}
// Test robust with empty response, zero buffer size, and null length
{
glGetIntegervRobustANGLE(GL_COMPRESSED_TEXTURE_FORMATS, 0, nullptr, resultBuf.data());
EXPECT_GL_NO_ERROR();
// Must not touch the buffer
EXPECT_TRUE(std::all_of(resultBuf.begin(), resultBuf.end(),
[](GLint value) { return value == 3; }));
}
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(RobustClientMemoryTest);
ANGLE_INSTANTIATE_TEST_ES2(RobustClientMemoryNoExtensionsTest);
} // namespace angle