Vulkan: DrawElements with line loops client side memory support
- Also enables 6 new tests in LineLoopTests.cpp in angle_end2end
Bug: angleproject:2458
Change-Id: I4aec12b0ac780e81e6811f1199a5acaf17d9b982
Reviewed-on: https://chromium-review.googlesource.com/1010411
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Luc Ferron <lucferron@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
index d13e6aa..5b34f65 100644
--- a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
@@ -405,22 +405,24 @@
}
// Handle GL_LINE_LOOP drawElements.
- gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer().get();
- if (!elementArrayBuffer)
- {
- UNIMPLEMENTED();
- return gl::InternalError() << "Line loop indices in client memory not supported";
- }
-
- BufferVk *elementArrayBufferVk = vk::GetImpl(elementArrayBuffer);
-
- VkIndexType indexType = gl_vk::GetIndexType(drawCallParams.type());
-
if (mDirtyLineLoopTranslation)
{
- ANGLE_TRY(mLineLoopHelper.getIndexBufferForElementArrayBuffer(
- renderer, elementArrayBufferVk, indexType, drawCallParams.indexCount(),
- &mCurrentElementArrayBufferHandle, &mCurrentElementArrayBufferOffset));
+ gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer().get();
+ VkIndexType indexType = gl_vk::GetIndexType(drawCallParams.type());
+
+ if (!elementArrayBuffer)
+ {
+ ANGLE_TRY(mLineLoopHelper.getIndexBufferForClientElementArray(
+ renderer, drawCallParams.indices(), indexType, drawCallParams.indexCount(),
+ &mCurrentElementArrayBufferHandle, &mCurrentElementArrayBufferOffset));
+ }
+ else
+ {
+ BufferVk *elementArrayBufferVk = vk::GetImpl(elementArrayBuffer);
+ ANGLE_TRY(mLineLoopHelper.getIndexBufferForElementArrayBuffer(
+ renderer, elementArrayBufferVk, indexType, drawCallParams.indexCount(),
+ &mCurrentElementArrayBufferHandle, &mCurrentElementArrayBufferOffset));
+ }
}
ANGLE_TRY(onIndexedDraw(context, renderer, drawCallParams, drawNode, newCommandBuffer));
@@ -485,7 +487,7 @@
{
ANGLE_TRY(onDraw(context, renderer, drawCallParams, drawNode, newCommandBuffer));
- if (!mState.getElementArrayBuffer().get())
+ if (!mState.getElementArrayBuffer().get() && drawCallParams.mode() != GL_LINE_LOOP)
{
ANGLE_TRY(drawCallParams.ensureIndexRangeResolved(context));
ANGLE_TRY(streamIndexData(renderer, drawCallParams));
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
index f861153..9306f9e 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
@@ -377,6 +377,34 @@
return gl::NoError();
}
+gl::Error LineLoopHelper::getIndexBufferForClientElementArray(RendererVk *renderer,
+ const void *indicesInput,
+ VkIndexType indexType,
+ int indexCount,
+ VkBuffer *bufferHandleOut,
+ VkDeviceSize *bufferOffsetOut)
+{
+ // TODO(lucferron): we'll eventually need to support uint8, emulated on 16 since Vulkan only
+ // supports 16 / 32.
+ ASSERT(indexType == VK_INDEX_TYPE_UINT16 || indexType == VK_INDEX_TYPE_UINT32);
+
+ uint8_t *indices = nullptr;
+ uint32_t offset = 0;
+
+ auto unitSize = (indexType == VK_INDEX_TYPE_UINT16 ? sizeof(uint16_t) : sizeof(uint32_t));
+ size_t allocateBytes = unitSize * (indexCount + 1);
+ ANGLE_TRY(mDynamicIndexBuffer.allocate(renderer, allocateBytes,
+ reinterpret_cast<uint8_t **>(&indices), bufferHandleOut,
+ &offset, nullptr));
+ *bufferOffsetOut = static_cast<VkDeviceSize>(offset);
+
+ memcpy(indices, indicesInput, unitSize * indexCount);
+ memcpy(indices + unitSize * indexCount, indicesInput, unitSize);
+
+ ANGLE_TRY(mDynamicIndexBuffer.flush(renderer->getDevice()));
+ return gl::NoError();
+}
+
void LineLoopHelper::destroy(VkDevice device)
{
mDynamicIndexBuffer.destroy(device);
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.h b/src/libANGLE/renderer/vulkan/vk_helpers.h
index 237c85c..c9ca56b 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.h
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.h
@@ -128,6 +128,13 @@
int indexCount,
VkBuffer *bufferHandleOut,
VkDeviceSize *bufferOffsetOut);
+ gl::Error getIndexBufferForClientElementArray(RendererVk *renderer,
+ const void *indicesInput,
+ VkIndexType indexType,
+ int indexCount,
+ VkBuffer *bufferHandleOut,
+ VkDeviceSize *bufferOffsetOut);
+
void destroy(VkDevice device);
static void Draw(int count, CommandBuffer *commandBuffer);
diff --git a/src/tests/gl_tests/LineLoopTest.cpp b/src/tests/gl_tests/LineLoopTest.cpp
index 4566a37..3f0f999 100644
--- a/src/tests/gl_tests/LineLoopTest.cpp
+++ b/src/tests/gl_tests/LineLoopTest.cpp
@@ -112,6 +112,9 @@
TEST_P(LineLoopTest, LineLoopUByteIndices)
{
+ // TODO(fjhenigman): UByte not yet supported in VertexArrayVk
+ ANGLE_SKIP_TEST_IF(IsVulkan());
+
// 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.
@@ -146,6 +149,9 @@
TEST_P(LineLoopTest, LineLoopUByteIndexBuffer)
{
+ // TODO(fjhenigman): UByte not yet supported in VertexArrayVk
+ ANGLE_SKIP_TEST_IF(IsVulkan());
+
// Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details
ignoreD3D11SDKLayersWarnings();
@@ -163,6 +169,11 @@
TEST_P(LineLoopTest, LineLoopUShortIndexBuffer)
{
+ // TODO(lucferron): Looks like we have a bug supporting ushort as index buffers
+ // on line loops drawing.
+ // http://anglebug.com/2473
+ ANGLE_SKIP_TEST_IF(IsVulkan());
+
// Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details
ignoreD3D11SDKLayersWarnings();
@@ -202,4 +213,9 @@
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
-ANGLE_INSTANTIATE_TEST(LineLoopTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES());
+ANGLE_INSTANTIATE_TEST(LineLoopTest,
+ ES2_D3D9(),
+ ES2_D3D11(),
+ ES2_OPENGL(),
+ ES2_OPENGLES(),
+ ES2_VULKAN());
diff --git a/src/tests/gl_tests/SimpleOperationTest.cpp b/src/tests/gl_tests/SimpleOperationTest.cpp
index f51c439..6d5cfc3 100644
--- a/src/tests/gl_tests/SimpleOperationTest.cpp
+++ b/src/tests/gl_tests/SimpleOperationTest.cpp
@@ -747,6 +747,52 @@
EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::yellow);
}
+// Draw a line loop using a drawElement call and client side memory.
+TEST_P(SimpleOperationTest, DrawElementsLineLoopUsingClientSideMemory)
+{
+ ANGLE_GL_PROGRAM(program, kBasicVertexShader, kGreenFragmentShader);
+ glUseProgram(program);
+
+ // We expect to draw a square with these 4 vertices with a drawArray call.
+ std::vector<Vector3> vertices;
+ CreatePixelCenterWindowCoords({{32, 96}, {32, 32}, {96, 32}, {96, 96}}, getWindowWidth(),
+ getWindowHeight(), &vertices);
+
+ // If we use these indices to draw however, we should be drawing an hourglass.
+ std::vector<GLushort> indices{3, 2, 1, 0};
+
+ GLint positionLocation = glGetAttribLocation(program, "position");
+ ASSERT_NE(-1, positionLocation);
+
+ GLBuffer vertexBuffer;
+ glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
+ GL_STATIC_DRAW);
+
+ glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
+ glEnableVertexAttribArray(positionLocation);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDrawElements(GL_LINE_LOOP, 4, GL_UNSIGNED_SHORT, indices.data());
+ glDisableVertexAttribArray(positionLocation);
+
+ ASSERT_GL_NO_ERROR();
+
+ int quarterWidth = getWindowWidth() / 4;
+ int quarterHeight = getWindowHeight() / 4;
+
+ // Bottom left
+ EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight, GLColor::green);
+
+ // Top left
+ EXPECT_PIXEL_COLOR_EQ(quarterWidth, (quarterHeight * 3), GLColor::green);
+
+ // Top right
+ EXPECT_PIXEL_COLOR_EQ((quarterWidth * 3), (quarterHeight * 3) - 1, GLColor::green);
+
+ // Verify line is closed between the 2 last vertices
+ EXPECT_PIXEL_COLOR_EQ((quarterWidth * 2), quarterHeight, GLColor::green);
+}
+
// Tests rendering to a user framebuffer.
TEST_P(SimpleOperationTest, RenderToTexture)
{
diff --git a/src/tests/gl_tests/StateChangeTest.cpp b/src/tests/gl_tests/StateChangeTest.cpp
index cf861d6..3136378 100644
--- a/src/tests/gl_tests/StateChangeTest.cpp
+++ b/src/tests/gl_tests/StateChangeTest.cpp
@@ -1008,15 +1008,9 @@
glUseProgram(program);
// We expect to draw a square with these 4 vertices with a drawArray call.
- auto pixelPoints =
- std::vector<Vector2>{{8.5f, 8.5f}, {8.5f, 24.5f}, {24.5f, 8.5f}, {24.5f, 24.5f}};
-
- auto vertices = std::vector<Vector3>();
- for (Vector2 pixelPoint : pixelPoints)
- {
- vertices.emplace_back(Vector3(pixelPoint[0] * 2 / getWindowWidth() - 1,
- pixelPoint[1] * 2 / getWindowHeight() - 1, 0.0f));
- }
+ std::vector<Vector3> vertices;
+ CreatePixelCenterWindowCoords({{8, 8}, {8, 24}, {24, 8}, {24, 24}}, getWindowWidth(),
+ getWindowHeight(), &vertices);
// If we use these indices to draw however, we should be drawing an hourglass.
auto indices = std::vector<GLushort>{0, 2, 1, 3};
@@ -1051,15 +1045,9 @@
glUseProgram(program);
// We expect to draw a square with these 4 vertices with a drawArray call.
- auto pixelPoints =
- std::vector<Vector2>{{8.5f, 8.5f}, {8.5f, 24.5f}, {24.5f, 8.5f}, {24.5f, 24.5f}};
-
- auto vertices = std::vector<Vector3>();
- for (Vector2 pixelPoint : pixelPoints)
- {
- vertices.emplace_back(Vector3(pixelPoint[0] * 2 / getWindowWidth() - 1,
- pixelPoint[1] * 2 / getWindowHeight() - 1, 0.0f));
- }
+ std::vector<Vector3> vertices;
+ CreatePixelCenterWindowCoords({{8, 8}, {8, 24}, {24, 8}, {24, 24}}, getWindowWidth(),
+ getWindowHeight(), &vertices);
// If we use these indices to draw however, we should be drawing an hourglass.
auto indices = std::vector<GLushort>{0, 2, 1, 3};
diff --git a/src/tests/test_utils/ANGLETest.cpp b/src/tests/test_utils/ANGLETest.cpp
index 3e87c4b..e82fb5f 100644
--- a/src/tests/test_utils/ANGLETest.cpp
+++ b/src/tests/test_utils/ANGLETest.cpp
@@ -139,6 +139,19 @@
return result;
}
+void CreatePixelCenterWindowCoords(const std::vector<Vector2> &pixelPoints,
+ int windowWidth,
+ int windowHeight,
+ std::vector<Vector3> *outVertices)
+{
+ for (Vector2 pixelPoint : pixelPoints)
+ {
+ outVertices->emplace_back(Vector3((pixelPoint[0] + 0.5f) * 2.0f / windowWidth - 1.0f,
+ (pixelPoint[1] + 0.5f) * 2.0f / windowHeight - 1.0f,
+ 0.0f));
+ }
+}
+
angle::Vector4 GLColor::toNormalizedVector() const
{
return angle::Vector4(ColorNorm(R), ColorNorm(G), ColorNorm(B), ColorNorm(A));
diff --git a/src/tests/test_utils/ANGLETest.h b/src/tests/test_utils/ANGLETest.h
index 82b85fc..7b15dab 100644
--- a/src/tests/test_utils/ANGLETest.h
+++ b/src/tests/test_utils/ANGLETest.h
@@ -111,6 +111,14 @@
struct WorkaroundsD3D;
+// The input here for pixelPoints are the expected integer window coordinates, we add .5 to every
+// one of them and re-scale the numbers to be between [-1,1]. Using this technique, we can make
+// sure the rasterization stage will end up drawing pixels at the expected locations.
+void CreatePixelCenterWindowCoords(const std::vector<Vector2> &pixelPoints,
+ int windowWidth,
+ int windowHeight,
+ std::vector<Vector3> *outVertices);
+
// Useful to cast any type to GLubyte.
template <typename TR, typename TG, typename TB, typename TA>
GLColor MakeGLColor(TR r, TG g, TB b, TA a)