I'm having really hard time with a submitting a buffer copy command, in a way that doesn't make any sense to me. I'm getting a "Device Lost" error from the validation layer when I try to copy from a staging buffer to a device local buffer. What's really confusing about it is that it's only happening when I call the copy function in specific ways.
Here's the code:
Buffer.cpp
#include "Buffer.h"
namespace vkr
{
Buffer::Buffer() : buffer(VK_NULL_HANDLE), size(0) {}
Buffer::Buffer(
const VmaAllocator & _allocator,
const vk::DeviceSize bufferSize,
const vk::BufferUsageFlags usage,
const VmaMemoryUsage memoryUsageFlags
) :
size(bufferSize)
{
vk::BufferCreateInfo bufferInfo = {};
bufferInfo.size = size;
bufferInfo.usage = usage;
bufferInfo.sharingMode = vk::SharingMode::eExclusive;
VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = memoryUsageFlags;
auto vanillaBufferInfo = (VkBufferCreateInfo)bufferInfo;
vmaCreateBuffer(_allocator, &vanillaBufferInfo, &allocInfo, &buffer, &allocation, nullptr);
}
Buffer::Buffer(Buffer && otherBuffer)
{
buffer = otherBuffer.buffer;
allocation = otherBuffer.allocation;
size = otherBuffer.size;
otherBuffer.buffer = VK_NULL_HANDLE;
otherBuffer.allocation = VmaAllocation();
otherBuffer.size = 0;
}
Buffer & Buffer::operator=(Buffer && otherBuffer)
{
if (this != &otherBuffer) {
buffer = otherBuffer.buffer;
allocation = otherBuffer.allocation;
size = otherBuffer.size;
otherBuffer.buffer = VK_NULL_HANDLE;
otherBuffer.allocation = VmaAllocation();
otherBuffer.size = 0;
}
return *this;
}
Buffer::operator vk::Buffer() const
{
return vk::Buffer(buffer);
}
Buffer::operator VkBuffer() const
{
return buffer;
}
void Buffer::copyInto(const VmaAllocator & _allocator, const void* dataSrc)
{
void* mappedData;
vmaMapMemory(_allocator, allocation, &mappedData);
memcpy(mappedData, dataSrc, (size_t)size);
vmaUnmapMemory(_allocator, allocation);
}
void Buffer::copyInto(const VmaAllocator & _allocator, const void* dataSrc, const size_t newSize)
{
void* mappedData;
vmaMapMemory(_allocator, allocation, &mappedData);
memcpy(mappedData, dataSrc, newSize);
vmaUnmapMemory(_allocator, allocation);
}
void Buffer::destroy(const VmaAllocator & _allocator)
{
vmaDestroyBuffer(_allocator, buffer, allocation);
}
}
Staged Buffer:
#include "StagedBufferFactory.h"
namespace vkr
{
namespace StagedBufferFactory
{
vkr::Buffer create(
vk::Device & device,
vk::CommandPool & commandPool,
vk::Queue & graphicsQueue,
const vk::BufferUsageFlags usageFlags,
const vk::DeviceSize size,
const void * dataSrc,
const VmaAllocator & _allocator
)
{
// Staging buffer that we load the data onto that's visible to our CPU
vk::BufferUsageFlags stagingUsageFlags = vk::BufferUsageFlagBits::eTransferSrc;
vkr::Buffer stagingBuffer{ _allocator, size, stagingUsageFlags, VMA_MEMORY_USAGE_CPU_ONLY };
stagingBuffer.copyInto(_allocator, &dataSrc, (size_t)size);
// This is our buffer located on our GPU, inaccessible to our CPU
vk::BufferUsageFlags gpuBufferUsageFlags = vk::BufferUsageFlagBits::eTransferDst | usageFlags;
vkr::Buffer gpuBuffer{ _allocator, size, gpuBufferUsageFlags, VMA_MEMORY_USAGE_GPU_ONLY };
copyBuffer(device, commandPool, graphicsQueue, stagingBuffer, gpuBuffer);
stagingBuffer.destroy(_allocator);
return gpuBuffer;
}
void copyBuffer(
vk::Device & device,
vk::CommandPool & commandPool,
vk::Queue & graphicsQueue,
vkr::Buffer & stagingBuffer,
vkr::Buffer & gpuBuffer
)
{
vk::CommandBufferAllocateInfo allocInfo = {};
allocInfo.level = vk::CommandBufferLevel::ePrimary;
allocInfo.commandPool = commandPool;
allocInfo.commandBufferCount = 1;
vk::CommandBuffer commandBuffer;
device.allocateCommandBuffers(&allocInfo, &commandBuffer);
vk::CommandBufferBeginInfo beginInfo = {};
beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
commandBuffer.begin(&beginInfo);
vk::BufferCopy copyRegion = {};
copyRegion.srcOffset = 0;
copyRegion.dstOffset = 0;
copyRegion.size = stagingBuffer.size;
commandBuffer.copyBuffer(stagingBuffer.buffer, gpuBuffer.buffer, 1, ©Region);
commandBuffer.end();
vk::SubmitInfo submitInfo = {};
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
// Create fence to ensure that the command buffer has finished executing
vk::FenceCreateInfo fenceInfo{};
vk::Fence fence = device.createFence(fenceInfo);
// Submit to the queue
graphicsQueue.submit(1, &submitInfo, fence);
graphicsQueue.waitIdle();
//device.waitForFences({ fence }, true, 100000000000);
device.destroyFence(fence);
device.free(commandPool, 1, &commandBuffer);
}
}
}
Buffer Creation:
void HelloTriangleApplication::createIndexBuffer()
{
vk::DeviceSize bufferSize = sizeof(indices[0]) * indices.size();
//// Staging buffer that we load the data onto that's visible to our CPU
vk::BufferUsageFlags stagingUsageFlags = vk::BufferUsageFlagBits::eTransferSrc;
vkr::Buffer stagingBuffer{ vulkanAllocator, bufferSize, stagingUsageFlags, VMA_MEMORY_USAGE_CPU_ONLY };
stagingBuffer.copyInto(vulkanAllocator, indices.data());
//// This is our buffer located on our GPU, inaccessible to our CPU
vk::BufferUsageFlags indexUsageFlags = vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eIndexBuffer;
indexBuffer = vkr::Buffer{ vulkanAllocator, bufferSize, indexUsageFlags, VMA_MEMORY_USAGE_GPU_ONLY };
vkr::StagedBufferFactory::copyBuffer(device, commandPool, graphicsQueue, stagingBuffer, indexBuffer);
stagingBuffer.destroy(vulkanAllocator);
}
So this code works without issue... but if I try using my vkr::StagedBuffer::create function (which calls this exact code as far as I can tell) the program doesn't work. I get a black screen (instead of my rainbow-colored rectangle) and occasionally the validation layer tells me that "Device Lost"
void HelloTriangleApplication::createIndexBuffer()
{
vk::DeviceSize bufferSize = sizeof(indices[0]) * indices.size();
indexBuffer = vkr::StagedBufferFactory::create(device, commandPool, graphicsQueue, vk::BufferUsageFlagBits::eIndexBuffer, bufferSize, indices.data(), vulkanAllocator);
}
I'm tearing my hair out, and I definitely don't understand why this would ever be the case.
&dataSrcis the address of the local stack variable, you probably meantdataSrcinstead, So your index buffer is full of junk, which may or may not be the cause of your issue. It'll definitely cause the black screen, but I'm not so sure about the validation error. It might be a bug in the driver when handling out-of-bounds indices? - Frank