Vulkan: Add DispatchUtilsVK

This class provides a set of compute-based internal utilities.
Currently, buffer clear and copy are implemented.  Other possibilities
include more efficient mip map generation, or specialized texture
operations.

VertexArrayVk::updateIndexTranslation() is updated to convert the
GL_UNSIGNED_BYTE index buffer to a GL_UNSIGNED_SHORT one using this
class to avoid a CPU readback.

The vk::Format class is augmented with a few flags (IsInt, IsUnsigned)
to be able to select the appropriate shader based on the format (float,
int or uint).

Bug: angleproject:2958,angleproject:3003
Change-Id: Ie35519deb3c32a3da5ccf74080c70092c9287f0a
Reviewed-on: https://chromium-review.googlesource.com/c/1336307
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
index 2b2c61f..e032b1b 100644
--- a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
@@ -23,13 +23,15 @@
 {
 namespace
 {
-constexpr size_t kDynamicVertexDataSize    = 1024 * 1024;
-constexpr size_t kDynamicIndexDataSize     = 1024 * 8;
-constexpr size_t kMaxVertexFormatAlignment = 4;
-constexpr VkBufferUsageFlags kVertexBufferUsageFlags =
-    VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
-constexpr VkBufferUsageFlags kIndexBufferUsageFlags =
-    VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
+constexpr size_t kDynamicVertexDataSize              = 1024 * 1024;
+constexpr size_t kDynamicIndexDataSize               = 1024 * 8;
+constexpr size_t kMaxVertexFormatAlignment           = 4;
+constexpr VkBufferUsageFlags kVertexBufferUsageFlags = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
+                                                       VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
+                                                       VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
+constexpr VkBufferUsageFlags kIndexBufferUsageFlags = VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
+                                                      VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
+                                                      VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
 
 bool BindingIsAligned(const gl::VertexBinding &binding, unsigned componentSize)
 {
@@ -176,7 +178,6 @@
                                                  const gl::VertexBinding &binding,
                                                  size_t attribIndex)
 {
-
     // Needed before reading buffer or we could get stale data.
     ANGLE_TRY(contextVk->getRenderer()->finish(contextVk));
 
@@ -557,16 +558,60 @@
 {
     ASSERT(type != gl::DrawElementsType::InvalidEnum);
 
+    RendererVk *renderer = contextVk->getRenderer();
     gl::Buffer *glBuffer = mState.getElementArrayBuffer();
 
     if (!glBuffer)
     {
         ANGLE_TRY(streamIndexData(contextVk, type, indexCount, indices, &mDynamicIndexData));
     }
+    else if (renderer->getFormat(angle::FormatID::R16_UINT).vkSupportsStorageBuffer)
+    {
+        BufferVk *bufferVk = vk::GetImpl(glBuffer);
+
+        ASSERT(type == gl::DrawElementsType::UnsignedByte);
+
+        intptr_t offsetIntoSrcData = reinterpret_cast<intptr_t>(indices);
+        size_t srcDataSize         = static_cast<size_t>(bufferVk->getSize()) - offsetIntoSrcData;
+
+        mTranslatedByteIndexData.releaseRetainedBuffers(renderer);
+
+        ANGLE_TRY(mTranslatedByteIndexData.allocate(contextVk, sizeof(GLushort) * srcDataSize,
+                                                    nullptr, nullptr,
+                                                    &mCurrentElementArrayBufferOffset, nullptr));
+        mCurrentElementArrayBuffer = mTranslatedByteIndexData.getCurrentBuffer();
+
+        vk::BufferHelper *dest = mTranslatedByteIndexData.getCurrentBuffer();
+        vk::BufferHelper *src  = &bufferVk->getBuffer();
+
+        ANGLE_TRY(src->initBufferView(contextVk, renderer->getFormat(angle::FormatID::R8_UINT)));
+        ANGLE_TRY(dest->initBufferView(contextVk, renderer->getFormat(angle::FormatID::R16_UINT)));
+
+        // Copy relevant section of the source into destination at allocated offset.  Note that the
+        // offset returned by allocate() above is in bytes, while our allocated array is of
+        // GLushorts.
+        DispatchUtilsVk::CopyParameters params = {};
+        params.destOffset =
+            static_cast<size_t>(mCurrentElementArrayBufferOffset) / sizeof(GLushort);
+        params.srcOffset = offsetIntoSrcData;
+        params.size      = srcDataSize;
+
+        // Note: this is a copy, which implicitly converts between formats.  Once support for
+        // primitive restart is added, a specialized shader is likely needed to special case 0xFF ->
+        // 0xFFFF.
+        ANGLE_TRY(renderer->getDispatchUtils()->copyBuffer(contextVk, dest, src, params));
+    }
     else
     {
+        // If it's not possible to convert the buffer with compute, opt for a CPU read back for now.
+        // TODO(syoussefi): R8G8B8A8_UINT is required to have storage texel buffer support, so a
+        // specialized shader code can be made to read two ubyte indices and output them in R and B
+        // (or A and G based on endianness?) with 0 on the other channels.  If specialized, we might
+        // as well support the ubyte to ushort case with correct handling of primitive restart.
+        // http://anglebug.com/3003
+
         // Needed before reading buffer or we could get stale data.
-        ANGLE_TRY(contextVk->getRenderer()->finish(contextVk));
+        ANGLE_TRY(renderer->finish(contextVk));
 
         ASSERT(type == gl::DrawElementsType::UnsignedByte);
         // Unsigned bytes don't have direct support in Vulkan so we have to expand the