Vulkan: Simple buffer creation.

This is necessary to initialize vertex arrays.

BUG=angleproject:1579

Change-Id: Ic5a232d5cdfaa75b41241901de842e62ff3b173f
Reviewed-on: https://chromium-review.googlesource.com/406645
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/BufferVk.cpp b/src/libANGLE/renderer/vulkan/BufferVk.cpp
index 0b1babd..88d98c8 100644
--- a/src/libANGLE/renderer/vulkan/BufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/BufferVk.cpp
@@ -10,11 +10,13 @@
 #include "libANGLE/renderer/vulkan/BufferVk.h"
 
 #include "common/debug.h"
+#include "libANGLE/renderer/vulkan/ContextVk.h"
+#include "libANGLE/renderer/vulkan/RendererVk.h"
 
 namespace rx
 {
 
-BufferVk::BufferVk(const gl::BufferState &state) : BufferImpl(state)
+BufferVk::BufferVk(const gl::BufferState &state) : BufferImpl(state), mRequiredSize(0)
 {
 }
 
@@ -22,16 +24,80 @@
 {
 }
 
-gl::Error BufferVk::setData(GLenum target, const void *data, size_t size, GLenum usage)
+gl::Error BufferVk::setData(ContextImpl *context,
+                            GLenum target,
+                            const void *data,
+                            size_t size,
+                            GLenum usage)
 {
-    UNIMPLEMENTED();
-    return gl::Error(GL_INVALID_OPERATION);
+    ContextVk *contextVk = GetAs<ContextVk>(context);
+    auto device          = contextVk->getDevice();
+
+    // TODO(jmadill): Proper usage bit implementation. Likely will involve multiple backing buffers
+    // like in D3D11.
+    VkBufferCreateInfo createInfo;
+    createInfo.sType                 = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+    createInfo.pNext                 = nullptr;
+    createInfo.flags                 = 0;
+    createInfo.size                  = size;
+    createInfo.usage                 = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+    createInfo.sharingMode           = VK_SHARING_MODE_EXCLUSIVE;
+    createInfo.queueFamilyIndexCount = 0;
+    createInfo.pQueueFamilyIndices   = nullptr;
+
+    vk::Buffer newBuffer(device);
+    ANGLE_TRY(newBuffer.init(createInfo));
+
+    // Find a compatible memory pool index. If the index doesn't change, we could cache it.
+    // Not finding a valid memory pool means an out-of-spec driver, or internal error.
+    // TODO(jmadill): More efficient memory allocation.
+    VkMemoryRequirements memoryRequirements;
+    vkGetBufferMemoryRequirements(device, newBuffer.getHandle(), &memoryRequirements);
+
+    // The requirements size is not always equal to the specified API size.
+    ASSERT(memoryRequirements.size >= size);
+    mRequiredSize = static_cast<size_t>(memoryRequirements.size);
+
+    VkPhysicalDeviceMemoryProperties memoryProperties;
+    vkGetPhysicalDeviceMemoryProperties(contextVk->getRenderer()->getPhysicalDevice(),
+                                        &memoryProperties);
+
+    auto memoryTypeIndex =
+        FindMemoryType(memoryProperties, memoryRequirements,
+                       VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+    ANGLE_VK_CHECK(memoryTypeIndex.valid(), VK_ERROR_INCOMPATIBLE_DRIVER);
+
+    VkMemoryAllocateInfo allocInfo;
+    allocInfo.sType           = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+    allocInfo.pNext           = nullptr;
+    allocInfo.memoryTypeIndex = memoryTypeIndex.value();
+    allocInfo.allocationSize  = memoryRequirements.size;
+
+    ANGLE_TRY(newBuffer.getMemory().allocate(allocInfo));
+    ANGLE_TRY(newBuffer.bindMemory());
+
+    mBuffer = std::move(newBuffer);
+
+    if (data)
+    {
+        ANGLE_TRY(setDataImpl(static_cast<const uint8_t *>(data), size, 0));
+    }
+
+    return gl::NoError();
 }
 
-gl::Error BufferVk::setSubData(GLenum target, const void *data, size_t size, size_t offset)
+gl::Error BufferVk::setSubData(ContextImpl *context,
+                               GLenum target,
+                               const void *data,
+                               size_t size,
+                               size_t offset)
 {
-    UNIMPLEMENTED();
-    return gl::Error(GL_INVALID_OPERATION);
+    ASSERT(mBuffer.getHandle() != VK_NULL_HANDLE);
+    ASSERT(mBuffer.getMemory().getHandle() != VK_NULL_HANDLE);
+
+    ANGLE_TRY(setDataImpl(static_cast<const uint8_t *>(data), size, offset));
+
+    return gl::NoError();
 }
 
 gl::Error BufferVk::copySubData(BufferImpl *source,
@@ -45,20 +111,33 @@
 
 gl::Error BufferVk::map(GLenum access, GLvoid **mapPtr)
 {
-    UNIMPLEMENTED();
-    return gl::Error(GL_INVALID_OPERATION);
+    ASSERT(mBuffer.getHandle() != VK_NULL_HANDLE);
+    ASSERT(mBuffer.getMemory().getHandle() != VK_NULL_HANDLE);
+
+    ANGLE_TRY(
+        mBuffer.getMemory().map(0, mState.getSize(), 0, reinterpret_cast<uint8_t **>(mapPtr)));
+
+    return gl::NoError();
 }
 
 gl::Error BufferVk::mapRange(size_t offset, size_t length, GLbitfield access, GLvoid **mapPtr)
 {
-    UNIMPLEMENTED();
-    return gl::Error(GL_INVALID_OPERATION);
+    ASSERT(mBuffer.getHandle() != VK_NULL_HANDLE);
+    ASSERT(mBuffer.getMemory().getHandle() != VK_NULL_HANDLE);
+
+    ANGLE_TRY(mBuffer.getMemory().map(offset, length, 0, reinterpret_cast<uint8_t **>(mapPtr)));
+
+    return gl::NoError();
 }
 
 gl::Error BufferVk::unmap(GLboolean *result)
 {
-    UNIMPLEMENTED();
-    return gl::Error(GL_INVALID_OPERATION);
+    ASSERT(mBuffer.getHandle() != VK_NULL_HANDLE);
+    ASSERT(mBuffer.getMemory().getHandle() != VK_NULL_HANDLE);
+
+    mBuffer.getMemory().unmap();
+
+    return gl::NoError();
 }
 
 gl::Error BufferVk::getIndexRange(GLenum type,
@@ -71,4 +150,17 @@
     return gl::Error(GL_INVALID_OPERATION);
 }
 
+vk::Error BufferVk::setDataImpl(const uint8_t *data, size_t size, size_t offset)
+{
+    uint8_t *mapPointer = nullptr;
+    ANGLE_TRY(mBuffer.getMemory().map(offset, size, 0, &mapPointer));
+    ASSERT(mapPointer);
+
+    memcpy(mapPointer, data, size);
+
+    mBuffer.getMemory().unmap();
+
+    return vk::NoError();
+}
+
 }  // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/BufferVk.h b/src/libANGLE/renderer/vulkan/BufferVk.h
index 2779296..8c73411 100644
--- a/src/libANGLE/renderer/vulkan/BufferVk.h
+++ b/src/libANGLE/renderer/vulkan/BufferVk.h
@@ -11,6 +11,7 @@
 #define LIBANGLE_RENDERER_VULKAN_BUFFERVK_H_
 
 #include "libANGLE/renderer/BufferImpl.h"
+#include "libANGLE/renderer/vulkan/renderervk_utils.h"
 
 namespace rx
 {
@@ -21,8 +22,16 @@
     BufferVk(const gl::BufferState &state);
     ~BufferVk() override;
 
-    gl::Error setData(GLenum target, const void *data, size_t size, GLenum usage) override;
-    gl::Error setSubData(GLenum target, const void *data, size_t size, size_t offset) override;
+    gl::Error setData(ContextImpl *context,
+                      GLenum target,
+                      const void *data,
+                      size_t size,
+                      GLenum usage) override;
+    gl::Error setSubData(ContextImpl *context,
+                         GLenum target,
+                         const void *data,
+                         size_t size,
+                         size_t offset) override;
     gl::Error copySubData(BufferImpl *source,
                           GLintptr sourceOffset,
                           GLintptr destOffset,
@@ -36,6 +45,12 @@
                             size_t count,
                             bool primitiveRestartEnabled,
                             gl::IndexRange *outRange) override;
+
+  private:
+    vk::Error setDataImpl(const uint8_t *data, size_t size, size_t offset);
+
+    vk::Buffer mBuffer;
+    size_t mRequiredSize;
 };
 
 }  // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp
index bb8de55..53a6f9e 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp
@@ -509,10 +509,15 @@
 
 void RendererVk::generateCaps(gl::Caps * /*outCaps*/,
                               gl::TextureCapsMap * /*outTextureCaps*/,
-                              gl::Extensions * /*outExtensions*/,
+                              gl::Extensions *outExtensions,
                               gl::Limitations * /* outLimitations */) const
 {
     // TODO(jmadill): Caps.
+
+    // Enable this for simple buffer readback testing, but some functionality is missing.
+    // TODO(jmadill): Support full mapBufferRange extension.
+    outExtensions->mapBuffer      = true;
+    outExtensions->mapBufferRange = true;
 }
 
 const gl::Caps &RendererVk::getNativeCaps() const
diff --git a/src/libANGLE/renderer/vulkan/renderervk_utils.cpp b/src/libANGLE/renderer/vulkan/renderervk_utils.cpp
index f42452b..79e1ca6 100644
--- a/src/libANGLE/renderer/vulkan/renderervk_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/renderervk_utils.cpp
@@ -763,6 +763,67 @@
     return NoError();
 }
 
