@@ -6153,6 +6153,264 @@ static void TestMemoryUsage()
61536153 }
61546154}
61556155
6156+ static void TestDataUploadingWithStagingBuffer ()
6157+ {
6158+ wprintf (L" Testing data uploading with staging buffer...\n " );
6159+
6160+ // Generate some random data to fill the uniform buffer with.
6161+ const VkDeviceSize bufferSize = 65536 ;
6162+ std::vector<std::uint8_t > bufferData (bufferSize);
6163+ for (auto & bufferByte : bufferData) {
6164+ bufferByte = static_cast <std::uint8_t >(rand () % 256 );
6165+ }
6166+
6167+ VkBufferCreateInfo uniformBufferCI = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
6168+ uniformBufferCI.size = bufferSize;
6169+ uniformBufferCI.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change this if you want to create another type of buffer.
6170+
6171+ VmaAllocationCreateInfo uniformBufferAllocCI = {};
6172+ uniformBufferAllocCI.usage = VMA_MEMORY_USAGE_AUTO;
6173+ uniformBufferAllocCI.flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
6174+
6175+ VkBuffer uniformBuffer = VK_NULL_HANDLE;
6176+ VmaAllocation uniformBufferAlloc = {};
6177+ VmaAllocationInfo uniformBufferAllocInfo = {};
6178+
6179+ VkResult result = vmaCreateBuffer (g_hAllocator, &uniformBufferCI, &uniformBufferAllocCI, &uniformBuffer, &uniformBufferAlloc, &uniformBufferAllocInfo);
6180+ TEST (result == VK_SUCCESS);
6181+
6182+ // We need to check if the uniform buffer really ended NOT up in mappable memory.
6183+ VkMemoryPropertyFlags memPropFlags;
6184+ vmaGetAllocationMemoryProperties (g_hAllocator, uniformBufferAlloc, &memPropFlags);
6185+ TEST (!(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
6186+
6187+ VkBufferCreateInfo stagingBufferCI = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
6188+ stagingBufferCI.size = bufferSize;
6189+ stagingBufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
6190+
6191+ VmaAllocationCreateInfo stagingBufferAllocCI = {};
6192+ stagingBufferAllocCI.usage = VMA_MEMORY_USAGE_AUTO;
6193+ stagingBufferAllocCI.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
6194+
6195+ VkBuffer stagingBuffer = VK_NULL_HANDLE;
6196+ VmaAllocation stagingBufferAlloc = {};
6197+ VmaAllocationInfo stagingBufferAllocInfo = {};
6198+
6199+ result = vmaCreateBuffer (g_hAllocator, &stagingBufferCI, &stagingBufferAllocCI, &stagingBuffer, &stagingBufferAlloc, &stagingBufferAllocInfo);
6200+ TEST (result == VK_SUCCESS);
6201+
6202+ TEST (stagingBufferAllocInfo.pMappedData != nullptr );
6203+ std::memcpy (stagingBufferAllocInfo.pMappedData , bufferData.data (), bufferData.size ());
6204+
6205+ result = vmaFlushAllocation (g_hAllocator, uniformBufferAlloc, 0 , VK_WHOLE_SIZE);
6206+ TEST (result == VK_SUCCESS);
6207+
6208+ BeginSingleTimeCommands ();
6209+
6210+ VkBufferMemoryBarrier bufferMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
6211+ bufferMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
6212+ bufferMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
6213+ bufferMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
6214+ bufferMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
6215+ bufferMemBarrier.buffer = stagingBuffer;
6216+ bufferMemBarrier.offset = 0 ;
6217+ bufferMemBarrier.size = VK_WHOLE_SIZE;
6218+
6219+ vkCmdPipelineBarrier (g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0 , 0 , nullptr , 1 , &bufferMemBarrier, 0 , nullptr );
6220+
6221+ VkBufferCopy bufferCopy = {};
6222+ bufferCopy.srcOffset = 0 ;
6223+ bufferCopy.dstOffset = 0 ;
6224+ bufferCopy.size = bufferSize;
6225+
6226+ vkCmdCopyBuffer (g_hTemporaryCommandBuffer, stagingBuffer, uniformBuffer, 1 , &bufferCopy);
6227+
6228+ VkBufferMemoryBarrier bufferMemBarrier2 = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
6229+ bufferMemBarrier2.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
6230+ bufferMemBarrier2.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT; // Change this if you want to create another type of buffer.
6231+ bufferMemBarrier2.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
6232+ bufferMemBarrier2.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
6233+ bufferMemBarrier2.buffer = uniformBuffer;
6234+ bufferMemBarrier2.offset = 0 ;
6235+ bufferMemBarrier2.size = VK_WHOLE_SIZE;
6236+
6237+ vkCmdPipelineBarrier (g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0 , 0 , nullptr , 1 , &bufferMemBarrier2, 0 , nullptr );
6238+
6239+ EndSingleTimeCommands ();
6240+
6241+ vmaDestroyBuffer (g_hAllocator, stagingBuffer, stagingBufferAlloc);
6242+ vmaDestroyBuffer (g_hAllocator, uniformBuffer, uniformBufferAlloc);
6243+ }
6244+
6245+ static void TestDataUploadingWithMappedMemory () {
6246+ wprintf (L" Testing data uploading with mapped memory and memcpy...\n " );
6247+
6248+ // Generate some random data to fill the uniform buffer with.
6249+ const VkDeviceSize bufferSize = 65536 ;
6250+ std::vector<std::uint8_t > bufferData (bufferSize);
6251+ for (auto & bufferByte : bufferData) {
6252+ bufferByte = static_cast <std::uint8_t >(rand () % 256 );
6253+ }
6254+
6255+ VkBufferCreateInfo uniformBufferCI = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
6256+ uniformBufferCI.size = bufferSize;
6257+ uniformBufferCI.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; // Change this if you want to create another type of buffer.
6258+
6259+ VmaAllocationCreateInfo uniformBufferAllocCI = {};
6260+ uniformBufferAllocCI.usage = VMA_MEMORY_USAGE_AUTO;
6261+ uniformBufferAllocCI.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; // We want memory to be mapped so we can use memcpy to update it
6262+
6263+ VkBuffer uniformBuffer = VK_NULL_HANDLE;
6264+ VmaAllocation uniformBufferAlloc = {};
6265+ VmaAllocationInfo uniformBufferAllocInfo = {};
6266+
6267+ VkResult result = vmaCreateBuffer (g_hAllocator, &uniformBufferCI, &uniformBufferAllocCI, &uniformBuffer, &uniformBufferAlloc, &uniformBufferAllocInfo);
6268+ TEST (result == VK_SUCCESS);
6269+
6270+ // We need to check if the uniform buffer really ended up in mappable memory.
6271+ VkMemoryPropertyFlags memPropFlags;
6272+ vmaGetAllocationMemoryProperties (g_hAllocator, uniformBufferAlloc, &memPropFlags);
6273+ TEST (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
6274+
6275+ TEST (uniformBufferAllocInfo.pMappedData != nullptr );
6276+ std::memcpy (uniformBufferAllocInfo.pMappedData , bufferData.data (), bufferData.size ());
6277+
6278+ // We don't need to check for VK_MEMORY_PROPERTY_HOST_COHERENT_BIT because both vmaFlushAllocation and vmaInvalidateAllocation check for this internally.
6279+ result = vmaFlushAllocation (g_hAllocator, uniformBufferAlloc, 0 , VK_WHOLE_SIZE);
6280+ TEST (result == VK_SUCCESS);
6281+
6282+ BeginSingleTimeCommands ();
6283+
6284+ VkBufferMemoryBarrier bufferMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
6285+ bufferMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
6286+ bufferMemBarrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT; // Change this if you want to create another type of buffer.
6287+ bufferMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
6288+ bufferMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
6289+ bufferMemBarrier.buffer = uniformBuffer;
6290+ bufferMemBarrier.offset = 0 ;
6291+ bufferMemBarrier.size = VK_WHOLE_SIZE;
6292+
6293+ vkCmdPipelineBarrier (g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0 , 0 , nullptr , 1 , &bufferMemBarrier, 0 , nullptr );
6294+
6295+ EndSingleTimeCommands ();
6296+
6297+ vmaDestroyBuffer (g_hAllocator, uniformBuffer, uniformBufferAlloc);
6298+ }
6299+
6300+ static void TestAdvancedDataUploading () {
6301+ wprintf (L" Testing advanced data uploading...\n " );
6302+
6303+ // Generate some random data to fill the uniform buffer with.
6304+ const VkDeviceSize bufferSize = 65536 ;
6305+ std::vector<std::uint8_t > bufferData (bufferSize);
6306+ for (auto & bufferByte : bufferData) {
6307+ bufferByte = static_cast <std::uint8_t >(rand () % 256 );
6308+ }
6309+
6310+ VkBufferCreateInfo uniformBufferCI = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
6311+ uniformBufferCI.size = bufferSize;
6312+ uniformBufferCI.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change this if you want to create another type of buffer.
6313+
6314+ VmaAllocationCreateInfo uniformBufferAllocCI = {};
6315+ uniformBufferAllocCI.usage = VMA_MEMORY_USAGE_AUTO;
6316+ uniformBufferAllocCI.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
6317+
6318+ VkBuffer uniformBuffer = VK_NULL_HANDLE;
6319+ VmaAllocation uniformBufferAlloc = {};
6320+ VmaAllocationInfo uniformBufferAllocInfo = {};
6321+
6322+ VkResult result = vmaCreateBuffer (g_hAllocator, &uniformBufferCI, &uniformBufferAllocCI, &uniformBuffer, &uniformBufferAlloc, &uniformBufferAllocInfo);
6323+ TEST (result == VK_SUCCESS);
6324+
6325+ VkMemoryPropertyFlags memPropFlags;
6326+ vmaGetAllocationMemoryProperties (g_hAllocator, uniformBufferAlloc, &memPropFlags);
6327+
6328+ if (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
6329+ // The allocation ended up as mapped memory, meaning we can update it simply by using std::memcpy.
6330+ TEST (uniformBufferAllocInfo.pMappedData != nullptr );
6331+ std::memcpy (uniformBufferAllocInfo.pMappedData , bufferData.data (), bufferData.size ());
6332+
6333+ // We don't need to check for VK_MEMORY_PROPERTY_HOST_COHERENT_BIT because both vmaFlushAllocation and vmaInvalidateAllocation check for this internally.
6334+ result = vmaFlushAllocation (g_hAllocator, uniformBufferAlloc, 0 , VK_WHOLE_SIZE);
6335+ TEST (result == VK_SUCCESS);
6336+
6337+ BeginSingleTimeCommands ();
6338+
6339+ VkBufferMemoryBarrier bufferMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
6340+ bufferMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
6341+ bufferMemBarrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT; // Change this if you want to create another type of buffer.
6342+ bufferMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
6343+ bufferMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
6344+ bufferMemBarrier.buffer = uniformBuffer;
6345+ bufferMemBarrier.offset = 0 ;
6346+ bufferMemBarrier.size = VK_WHOLE_SIZE;
6347+
6348+ vkCmdPipelineBarrier (g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0 , 0 , nullptr , 1 , &bufferMemBarrier, 0 , nullptr );
6349+
6350+ EndSingleTimeCommands ();
6351+ }
6352+ else {
6353+ // The allocation did not end up in mapped memory, so we need a staging buffer and a copy operation to update it.
6354+ VkBufferCreateInfo stagingBufferCI = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
6355+ stagingBufferCI.size = bufferSize;
6356+ stagingBufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
6357+
6358+ VmaAllocationCreateInfo stagingBufferAllocCI = {};
6359+ stagingBufferAllocCI.usage = VMA_MEMORY_USAGE_AUTO;
6360+ stagingBufferAllocCI.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
6361+
6362+ VkBuffer stagingBuffer = VK_NULL_HANDLE;
6363+ VmaAllocation stagingBufferAlloc = {};
6364+ VmaAllocationInfo stagingBufferAllocInfo = {};
6365+
6366+ result = vmaCreateBuffer (g_hAllocator, &stagingBufferCI, &stagingBufferAllocCI, &stagingBuffer, &stagingBufferAlloc, &stagingBufferAllocInfo);
6367+ TEST (result == VK_SUCCESS);
6368+
6369+ TEST (stagingBufferAllocInfo.pMappedData != nullptr );
6370+ std::memcpy (stagingBufferAllocInfo.pMappedData , bufferData.data (), bufferData.size ());
6371+
6372+ result = vmaFlushAllocation (g_hAllocator, uniformBufferAlloc, 0 , VK_WHOLE_SIZE);
6373+ TEST (result == VK_SUCCESS);
6374+
6375+ BeginSingleTimeCommands ();
6376+
6377+ VkBufferMemoryBarrier bufferMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
6378+ bufferMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
6379+ bufferMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
6380+ bufferMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
6381+ bufferMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
6382+ bufferMemBarrier.buffer = stagingBuffer;
6383+ bufferMemBarrier.offset = 0 ;
6384+ bufferMemBarrier.size = VK_WHOLE_SIZE;
6385+
6386+ vkCmdPipelineBarrier (g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0 , 0 , nullptr , 1 , &bufferMemBarrier, 0 , nullptr );
6387+
6388+ VkBufferCopy bufferCopy = {};
6389+ bufferCopy.srcOffset = 0 ;
6390+ bufferCopy.dstOffset = 0 ;
6391+ bufferCopy.size = bufferSize;
6392+
6393+ vkCmdCopyBuffer (g_hTemporaryCommandBuffer, stagingBuffer, uniformBuffer, 1 , &bufferCopy);
6394+
6395+ VkBufferMemoryBarrier bufferMemBarrier2 = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
6396+ bufferMemBarrier2.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
6397+ bufferMemBarrier2.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT; // Change this if you want to create another type of buffer.
6398+ bufferMemBarrier2.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
6399+ bufferMemBarrier2.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
6400+ bufferMemBarrier2.buffer = uniformBuffer;
6401+ bufferMemBarrier2.offset = 0 ;
6402+ bufferMemBarrier2.size = VK_WHOLE_SIZE;
6403+
6404+ vkCmdPipelineBarrier (g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0 , 0 , nullptr , 1 , &bufferMemBarrier2, 0 , nullptr );
6405+
6406+ EndSingleTimeCommands ();
6407+
6408+ vmaDestroyBuffer (g_hAllocator, stagingBuffer, stagingBufferAlloc);
6409+ }
6410+
6411+ vmaDestroyBuffer (g_hAllocator, uniformBuffer, uniformBufferAlloc);
6412+ }
6413+
61566414static uint32_t FindDeviceCoherentMemoryTypeBits ()
61576415{
61586416 VkPhysicalDeviceMemoryProperties memProps;
@@ -8352,6 +8610,9 @@ void Test()
83528610 TestAllocationsInitialization ();
83538611#endif
83548612 TestMemoryUsage ();
8613+ TestDataUploadingWithStagingBuffer ();
8614+ TestDataUploadingWithMappedMemory ();
8615+ TestAdvancedDataUploading ();
83558616 TestDeviceCoherentMemory ();
83568617 TestStatistics ();
83578618 TestAliasing ();
0 commit comments