diff --git a/base/CommandLineParser.hpp b/base/CommandLineParser.hpp new file mode 100644 index 00000000..fc100028 --- /dev/null +++ b/base/CommandLineParser.hpp @@ -0,0 +1,117 @@ +/* + * Simple command line parse + * + * Copyright (C) 2016-2022 by Sascha Willems - www.saschawillems.de + * + * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + */ + +#pragma once + +#include +#include +#include + +class CommandLineParser +{ +public: + struct CommandLineOption { + std::vector commands; + std::string value; + bool hasValue = false; + std::string help; + bool set = false; + }; + std::unordered_map options; + + void add(std::string name, std::vector commands, bool hasValue, std::string help) + { + options[name].commands = commands; + options[name].help = help; + options[name].set = false; + options[name].hasValue = hasValue; + options[name].value = ""; + } + + void printHelp() + { + std::cout << "Available command line options:\n"; + for (auto option : options) { + std::cout << " "; + for (size_t i = 0; i < option.second.commands.size(); i++) { + std::cout << option.second.commands[i]; + if (i < option.second.commands.size() - 1) { + std::cout << ", "; + } + } + std::cout << ": " << option.second.help << "\n"; + } + std::cout << "Press any key to close..."; + } + + void parse(std::vector arguments) + { + bool printHelp = false; + // Known arguments + for (auto& option : options) { + for (auto& command : option.second.commands) { + for (size_t i = 0; i < arguments.size(); i++) { + if (strcmp(arguments[i], command.c_str()) == 0) { + option.second.set = true; + // Get value + if (option.second.hasValue) { + if (arguments.size() > i + 1) { + option.second.value = arguments[i + 1]; + } + if (option.second.value == "") { + printHelp = true; + break; + } + } + } + } + } + } + // Print help for unknown arguments or missing argument values + if (printHelp) { + options["help"].set = true; + } + } + + void parse(int argc, char* argv[]) + { + std::vector args; + for (int i = 0; i < __argc; i++) { + args.push_back(__argv[i]); + }; + parse(args); + } + + bool isSet(std::string name) + { + return ((options.find(name) != options.end()) && options[name].set); + } + + std::string getValueAsString(std::string name, std::string defaultValue) + { + assert(options.find(name) != options.end()); + std::string value = options[name].value; + return (value != "") ? value : defaultValue; + } + + int32_t getValueAsInt(std::string name, int32_t defaultValue) + { + assert(options.find(name) != options.end()); + std::string value = options[name].value; + if (value != "") { + char* numConvPtr; + int32_t intVal = strtol(value.c_str(), &numConvPtr, 10); + return (intVal > 0) ? intVal : defaultValue; + } + else { + return defaultValue; + } + return int32_t(); + } + +}; \ No newline at end of file diff --git a/base/vulkanexamplebase.cpp b/base/vulkanexamplebase.cpp index ea7f0203..0e611c3f 100644 --- a/base/vulkanexamplebase.cpp +++ b/base/vulkanexamplebase.cpp @@ -775,6 +775,22 @@ VulkanExampleBase::VulkanExampleBase(bool enableValidation) settings.validation = enableValidation; // Command line arguments + commandLineParser.add("help", { "--help" }, 0, "Show help"); + commandLineParser.add("validation", { "-v", "--validation" }, 0, "Enable validation layers"); + commandLineParser.add("vsync", { "-vs", "--vsync" }, 0, "Enable V-Sync"); + commandLineParser.add("fullscreen", { "-f", "--fullscreen" }, 0, "Start in fullscreen mode"); + commandLineParser.add("width", { "-w", "--width" }, 1, "Set window width"); + commandLineParser.add("height", { "-h", "--height" }, 1, "Set window height"); + commandLineParser.add("shaders", { "-s", "--shaders" }, 1, "Select shader type to use (glsl or hlsl)"); + commandLineParser.add("gpuselection", { "-g", "--gpu" }, 1, "Select GPU to run on"); + commandLineParser.add("gpulist", { "-gl", "--listgpus" }, 0, "Display a list of available Vulkan devices"); + commandLineParser.add("benchmark", { "-b", "--benchmark" }, 0, "Run example in benchmark mode"); + commandLineParser.add("benchmarkwarmup", { "-bw", "--benchwarmup" }, 1, "Set warmup time for benchmark mode in seconds"); + commandLineParser.add("benchmarkruntime", { "-br", "--benchruntime" }, 1, "Set duration time for benchmark mode in seconds"); + commandLineParser.add("benchmarkresultfile", { "-bf", "--benchfilename" }, 1, "Set file name for benchmark results"); + commandLineParser.add("benchmarkresultframes", { "-bt", "--benchframetimes" }, 0, "Save frame times to benchmark results file"); + commandLineParser.add("benchmarkframes", { "-bfs", "--benchmarkframes" }, 1, "Only render the given number of frames"); + commandLineParser.parse(args); if (commandLineParser.isSet("help")) { #if defined(_WIN32) @@ -2922,104 +2938,3 @@ void VulkanExampleBase::setupSwapChain() } void VulkanExampleBase::OnUpdateUIOverlay(vks::UIOverlay *overlay) {} - -// Command line argument parser class - -CommandLineParser::CommandLineParser() -{ - add("help", { "--help" }, 0, "Show help"); - add("validation", {"-v", "--validation"}, 0, "Enable validation layers"); - add("vsync", {"-vs", "--vsync"}, 0, "Enable V-Sync"); - add("fullscreen", { "-f", "--fullscreen" }, 0, "Start in fullscreen mode"); - add("width", { "-w", "--width" }, 1, "Set window width"); - add("height", { "-h", "--height" }, 1, "Set window height"); - add("shaders", { "-s", "--shaders" }, 1, "Select shader type to use (glsl or hlsl)"); - add("gpuselection", { "-g", "--gpu" }, 1, "Select GPU to run on"); - add("gpulist", { "-gl", "--listgpus" }, 0, "Display a list of available Vulkan devices"); - add("benchmark", { "-b", "--benchmark" }, 0, "Run example in benchmark mode"); - add("benchmarkwarmup", { "-bw", "--benchwarmup" }, 1, "Set warmup time for benchmark mode in seconds"); - add("benchmarkruntime", { "-br", "--benchruntime" }, 1, "Set duration time for benchmark mode in seconds"); - add("benchmarkresultfile", { "-bf", "--benchfilename" }, 1, "Set file name for benchmark results"); - add("benchmarkresultframes", { "-bt", "--benchframetimes" }, 0, "Save frame times to benchmark results file"); - add("benchmarkframes", { "-bfs", "--benchmarkframes" }, 1, "Only render the given number of frames"); -} - -void CommandLineParser::add(std::string name, std::vector commands, bool hasValue, std::string help) -{ - options[name].commands = commands; - options[name].help = help; - options[name].set = false; - options[name].hasValue = hasValue; - options[name].value = ""; -} - -void CommandLineParser::printHelp() -{ - std::cout << "Available command line options:\n"; - for (auto option : options) { - std::cout << " "; - for (size_t i = 0; i < option.second.commands.size(); i++) { - std::cout << option.second.commands[i]; - if (i < option.second.commands.size() - 1) { - std::cout << ", "; - } - } - std::cout << ": " << option.second.help << "\n"; - } - std::cout << "Press any key to close..."; -} - -void CommandLineParser::parse(std::vector arguments) -{ - bool printHelp = false; - // Known arguments - for (auto& option : options) { - for (auto& command : option.second.commands) { - for (size_t i = 0; i < arguments.size(); i++) { - if (strcmp(arguments[i], command.c_str()) == 0) { - option.second.set = true; - // Get value - if (option.second.hasValue) { - if (arguments.size() > i + 1) { - option.second.value = arguments[i + 1]; - } - if (option.second.value == "") { - printHelp = true; - break; - } - } - } - } - } - } - // Print help for unknown arguments or missing argument values - if (printHelp) { - options["help"].set = true; - } -} - -bool CommandLineParser::isSet(std::string name) -{ - return ((options.find(name) != options.end()) && options[name].set); -} - -std::string CommandLineParser::getValueAsString(std::string name, std::string defaultValue) -{ - assert(options.find(name) != options.end()); - std::string value = options[name].value; - return (value != "") ? value : defaultValue; -} - -int32_t CommandLineParser::getValueAsInt(std::string name, int32_t defaultValue) -{ - assert(options.find(name) != options.end()); - std::string value = options[name].value; - if (value != "") { - char* numConvPtr; - int32_t intVal = strtol(value.c_str(), &numConvPtr, 10); - return (intVal > 0) ? intVal : defaultValue; - } else { - return defaultValue; - } - return int32_t(); -} diff --git a/base/vulkanexamplebase.h b/base/vulkanexamplebase.h index c03f9e6a..9785164a 100644 --- a/base/vulkanexamplebase.h +++ b/base/vulkanexamplebase.h @@ -59,6 +59,7 @@ #include "vulkan/vulkan.h" +#include "CommandLineParser.hpp" #include "keycodes.hpp" #include "VulkanTools.h" #include "VulkanDebug.h" @@ -72,26 +73,6 @@ #include "camera.hpp" #include "benchmark.hpp" -class CommandLineParser -{ -public: - struct CommandLineOption { - std::vector commands; - std::string value; - bool hasValue = false; - std::string help; - bool set = false; - }; - std::unordered_map options; - CommandLineParser(); - void add(std::string name, std::vector commands, bool hasValue, std::string help); - void printHelp(); - void parse(std::vector arguments); - bool isSet(std::string name); - std::string getValueAsString(std::string name, std::string defaultValue); - int32_t getValueAsInt(std::string name, int32_t defaultValue); -}; - class VulkanExampleBase { private: diff --git a/examples/computeheadless/computeheadless.cpp b/examples/computeheadless/computeheadless.cpp index d11a2235..d6996f0c 100644 --- a/examples/computeheadless/computeheadless.cpp +++ b/examples/computeheadless/computeheadless.cpp @@ -1,13 +1,11 @@ /* * Vulkan Example - Minimal headless compute example * -* Copyright (C) 2017 by Sascha Willems - www.saschawillems.de +* Copyright (C) 2017-2022 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ -// TODO: separate transfer queue (if not supported by compute queue) including buffer ownership transfer - #if defined(_WIN32) #pragma comment(linker, "/subsystem:console") #elif defined(VK_USE_PLATFORM_ANDROID_KHR) @@ -31,6 +29,7 @@ #endif #include #include "VulkanTools.h" +#include "CommandLineParser.hpp" #if defined(VK_USE_PLATFORM_ANDROID_KHR) android_app* androidapp; @@ -60,6 +59,8 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback( return VK_FALSE; } +CommandLineParser commandLineParser; + class VulkanExample { public: @@ -413,10 +414,11 @@ public: VkSpecializationMapEntry specializationMapEntry = vks::initializers::specializationMapEntry(0, 0, sizeof(uint32_t)); VkSpecializationInfo specializationInfo = vks::initializers::specializationInfo(1, &specializationMapEntry, sizeof(SpecializationData), &specializationData); - // TODO: There is no command line arguments parsing (nor Android settings) for this - // example, so we have no way of picking between GLSL or HLSL shaders. - // Hard-code to glsl for now. - const std::string shadersPath = getAssetPath() + "shaders/glsl/computeheadless/"; + std::string shaderDir = "glsl"; + if (commandLineParser.isSet("shaders")) { + shaderDir = commandLineParser.getValueAsString("shaders", "glsl"); + } + const std::string shadersPath = getAssetPath() + "shaders/"+shaderDir+"/computeheadless/"; VkPipelineShaderStageCreateInfo shaderStage = {}; shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -607,10 +609,18 @@ void android_main(android_app* state) { } } #else -int main() { +int main(int argc, char* argv[]) { + commandLineParser.add("help", { "--help" }, 0, "Show help"); + commandLineParser.add("shaders", { "-s", "--shaders" }, 1, "Select shader type to use (glsl or hlsl)"); + commandLineParser.parse(argc, argv); + if (commandLineParser.isSet("help")) { + commandLineParser.printHelp(); + std::cin.get(); + return 0; + } VulkanExample *vulkanExample = new VulkanExample(); std::cout << "Finished. Press enter to terminate..."; - getchar(); + std::cin.get(); delete(vulkanExample); return 0; } diff --git a/examples/renderheadless/renderheadless.cpp b/examples/renderheadless/renderheadless.cpp index d259ff77..5f1d80f1 100644 --- a/examples/renderheadless/renderheadless.cpp +++ b/examples/renderheadless/renderheadless.cpp @@ -1,7 +1,7 @@ /* * Vulkan Example - Minimal headless rendering example * -* Copyright (C) 2017 by Sascha Willems - www.saschawillems.de +* Copyright (C) 2017-2022 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ @@ -35,6 +35,7 @@ #endif #include #include "VulkanTools.h" +#include "CommandLineParser.hpp" #if defined(VK_USE_PLATFORM_ANDROID_KHR) android_app* androidapp; @@ -64,6 +65,8 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback( return VK_FALSE; } +CommandLineParser commandLineParser; + class VulkanExample { public: @@ -645,10 +648,11 @@ public: pipelineCreateInfo.pVertexInputState = &vertexInputState; - // TODO: There is no command line arguments parsing (nor Android settings) for this - // example, so we have no way of picking between GLSL or HLSL shaders. - // Hard-code to glsl for now. - const std::string shadersPath = getAssetPath() + "shaders/glsl/renderheadless/"; + std::string shaderDir = "glsl"; + if (commandLineParser.isSet("shaders")) { + shaderDir = commandLineParser.getValueAsString("shaders", "glsl"); + } + const std::string shadersPath = getAssetPath() + "shaders/" + shaderDir + "/renderheadless/"; shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; @@ -941,10 +945,18 @@ void android_main(android_app* state) { } } #else -int main() { +int main(int argc, char* argv[]) { + commandLineParser.add("help", { "--help" }, 0, "Show help"); + commandLineParser.add("shaders", { "-s", "--shaders" }, 1, "Select shader type to use (glsl or hlsl)"); + commandLineParser.parse(argc, argv); + if (commandLineParser.isSet("help")) { + commandLineParser.printHelp(); + std::cin.get(); + return 0; + } VulkanExample *vulkanExample = new VulkanExample(); std::cout << "Finished. Press enter to terminate..."; - getchar(); + std::cin.get(); delete(vulkanExample); return 0; }