blob: fa3a6971f49c4c0d7d1f3dc7675cc8db13901566 [file]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <chrono>
#include <vector>
#include <vulkan/vulkan.h>
#define PRINT_STDERR(format, ...) \
fprintf(stderr, "%s:%d " format "\n", __FILE__, __LINE__, ##__VA_ARGS__)
class VkCopyTest {
public:
VkCopyTest(uint32_t buffer_size) : buffer_size_(buffer_size) {}
bool Initialize();
bool Exec();
private:
bool InitVulkan();
bool InitBuffers(uint32_t buffer_size);
bool is_initialized_ = false;
uint32_t buffer_size_;
VkPhysicalDevice vk_physical_device_;
VkDevice vk_device_;
VkQueue vk_queue_;
VkBuffer vk_buffer_[2];
VkDeviceMemory vk_device_memory_[0];
VkCommandPool vk_command_pool_;
VkCommandBuffer vk_command_buffer_;
};
bool VkCopyTest::Initialize() {
if (is_initialized_)
return false;
if (!InitVulkan()) {
PRINT_STDERR("failed to initialize Vulkan");
return false;
}
if (!InitBuffers(buffer_size_)) {
PRINT_STDERR("InitBuffers failed");
return false;
}
is_initialized_ = true;
return true;
}
bool VkCopyTest::InitVulkan() {
VkInstanceCreateInfo create_info{
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0, // VkInstanceCreateFlags flags;
nullptr, // const VkApplicationInfo* pApplicationInfo;
0, // uint32_t enabledLayerCount;
nullptr, // const char* const* ppEnabledLayerNames;
0, // uint32_t enabledExtensionCount;
nullptr, // const char* const* ppEnabledExtensionNames;
};
VkAllocationCallbacks* allocation_callbacks = nullptr;
VkInstance instance;
VkResult result;
result = vkCreateInstance(&create_info, allocation_callbacks, &instance);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkCreateInstance failed %d", result);
return false;
}
uint32_t physical_device_count;
result = vkEnumeratePhysicalDevices(instance, &physical_device_count, nullptr);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkEnumeratePhysicalDevices failed %d", result);
return false;
}
if (physical_device_count < 1) {
PRINT_STDERR("unexpected physical_device_count %d", physical_device_count);
return false;
}
std::vector<VkPhysicalDevice> physical_devices(physical_device_count);
result = vkEnumeratePhysicalDevices(instance, &physical_device_count, physical_devices.data());
if (result != VK_SUCCESS) {
PRINT_STDERR("vkEnumeratePhysicalDevices failed %d", result);
return false;
}
uint32_t queue_family_count;
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[0], &queue_family_count, nullptr);
if (queue_family_count < 1) {
PRINT_STDERR("invalid queue_family_count %d", queue_family_count);
return false;
}
std::vector<VkQueueFamilyProperties> queue_family_properties(queue_family_count);
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[0], &queue_family_count,
queue_family_properties.data());
int32_t queue_family_index = -1;
for (uint32_t i = 0; i < queue_family_count; i++) {
if (queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
queue_family_index = i;
break;
}
}
if (queue_family_index < 0) {
PRINT_STDERR("couldn't find an appropriate queue");
return false;
}
float queue_priorities[1] = {0.0};
VkDeviceQueueCreateInfo queue_create_info = {.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueFamilyIndex = 0,
.queueCount = 1,
.pQueuePriorities = queue_priorities};
VkDeviceCreateInfo createInfo = {.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queue_create_info,
.enabledLayerCount = 0,
.ppEnabledLayerNames = nullptr,
.enabledExtensionCount = 0,
.ppEnabledExtensionNames = nullptr,
.pEnabledFeatures = nullptr};
VkDevice vkdevice;
if ((result = vkCreateDevice(physical_devices[0], &createInfo, nullptr /* allocationcallbacks */,
&vkdevice)) != VK_SUCCESS) {
PRINT_STDERR("vkCreateDevice failed: %d", result);
return false;
}
vk_physical_device_ = physical_devices[0];
vk_device_ = vkdevice;
vkGetDeviceQueue(vkdevice, queue_family_index, 0, &vk_queue_);
return true;
}
bool VkCopyTest::InitBuffers(uint32_t buffer_size) {
VkResult result;
VkPhysicalDeviceMemoryProperties memory_props;
vkGetPhysicalDeviceMemoryProperties(vk_physical_device_, &memory_props);
uint32_t memory_type = 0;
for (; memory_type < 32; memory_type++) {
if (memory_props.memoryTypes[memory_type].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
break;
}
if (memory_type >= 32) {
PRINT_STDERR("Can't find compatible mappable memory for image");
return false;
}
for (uint32_t i = 0; i < 2; i++) {
VkBufferCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = buffer_size,
.usage = (i == 0) ? VK_BUFFER_USAGE_TRANSFER_SRC_BIT : VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
result = vkCreateBuffer(vk_device_, &create_info, nullptr, &vk_buffer_[i]);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkCreateBuffer failed: %d", result);
return false;
}
VkMemoryAllocateInfo alloc_info = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = nullptr,
.allocationSize = buffer_size,
.memoryTypeIndex = memory_type,
};
result = vkAllocateMemory(vk_device_, &alloc_info, nullptr, &vk_device_memory_[i]);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkAllocateMemory failed: %d", result);
return false;
}
void* addr;
result = vkMapMemory(vk_device_, vk_device_memory_[i], 0, VK_WHOLE_SIZE, 0, &addr);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkMapMeory failed: %d", result);
return false;
}
memset(addr, (uint8_t)i, buffer_size);
vkUnmapMemory(vk_device_, vk_device_memory_[i]);
result = vkBindBufferMemory(vk_device_, vk_buffer_[i], vk_device_memory_[i], 0);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkBindBufferMemory failed: %d", result);
return false;
}
}
VkCommandPoolCreateInfo command_pool_create_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueFamilyIndex = 0,
};
result = vkCreateCommandPool(vk_device_, &command_pool_create_info, nullptr, &vk_command_pool_);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkCreateCommandPool failed: %d", result);
return false;
}
VkCommandBufferAllocateInfo command_buffer_create_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.pNext = nullptr,
.commandPool = vk_command_pool_,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1};
result = vkAllocateCommandBuffers(vk_device_, &command_buffer_create_info, &vk_command_buffer_);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkAllocateCommandBuffers failed: %d", result);
return false;
}
VkCommandBufferBeginInfo begin_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = nullptr,
.flags = 0,
.pInheritanceInfo = nullptr, // ignored for primary buffers
};
result = vkBeginCommandBuffer(vk_command_buffer_, &begin_info);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkBeginCommandBuffer failed: %d", result);
return false;
}
VkBufferCopy copy_region = {
.srcOffset = 0,
.dstOffset = 0,
.size = buffer_size,
};
vkCmdCopyBuffer(vk_command_buffer_, vk_buffer_[0], vk_buffer_[1], 1, &copy_region);
result = vkEndCommandBuffer(vk_command_buffer_);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkEndCommandBuffer failed: %d", result);
return false;
}
return true;
}
bool VkCopyTest::Exec() {
VkSubmitInfo submit_info = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreCount = 0,
.pWaitSemaphores = nullptr,
.pWaitDstStageMask = nullptr,
.commandBufferCount = 1,
.pCommandBuffers = &vk_command_buffer_,
.signalSemaphoreCount = 0,
.pSignalSemaphores = nullptr,
};
VkResult result;
if ((result = vkQueueSubmit(vk_queue_, 1, &submit_info, VK_NULL_HANDLE)) != VK_SUCCESS) {
PRINT_STDERR("vkQueueSubmit failed");
return false;
}
vkQueueWaitIdle(vk_queue_);
return true;
}
int main(void) {
uint32_t buffer_size = 60 * 1024 * 1024;
uint32_t iterations = 1000;
VkCopyTest app(buffer_size);
if (!app.Initialize()) {
PRINT_STDERR("could not initialize app");
return -1;
}
printf("Copying buffer_size %u iterations %u...", buffer_size, iterations);
fflush(stdout);
auto start = std::chrono::high_resolution_clock::now();
for (uint32_t iter = 0; iter < iterations; iter++) {
if (!app.Exec()) {
PRINT_STDERR("Exec failed");
return -1;
}
}
std::chrono::duration<double> elapsed = std::chrono::high_resolution_clock::now() - start;
printf("copy rate %g MB/s", (double)buffer_size * iterations / 1024 / 1024 / elapsed.count());
return 0;
}