From dafc4da14ae18308a87111ff2c1697ca896cd36f Mon Sep 17 00:00:00 2001 From: saschawillems Date: Wed, 20 Jul 2016 22:21:52 +0200 Subject: [PATCH] Added a dedicated buffer object class --- base/vulkanbuffer.hpp | 148 ++++++++++++++++++++++++++++++++++++++++++ base/vulkandevice.hpp | 49 ++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 base/vulkanbuffer.hpp diff --git a/base/vulkanbuffer.hpp b/base/vulkanbuffer.hpp new file mode 100644 index 00000000..7148b9be --- /dev/null +++ b/base/vulkanbuffer.hpp @@ -0,0 +1,148 @@ +/* +* Vulkan buffer class +* +* Encapsulates a Vulkan buffer +* +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#pragma once + +#include + +#include "vulkan/vulkan.h" +#include "vulkantools.h" + +namespace vk +{ + /** + * @brief Encapsulates access to a Vulkan buffer backed up by device memory + * @note To be filled by an external source like the VulkanDevice + */ + struct Buffer + { + VkBuffer buffer; + VkDevice device; + VkDeviceMemory memory; + VkDescriptorBufferInfo descriptor; + VkDeviceSize size = 0; + VkDeviceSize alignment = 0; + void* mapped = nullptr; + + /** @brief Usage flags to be filled by external source at buffer creation (to query at some later point) */ + VkBufferUsageFlags usageFlags; + /** @brief Memory propertys flags to be filled by external source at buffer creation (to query at some later point) */ + VkMemoryPropertyFlags memoryPropertyFlags; + + /** + * Map a memory range of this buffer. If successful, mapped points to the specified buffer range. + * + * @param size (Optional) Size of the memory range to map. Pass VK_WHOLE_SIZE to map the complete buffer range. + * @param offset (Optional) Byte offset from beginning + * + * @return VkResult of the buffer mapping call + */ + VkResult map(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0) + { + return vkMapMemory(device, memory, offset, size, 0, &mapped); + } + + /** + * Unmap a mapped memory range + * + * @note Does not return a result as vkUnmapMemory can't fail + */ + void unmap() + { + if (mapped) + { + vkUnmapMemory(device, memory); + mapped = nullptr; + } + } + + /** + * Attach the allocated memory block to the buffer + * + * @param offset (Optional) Byte offset (from the beginning) for the memory region to bind + * + * @return VkResult of the bindBufferMemory call + */ + VkResult bind(VkDeviceSize offset = 0) + { + return vkBindBufferMemory(device, buffer, memory, offset); + } + + /** + * Setup the default descriptor for this buffer + * + * @param size (Optional) Size of the memory range of the descriptor + * @param offset (Optional) Byte offset from beginning + * + */ + void setupDescriptor(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0) + { + descriptor.offset = offset; + descriptor.buffer = buffer; + descriptor.range = size; + } + + /** + * Flush a memory range of the buffer to make it visible to the device + * + * @note Only required for non-coherent memory + * + * @param size (Optional) Size of the memory range to flush. Pass VK_WHOLE_SIZE to flush the complete buffer range. + * @param offset (Optional) Byte offset from beginning + * + * @return VkResult of the flush call + */ + VkResult flush(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0) + { + VkMappedMemoryRange mappedRange = {}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = memory; + mappedRange.offset = offset; + mappedRange.size = size; + return vkFlushMappedMemoryRanges(device, 1, &mappedRange); + } + + /** + * Invalidate a memory range of the buffer to make it visible to the host + * + * @note Only required for non-coherent memory + * + * @param size (Optional) Size of the memory range to invalidate. Pass VK_WHOLE_SIZE to invalidate the complete buffer range. + * @param offset (Optional) Byte offset from beginning + * + * @return VkResult of the invalidate call + */ + VkResult invalidate(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0) + { + VkMappedMemoryRange mappedRange = {}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = memory; + mappedRange.offset = offset; + mappedRange.size = size; + return vkInvalidateMappedMemoryRanges(device, 1, &mappedRange); + } + + /** + * Release all Vulkan resources held by this buffer + */ + void destroy() + { + if (buffer) + { + vkDestroyBuffer(device, buffer, nullptr); + } + if (memory) + { + vkFreeMemory(device, memory, nullptr); + } + } + + }; +} \ No newline at end of file diff --git a/base/vulkandevice.hpp b/base/vulkandevice.hpp index db570fb9..5231087a 100644 --- a/base/vulkandevice.hpp +++ b/base/vulkandevice.hpp @@ -13,6 +13,7 @@ #include #include "vulkan/vulkan.h" #include "vulkantools.h" +#include "vulkanbuffer.hpp" namespace vk { @@ -158,5 +159,53 @@ namespace vk return VK_SUCCESS; } + + /** + * Create a buffer on the device + * + * @param usageFlags Usage flag bitmask for the buffer (i.e. index, vertex, uniform buffer) + * @param memoryPropertyFlags Memory properties for this buffer (i.e. device local, host visible, coherent) + * @param buffer Pointer to a vk::Vulkan buffer object + * @param size Size of the buffer in byes + * @param data Pointer to the data that should be copied to the buffer after creation (optional, if not set, no data is copied over) + * + * @return VK_SUCCESS if buffer handle and memory have been created and (optionally passed) data has been copied + */ + VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, vk::Buffer *buffer, VkDeviceSize size, void *data = nullptr) + { + buffer->device = device; + + // Create the buffer handle + VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(usageFlags, size); + VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &buffer->buffer)); + + // Create the memory backing up the buffer handle + VkMemoryRequirements memReqs; + VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo(); + vkGetBufferMemoryRequirements(device, buffer->buffer, &memReqs); + memAlloc.allocationSize = memReqs.size; + // Find a memory type index that fits the properties of the buffer + memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags); + VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &buffer->memory)); + + buffer->alignment = memReqs.alignment; + buffer->size = memAlloc.allocationSize; + buffer->usageFlags = usageFlags; + buffer->memoryPropertyFlags = memoryPropertyFlags; + + // If a pointer to the buffer data has been passed, map the buffer and copy over the data + if (data != nullptr) + { + VK_CHECK_RESULT(buffer->map()); + memcpy(buffer->mapped, data, size); + buffer->unmap(); + } + + // Initialize a default descriptor that covers the whole buffer size + buffer->setupDescriptor(); + + // Attach the memory to the buffer object + return buffer->bind(); + } }; }