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) \