diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt
index 3f589c1d7..108bf6d08 100644
--- a/util/test/demos/CMakeLists.txt
+++ b/util/test/demos/CMakeLists.txt
@@ -113,6 +113,7 @@ set(VULKAN_SRC
vk/vk_leak_check.cpp
vk/vk_line_raster.cpp
vk/vk_load_store_none.cpp
+ vk/vk_mem_bench.cpp
vk/vk_mesh_zoo.cpp
vk/vk_misaligned_dirty.cpp
vk/vk_multi_entry.cpp
diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj
index badcb279b..a56041868 100644
--- a/util/test/demos/demos.vcxproj
+++ b/util/test/demos/demos.vcxproj
@@ -308,6 +308,7 @@
+
diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters
index 3d7bbf1c6..b482be2d8 100644
--- a/util/test/demos/demos.vcxproj.filters
+++ b/util/test/demos/demos.vcxproj.filters
@@ -649,6 +649,9 @@
D3D12\demos
+
+ Vulkan\demos
+
Android
diff --git a/util/test/demos/vk/vk_mem_bench.cpp b/util/test/demos/vk/vk_mem_bench.cpp
new file mode 100644
index 000000000..cb4fe424f
--- /dev/null
+++ b/util/test/demos/vk/vk_mem_bench.cpp
@@ -0,0 +1,744 @@
+/******************************************************************************
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2023 Baldur Karlsson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ ******************************************************************************/
+
+#include
+#include "3rdparty/fmt/core.h"
+#include "vk_test.h"
+
+#if defined(SSE_TEST)
+#include
+#endif
+
+struct Alloc
+{
+ VkDevice device;
+ std::string name;
+ VkDeviceMemory mem;
+ VkMemoryPropertyFlags flags;
+ VkBuffer buf;
+ uint32_t type;
+ VkDeviceSize size;
+ byte *data = NULL;
+
+ void map() { vkMapMemory(device, mem, 0, VK_WHOLE_SIZE, 0, (void **)&data); }
+ void unmap() { vkUnmapMemory(device, mem); }
+};
+
+byte *refData = NULL;
+size_t dummyStart, dummyEnd;
+
+namespace FindDiffRange_shipping
+{
+#if 0
+static __m128 zero = {0};
+#endif
+
+// assumes a and b both point to 16-byte aligned 16-byte chunks of memory.
+
+// Returns if they're equal or different
+bool Vec16NotEqual(void *a, void *b)
+{
+// disabled SSE version as it's acting dodgy
+#if 0
+ __m128 avec = _mm_load_ps(aflt);
+ __m128 bvec = _mm_load_ps(bflt);
+
+ __m128 diff = _mm_xor_ps(avec, bvec);
+
+ __m128 eq = _mm_cmpeq_ps(diff, zero);
+ int mask = _mm_movemask_ps(eq);
+ int signMask = _mm_movemask_ps(diff);
+
+ // first check ensures that diff is floatequal to zero (ie. avec bitwise equal to bvec).
+ // HOWEVER -0 is floatequal to 0, so we ensure no sign bits are set on diff
+ if((mask^0xf) || signMask != 0)
+ {
+ return true;
+ }
+
+ return false;
+#elif defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || \
+ defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+ uint64_t *a64 = (uint64_t *)a;
+ uint64_t *b64 = (uint64_t *)b;
+
+ return a64[0] != b64[0] || a64[1] != b64[1];
+#else
+ uint32_t *a32 = (uint32_t *)a;
+ uint32_t *b32 = (uint32_t *)b;
+
+ return a32[0] != b32[0] || a32[1] != b32[1] || a32[2] != b32[2] || a32[3] != b32[3];
+#endif
+}
+
+bool FindDiffRange(void *a, void *b, size_t bufSize, size_t &diffStart, size_t &diffEnd)
+{
+ TEST_ASSERT(uintptr_t(a) % 16 == 0, "misaligned");
+ TEST_ASSERT(uintptr_t(b) % 16 == 0, "misaligned");
+
+ diffStart = bufSize + 1;
+ diffEnd = 0;
+
+ size_t alignedSize = bufSize & (~0xf);
+ size_t numVecs = alignedSize / 16;
+
+ size_t offs = 0;
+
+ float *aflt = (float *)a;
+ float *bflt = (float *)b;
+
+ // sweep to find the start of differences
+ for(size_t v = 0; v < numVecs; v++)
+ {
+ if(Vec16NotEqual(aflt, bflt))
+ {
+ diffStart = offs;
+ break;
+ }
+
+ aflt += 4;
+ bflt += 4;
+ offs += 4 * sizeof(float);
+ }
+
+ // make sure we're byte-accurate, to comply with WRITE_NO_OVERWRITE
+ while(diffStart < bufSize && *((byte *)a + diffStart) == *((byte *)b + diffStart))
+ diffStart++;
+
+ // do we have some unaligned bytes at the end of the buffer?
+ if(bufSize > alignedSize)
+ {
+ size_t numBytes = bufSize - alignedSize;
+
+ // if we haven't even found a start, check in these bytes
+ if(diffStart > bufSize)
+ {
+ offs = alignedSize;
+
+ for(size_t by = 0; by < numBytes; by++)
+ {
+ if(*((byte *)a + alignedSize + by) != *((byte *)b + alignedSize + by))
+ {
+ diffStart = offs;
+ break;
+ }
+
+ offs++;
+ }
+ }
+
+ // sweep from the last byte to find the end
+ for(size_t by = 0; by < numBytes; by++)
+ {
+ if(*((byte *)a + bufSize - 1 - by) != *((byte *)b + bufSize - 1 - by))
+ {
+ diffEnd = bufSize - by;
+ break;
+ }
+ }
+ }
+
+ // if we haven't found a start, or we've found a start AND and end,
+ // then we're done.
+ if(diffStart > bufSize || diffEnd > 0)
+ return diffStart < bufSize;
+
+ offs = alignedSize;
+
+ // sweep from the last __m128
+ aflt = (float *)a + offs / sizeof(float) - 4;
+ bflt = (float *)b + offs / sizeof(float) - 4;
+
+ for(size_t v = 0; v < numVecs; v++)
+ {
+ if(Vec16NotEqual(aflt, bflt))
+ {
+ diffEnd = offs;
+ break;
+ }
+
+ aflt -= 4;
+ bflt -= 4;
+ offs -= 16;
+ }
+
+ // make sure we're byte-accurate, to comply with WRITE_NO_OVERWRITE
+ while(diffEnd > 0 && *((byte *)a + diffEnd - 1) == *((byte *)b + diffEnd - 1))
+ diffEnd--;
+
+ // if we found a start then we necessarily found an end
+ return diffStart < bufSize;
+}
+};
+
+void stream_memcpy(void *dst, void *src, size_t len)
+{
+ char *d = (char *)dst;
+ char *s = (char *)src;
+
+#if defined(SSE_TEST)
+ /* If dst and src are not co-aligned, or if SSE4.1 is not present, fallback to memcpy(). */
+ if(((uintptr_t)d & 15) != ((uintptr_t)s & 15))
+ {
+ memcpy(d, s, len);
+ return;
+ }
+
+ /* memcpy() the misaligned header. At the end of this if block, and
+ * are aligned to a 16-byte boundary or == 0.
+ */
+ if((uintptr_t)d & 15)
+ {
+ uintptr_t bytes_before_alignment_boundary = 16 - ((uintptr_t)d & 15);
+ TEST_ASSERT(bytes_before_alignment_boundary < 16, "!");
+
+ memcpy(d, s, std::min(bytes_before_alignment_boundary, len));
+
+ d = (char *)AlignUp((uintptr_t)d, (uintptr_t)16ULL);
+ s = (char *)AlignUp((uintptr_t)s, (uintptr_t)16ULL);
+ len -= std::min(bytes_before_alignment_boundary, len);
+ }
+
+ if(len >= 64)
+ _mm_mfence();
+
+ while(len >= 64)
+ {
+ __m128i *dst_cacheline = (__m128i *)d;
+ __m128i *src_cacheline = (__m128i *)s;
+
+ __m128i temp1 = _mm_stream_load_si128(src_cacheline + 0);
+ __m128i temp2 = _mm_stream_load_si128(src_cacheline + 1);
+ __m128i temp3 = _mm_stream_load_si128(src_cacheline + 2);
+ __m128i temp4 = _mm_stream_load_si128(src_cacheline + 3);
+
+ _mm_store_si128(dst_cacheline + 0, temp1);
+ _mm_store_si128(dst_cacheline + 1, temp2);
+ _mm_store_si128(dst_cacheline + 2, temp3);
+ _mm_store_si128(dst_cacheline + 3, temp4);
+
+ d += 64;
+ s += 64;
+ len -= 64;
+ }
+#endif
+ /* memcpy() the tail. */
+ if(len)
+ {
+ memcpy(d, s, len);
+ }
+}
+
+RD_TEST(VK_Mem_Bench, VulkanGraphicsTest)
+{
+ static constexpr const char *Description = "Memory mapping benchmark";
+
+ bool bench = true;
+ VkDeviceSize maxMemory = 500 * 1024 * 1024;
+ uint32_t submits = 20;
+
+ void Prepare(int argc, char **argv)
+ {
+ for(int i = 0; i < argc; i++)
+ {
+ if(!strcmp(argv[i], "--bench"))
+ {
+ bench = true;
+ }
+ if(!strcmp(argv[i], "--maxmem") && i + 1 < argc)
+ {
+ maxMemory = atoi(argv[i + 1]) * 1024 * 1024;
+ }
+ if(!strcmp(argv[i], "--submits") && i + 1 < argc)
+ {
+ submits = atoi(argv[i + 1]);
+ }
+ }
+
+ forceComputeQueue = true;
+ forceTransferQueue = true;
+
+ VulkanGraphicsTest::Prepare(argc, argv);
+ }
+
+ int main()
+ {
+ // initialise, create window, create context, etc
+ if(!Init())
+ return 3;
+
+ VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo());
+
+ vkh::GraphicsPipelineCreateInfo pipeCreateInfo;
+
+ pipeCreateInfo.layout = layout;
+ pipeCreateInfo.renderPass = mainWindow->rp;
+
+ pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {vkh::vertexBind(0, DefaultA2V)};
+ pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = {
+ vkh::vertexAttr(0, 0, DefaultA2V, pos), vkh::vertexAttr(1, 0, DefaultA2V, col),
+ vkh::vertexAttr(2, 0, DefaultA2V, uv),
+ };
+
+ pipeCreateInfo.stages = {
+ CompileShaderModule(VKDefaultVertex, ShaderLang::glsl, ShaderStage::vert, "main"),
+ CompileShaderModule(VKDefaultPixel, ShaderLang::glsl, ShaderStage::frag, "main"),
+ };
+
+ AllocatedImage img(
+ this,
+ vkh::ImageCreateInfo(mainWindow->scissor.extent.width, mainWindow->scissor.extent.height, 0,
+ mainWindow->format,
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY}));
+
+ VkImageView imgview = createImageView(
+ vkh::ImageViewCreateInfo(img.image, VK_IMAGE_VIEW_TYPE_2D, mainWindow->format));
+
+ vkh::RenderPassCreator renderPassCreateInfo;
+
+ renderPassCreateInfo.attachments.push_back(
+ vkh::AttachmentDescription(mainWindow->format, VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL, VK_ATTACHMENT_LOAD_OP_CLEAR));
+
+ renderPassCreateInfo.addSubpass({VkAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL})});
+
+ VkRenderPass renderPass = createRenderPass(renderPassCreateInfo);
+
+ VkFramebuffer framebuffer = createFramebuffer(
+ vkh::FramebufferCreateInfo(renderPass, {imgview}, mainWindow->scissor.extent));
+
+ pipeCreateInfo.renderPass = renderPass;
+
+ VkPipeline pipe = createGraphicsPipeline(pipeCreateInfo);
+
+ AllocatedBuffer vb(this, vkh::BufferCreateInfo(sizeof(DefaultTri) + 128 * 1024,
+ VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
+
+ vb.upload(DefaultTri);
+
+ const VkPhysicalDeviceMemoryProperties *props = NULL;
+ vmaGetMemoryProperties(allocator, &props);
+
+ std::vector allocs;
+
+ VkDeviceSize refDataSize = 0;
+
+ uint32_t heapTypeCount[16] = {};
+
+ for(uint32_t m = 0; m < props->memoryTypeCount; m++)
+ {
+ const VkMemoryType &type = props->memoryTypes[m];
+ if((type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
+ {
+ heapTypeCount[type.heapIndex]++;
+ }
+ }
+
+ for(uint32_t m = 0; m < props->memoryTypeCount; m++)
+ {
+ const VkMemoryType &type = props->memoryTypes[m];
+ const VkMemoryHeap &heap = props->memoryHeaps[type.heapIndex];
+ if((type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
+ {
+ Alloc alloc;
+ alloc.device = device;
+ alloc.type = m;
+ alloc.flags = type.propertyFlags;
+
+ VkMemoryAllocateInfo info = {};
+ info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ info.allocationSize =
+ AlignUp(std::min(maxMemory, ((heap.size * 7) / 10) / heapTypeCount[type.heapIndex]),
+ (VkDeviceSize)256ULL);
+ info.memoryTypeIndex = m;
+
+ vkAllocateMemory(device, &info, NULL, &alloc.mem);
+ vkCreateBuffer(
+ device, vkh::BufferCreateInfo(info.allocationSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT),
+ NULL, &alloc.buf);
+ vkBindBufferMemory(device, alloc.buf, alloc.mem, 0);
+
+ alloc.size = info.allocationSize;
+
+ if(bench && alloc.size > refDataSize)
+ refDataSize = alloc.size;
+
+ alloc.name = fmt::format("Mem {} ({:04}MB):", m, info.allocationSize >> 20);
+ if((type.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
+ alloc.name += " CACHED";
+ if((type.propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
+ alloc.name += " COHERENT";
+ if((type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
+ alloc.name += " DEVICE";
+
+ allocs.push_back(alloc);
+ }
+ }
+
+ if(bench)
+ refData = new byte[refDataSize];
+
+ AllocatedBuffer readback;
+
+ if(bench)
+ readback =
+ AllocatedBuffer(this, vkh::BufferCreateInfo(refDataSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_UNKNOWN,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+ VK_MEMORY_PROPERTY_HOST_CACHED_BIT}));
+
+ using ScanFunction = std::function;
+
+ byte *scratch = new byte[65536];
+
+ std::vector scanners;
+ auto blockScan = [scratch](VkDeviceSize blockSize, bool streamMemcpy, Alloc &, byte *data,
+ VkDeviceSize size) {
+ if(blockSize == 0)
+ {
+ FindDiffRange_shipping::FindDiffRange(data, refData, size, dummyStart, dummyEnd);
+ return;
+ }
+
+ for(VkDeviceSize i = 0; i < size; i += blockSize)
+ {
+ size_t chunkSize = std::min(blockSize, size - i);
+ if(streamMemcpy)
+ stream_memcpy(scratch, data + i, chunkSize);
+ else
+ memcpy(scratch, data + i, chunkSize);
+ FindDiffRange_shipping::FindDiffRange(scratch, refData, chunkSize, dummyStart, dummyEnd);
+ }
+ };
+
+ scanners.push_back([&blockScan](Alloc &a, byte *data, VkDeviceSize size) {
+ blockScan(0, false, a, data, size);
+ return "direct";
+ });
+ scanners.push_back([&blockScan](Alloc &a, byte *data, VkDeviceSize size) {
+ blockScan(128, false, a, data, size);
+ return "block_128";
+ });
+ scanners.push_back([&blockScan](Alloc &a, byte *data, VkDeviceSize size) {
+ blockScan(1024, false, a, data, size);
+ return "block_1024";
+ });
+ scanners.push_back([&blockScan](Alloc &a, byte *data, VkDeviceSize size) {
+ blockScan(65536, false, a, data, size);
+ return "block_65536";
+ });
+ scanners.push_back([&blockScan](Alloc &a, byte *data, VkDeviceSize size) {
+ blockScan(128, true, a, data, size);
+ return "block_128_stream";
+ });
+ scanners.push_back([&blockScan](Alloc &a, byte *data, VkDeviceSize size) {
+ blockScan(1024, true, a, data, size);
+ return "block_1024_stream";
+ });
+ scanners.push_back([&blockScan](Alloc &a, byte *data, VkDeviceSize size) {
+ blockScan(65536, true, a, data, size);
+ return "block_65536_stream";
+ });
+
+ auto gpuReadback = [this, &readback](VkQueue q, VkCommandBuffer cmd, Alloc &a) -> byte * {
+ vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo());
+
+ VkBufferCopy region = {0, 0, a.size};
+
+ vkCmdCopyBuffer(cmd, a.buf, readback.buffer, 1, ®ion);
+
+ vkh::cmdPipelineBarrier(
+ cmd, {}, {vkh::BufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT,
+ readback.buffer)});
+
+ vkEndCommandBuffer(cmd);
+
+ std::vector cmds = {cmd};
+ VkSubmitInfo submit = vkh::SubmitInfo(cmds);
+ CHECK_VKR(vkQueueSubmit(q, 1, &submit, VK_NULL_HANDLE));
+ vkQueueWaitIdle(q);
+
+ byte *ret = readback.map();
+
+ if((a.flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0)
+ {
+ VmaAllocationInfo info;
+ vmaGetAllocationInfo(allocator, readback.alloc, &info);
+
+ VkMappedMemoryRange range = {
+ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, NULL, info.deviceMemory, info.offset, info.size,
+ };
+
+ vkInvalidateMappedMemoryRanges(device, 1, &range);
+ }
+
+ return ret;
+ };
+
+ struct GPUReadbackFamily
+ {
+ uint32_t family;
+ std::string name;
+ VkQueue q;
+ VkCommandPool pool;
+ VkCommandBuffer cmd;
+ };
+
+ GPUReadbackFamily readbackFamily[3] = {
+ {queueFamilyIndex, "default"},
+ {computeQueueFamilyIndex, "compute"},
+ {transferQueueFamilyIndex, "transfer"},
+ };
+ for(size_t i = 0; i < ARRAY_COUNT(readbackFamily); i++)
+ {
+ GPUReadbackFamily &f = readbackFamily[i];
+ if(f.family == ~0U)
+ continue;
+
+ vkGetDeviceQueue(device, f.family, 0, &f.q);
+
+ CHECK_VKR(vkCreateCommandPool(
+ device,
+ vkh::CommandPoolCreateInfo(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, f.family),
+ NULL, &f.pool));
+
+ CHECK_VKR(vkAllocateCommandBuffers(device, vkh::CommandBufferAllocateInfo(f.pool, 1), &f.cmd));
+
+ scanners.push_back(
+ [this, &readback, f, &blockScan, &gpuReadback](Alloc &a, byte *, VkDeviceSize size) {
+ vkResetCommandBuffer(f.cmd, 0);
+
+ byte *data = gpuReadback(f.q, f.cmd, a);
+ blockScan(0, false, a, data, size);
+ readback.unmap();
+
+ return "gpu_" + f.name + "_direct";
+ });
+
+ scanners.push_back(
+ [this, &readback, f, &blockScan, &gpuReadback](Alloc &a, byte *, VkDeviceSize size) {
+ vkResetCommandBuffer(f.cmd, 0);
+
+ byte *data = gpuReadback(f.q, f.cmd, a);
+ blockScan(128, false, a, data, size);
+ readback.unmap();
+
+ return "gpu_" + f.name + "_128";
+ });
+
+ scanners.push_back(
+ [this, &readback, f, &blockScan, &gpuReadback](Alloc &a, byte *, VkDeviceSize size) {
+ vkResetCommandBuffer(f.cmd, 0);
+
+ byte *data = gpuReadback(f.q, f.cmd, a);
+ blockScan(128, true, a, data, size);
+ readback.unmap();
+
+ return "gpu_" + f.name + "_128_streaming";
+ });
+
+ scanners.push_back(
+ [this, &readback, f, &blockScan, &gpuReadback](Alloc &a, byte *, VkDeviceSize size) {
+ vkResetCommandBuffer(f.cmd, 0);
+
+ byte *data = gpuReadback(f.q, f.cmd, a);
+ blockScan(1024, false, a, data, size);
+ readback.unmap();
+
+ return "gpu_" + f.name + "_1024";
+ });
+
+ scanners.push_back(
+ [this, &readback, f, &blockScan, &gpuReadback](Alloc &a, byte *, VkDeviceSize size) {
+ vkResetCommandBuffer(f.cmd, 0);
+
+ byte *data = gpuReadback(f.q, f.cmd, a);
+ blockScan(1024, true, a, data, size);
+ readback.unmap();
+
+ return "gpu_" + f.name + "_1024_streaming";
+ });
+ }
+
+ typedef std::chrono::high_resolution_clock Clock;
+ typedef std::chrono::time_point Time;
+
+ uint32_t seed = 0x31F10ca8;
+ for(size_t i = 0; i < refDataSize; i++)
+ {
+ seed = (~seed) ^ (seed >> 5);
+ refData[i] = seed & 0xff;
+ }
+
+ while(Running())
+ {
+ for(Alloc &a : allocs)
+ {
+ a.map();
+
+ if(bench)
+ {
+#if defined(WIN32) || defined(_WIN32)
+ char OSName[] = "Windows";
+#else
+ char OSName[] = "Linux";
+#endif
+ TEST_LOG("-------- %s on %s", physProperties.deviceName, OSName);
+
+ memcpy(a.data, refData, a.size);
+ }
+
+ double bestSpeed = 0.0;
+ std::string bestScannerName;
+ for(size_t scan = 0; scan < scanners.size(); scan++)
+ {
+ Time prev = Clock::now();
+
+ std::string scannerName;
+
+ int submitsCompleted = 0;
+ for(uint32_t s = 0; s < submits; s++)
+ {
+ VkCommandBuffer cmd = GetCommandBuffer();
+
+ vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo());
+
+ VkBufferCopy region;
+ region.size = 128;
+ region.srcOffset = 0;
+ region.dstOffset = 128;
+ vkCmdCopyBuffer(cmd, a.buf, a.buf, 1, ®ion);
+
+ vkCmdBeginRenderPass(
+ cmd, vkh::RenderPassBeginInfo(renderPass, framebuffer, mainWindow->scissor,
+ {vkh::ClearValue(0.2f, 0.2f, 0.2f, 1.0f)}),
+ VK_SUBPASS_CONTENTS_INLINE);
+
+ vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
+ vkCmdSetViewport(cmd, 0, 1, &mainWindow->viewport);
+ vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor);
+ vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0});
+ vkCmdDraw(cmd, 3, 1, 0, 0);
+
+ vkCmdEndRenderPass(cmd);
+
+ vkEndCommandBuffer(cmd);
+
+ if(bench)
+ scannerName = scanners[scan](a, a.data, a.size);
+
+ Submit(99, 99, {cmd});
+ submitsCompleted++;
+
+ if(bench)
+ {
+ Time cur = Clock::now();
+ double timeMS =
+ double(std::chrono::duration_cast(cur - prev).count()) /
+ 1000.0;
+ if(timeMS > 10000)
+ break;
+ }
+ }
+
+ if(!bench)
+ break;
+
+ Time cur = Clock::now();
+ double timeMS =
+ double(std::chrono::duration_cast(cur - prev).count()) /
+ 1000.0;
+
+ std::string data = a.name;
+ data.resize(32, ' ');
+ data += "scanned by ";
+ data += scannerName;
+ data.resize(70, ' ');
+ double speed = double((submitsCompleted * a.size) >> 20) / double(timeMS / 1000.0);
+ data += fmt::format("{:8.2f} MS for {} submits = {:8.2f} MB/s", timeMS, submitsCompleted,
+ speed);
+ TEST_LOG("%s", data.c_str());
+ if(speed > bestSpeed * 1.02)
+ {
+ bestScannerName = scannerName;
+ bestSpeed = speed;
+ }
+ }
+
+ if(bench)
+ {
+ TEST_LOG("--------");
+ TEST_LOG("%s's best scanner is %s", a.name.c_str(), bestScannerName.c_str());
+ TEST_LOG("--------");
+ }
+
+ a.unmap();
+ }
+ if(bench)
+ TEST_LOG("");
+
+ {
+ VkCommandBuffer cmd = GetCommandBuffer();
+
+ vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo());
+
+ VkImage swapimg =
+ StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL);
+
+ vkh::cmdPipelineBarrier(
+ cmd, {
+ vkh::ImageMemoryBarrier(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL,
+ VK_IMAGE_LAYOUT_GENERAL, img.image),
+ });
+
+ blitToSwap(cmd, img.image, VK_IMAGE_LAYOUT_GENERAL, swapimg, VK_IMAGE_LAYOUT_GENERAL);
+
+ FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL);
+
+ vkEndCommandBuffer(cmd);
+
+ Submit(0, 1, {cmd});
+ }
+
+ Present();
+ }
+
+ for(size_t i = 0; i < ARRAY_COUNT(readbackFamily); i++)
+ vkDestroyCommandPool(device, readbackFamily[i].pool, NULL);
+ for(Alloc &a : allocs)
+ {
+ vkDestroyBuffer(device, a.buf, NULL);
+ vkFreeMemory(device, a.mem, NULL);
+ }
+
+ return 0;
+ }
+};
+
+REGISTER_TEST();
diff --git a/util/test/demos/vk/vk_parameter_zoo.cpp b/util/test/demos/vk/vk_parameter_zoo.cpp
index d5808f29a..9ab2f60f6 100644
--- a/util/test/demos/vk/vk_parameter_zoo.cpp
+++ b/util/test/demos/vk/vk_parameter_zoo.cpp
@@ -1740,7 +1740,8 @@ void main()
setMarker(queue, "before_empty");
{
- VkSubmitInfo submit = vkh::SubmitInfo({});
+ std::vector cmds = {};
+ VkSubmitInfo submit = vkh::SubmitInfo(cmds);
CHECK_VKR(vkQueueSubmit(queue, 1, &submit, VK_NULL_HANDLE));
}
if(hasExt(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME))