So I've been trying to implement a multi-threaded resource system w/ vulkan in my free time, where a thread can request a resource to be loaded, and it gets pushed into a queue. On another thread, the resource (as of right now, a mesh) gets loaded from a file, and I map the data to a staging buffer. The issue comes in where I record the command buffer to copy the data to a GPU buffer. I record a secondary command buffer w/ just the vkCmdCopyBuffer command, and push it to a queue to be executed from a primary command buffer on the main thread to a transfer-only queue. As far as I can tell, the staging works fine, and the mesh is drawn and looks perfectly fine, but my validation layers (VK_LAYER_LUNARG_standard_validation) spam tell me: "vkCmdBindIndexBuffer(): Cannot read invalid region of memory allocation 0x16 for bound Buffer object 0x15, please fill the memory before using," and the vertex buffer binding gives me an identical message. Both buffers were created with the proper bits, TRANSFER_SRC for the staging buffer, TRANSFER_DST for the gpu buffer (plus index and vertex buffer usage bits). I use Vulkan Memory Allocator from GPUOpen to handle buffer memory allocation, and I'm careful to make sure that the staging buffer is mapped properly and isn't deleted before the command finishes. The validation layers stop spamming telling me this error if I switch the copy commands to using primary buffers, even when recorded in the same way (i.e. just changing the level parameter), but everything I've seen recommends recording secondary command buffers simultaneously on worker threads, and submitting them on the main thread later. Any ideas on why my validation layers are freaking out, or did I just skip over something when reading the spec?
Here's some relevant code:
Recording the secondary command buffer:
// on a worker thread
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
allocInfo.commandPool = transferCommandPool;
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); // Need to reuse, dont' alloc each time
// Iffy about this, what's the proper way to set this up?
VkCommandBufferInheritanceInfo inherInfo = {};
inherInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
inherInfo.renderPass = VK_NULL_HANDLE;
inherInfo.framebuffer = VK_NULL_HANDLE;
inherInfo.occlusionQueryEnable = VK_FALSE;
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
beginInfo.pInheritanceInfo = &inherInfo;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
VkBufferCopy vbCopyRegion = {};
vbCopyRegion.srcOffset = 0;
vbCopyRegion.dstOffset = 0;
vbCopyRegion.size = meshData.size();
vkCmdCopyBuffer(commandBuffer, stagingBuffer, meshBuffer, 1, &vbCopyRegion);
vkEndCommandBuffer(commandBuffer);
// push it to a queue
Recording the primary command buffer:
// on the main thread
// transferCommandQueuePrimaryBuffer is the primary command buffer I submit eachf rame
vkResetCommandBuffer(transferCommandQueuePrimaryBuffer, 0);
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(transferCommandQueuePrimaryBuffer, &beginInfo);
// transferCommandQueue being a vector of secondary command buffers
vkCmdExecuteCommands(transferCommandQueuePrimaryBuffer, transferCommandQueue.size(), transferCommandQueue.data());
vkEndCommandBuffer(transferCommandQueuePrimaryBuffer);
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &transferCommandQueuePrimaryBuffer;
vkQueueSubmit(transferQueue, 1, &submitInfo, transferCommandQueueSubmitFence);
vkQueueWaitIdle(transferQueue); // placeholder sync while I work on making a way to properly signal the resource has been loaded (w/ fences probably)