vkbench: initial commit
This is the initial commit of vkbench. And contains a example test,
submitTest, which submit empty command buffers into the queue.
BUG=chromium:936705
TEST=build and run 'vkbench --notemp'
Change-Id: Ica55d97ac2348da7527cd17418fe4a989d30748e
Signed-off-by: Po-Hsien Wang <pwang@chromium.org>
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..f1503a0
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.7)
+project(vkbench)
+
+set(CMAKE_VERBOSE_MAKEFILE:BOOL ON)
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_BUILD_TYPE Debug)
+
+find_package(Vulkan REQUIRED)
+
+include_directories(.)
+include_directories(${Vulkan_INCLUDE_DIRS})
+link_libraries(${Vulkan_LIBRARIES})
+
+add_executable(vkbench src/main.cc src/utils.cc src/testBase.cc src/testUtil.cc
+ src/submitTest.cc src/submitTest.h)
diff --git a/src/main.cc b/src/main.cc
new file mode 100644
index 0000000..2027207
--- /dev/null
+++ b/src/main.cc
@@ -0,0 +1,207 @@
+// Copyright 2019 The Chromium OS 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 <getopt.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <algorithm>
+#include <iterator>
+#include <map>
+#include <vector>
+
+#include "submitTest.h"
+#include "utils.h"
+
+// g_iteration determines the total iteration to run for each test
+int g_iteration = 1;
+// g_verbose determine the logging level to print into the screen.
+int g_verbose = false;
+// g_vlayer enables the vulkan verification layer if it is set.
+int g_vlayer = false;
+// g_notemp skips the temperature checking if it is set.
+int g_notemp = false;
+
+// all_tests list all the available tests.
+std::vector<vkbench::testBase*> all_tests = {
+ new vkbench::SubmitTest(10),
+ new vkbench::SubmitTest(100),
+ new vkbench::SubmitTest(1000),
+ new vkbench::SubmitTest(10000),
+};
+// kLongOptions defines the options for argument options.
+const static struct option kLongOptions[] = {
+ {"iterations", required_argument, nullptr, 'i'},
+ {"tests", required_argument, nullptr, 't'},
+ {"blacklist", required_argument, nullptr, 'b'},
+ {"list", no_argument, nullptr, 'l'},
+ {"help", no_argument, nullptr, 'h'},
+ {"notemp", no_argument, &g_notemp, 1},
+ {"vlayer", no_argument, &g_vlayer, 1},
+ {"verbose", no_argument, &g_verbose, 1},
+ {0, 0, 0, 0}};
+
+// TimeTest times the test by looping it iteration times.
+inline uint64_t TimeTest(vkbench::testBase* test, uint64_t iteration) {
+ uint64_t start = GetUTime();
+ bool passed = true;
+ try {
+ test->TestSetup();
+ for (int i = 0; i < iteration && passed; i++) {
+ passed = test->TestRun();
+ }
+ test->TestCleanup();
+ } catch (std::system_error e) {
+ DEBUG("Error while timing test %s.", test->Name())
+ throw e;
+ }
+ if (!passed) {
+ return std::numeric_limits<uint64_t>::max();
+ }
+ return GetUTime() - start;
+}
+
+// Run the test and pre/post processes.
+// @param target_temperature: The test would try to start until the machine get
+// to this temperature unless notemp is set.
+// @param duration_us: The test would to iterate till duration_us is reached.
+// @return: true if the test passes.
+bool Run(vkbench::testBase* test,
+ const double init_temperature,
+ const uint64_t duration_us = 1000000) {
+ try {
+ test->VkInitialization();
+ test->TestInitialization();
+ if (!g_notemp) {
+ double temperature = 0;
+ double cooldown_temperature = std::min(45.0, init_temperature + 6.0);
+ double
+ wait = WaitForCoolMachine(cooldown_temperature, 30.0, &temperature);
+ LOG("Waited %f second for machine temperature to cool down to %f.", wait,
+ temperature)
+ }
+ // Do some iterations since initial timings may vary.
+ TimeTest(test, 2);
+ // Target minimum iteration is 1s for each test.
+ uint64_t time = 0;
+ uint64_t iteration = 1;
+ double score = -1.f;
+ do {
+ time = TimeTest(test, iteration);
+ DEBUG("iterations: %llu, time: %llu us, time/iter: %llu us", iteration,
+ time, time / iteration)
+ if (time > duration_us) {
+ if (time == std::numeric_limits<uint64_t>::max()) {
+ return false;
+ } else {
+ score = time / iteration;
+ break;
+ }
+ }
+ iteration = iteration * 2;
+ } while ((1ull << 40) > iteration);
+// Returns 0.0 if it ran max iterations in less than test time.
+ if (score <= 0.01f) LOG("%s: measurement may not be accurate.",
+ test->Name())
+ score = test->FormatMeasurement(score);
+ LOG("@RESULT: %46s = %10.2f %-15s", test->Name(), score, test->Unit())
+
+ } catch (std::system_error e) {
+ DEBUG("Error while running test: %s", e.what())
+ return false;
+ }
+ test->TestDestroy();
+ test->VkDestroy();
+ return true;
+}
+
+void PrintTests() {
+ for (const auto& test : all_tests) {
+ LOG("%s: %s", test->Name(), test->Desp())
+ }
+}
+
+void PrintHelp() {
+ LOG(R",(
+Usage: vkbench [OPTIONS]
+ -i, --iterations=N Specify the iterations to run the tests.
+ -t, --tests=TESTS Tests to run in colon separated form.
+ -b, --blacklist=TESTS Tests to not run in colon separated form.
+ --list List the tests available.
+ --verbose Show verbose messages.
+ --vlayer Enable vulkan verification layer.
+ --notemp Do not wait for temperature to cool down.),")
+}
+
+bool ParseArgv(int argc, char** argv) {
+ std::vector<std::string> enabled_tests;
+ std::vector<std::string> disabled_tests;
+ int c;
+ int option_index = 0;
+ while ((c = getopt_long(argc, argv, "i:t:b:", kLongOptions, &option_index)) !=
+ -1) {
+ if (c == 'i') {
+ g_iteration = atoi(optarg);
+ } else if (c == 't') {
+ enabled_tests = SplitString(std::string(optarg), ':');
+ } else if (c == 'b') {
+ disabled_tests = SplitString(std::string(optarg), ':');
+ } else if (c == 'l') {
+ PrintTests();
+ return false;
+ } else if (c == '?' || c == 'h') {
+ PrintHelp();
+ return false;
+ }
+ }
+
+ if (optind < argc) {
+ ERROR("Unknown argv: ")
+ while (optind < argc) ERROR("%s ", argv[optind++])
+ return false;
+ }
+
+ all_tests.erase(
+ remove_if(
+ all_tests.begin(), all_tests.end(),
+ [enabled_tests, disabled_tests](const vkbench::testBase* test) {
+ bool isDisabled =
+ !IsItemInVector(enabled_tests, test->Name(), true) ||
+ IsItemInVector(disabled_tests, test->Name(), false);
+ if (isDisabled)
+ delete test;
+ return isDisabled;
+ }),
+ all_tests.end());
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ if (!ParseArgv(argc, argv))
+ return 0;
+
+ double init_temperature = g_notemp ? 40 : GetMachineTemperature();
+ std::map<const char*, int> failed_test;
+ LOG("@TEST_BEGIN")
+ PrintDateTime();
+ for (auto i = 0; i<all_tests.size(); i++ ){
+ auto &test = all_tests[i];
+ for (auto iter = 0; iter < g_iteration; i++) {
+ if (!Run(test, init_temperature, 1000000 )) {
+ failed_test[test->Name()] += 1;
+ }
+ }
+ }
+ PrintDateTime();
+ LOG("@TEST_END")
+
+ for (auto& keyval : failed_test) {
+ LOG("%s failed %d times.", keyval.first, keyval.second)
+ }
+
+ for (auto& test : all_tests) {
+ delete test;
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/src/submitTest.cc b/src/submitTest.cc
new file mode 100644
index 0000000..efba1b4
--- /dev/null
+++ b/src/submitTest.cc
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium OS 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 "submitTest.h"
+#include "testUtil.h"
+
+namespace vkbench {
+void SubmitTest::TestInitialization() {
+ fences_.push_back(device_.createFence({vk::FenceCreateFlagBits::eSignaled}));
+ // Create empty cmdBuffer
+ cmd_buffers_ = device_.allocateCommandBuffers(
+ {cmd_pool_, vk::CommandBufferLevel::ePrimary, 1});
+ cmd_buffers_[0].begin({vk::CommandBufferUsageFlagBits::eSimultaneousUse});
+ cmd_buffers_[0].end();
+
+ for (auto& t : smt_infos_) {
+ t.setCommandBufferCount(1);
+ t.setPCommandBuffers(&cmd_buffers_[0]);
+ }
+}
+
+bool SubmitTest::TestRun() {
+ device_.waitForFences(fences_, VK_TRUE, std::numeric_limits<uint64_t>::max());
+ device_.resetFences(fences_);
+ gfx_queue_.submit(smt_infos_, fences_[0], {});
+ return true;
+}
+
+void SubmitTest::TestCleanup() {
+ device_.waitForFences(fences_, VK_TRUE, std::numeric_limits<uint64_t>::max());
+}
+
+void SubmitTest::TestDestroy() {
+ if (!device_)
+ return;
+
+ if (cmd_pool_)
+ device_.freeCommandBuffers(cmd_pool_, cmd_buffers_);
+ for (auto &fence : fences_) {
+ device_.destroyFence(fence);
+ }
+}
+} // namespace vkbench
diff --git a/src/submitTest.h b/src/submitTest.h
new file mode 100644
index 0000000..8a5c647
--- /dev/null
+++ b/src/submitTest.h
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VKBENCH_SUBMITTEST_H
+#define VKBENCH_SUBMITTEST_H
+
+#include "testBase.h"
+
+namespace vkbench {
+class SubmitTest : public testBase {
+ public:
+ SubmitTest(uint64_t submitCnt){
+ sprintf(name_, "SubmitTest@%lu", submitCnt);
+ sprintf(desp_, "Times the time used when submitting %lu empty calls.",
+ submitCnt);
+ smt_infos_.resize(submitCnt);
+ }
+ const char* Name() const override { return name_; }
+ const char* Desp() const override { return desp_; }
+ ~SubmitTest() override = default;
+ const char* Unit() const override { return "us"; }
+
+ protected:
+ void TestInitialization() override;
+ bool TestRun() override;
+ void TestCleanup() override;
+ void TestDestroy() override;
+
+ private:
+ char name_[100];
+ char desp_[1024];
+ std::vector<vk::Fence> fences_;
+ std::vector<vk::SubmitInfo> smt_infos_;
+ std::vector<vk::CommandBuffer> cmd_buffers_;
+ DISALLOW_COPY_AND_ASSIGN(SubmitTest);
+};
+} // namespace vkbench
+#endif // VKBENCH_SUBMITTEST_H
diff --git a/src/testBase.cc b/src/testBase.cc
new file mode 100644
index 0000000..6c2834a
--- /dev/null
+++ b/src/testBase.cc
@@ -0,0 +1,131 @@
+// Copyright 2019 The Chromium OS 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 "testBase.h"
+#include <unistd.h>
+#include <limits>
+#include <vulkan/vulkan.hpp>
+#include "utils.h"
+#include "testUtil.h"
+
+extern int g_vlayer;
+
+namespace vkbench {
+void testBase::VkInitialization() {
+ CreateInstance();
+ ChoosePhysical_device_();
+ CreateLogicalDevice();
+ CreateCommandPool();
+}
+
+VKAPI_ATTR VkBool32 VKAPI_CALL
+ValidationCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
+ VkDebugUtilsMessageTypeFlagsEXT messageType,
+ const VkDebugUtilsMessengerCallbackDataEXT* kPcallbackData,
+ void* pUserData) {
+ UNUSED(messageType);
+ UNUSED(pUserData);
+ if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
+ DEBUG("%s", kPcallbackData->pMessage)
+ } else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT ||
+ messageSeverity ==
+ VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
+ LOG("%s", kPcallbackData->pMessage)
+ } else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
+ ERROR("%s", kPcallbackData->pMessage)
+ } else {
+ ABORT(1, "%s", kPcallbackData->pMessage)
+ }
+ return VK_FALSE;
+}
+
+bool testBase::CreateInstance() {
+ const std::vector<const char*> kDebugExtension = {"VK_EXT_debug_utils"};
+ vk::ApplicationInfo
+ appInfo("vkbench", 1, "Vulkan.hpp", 1, VK_API_VERSION_1_1);
+ vk::InstanceCreateInfo createInfo({}, &appInfo);
+
+ if (g_vlayer) {
+ if (!IsLayersSupported(kValidation_layers_) ||
+ !IsExtensionSupported(kDebugExtension)) {
+ DEBUG("Validation layer is not supported. Less log will be printed.")
+ enable_validation_layer_ = false;
+ } else {
+ createInfo.setEnabledExtensionCount(kDebugExtension.size())
+ .setPpEnabledExtensionNames(kDebugExtension.data())
+ .setEnabledLayerCount(kValidation_layers_.size())
+ .setPpEnabledLayerNames(kValidation_layers_.data());
+ enable_validation_layer_ = true;
+ }
+ }
+ instance_ = vk::createInstance(createInfo);
+ if (enable_validation_layer_) {
+ vk::DebugUtilsMessengerCreateInfoEXT debugCreateInfo;
+ debugCreateInfo
+ .setMessageSeverity(
+ vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning
+ | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError)
+ .setMessageType(vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral
+ | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation)
+ .setPfnUserCallback(ValidationCallback);
+ CreateDebugUtilsMessengerEXT(instance_,
+ &debugCreateInfo,
+ nullptr,
+ &debug_messenger_);
+ }
+ return true;
+}
+
+bool testBase::ChoosePhysical_device_() {
+ if (!instance_)
+ return false;
+ std::vector<vk::PhysicalDevice>
+ physical_devices = instance_.enumeratePhysicalDevices();
+ if (physical_devices.size() == 0) {
+ ERROR("No available Vk physical_device_.")
+ return false;
+ }
+ // Choose the first device we found.
+ physical_device_ = physical_devices[0];
+ gfx_queue_idx_ = ChooseGFXQueueFamilies(physical_device_);
+ return true;
+}
+
+bool testBase::CreateLogicalDevice() {
+ float tmp = 1.0f;
+ vk::DeviceQueueCreateInfo queueCreateInfo({}, gfx_queue_idx_, 1, &tmp);
+ vk::DeviceCreateInfo deviceCreateInfo({}, 1, &queueCreateInfo);
+ if (enable_validation_layer_) {
+ deviceCreateInfo.setEnabledLayerCount(kValidation_layers_.size());
+ deviceCreateInfo.setPpEnabledLayerNames(kValidation_layers_.data());
+ }
+ device_ = physical_device_.createDevice(deviceCreateInfo);
+ gfx_queue_ = device_.getQueue(gfx_queue_idx_, /* Queue Index */ 0);
+ return true;
+}
+
+bool testBase::CreateCommandPool() {
+ cmd_pool_ =
+ device_.createCommandPool({vk::CommandPoolCreateFlags(), gfx_queue_idx_});
+ return true;
+}
+
+void testBase::VkDestroy() {
+ if (!instance_) {
+ ERROR("Can't destroy any resource without a valid vulkan instance.")
+ return;
+ }
+ if (device_) {
+ if (cmd_pool_) {
+ device_.destroy(cmd_pool_, nullptr);
+ }
+ device_.destroy();
+ }
+ if (enable_validation_layer_) {
+ DestroyDebugUtilsMessengerEXT(instance_, debug_messenger_, nullptr);
+ }
+ instance_.destroy();
+}
+
+} // namespace vkbench
\ No newline at end of file
diff --git a/src/testBase.h b/src/testBase.h
new file mode 100644
index 0000000..e5cf210
--- /dev/null
+++ b/src/testBase.h
@@ -0,0 +1,68 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef __VKBENCH_TESTBASE_H__
+#define __VKBENCH_TESTBASE_H__
+
+#include <string>
+#include <vector>
+#include <vulkan/vulkan.hpp>
+
+#include "utils.h"
+
+namespace vkbench {
+class testBase {
+ public:
+ virtual ~testBase() = default;
+ // Name of test.
+ virtual const char* Name() const = 0;
+ // Description of the test.
+ virtual const char* Desp() const = 0;
+ // Unit for formatted measurement.
+ virtual const char* Unit() const = 0;
+ // Given time elapse per iteration, format it into meaningful numbers.
+ virtual double FormatMeasurement(double time) { return time; }
+
+ // Vulkan initialization for allocating universal resources. Must be called
+ // before TestInitialization.
+ virtual void VkInitialization();
+ // Test related resources allocation. The resources allocated here would be
+ // shared by all TestRun iterations. Time spent here will not be recorded.
+ virtual void TestInitialization(){}
+ // Test configuration before running the test. This will run once before
+ // each TestRun loop. Time spent here will be recorded.
+ virtual void TestSetup(){}
+ // Test body. Time spent will be recorded.
+ virtual bool TestRun() = 0;
+ // Test cleanup after looping TestRun. Time spent here will be recorded.
+ virtual void TestCleanup() = 0;
+ // Free and Destroy any resources allocated during TestInitialization. Time
+ // spent here will not be recorded.
+ virtual void TestDestroy() = 0;
+ // Free and Destroy any general vulkan resources
+ virtual void VkDestroy();
+
+ protected:
+ // Vulkan general method.
+ virtual bool CreateInstance();
+ virtual bool ChoosePhysical_device_();
+ virtual bool CreateLogicalDevice();
+ virtual bool CreateCommandPool();
+
+ // Vulkan handles
+ const std::vector<const char*> kValidation_layers_ = {
+ "VK_LAYER_LUNARG_standard_validation", "VK_LAYER_GOOGLE_threading"};
+ bool enable_validation_layer_ = false;
+
+ vk::DebugUtilsMessengerEXT debug_messenger_;
+ vk::Instance instance_;
+ vk::PhysicalDevice physical_device_;
+ vk::Device device_;
+ vk::Queue gfx_queue_;
+ vk::CommandPool cmd_pool_;
+ uint32_t gfx_queue_idx_ = -1;
+};
+
+} // namespace vkbench
+#endif
diff --git a/src/testUtil.cc b/src/testUtil.cc
new file mode 100644
index 0000000..6e84530
--- /dev/null
+++ b/src/testUtil.cc
@@ -0,0 +1,97 @@
+// Copyright 2019 The Chromium OS 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 <limits>
+#include <vulkan/vulkan.hpp>
+#include "testUtil.h"
+#include "utils.h"
+
+bool vkbench::IsLayersSupported(const std::vector<const char*>& kLayers) {
+ std::vector<vk::LayerProperties>
+ availLayers = vk::enumerateInstanceLayerProperties();
+ for (const auto& layer : kLayers) {
+ bool found = false;
+ for (const auto availLayer : availLayers) {
+ if (!strcmp(availLayer.layerName, layer)) {
+ DEBUG("%s Layer is support.", layer)
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ DEBUG("Layer %s is not support.", layer)
+ return false;
+ }
+ }
+ return true;
+}
+
+bool vkbench::IsExtensionSupported(
+ const std::vector<const char*>& kExtensions) {
+ std::vector<vk::ExtensionProperties>
+ availExtensions = vk::enumerateInstanceExtensionProperties();
+ for (const auto extension : kExtensions) {
+ bool found = false;
+ for (const auto availExtension : availExtensions) {
+ if (!strcmp(availExtension.extensionName, extension)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ DEBUG("Extension %s is not supported.", extension)
+ return false;
+ }
+ }
+ return true;
+}
+
+vk::Result vkbench::CreateDebugUtilsMessengerEXT(
+ vk::Instance instance,
+ const vk::DebugUtilsMessengerCreateInfoEXT* kPcreateInfo,
+ const vk::AllocationCallbacks* kPallocator,
+ vk::DebugUtilsMessengerEXT* pdebug_messengeer) {
+ auto func = (PFN_vkCreateDebugUtilsMessengerEXT) instance.getProcAddr(
+ "vkCreateDebugUtilsMessengerEXT");
+ if (func != nullptr) {
+ return static_cast<vk::Result>(func(
+ instance,
+ reinterpret_cast<const VkDebugUtilsMessengerCreateInfoEXT*>(
+ kPcreateInfo),
+ reinterpret_cast<const VkAllocationCallbacks*>(kPallocator),
+ reinterpret_cast<VkDebugUtilsMessengerEXT*>(pdebug_messengeer)));
+ } else {
+ return vk::Result::eErrorExtensionNotPresent;
+ }
+}
+
+void vkbench::DestroyDebugUtilsMessengerEXT(
+ vk::Instance instance,
+ vk::DebugUtilsMessengerEXT debug_messengeer,
+ const vk::AllocationCallbacks* kPAllocator) {
+ auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) instance.getProcAddr(
+ "vkDestroyDebugUtilsMessengerEXT");
+ if (func != nullptr) {
+ func(instance,
+ static_cast<VkDebugUtilsMessengerEXT>(debug_messengeer),
+ reinterpret_cast<const VkAllocationCallbacks*>(
+ static_cast<const vk::AllocationCallbacks*>(kPAllocator)));
+ }
+}
+
+uint32_t vkbench::ChooseGFXQueueFamilies(
+ const vk::PhysicalDevice& kPhysical_device) {
+ uint32_t gfxQueueIdx = UINT32_MAX;
+ std::vector<vk::QueueFamilyProperties>
+ props = kPhysical_device.getQueueFamilyProperties();
+ for (uint32_t i = 0; i < props.size(); i++) {
+ if (props[i].queueCount <= 0)
+ continue;
+ if (props[i].queueFlags & vk::QueueFlagBits::eGraphics) {
+ gfxQueueIdx = i;
+ break;
+ }
+ }
+ return gfxQueueIdx;
+}
\ No newline at end of file
diff --git a/src/testUtil.h b/src/testUtil.h
new file mode 100644
index 0000000..0a87ebd
--- /dev/null
+++ b/src/testUtil.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef __VKBENCH_TESTUTIL_H__
+#define __VKBENCH_TESTUTIL_H__
+#include <string>
+#include <vulkan/vulkan.hpp>
+
+namespace vkbench {
+bool IsLayersSupported(const std::vector<const char*>& kLayers);
+bool IsExtensionSupported(const std::vector<const char*>& kExtensions);
+uint32_t ChooseGFXQueueFamilies(const vk::PhysicalDevice& kPhysical_device);
+vk::Result CreateDebugUtilsMessengerEXT(
+ vk::Instance instance,
+ const vk::DebugUtilsMessengerCreateInfoEXT* kPcreateInfo,
+ const vk::AllocationCallbacks* kPallocator,
+ vk::DebugUtilsMessengerEXT* pdebug_messengeer);
+void DestroyDebugUtilsMessengerEXT(vk::Instance instance,
+ vk::DebugUtilsMessengerEXT debug_messengeer,
+ const vk::AllocationCallbacks* kPAllocator);
+} // namespace vkbench
+#endif
diff --git a/src/utils.cc b/src/utils.cc
new file mode 100644
index 0000000..6d665a8
--- /dev/null
+++ b/src/utils.cc
@@ -0,0 +1,101 @@
+// Copyright 2019 The Chromium OS 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 "utils.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <chrono>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+const std::string kTemperatureScript =
+ "/usr/local/autotest/bin/temperature.py --maximum";
+
+void PrintDateTime() {
+ time_t timer;
+ char buffer[26];
+ time(&timer);
+ struct tm* tm_info = localtime(&timer);
+ strftime(buffer, 26, "%Y-%m-%d %H:%M:%S", tm_info);
+ LOG("# DateTime: %s", buffer)
+}
+
+/* Execute a shell command and return its file descriptor for reading output. */
+/* @param command: command to be run. */
+/* @param result: the stdout of the command output. */
+/* @return true if the command is executed successfully. */
+bool ExecuteCommand(const std::string& kCommand,
+ std::string* result = nullptr) {
+ FILE* fd = popen(kCommand.c_str(), "r");
+ if (!fd) {
+ return false;
+ }
+
+ if (result) {
+ fseek(fd, 0, SEEK_END);
+ long size = ftell(fd);
+ fseek(fd, 0, SEEK_SET);
+ fread(&result[0], sizeof(char), size, fd);
+ }
+ return pclose(fd) == 0;
+}
+
+std::vector<std::string> SplitString(const std::string& kInput,
+ char delimiter) {
+ std::vector<std::string> result;
+ std::stringstream srcStream(kInput);
+
+ std::string token;
+ while (getline(srcStream, token, delimiter)) {
+ result.push_back(token);
+ }
+ return result;
+}
+
+// Returns currently measured temperature.
+double GetMachineTemperature() {
+ std::string temp_str;
+ double temperature;
+ if (!ExecuteCommand(kTemperatureScript, &temp_str)) {
+ return 10000;
+ }
+ temperature = strtod(temp_str.c_str(), nullptr);
+ if (temperature < 10.0 || temperature > 150.0) {
+ DEBUG("Warning: ignoring temperature reading of %f'C.", temperature)
+ }
+ return temperature;
+}
+
+// Waits up to timeout seconds to reach cold_temperature in Celsius.
+// @param cold_temperature: the target temperature in celsius.
+// @param timeout: the timeout to wait.
+// @param temperature: the final temperature of the machine.
+// @return: the total second of wait time.
+double WaitForCoolMachine(double cold_temperature,
+ double timeout,
+ double* temperature) {
+ uint64_t start, now, end;
+ start = now = GetUTime();
+ end = now + 1e6 * timeout;
+ do {
+ *temperature = GetMachineTemperature();
+ if (*temperature < cold_temperature)
+ break;
+ sleep(1.0);
+ now = GetUTime();
+ } while (now < end);
+ return 1.0e-6 * (now - start);
+}
+
+bool IsItemInVector(const std::vector<std::string>& list,
+ const char* value,
+ bool empty_value = false) {
+ if (list.empty())
+ return empty_value;
+ return !(find(list.begin(), list.end(), std::string(value)) == list.end());
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..9f16811
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,84 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+#include <stdarg.h>
+#include <sys/time.h>
+#include <string>
+#include <vector>
+#include <vulkan/vulkan.hpp>
+
+extern int g_verbose;
+void PrintDateTime();
+std::vector<std::string> SplitString(const std::string& kInput, char delimiter);
+// For thermal monitoring of system.
+double GetMachineTemperature();
+// Wait for machine to cool with temperature in Celsius and timeout in seconds.
+// Returns the time spent waiting and sets the last observed temperature.
+double WaitForCoolMachine(double cold_temperature,
+ double timeout,
+ double* temperature);
+
+inline uint64_t GetUTime() {
+ struct timeval tv = {};
+ gettimeofday(&tv, nullptr);
+ return tv.tv_usec + 1000000ULL * static_cast<uint64_t>(tv.tv_sec);
+}
+
+bool IsItemInVector(const std::vector<std::string>& list,
+ const char* value,
+ bool empty_value);
+
+inline void DbgPrintf(const char* filename,
+ int line,
+ FILE* fileid,
+ const char* format,
+ ...) {
+ va_list args;
+ va_start(args, format);
+ char string_format[strlen(format) + 1024];
+ char debug_header[1024] = "\0";
+ if (g_verbose) {
+ sprintf(debug_header, "[%s:%d]", filename, line);
+ sprintf(string_format, "%30s %s\n", debug_header, format);
+ } else {
+ sprintf(string_format, "%s\n", format);
+ }
+ vfprintf(fileid, string_format, args);
+ va_end(args);
+}
+
+#define DEBUG(fmt, ...) \
+ { \
+ if (g_verbose) { \
+ LOG(fmt, ##__VA_ARGS__); \
+ } \
+ }
+#define LOG(fmt, ...) \
+ { DbgPrintf(__FILE__, __LINE__, stdout, fmt, ##__VA_ARGS__); }
+#define ERROR(fmt, ...) \
+ { DbgPrintf(__FILE__, __LINE__, stderr, fmt, ##__VA_ARGS__); }
+#define ABORT(code, fmt, ...) \
+ { \
+ ERROR(fmt, ##__VA_ARGS__); \
+ exit(code); \
+ }
+
+// Put this in the declarations for a class to be uncopyable.
+#define DISALLOW_COPY(TypeName) TypeName(const TypeName&) = delete
+
+// Put this in the declarations for a class to be unassignable.
+#define DISALLOW_ASSIGN(TypeName) TypeName& operator=(const TypeName&) = delete
+
+// Put this in the declarations for a class to be uncopyable and unassignable.
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ DISALLOW_COPY(TypeName); \
+ DISALLOW_ASSIGN(TypeName)
+
+// Ignore warning for linter for unused variable.
+#define UNUSED(x) (void)(x)
+
+#endif