Commit e03498f2 by Frank Henigman Committed by Commit Bot

Increase instanced draw testing.

Fill some gaps in the ANGLE end2end instanced draw test. The only testing of points was on D3D11 and D3D11_FL9_3. There was little testing of number of instances and different divisors. Remove old one-off tests which are now redundant. Disable the D3D11_FL9_3 points test which was passing purely by chance. That code has bugs and only supports data from a buffer, while the test uses data in client memory. BUG=angleproject:2672 Change-Id: I5b5707a6fdd0960e81c25a2eb8ed96a1452d9868 Reviewed-on: https://chromium-review.googlesource.com/c/1450789 Commit-Queue: Frank Henigman <fjhenigman@chromium.org> Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent cd4f1fba
...@@ -9,6 +9,25 @@ ...@@ -9,6 +9,25 @@
using namespace angle; using namespace angle;
namespace
{
enum Geometry
{
Quad,
Point
};
enum Storage
{
Buffer,
Memory
};
enum Draw
{
Indexed,
NonIndexed
};
} // namespace
class InstancingTest : public ANGLETest class InstancingTest : public ANGLETest
{ {
protected: protected:
...@@ -25,15 +44,15 @@ class InstancingTest : public ANGLETest ...@@ -25,15 +44,15 @@ class InstancingTest : public ANGLETest
void TearDown() override void TearDown() override
{ {
glDeleteBuffers(1, &mInstanceBuffer); glDeleteBuffers(1, &mInstanceBuffer);
glDeleteProgram(mProgram0); glDeleteProgram(mProgram[0]);
glDeleteProgram(mProgram1); glDeleteProgram(mProgram[1]);
} }
void SetUp() override void SetUp() override
{ {
ANGLETest::SetUp(); ANGLETest::SetUp();
for (int i = 0; i < kMaxDrawn; ++i) for (unsigned i = 0; i < kMaxDrawn; ++i)
{ {
mInstanceData[i] = i * kDrawSize; mInstanceData[i] = i * kDrawSize;
} }
...@@ -54,70 +73,86 @@ class InstancingTest : public ANGLETest ...@@ -54,70 +73,86 @@ class InstancingTest : public ANGLETest
// attrib 0 is instanced // attrib 0 is instanced
const std::string inst0 = inst + pos + main; const std::string inst0 = inst + pos + main;
mProgram0 = CompileProgram(inst0.c_str(), essl1_shaders::fs::Red()); mProgram[0] = CompileProgram(inst0.c_str(), essl1_shaders::fs::Red());
ASSERT_NE(0u, mProgram0); ASSERT_NE(0u, mProgram[0]);
ASSERT_EQ(0, glGetAttribLocation(mProgram0, "a_instance")); ASSERT_EQ(0, glGetAttribLocation(mProgram[0], "a_instance"));
ASSERT_EQ(1, glGetAttribLocation(mProgram0, "a_position")); ASSERT_EQ(1, glGetAttribLocation(mProgram[0], "a_position"));
// attrib 1 is instanced // attrib 1 is instanced
const std::string inst1 = pos + inst + main; const std::string inst1 = pos + inst + main;
mProgram1 = CompileProgram(inst1.c_str(), essl1_shaders::fs::Red()); mProgram[1] = CompileProgram(inst1.c_str(), essl1_shaders::fs::Red());
ASSERT_NE(0u, mProgram1); ASSERT_NE(0u, mProgram[1]);
ASSERT_EQ(1, glGetAttribLocation(mProgram1, "a_instance")); ASSERT_EQ(1, glGetAttribLocation(mProgram[1], "a_instance"));
ASSERT_EQ(0, glGetAttribLocation(mProgram1, "a_position")); ASSERT_EQ(0, glGetAttribLocation(mProgram[1], "a_position"));
glClearColor(0.0f, 0.0f, 1.0f, 1.0f); glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
} }
void runTest( void runTest(unsigned numInstance,
unsigned numInstance,
unsigned divisor, unsigned divisor,
bool attribZeroInstanced, // true: attrib 0 is instance, false: attrib 1 is instanced const int instanceAttrib, // which attrib is instanced: 0 or 1
bool points, // true: draw points, false: draw quad Geometry geometry,
bool indexed, // true: DrawElements, false: DrawArrays Draw draw,
bool offset, // true: pass nonzero offset to DrawArrays, false: zero offset Storage storage,
bool buffer) // true: use instance data in buffer, false: in client memory unsigned offset) // for NonIndexed/DrawArrays only
{ {
ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_ANGLE_instanced_arrays"));
// TODO: Fix these. http://anglebug.com/3129
ANGLE_SKIP_TEST_IF(IsD3D9() && draw == Indexed && geometry == Point);
ANGLE_SKIP_TEST_IF(IsD3D9() && IsAMD() && geometry == Point);
// D3D11 FL9_3 has a special codepath that emulates instanced points rendering
// but it has bugs and was only implemented for vertex positions in a buffer object,
// not client memory as used in this test.
ANGLE_SKIP_TEST_IF(IsD3D11_FL93() && geometry == Point);
// Unknown problem. FL9_3 is not officially supported anyway.
ANGLE_SKIP_TEST_IF(IsD3D11_FL93() && geometry == Quad && draw == NonIndexed);
// The window is divided into kMaxDrawn slices of size kDrawSize. // The window is divided into kMaxDrawn slices of size kDrawSize.
// The slice drawn into is determined by the instance datum. // The slice drawn into is determined by the instance datum.
// The instance data array selects all the slices in order. // The instance data array selects all the slices in order.
// 'lastDrawn' is the index (zero-based) of the last slice into which we draw. // 'lastDrawn' is the index (zero-based) of the last slice into which we draw.
const unsigned lastDrawn = (numInstance - 1) / divisor; const unsigned lastDrawn = (numInstance - 1) / divisor;
const int instanceAttrib = attribZeroInstanced ? 0 : 1; // Ensure the numInstance and divisor parameters are valid.
const int positionAttrib = attribZeroInstanced ? 1 : 0; ASSERT_TRUE(lastDrawn < kMaxDrawn);
ASSERT_TRUE(instanceAttrib == 0 || instanceAttrib == 1);
const int positionAttrib = 1 - instanceAttrib;
glUseProgram(attribZeroInstanced ? mProgram0 : mProgram1); glUseProgram(mProgram[instanceAttrib]);
glBindBuffer(GL_ARRAY_BUFFER, buffer ? mInstanceBuffer : 0); glBindBuffer(GL_ARRAY_BUFFER, storage == Buffer ? mInstanceBuffer : 0);
glVertexAttribPointer(instanceAttrib, 1, GL_FLOAT, GL_FALSE, 0, glVertexAttribPointer(instanceAttrib, 1, GL_FLOAT, GL_FALSE, 0,
buffer ? nullptr : mInstanceData); storage == Buffer ? nullptr : mInstanceData);
glEnableVertexAttribArray(instanceAttrib); glEnableVertexAttribArray(instanceAttrib);
glVertexAttribDivisorANGLE(instanceAttrib, divisor); glVertexAttribDivisorANGLE(instanceAttrib, divisor);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 0, glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 0,
points ? kPointVertices : kQuadVertices); geometry == Point ? kPointVertices : kQuadVertices);
glEnableVertexAttribArray(positionAttrib); glEnableVertexAttribArray(positionAttrib);
glVertexAttribDivisorANGLE(positionAttrib, 0); glVertexAttribDivisorANGLE(positionAttrib, 0);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
if (points) if (geometry == Point)
{ {
if (indexed) if (draw == Indexed)
glDrawElementsInstancedANGLE(GL_POINTS, ArraySize(kPointIndices), GL_UNSIGNED_SHORT, glDrawElementsInstancedANGLE(GL_POINTS, ArraySize(kPointIndices), GL_UNSIGNED_SHORT,
kPointIndices, numInstance); kPointIndices, numInstance);
else else
glDrawArraysInstancedANGLE(GL_POINTS, offset ? 2 : 0, 4, numInstance); glDrawArraysInstancedANGLE(GL_POINTS, offset, 4 /*vertices*/, numInstance);
} }
else else
{ {
if (indexed) if (draw == Indexed)
glDrawElementsInstancedANGLE(GL_TRIANGLES, ArraySize(kQuadIndices), glDrawElementsInstancedANGLE(GL_TRIANGLES, ArraySize(kQuadIndices),
GL_UNSIGNED_SHORT, kQuadIndices, numInstance); GL_UNSIGNED_SHORT, kQuadIndices, numInstance);
else else
glDrawArraysInstancedANGLE(GL_TRIANGLES, offset ? 4 : 0, 6, numInstance); glDrawArraysInstancedANGLE(GL_TRIANGLES, offset, 6 /*vertices*/, numInstance);
} }
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -134,16 +169,16 @@ class InstancingTest : public ANGLETest ...@@ -134,16 +169,16 @@ class InstancingTest : public ANGLETest
for (unsigned j = 0; j < 8; j += 2) for (unsigned j = 0; j < 8; j += 2)
{ {
int ix = static_cast<int>((kPointVertices[j] + 1.0f) / 2.0f * getWindowWidth()); int ix = static_cast<int>((kPointVertices[j] + 1.0f) / 2.0f * getWindowWidth());
EXPECT_PIXEL_COLOR_EQ(ix, iy, i <= lastDrawn ? GLColor::red : GLColor::blue); EXPECT_PIXEL_COLOR_EQ(ix, iy, i <= lastDrawn ? GLColor::red : GLColor::blue)
<< std::endl;
} }
} }
} }
GLuint mProgram0; GLuint mProgram[2];
GLuint mProgram1;
GLuint mInstanceBuffer; GLuint mInstanceBuffer;
static constexpr int kMaxDrawn = 4; static constexpr unsigned kMaxDrawn = 16;
static constexpr float kDrawSize = 2.0 / kMaxDrawn; static constexpr float kDrawSize = 2.0 / kMaxDrawn;
GLfloat mInstanceData[kMaxDrawn]; GLfloat mInstanceData[kMaxDrawn];
...@@ -183,90 +218,119 @@ class InstancingTest : public ANGLETest ...@@ -183,90 +218,119 @@ class InstancingTest : public ANGLETest
static constexpr GLushort kPointIndices[] = {1, 5, 3, 2}; static constexpr GLushort kPointIndices[] = {1, 5, 3, 2};
}; };
constexpr unsigned InstancingTest::kMaxDrawn;
constexpr float InstancingTest::kDrawSize;
constexpr GLfloat InstancingTest::kQuadVertices[]; constexpr GLfloat InstancingTest::kQuadVertices[];
constexpr GLfloat InstancingTest::kPointVertices[]; constexpr GLfloat InstancingTest::kPointVertices[];
constexpr GLushort InstancingTest::kQuadIndices[]; constexpr GLushort InstancingTest::kQuadIndices[];
constexpr GLushort InstancingTest::kPointIndices[]; constexpr GLushort InstancingTest::kPointIndices[];
class InstancingTestAllConfigs : public InstancingTest #define TEST_INDEXED(attrib, geometry, storage) \
{ TEST_P(InstancingTest, IndexedAttrib##attrib##geometry##storage) \
protected: { \
InstancingTestAllConfigs() {} runTest(11, 2, attrib, geometry, Indexed, storage, 0); \
}; }
class InstancingTestNo9_3 : public InstancingTest
{
protected:
InstancingTestNo9_3() {}
};
class InstancingTestPoints : public InstancingTest
{
protected:
InstancingTestPoints() {}
};
// This test uses a vertex shader with the first attribute (attribute zero) instanced.
// On D3D9 and D3D11 FL9_3, this triggers a special codepath that rearranges the input layout sent
// to D3D, to ensure that slot/stream zero of the input layout doesn't contain per-instance data.
TEST_P(InstancingTestAllConfigs, AttributeZeroInstanced)
{
ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_ANGLE_instanced_arrays"));
runTest(4, 1, true /* attrib 0 instanced */, false /* quads */, true /* DrawElements */,
false /* N/A */, false /* no buffer */);
}
// Same as AttributeZeroInstanced, but attribute zero is not instanced.
// This ensures the general instancing codepath (i.e. without rearranging the input layout) works as
// expected.
TEST_P(InstancingTestAllConfigs, AttributeZeroNotInstanced)
{
ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_ANGLE_instanced_arrays"));
runTest(4, 1, false /* attrib 1 instanced */, false /* quads */, true /* DrawElements */,
false /* N/A */, false /* no buffer */);
}
// Tests that the "first" parameter to glDrawArraysInstancedANGLE is only an offset into
// the non-instanced vertex attributes.
TEST_P(InstancingTestNo9_3, DrawArraysWithOffset)
{
ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_ANGLE_instanced_arrays"));
runTest(4, 1, false /* attribute 1 instanced */, false /* quads */, false /* DrawArrays */,
true /* offset>0 */, true /* buffer */);
}
// This test verifies instancing with GL_POINTS with glDrawArraysInstanced works.
// On D3D11 FL9_3, this triggers a special codepath that emulates instanced points rendering.
TEST_P(InstancingTestPoints, DrawArrays)
{
ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_ANGLE_instanced_arrays"));
// Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details
// On Win7, the D3D SDK Layers emits a false warning for these tests.
// This doesn't occur on Windows 10 (Version 1511) though.
ignoreD3D11SDKLayersWarnings();
runTest(4, 1, false /* attrib 1 instanced */, true /* points */, false /* DrawArrays */,
false /* offset=0 */, true /* buffer */);
}
// This test verifies instancing with GL_POINTS with glDrawElementsInstanced works. #define TEST_NONINDEXED(attrib, geometry, storage, offset) \
// On D3D11 FL9_3, this triggers a special codepath that emulates instanced points rendering. TEST_P(InstancingTest, NonIndexedAttrib##attrib##geometry##storage##Offset##offset) \
TEST_P(InstancingTestPoints, DrawElements) { \
{ runTest(11, 2, attrib, geometry, NonIndexed, storage, offset); \
ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_ANGLE_instanced_arrays")); }
// Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details #define TEST_DIVISOR(numInstance, divisor) \
// On Win7, the D3D SDK Layers emits a false warning for these tests. TEST_P(InstancingTest, Instances##numInstance##Divisor##divisor) \
// This doesn't occur on Windows 10 (Version 1511) though. { \
ignoreD3D11SDKLayersWarnings(); runTest(numInstance, divisor, 1, Quad, NonIndexed, Buffer, 0); \
}
runTest(4, 1, false /* attrib 1 instanced */, true /* points */, true /* DrawElements */, // D3D9 and D3D11 FL9_3, have a special codepath that rearranges the input layout sent to D3D,
false /* N/A */, true /* buffer */); // to ensure that slot/stream zero of the input layout doesn't contain per-instance data, so
} // we test with attribute 0 being instanced, as will as attribute 1 being instanced.
//
// Tests with a non-zero 'offset' check that "first" parameter to glDrawArraysInstancedANGLE is only
// an offset into the non-instanced vertex attributes.
TEST_INDEXED(0, Quad, Buffer)
TEST_INDEXED(0, Quad, Memory)
TEST_INDEXED(1, Quad, Buffer)
TEST_INDEXED(1, Quad, Memory)
TEST_INDEXED(0, Point, Buffer)
TEST_INDEXED(0, Point, Memory)
TEST_INDEXED(1, Point, Buffer)
TEST_INDEXED(1, Point, Memory)
// offset should be 0 or 4 for quads
TEST_NONINDEXED(0, Quad, Buffer, 0)
TEST_NONINDEXED(0, Quad, Buffer, 4)
TEST_NONINDEXED(0, Quad, Memory, 0)
TEST_NONINDEXED(0, Quad, Memory, 4)
TEST_NONINDEXED(1, Quad, Buffer, 0)
TEST_NONINDEXED(1, Quad, Buffer, 4)
TEST_NONINDEXED(1, Quad, Memory, 0)
TEST_NONINDEXED(1, Quad, Memory, 4)
// offset should be 0 or 2 for points
TEST_NONINDEXED(0, Point, Buffer, 0)
TEST_NONINDEXED(0, Point, Buffer, 2)
TEST_NONINDEXED(0, Point, Memory, 0)
TEST_NONINDEXED(0, Point, Memory, 2)
TEST_NONINDEXED(1, Point, Buffer, 0)
TEST_NONINDEXED(1, Point, Buffer, 2)
TEST_NONINDEXED(1, Point, Memory, 0)
TEST_NONINDEXED(1, Point, Memory, 2)
// The following tests produce each value of 'lastDrawn' in runTest() from 1 to kMaxDrawn, a few
// different ways.
TEST_DIVISOR(1, 1)
TEST_DIVISOR(1, 2)
TEST_DIVISOR(2, 1)
TEST_DIVISOR(3, 1)
TEST_DIVISOR(3, 2)
TEST_DIVISOR(4, 1)
TEST_DIVISOR(5, 1)
TEST_DIVISOR(5, 2)
TEST_DIVISOR(6, 1)
TEST_DIVISOR(6, 2)
TEST_DIVISOR(7, 1)
TEST_DIVISOR(7, 2)
TEST_DIVISOR(8, 1)
TEST_DIVISOR(8, 2)
TEST_DIVISOR(8, 4)
TEST_DIVISOR(9, 1)
TEST_DIVISOR(9, 2)
TEST_DIVISOR(10, 1)
TEST_DIVISOR(11, 1)
TEST_DIVISOR(11, 2)
TEST_DIVISOR(12, 1)
TEST_DIVISOR(12, 11)
TEST_DIVISOR(13, 1)
TEST_DIVISOR(13, 2)
TEST_DIVISOR(14, 1)
TEST_DIVISOR(15, 1)
TEST_DIVISOR(15, 2)
TEST_DIVISOR(16, 1)
TEST_DIVISOR(16, 3)
TEST_DIVISOR(16, 7)
TEST_DIVISOR(17, 2)
TEST_DIVISOR(20, 2)
TEST_DIVISOR(21, 2)
TEST_DIVISOR(23, 2)
TEST_DIVISOR(25, 5)
TEST_DIVISOR(25, 33)
TEST_DIVISOR(26, 2)
TEST_DIVISOR(26, 3)
TEST_DIVISOR(27, 2)
TEST_DIVISOR(27, 4)
TEST_DIVISOR(28, 3)
TEST_DIVISOR(29, 2)
TEST_DIVISOR(29, 11)
TEST_DIVISOR(30, 4)
TEST_DIVISOR(31, 6)
TEST_DIVISOR(32, 2)
TEST_DIVISOR(32, 3)
TEST_DIVISOR(32, 8)
TEST_DIVISOR(34, 3)
TEST_DIVISOR(34, 30)
class InstancingTestES3 : public InstancingTest class InstancingTestES3 : public InstancingTest
{ {
...@@ -285,11 +349,11 @@ TEST_P(InstancingTestES31, UpdateAttribBindingByVertexAttribDivisor) ...@@ -285,11 +349,11 @@ TEST_P(InstancingTestES31, UpdateAttribBindingByVertexAttribDivisor)
{ {
ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_ANGLE_instanced_arrays")); ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_ANGLE_instanced_arrays"));
glUseProgram(mProgram0); glUseProgram(mProgram[0]);
// Get the attribute locations // Get the attribute locations
GLint positionLoc = glGetAttribLocation(mProgram0, "a_position"); GLint positionLoc = glGetAttribLocation(mProgram[0], "a_position");
GLint instancePosLoc = glGetAttribLocation(mProgram0, "a_instance"); GLint instancePosLoc = glGetAttribLocation(mProgram[0], "a_instance");
ASSERT_NE(-1, positionLoc); ASSERT_NE(-1, positionLoc);
ASSERT_NE(-1, instancePosLoc); ASSERT_NE(-1, instancePosLoc);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -306,6 +370,9 @@ TEST_P(InstancingTestES31, UpdateAttribBindingByVertexAttribDivisor) ...@@ -306,6 +370,9 @@ TEST_P(InstancingTestES31, UpdateAttribBindingByVertexAttribDivisor)
const unsigned divisor = 1; const unsigned divisor = 1;
const unsigned lastDrawn = (numInstance - 1) / divisor; const unsigned lastDrawn = (numInstance - 1) / divisor;
// Ensure the numInstance and divisor parameters are valid.
ASSERT_TRUE(lastDrawn < kMaxDrawn);
// Set the formats by VertexAttribFormat // Set the formats by VertexAttribFormat
glVertexAttribFormat(positionLoc, 2, GL_FLOAT, GL_FALSE, 0); glVertexAttribFormat(positionLoc, 2, GL_FLOAT, GL_FALSE, 0);
glVertexAttribFormat(instancePosLoc, 1, GL_FLOAT, GL_FALSE, 0); glVertexAttribFormat(instancePosLoc, 1, GL_FLOAT, GL_FALSE, 0);
...@@ -469,22 +536,14 @@ TEST_P(InstancingTestES3, LargestDivisor) ...@@ -469,22 +536,14 @@ TEST_P(InstancingTestES3, LargestDivisor)
<< "Vertex attrib divisor read was not the same that was passed in."; << "Vertex attrib divisor read was not the same that was passed in.";
} }
// Use this to select which configurations (e.g. which renderer, which GLES major version) these ANGLE_INSTANTIATE_TEST(InstancingTestES3, ES3_OPENGL(), ES3_OPENGLES(), ES3_D3D11());
// tests should be run against. We test on D3D9 and D3D11 9_3 because they use special codepaths
// when attribute zero is instanced, unlike D3D11. ANGLE_INSTANTIATE_TEST(InstancingTestES31, ES31_OPENGL(), ES31_OPENGLES(), ES31_D3D11());
ANGLE_INSTANTIATE_TEST(InstancingTestAllConfigs,
ANGLE_INSTANTIATE_TEST(InstancingTest,
ES2_D3D9(), ES2_D3D9(),
ES2_D3D11(), ES2_D3D11(),
ES2_D3D11_FL9_3(), ES2_D3D11_FL9_3(),
ES2_OPENGL(), ES2_OPENGL(),
ES2_OPENGLES(), ES2_OPENGLES(),
ES2_VULKAN()); ES2_VULKAN());
// TODO(jmadill): Figure out the situation with DrawInstanced on FL 9_3
ANGLE_INSTANTIATE_TEST(InstancingTestNo9_3, ES2_D3D9(), ES2_D3D11());
ANGLE_INSTANTIATE_TEST(InstancingTestPoints, ES2_D3D11(), ES2_D3D11_FL9_3());
ANGLE_INSTANTIATE_TEST(InstancingTestES3, ES3_OPENGL(), ES3_OPENGLES(), ES3_D3D11());
ANGLE_INSTANTIATE_TEST(InstancingTestES31, ES31_OPENGL(), ES31_OPENGLES(), ES31_D3D11());
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