Use staging buffers for vertex and index data for uploading to device local memory

This commit is contained in:
saschawillems 2016-03-17 22:32:15 +01:00
parent a6a897f244
commit deccc3ada0

View file

@ -29,6 +29,11 @@
// Set to "true" to enable Vulkan's validation layers // Set to "true" to enable Vulkan's validation layers
// See vulkandebug.cpp for details // See vulkandebug.cpp for details
#define ENABLE_VALIDATION false #define ENABLE_VALIDATION false
// Set to "true" to use staging buffers for uploading
// vertex and index data to device local memory
// See "prepareVertices" for details on what's staging
// and on why to use it
#define USE_STAGING true
class VulkanExample : public VulkanExampleBase class VulkanExample : public VulkanExampleBase
{ {
@ -311,7 +316,7 @@ public:
// Setups vertex and index buffers for an indexed triangle, // Setups vertex and index buffers for an indexed triangle,
// uploads them to the VRAM and sets binding points and attribute // uploads them to the VRAM and sets binding points and attribute
// descriptions to match locations inside the shaders // descriptions to match locations inside the shaders
void prepareVertices() void prepareVertices(bool useStagingBuffers)
{ {
struct Vertex { struct Vertex {
float pos[3]; float pos[3];
@ -328,66 +333,191 @@ public:
// Setup indices // Setup indices
std::vector<uint32_t> indexBuffer = { 0, 1, 2 }; std::vector<uint32_t> indexBuffer = { 0, 1, 2 };
int indexBufferSize = indexBuffer.size() * sizeof(uint32_t); uint32_t indexBufferSize = indexBuffer.size() * sizeof(uint32_t);
indices.count = indexBuffer.size();
VkMemoryAllocateInfo memAlloc = {}; VkMemoryAllocateInfo memAlloc = {};
memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memAlloc.pNext = NULL;
memAlloc.allocationSize = 0;
memAlloc.memoryTypeIndex = 0;
VkMemoryRequirements memReqs; VkMemoryRequirements memReqs;
VkResult err;
void *data; void *data;
// Generate vertex buffer if (useStagingBuffers)
// Setup {
VkBufferCreateInfo bufInfo = {}; // Static data like vertex and index buffer should be stored on the device memory
bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; // for optimal (and fastest) access by the GPU
bufInfo.pNext = NULL; //
bufInfo.size = vertexBufferSize; // To achieve this we use so-called "staging buffers" :
bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; // - Create a buffer that's visible to the host (and can be mapped)
bufInfo.flags = 0; // - Copy the data to this buffer
// Copy vertex data to VRAM // - Create another buffer that's local on the device (VRAM) with the same size
memset(&vertices, 0, sizeof(vertices)); // - Copy the data from the host to the device using a command buffer
err = vkCreateBuffer(device, &bufInfo, nullptr, &vertices.buf);
assert(!err);
vkGetBufferMemoryRequirements(device, vertices.buf, &memReqs);
memAlloc.allocationSize = memReqs.size;
getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAlloc.memoryTypeIndex);
err = vkAllocateMemory(device, &memAlloc, nullptr, &vertices.mem);
assert(!err);
err = vkMapMemory(device, vertices.mem, 0, memAlloc.allocationSize, 0, &data);
assert(!err);
memcpy(data, vertexBuffer.data(), vertexBufferSize);
vkUnmapMemory(device, vertices.mem);
err = vkBindBufferMemory(device, vertices.buf, vertices.mem, 0);
assert(!err);
// Generate index buffer struct StagingBuffer {
// Setup VkDeviceMemory memory;
VkBufferCreateInfo indexbufferInfo = {}; VkBuffer buffer;
indexbufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; };
indexbufferInfo.pNext = NULL;
indexbufferInfo.size = indexBufferSize; struct {
indexbufferInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; StagingBuffer vertices;
indexbufferInfo.flags = 0; StagingBuffer indices;
// Copy index data to VRAM } stagingBuffers;
memset(&indices, 0, sizeof(indices));
err = vkCreateBuffer(device, &indexbufferInfo, nullptr, &indices.buf); // Buffer copies are done on the queue, so we need a command buffer for them
assert(!err); VkCommandBufferAllocateInfo cmdBufInfo = {};
vkGetBufferMemoryRequirements(device, indices.buf, &memReqs); cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
memAlloc.allocationSize = memReqs.size; cmdBufInfo.commandPool = cmdPool;
getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAlloc.memoryTypeIndex); cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
err = vkAllocateMemory(device, &memAlloc, nullptr, &indices.mem); cmdBufInfo.commandBufferCount = 1;
assert(!err);
err = vkMapMemory(device, indices.mem, 0, indexBufferSize, 0, &data); VkCommandBuffer copyCommandBuffer;
assert(!err); vkTools::checkResult(vkAllocateCommandBuffers(device, &cmdBufInfo, &copyCommandBuffer));
memcpy(data, indexBuffer.data(), indexBufferSize);
vkUnmapMemory(device, indices.mem); // Vertex buffer
err = vkBindBufferMemory(device, indices.buf, indices.mem, 0); VkBufferCreateInfo vertexBufferInfo = {};
assert(!err); vertexBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
indices.count = indexBuffer.size(); vertexBufferInfo.size = vertexBufferSize;
// Buffer is used as the copy source
vertexBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
// Create a host-visible buffer to copy the vertex data to (staging buffer)
vkTools::checkResult(vkCreateBuffer(device, &vertexBufferInfo, nullptr, &stagingBuffers.vertices.buffer));
vkGetBufferMemoryRequirements(device, stagingBuffers.vertices.buffer, &memReqs);
memAlloc.allocationSize = memReqs.size;
getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAlloc.memoryTypeIndex);
vkTools::checkResult(vkAllocateMemory(device, &memAlloc, nullptr, &stagingBuffers.vertices.memory));
// Map and copy
vkTools::checkResult(vkMapMemory(device, stagingBuffers.vertices.memory, 0, memAlloc.allocationSize, 0, &data));
memcpy(data, vertexBuffer.data(), vertexBufferSize);
vkUnmapMemory(device, stagingBuffers.vertices.memory);
vkTools::checkResult(vkBindBufferMemory(device, stagingBuffers.vertices.buffer, stagingBuffers.vertices.memory, 0));
// Create the destination buffer with device only visibility
// Buffer will be used as a vertex buffer and is the copy destination
vertexBufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
vkTools::checkResult(vkCreateBuffer(device, &vertexBufferInfo, nullptr, &vertices.buf));
vkGetBufferMemoryRequirements(device, vertices.buf, &memReqs);
memAlloc.allocationSize = memReqs.size;
getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAlloc.memoryTypeIndex);
vkTools::checkResult(vkAllocateMemory(device, &memAlloc, nullptr, &vertices.mem));
vkTools::checkResult(vkBindBufferMemory(device, vertices.buf, vertices.mem, 0));
// Index buffer
// todo : comment
VkBufferCreateInfo indexbufferInfo = {};
indexbufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
indexbufferInfo.size = indexBufferSize;
indexbufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
// Copy index data to a buffer visible to the host (staging buffer)
vkTools::checkResult(vkCreateBuffer(device, &indexbufferInfo, nullptr, &stagingBuffers.indices.buffer));
vkGetBufferMemoryRequirements(device, stagingBuffers.indices.buffer, &memReqs);
memAlloc.allocationSize = memReqs.size;
getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAlloc.memoryTypeIndex);
vkTools::checkResult(vkAllocateMemory(device, &memAlloc, nullptr, &stagingBuffers.indices.memory));
vkTools::checkResult(vkMapMemory(device, stagingBuffers.indices.memory, 0, indexBufferSize, 0, &data));
memcpy(data, indexBuffer.data(), indexBufferSize);
vkUnmapMemory(device, stagingBuffers.indices.memory);
vkTools::checkResult(vkBindBufferMemory(device, stagingBuffers.indices.buffer, stagingBuffers.indices.memory, 0));
// Create destination buffer with device only visibility
indexbufferInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
vkTools::checkResult(vkCreateBuffer(device, &indexbufferInfo, nullptr, &indices.buf));
vkGetBufferMemoryRequirements(device, indices.buf, &memReqs);
memAlloc.allocationSize = memReqs.size;
getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAlloc.memoryTypeIndex);
vkTools::checkResult(vkAllocateMemory(device, &memAlloc, nullptr, &indices.mem));
vkTools::checkResult(vkBindBufferMemory(device, indices.buf, indices.mem, 0));
indices.count = indexBuffer.size();
VkCommandBufferBeginInfo cmdBufferBeginInfo = {};
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
cmdBufferBeginInfo.pNext = NULL;
VkBufferCopy copyRegion = {};
// Put buffer region copies into command buffer
// Note that the staging buffer must not be deleted before the copies
// have been submitted and executed
vkTools::checkResult(vkBeginCommandBuffer(copyCommandBuffer, &cmdBufferBeginInfo));
// Vertex buffer
copyRegion.size = vertexBufferSize;
vkCmdCopyBuffer(
copyCommandBuffer,
stagingBuffers.vertices.buffer,
vertices.buf,
1,
&copyRegion);
// Index buffer
copyRegion.size = indexBufferSize;
vkCmdCopyBuffer(
copyCommandBuffer,
stagingBuffers.indices.buffer,
indices.buf,
1,
&copyRegion);
vkTools::checkResult(vkEndCommandBuffer(copyCommandBuffer));
// Submit copies to the queue
VkSubmitInfo copySubmitInfo = {};
copySubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
copySubmitInfo.commandBufferCount = 1;
copySubmitInfo.pCommandBuffers = &copyCommandBuffer;
vkTools::checkResult(vkQueueSubmit(queue, 1, &copySubmitInfo, VK_NULL_HANDLE));
vkTools::checkResult(vkQueueWaitIdle(queue));
// todo : sync necessary (fence, semaphore?)
// Destroy staging buffers
vkDestroyBuffer(device, stagingBuffers.vertices.buffer, nullptr);
vkFreeMemory(device, stagingBuffers.vertices.memory, nullptr);
vkDestroyBuffer(device, stagingBuffers.indices.buffer, nullptr);
vkFreeMemory(device, stagingBuffers.indices.memory, nullptr);
}
else
{
// Don't use staging
// Create host-visible buffers only and use these for rendering
// This is not advised for real world applications and will
// result in lower performances
// Vertex buffer
VkBufferCreateInfo vertexBufferInfo = {};
vertexBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
vertexBufferInfo.size = vertexBufferSize;
vertexBufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
// Copy vertex data to a buffer visible to the host
vkTools::checkResult(vkCreateBuffer(device, &vertexBufferInfo, nullptr, &vertices.buf));
vkGetBufferMemoryRequirements(device, vertices.buf, &memReqs);
memAlloc.allocationSize = memReqs.size;
getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAlloc.memoryTypeIndex);
vkTools::checkResult(vkAllocateMemory(device, &memAlloc, nullptr, &vertices.mem));
vkTools::checkResult(vkMapMemory(device, vertices.mem, 0, memAlloc.allocationSize, 0, &data));
memcpy(data, vertexBuffer.data(), vertexBufferSize);
vkUnmapMemory(device, vertices.mem);
vkTools::checkResult(vkBindBufferMemory(device, vertices.buf, vertices.mem, 0));
// Index buffer
VkBufferCreateInfo indexbufferInfo = {};
indexbufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
indexbufferInfo.size = indexBufferSize;
indexbufferInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
// Copy index data to a buffer visible to the host
memset(&indices, 0, sizeof(indices));
vkTools::checkResult(vkCreateBuffer(device, &indexbufferInfo, nullptr, &indices.buf));
vkGetBufferMemoryRequirements(device, indices.buf, &memReqs);
memAlloc.allocationSize = memReqs.size;
getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAlloc.memoryTypeIndex);
vkTools::checkResult(vkAllocateMemory(device, &memAlloc, nullptr, &indices.mem));
vkTools::checkResult(vkMapMemory(device, indices.mem, 0, indexBufferSize, 0, &data));
memcpy(data, indexBuffer.data(), indexBufferSize);
vkUnmapMemory(device, indices.mem);
vkTools::checkResult(vkBindBufferMemory(device, indices.buf, indices.mem, 0));
indices.count = indexBuffer.size();
}
// Binding description // Binding description
vertices.bindingDescriptions.resize(1); vertices.bindingDescriptions.resize(1);
@ -709,7 +839,7 @@ public:
{ {
VulkanExampleBase::prepare(); VulkanExampleBase::prepare();
prepareSemaphore(); prepareSemaphore();
prepareVertices(); prepareVertices(USE_STAGING);
prepareUniformBuffers(); prepareUniformBuffers();
setupDescriptorSetLayout(); setupDescriptorSetLayout();
preparePipelines(); preparePipelines();