mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-26 17:31:42 +00:00
* In a previous update in 2021 many copyright ranges were truncated accidentally, and some files have been copy-pasted with wrong years. These dates have been fixed based on git history and original copyright messages.
750 lines
22 KiB
C++
750 lines
22 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2023-2026 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 <chrono>
|
|
#include "3rdparty/fmt/core.h"
|
|
#include "vk_test.h"
|
|
|
|
#if defined(SSE_TEST)
|
|
#include <immintrin.h>
|
|
#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__) || \
|
|
(defined(__riscv) && __riscv_xlen == 64)
|
|
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, <d> and <s>
|
|
* are aligned to a 16-byte boundary or <len> == 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<Alloc> 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[(uint32_t)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<std::string(Alloc &, byte *, VkDeviceSize)>;
|
|
|
|
byte *scratch = new byte[65536];
|
|
|
|
std::vector<ScanFunction> scanners;
|
|
auto blockScan = [scratch](VkDeviceSize blockSize, bool streamMemcpy, Alloc &, byte *data,
|
|
VkDeviceSize size) {
|
|
if(blockSize == 0)
|
|
{
|
|
FindDiffRange_shipping::FindDiffRange(data, refData, (size_t)size, dummyStart, dummyEnd);
|
|
return;
|
|
}
|
|
|
|
for(VkDeviceSize i = 0; i < size; i += blockSize)
|
|
{
|
|
size_t chunkSize = (size_t)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<VkCommandBuffer> 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<Clock> 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, (size_t)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<std::chrono::microseconds>(cur - prev).count()) /
|
|
1000.0;
|
|
if(timeMS > 10000)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!bench)
|
|
break;
|
|
|
|
Time cur = Clock::now();
|
|
double timeMS =
|
|
double(std::chrono::duration_cast<std::chrono::microseconds>(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();
|