+// Buffer implementation.
+Buffer::Buffer()
+{
+}
+
+Buffer::Buffer(VkDevice device) : WrappedObject(device), mMemory(device)
+{
+}
+
+Buffer::Buffer(Buffer &&other)
+    : WrappedObject(std::move(other)), mMemory(std::move(other.getMemory()))
+{
+}
+
+Buffer::~Buffer()
+{
+    if (mHandle != VK_NULL_HANDLE)
+    {
+        ASSERT(validDevice());
+        vkDestroyBuffer(mDevice, mHandle, nullptr);
+    }
+}
+
+Buffer &Buffer::operator=(Buffer &&other)
+{
+    assignOpBase(std::move(other));
+    std::swap(mMemory, other.mMemory);
+    return *this;
+}
+
+Error Buffer::init(const VkBufferCreateInfo &createInfo)
+{
+    ASSERT(validDevice() && !valid());
+    ANGLE_VK_TRY(vkCreateBuffer(mDevice, &createInfo, nullptr, &mHandle));
+    return NoError();
+}
+
+Error Buffer::bindMemory()
+{
+    ASSERT(valid() && mMemory.valid());
+    ANGLE_VK_TRY(vkBindBufferMemory(mDevice, mHandle, mMemory.getHandle(), 0));
+    return NoError();
+}
+
 }  // namespace vk
 
