Use staging buffers for vertex and index data for uploading to device local memory
This commit is contained in:
parent
a6a897f244
commit
deccc3ada0
1 changed files with 184 additions and 54 deletions
|
|
@ -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, ©CommandBuffer));
|
||||||
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,
|
||||||
|
©Region);
|
||||||
|
// Index buffer
|
||||||
|
copyRegion.size = indexBufferSize;
|
||||||
|
vkCmdCopyBuffer(
|
||||||
|
copyCommandBuffer,
|
||||||
|
stagingBuffers.indices.buffer,
|
||||||
|
indices.buf,
|
||||||
|
1,
|
||||||
|
©Region);
|
||||||
|
|
||||||
|
vkTools::checkResult(vkEndCommandBuffer(copyCommandBuffer));
|
||||||
|
|
||||||
|
// Submit copies to the queue
|
||||||
|
VkSubmitInfo copySubmitInfo = {};
|
||||||
|
copySubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
|
copySubmitInfo.commandBufferCount = 1;
|
||||||
|
copySubmitInfo.pCommandBuffers = ©CommandBuffer;
|
||||||
|
|
||||||
|
vkTools::checkResult(vkQueueSubmit(queue, 1, ©SubmitInfo, 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();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue