Vulkan: Implement conversion to uint16 for drawElements with ubytes

Bug: angleproject:2646
Bug: angleproject:2659
Change-Id: If3c7a2b77d6acd18c8ca2522a427a43e10ed6db2
Reviewed-on: https://chromium-review.googlesource.com/1099420
Commit-Queue: Luc Ferron <lucferron@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
index 0b37d30..604e8b4 100644
--- a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
@@ -37,6 +37,7 @@
       mCurrentElementArrayBufferResource(nullptr),
       mDynamicVertexData(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, kDynamicVertexDataSize),
       mDynamicIndexData(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, kDynamicIndexDataSize),
+      mTranslatedByteIndexData(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, kDynamicIndexDataSize),
       mLineLoopHelper(renderer),
       mDirtyLineLoopTranslation(true),
       mVertexBuffersDirty(false),
@@ -52,6 +53,7 @@
 
     mDynamicVertexData.init(1, renderer);
     mDynamicIndexData.init(1, renderer);
+    mTranslatedByteIndexData.init(1, renderer);
 }
 
 VertexArrayVk::~VertexArrayVk()
@@ -63,6 +65,7 @@
     VkDevice device = vk::GetImpl(context)->getRenderer()->getDevice();
     mDynamicVertexData.destroy(device);
     mDynamicIndexData.destroy(device);
+    mTranslatedByteIndexData.destroy(device);
     mLineLoopHelper.destroy(device);
 }
 
@@ -495,11 +498,11 @@
 {
     ANGLE_TRY(onDraw(context, renderer, drawCallParams, commandBuffer, newCommandBuffer));
     bool isLineLoop  = drawCallParams.mode() == gl::PrimitiveMode::LineLoop;
-    uintptr_t offset = mState.getElementArrayBuffer().get() && !isLineLoop
-                           ? reinterpret_cast<uintptr_t>(drawCallParams.indices())
-                           : 0;
+    gl::Buffer *glBuffer = mState.getElementArrayBuffer().get();
+    uintptr_t offset =
+        glBuffer && !isLineLoop ? reinterpret_cast<uintptr_t>(drawCallParams.indices()) : 0;
 
-    if (!mState.getElementArrayBuffer().get() && !isLineLoop)
+    if (!glBuffer && !isLineLoop)
     {
         ANGLE_TRY(drawCallParams.ensureIndexRangeResolved(context));
         ANGLE_TRY(streamIndexData(renderer, drawCallParams));
@@ -512,15 +515,52 @@
         if (drawCallParams.type() == GL_UNSIGNED_BYTE &&
             drawCallParams.mode() != gl::PrimitiveMode::LineLoop)
         {
-            // TODO(fjhenigman): Index format translation for non line-loop calls.
-            UNIMPLEMENTED();
-            return gl::InternalError()
-                   << "Unsigned byte translation is not implemented for indices in a buffer object";
+            // Unsigned bytes don't have direct support in Vulkan so we have to expand the
+            // memory to a GLushort.
+            BufferVk *bufferVk   = vk::GetImpl(glBuffer);
+            void *srcDataMapping = nullptr;
+            ASSERT(!glBuffer->isMapped());
+            ANGLE_TRY(bufferVk->map(context, 0, &srcDataMapping));
+            uint8_t *srcData           = reinterpret_cast<uint8_t *>(srcDataMapping);
+            intptr_t offsetIntoSrcData = reinterpret_cast<intptr_t>(drawCallParams.indices());
+            srcData += offsetIntoSrcData;
+
+            // Allocate a new buffer that's double the size of the buffer provided by the user to
+            // go from unsigned byte to unsigned short.
+            uint8_t *allocatedData      = nullptr;
+            bool newBufferAllocated     = false;
+            uint32_t expandedDataOffset = 0;
+            mTranslatedByteIndexData.allocate(
+                renderer, static_cast<size_t>(bufferVk->getSize()) * 2, &allocatedData,
+                &mCurrentElementArrayBufferHandle, &expandedDataOffset, &newBufferAllocated);
+            mCurrentElementArrayBufferOffset = static_cast<VkDeviceSize>(expandedDataOffset);
+
+            // Expand the source into the destination
+            ASSERT(!context->getGLState().isPrimitiveRestartEnabled());
+            uint16_t *expandedDst = reinterpret_cast<uint16_t *>(allocatedData);
+            for (GLsizei index = 0; index < bufferVk->getSize() - offsetIntoSrcData; index++)
+            {
+                expandedDst[index] = static_cast<GLushort>(srcData[index]);
+            }
+
+            // Make sure our writes are available.
+            mTranslatedByteIndexData.flush(renderer->getDevice());
+            GLboolean result = false;
+            ANGLE_TRY(bufferVk->unmap(context, &result));
+
+            // We do not add the offset from the drawCallParams here because we've already copied
+            // the source starting at the offset requested.
+            commandBuffer->bindIndexBuffer(mCurrentElementArrayBufferHandle,
+                                           mCurrentElementArrayBufferOffset,
+                                           gl_vk::GetIndexType(drawCallParams.type()));
+        }
+        else
+        {
+            commandBuffer->bindIndexBuffer(mCurrentElementArrayBufferHandle,
+                                           mCurrentElementArrayBufferOffset + offset,
+                                           gl_vk::GetIndexType(drawCallParams.type()));
         }
 
-        commandBuffer->bindIndexBuffer(mCurrentElementArrayBufferHandle,
-                                       mCurrentElementArrayBufferOffset + offset,
-                                       gl_vk::GetIndexType(drawCallParams.type()));
         mLastIndexBufferOffset = offset;
 
         const gl::State &glState                  = context->getGLState();