+Optional<uint32_t> FindMemoryType(const VkPhysicalDeviceMemoryProperties &memoryProps,
+                                  const VkMemoryRequirements &requirements,
+                                  uint32_t propertyFlagMask)
+{
+    for (uint32_t typeIndex = 0; typeIndex < memoryProps.memoryTypeCount; ++typeIndex)
+    {
+        if ((requirements.memoryTypeBits & (1u << typeIndex)) != 0 &&
+            ((memoryProps.memoryTypes[typeIndex].propertyFlags & propertyFlagMask) ==
+             propertyFlagMask))
+        {
+            return typeIndex;
+        }
+    }
+
+    return Optional<uint32_t>::Invalid();
+}
+
 }  // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/renderervk_utils.h b/src/libANGLE/renderer/vulkan/renderervk_utils.h
index 4579aaf..4233b1f 100644
--- a/src/libANGLE/renderer/vulkan/renderervk_utils.h
+++ b/src/libANGLE/renderer/vulkan/renderervk_utils.h
@@ -12,6 +12,7 @@
 
 #include <vulkan/vulkan.h>
 
+#include "common/Optional.h"
 #include "libANGLE/Error.h"
 
 namespace gl
@@ -270,8 +271,31 @@
     VkDeviceSize mSize;
 };
 
+class Buffer final : public WrappedObject<VkBuffer>
+{
+  public:
+    Buffer();
+    Buffer(VkDevice device);
+    Buffer(Buffer &&other);
+    ~Buffer();
+    Buffer &operator=(Buffer &&other);
+
+    Error init(const VkBufferCreateInfo &createInfo);
+    Error bindMemory();
+
+    DeviceMemory &getMemory() { return mMemory; }
+    const DeviceMemory &getMemory() const { return mMemory; }
+
+  private:
+    DeviceMemory mMemory;
+};
+
 }  // namespace vk
 
+Optional<uint32_t> FindMemoryType(const VkPhysicalDeviceMemoryProperties &memoryProps,
+                                  const VkMemoryRequirements &requirements,
+                                  uint32_t propertyFlagMask);
+
 }  // namespace rx
 
 #define ANGLE_VK_TRY(command)                                          \