diff --git a/CMakeLists.txt b/CMakeLists.txt index 97f3603..0eef002 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ set(blueNoiseGen_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/image.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/arg_parse.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/utility.cpp ) add_compile_options( @@ -29,29 +30,48 @@ if(NOT DEFINED DISABLE_OPENCL OR NOT DISABLE_OPENCL) else() message(STATUS "Not checking for OpenCL") endif() +if(NOT DEFINED DISABLE_VULKAN OR NOT DISABLE_VULKAN) + find_package(Vulkan) + if(NOT DEFINED Vulkan_FOUND) + set(DISABLE_VULKAN True) + message(WARNING "Vulkan not found, Vulkan usage is disabled.") + endif() +else() + message(STATUS "Not checking for Vulkan") +endif() find_package(PNG REQUIRED) add_executable(blueNoiseGen ${blueNoiseGen_SOURCES}) target_compile_features(blueNoiseGen PUBLIC cxx_std_17) target_compile_definitions(blueNoiseGen PRIVATE CL_TARGET_OPENCL_VERSION=300) +target_include_directories(blueNoiseGen PUBLIC Threads::Threads ${PNG_INCLUDE_DIRS}) +target_link_libraries(blueNoiseGen PUBLIC Threads::Threads ${PNG_LIBRARIES}) + if(DEFINED DISABLE_OPENCL AND DISABLE_OPENCL) message(STATUS "OpenCL usage is disabled.") - target_include_directories(blueNoiseGen PUBLIC - Threads::Threads - ${PNG_INCLUDE_DIRS}) - target_link_libraries(blueNoiseGen PUBLIC - Threads::Threads - ${PNG_LIBRARIES}) target_compile_definitions(blueNoiseGen PRIVATE DITHERING_OPENCL_ENABLED=0) else() message(STATUS "OpenCL usage is enabled.") target_include_directories(blueNoiseGen PUBLIC - Threads::Threads - ${OpenCL_INCLUDE_DIRS} - ${PNG_INCLUDE_DIRS}) + ${OpenCL_INCLUDE_DIRS}) target_link_libraries(blueNoiseGen PUBLIC - Threads::Threads - ${OpenCL_LIBRARIES} - ${PNG_LIBRARIES}) + ${OpenCL_LIBRARIES}) target_compile_definitions(blueNoiseGen PRIVATE DITHERING_OPENCL_ENABLED=1) endif() + +if(DEFINED DISABLE_VULKAN AND DISABLE_VULKAN) + message(STATUS "Vulkan usage is disabled.") + target_compile_definitions(blueNoiseGen PRIVATE DITHERING_VULKAN_ENABLED=0) +else() + message(STATUS "Vulkan usage is enabled.") + target_include_directories(blueNoiseGen PUBLIC + ${Vulkan_INCLUDE_DIRS}) + target_link_libraries(blueNoiseGen PUBLIC + ${Vulkan_LIBRARIES}) + target_compile_definitions(blueNoiseGen PRIVATE DITHERING_VULKAN_ENABLED=1) + if(CMAKE_BUILD_TYPE MATCHES "Debug") + target_compile_definitions(blueNoiseGen PRIVATE VULKAN_VALIDATION=1) + else() + target_compile_definitions(blueNoiseGen PRIVATE VULKAN_VALIDATION=0) + endif() +endif() diff --git a/src/arg_parse.cpp b/src/arg_parse.cpp index 654fca0..b1010d3 100644 --- a/src/arg_parse.cpp +++ b/src/arg_parse.cpp @@ -7,6 +7,7 @@ Args::Args() : generate_blue_noise_(false), use_opencl_(true), overwrite_file_(false), + use_vulkan_(true), blue_noise_size_(32), threads_(4), output_filename_("output.png") {} @@ -26,7 +27,9 @@ void Args::DisplayHelp() { " -o | --output \tOutput filename to " "use\n" " --overwrite\t\t\t\tEnable overwriting of file (default " - "disabled)\n"; + "disabled)\n" + " --usevulkan | --nousevulkan\t\t\tUse/Disable Vulkan (enabled " + "by default)\n"; } bool Args::ParseArgs(int argc, char **argv) { @@ -69,6 +72,10 @@ bool Args::ParseArgs(int argc, char **argv) { output_filename_ = std::string(argv[1]); --argc; ++argv; + } else if (std::strcmp(argv[0], "--usevulkan") == 0) { + use_vulkan_ = true; + } else if (std::strcmp(argv[0], "--nousevulkan") == 0) { + use_vulkan_ = false; } else { std::cout << "WARNING: Ignoring invalid input \"" << argv[0] << "\"" << std::endl; diff --git a/src/arg_parse.hpp b/src/arg_parse.hpp index d6af176..c7fc9e7 100644 --- a/src/arg_parse.hpp +++ b/src/arg_parse.hpp @@ -14,6 +14,7 @@ struct Args { bool generate_blue_noise_; bool use_opencl_; bool overwrite_file_; + bool use_vulkan_; unsigned int blue_noise_size_; unsigned int threads_; std::string output_filename_; diff --git a/src/blue_noise.cpp b/src/blue_noise.cpp index c0b62da..dd9a664 100644 --- a/src/blue_noise.cpp +++ b/src/blue_noise.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -13,11 +14,153 @@ #include #endif +#if DITHERING_VULKAN_ENABLED == 1 +#include + +static std::vector VK_EXTENSIONS = {}; + +#if VULKAN_VALIDATION == 1 +const std::vector VALIDATION_LAYERS = { + "VK_LAYER_KHRONOS_validation"}; + +static VKAPI_ATTR VkBool32 VKAPI_CALL fn_VULKAN_DEBUG_CALLBACK( + VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, + const VkDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *) { + std::cerr << "Validation layer: " << p_callback_data->pMessage << std::endl; + + return VK_FALSE; +} +#endif // VULKAN_VALIDATION == 1 +#endif // DITHERING_VULKAN_ENABLED == 1 + #include "image.hpp" image::Bl dither::blue_noise(int width, int height, int threads, - bool use_opencl) { + bool use_opencl, bool use_vulkan) { bool using_opencl = false; + bool using_vulkan = false; + +#if DITHERING_VULKAN_ENABLED == 1 + if (use_vulkan) { + // Try to use Vulkan. +#if VULKAN_VALIDATION == 1 + // Check for validation support. + uint32_t layer_count; + vkEnumerateInstanceLayerProperties(&layer_count, nullptr); + + std::vector available_layers(layer_count); + vkEnumerateInstanceLayerProperties(&layer_count, available_layers.data()); + + bool validation_supported = true; + + for (const char *layer_name : VALIDATION_LAYERS) { + bool layer_found = false; + + for (const auto &layer_props : available_layers) { + if (std::strcmp(layer_name, layer_props.layerName) == 0) { + layer_found = true; + break; + } + } + + if (!layer_found) { + validation_supported = false; + break; + } + } + + if (!validation_supported) { + std::clog << "WARNING: Validation requested but not supported, cannot " + "use Vulkan!\n"; + goto ENDOF_VULKAN; + } + + VK_EXTENSIONS.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); +#endif // VULKAN_VALIDATION == 1 + + VkApplicationInfo app_info{}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pApplicationName = "Blue Noise Generation"; + app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.pEngineName = "No Engine"; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo create_info{}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pApplicationInfo = &app_info; + + create_info.enabledExtensionCount = VK_EXTENSIONS.size(); + create_info.ppEnabledExtensionNames = VK_EXTENSIONS.data(); + + VkDebugUtilsMessengerCreateInfoEXT debug_create_info{}; +#if VULKAN_VALIDATION == 1 + create_info.enabledLayerCount = VALIDATION_LAYERS.size(); + create_info.ppEnabledLayerNames = VALIDATION_LAYERS.data(); + + const auto populate_debug_info = + [](VkDebugUtilsMessengerCreateInfoEXT *info) { + info->sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + info->messageSeverity = + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + info->messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + info->pfnUserCallback = fn_VULKAN_DEBUG_CALLBACK; + }; + + populate_debug_info(&debug_create_info); + + create_info.pNext = &debug_create_info; + + create_info.enabledExtensionCount = 1; +#else + create_info.enabledLayerCount = 0; + create_info.pNext = nullptr; +#endif // VULKAN_VALIDATION == 1 + + VkInstance instance; + if (vkCreateInstance(&create_info, nullptr, &instance) != VK_SUCCESS) { + std::clog << "WARNING: Failed to create Vulkan instance!\n"; + goto ENDOF_VULKAN; + } + utility::Cleanup cleanup_vk_instance( + [](void *ptr) { vkDestroyInstance(*((VkInstance *)ptr), nullptr); }, + &instance); + +#if VULKAN_VALIDATION == 1 + populate_debug_info(&debug_create_info); + VkDebugUtilsMessengerEXT debug_messenger; + + auto create_debug_utils_messenger_func = + (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkCreateDebugUtilsMessengerEXT"); + if (create_debug_utils_messenger_func != nullptr && + create_debug_utils_messenger_func(instance, &debug_create_info, nullptr, + &debug_messenger) != VK_SUCCESS) { + std::clog << "WARNING: Failed to set up Vulkan debug messenger!\n"; + goto ENDOF_VULKAN; + } + utility::Cleanup cleanup_debug_messenger( + [instance](void *ptr) { + auto func = + (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkDestroyDebugUtilsMessengerEXT"); + if (func != nullptr) { + func(instance, *((VkDebugUtilsMessengerEXT *)ptr), nullptr); + } + }, + &debug_messenger); +#endif // VULKAN_VALIDATION == 1 + } +ENDOF_VULKAN: + std::clog << "TODO: Remove this once Vulkan support is implemented.\n"; + return {}; +#else + std::clog << "WARNING: Not compiled with Vulkan support!\n"; +#endif // DITHERING_VULKAN_ENABLED == 1 #if DITHERING_OPENCL_ENABLED == 1 if (use_opencl) { diff --git a/src/blue_noise.hpp b/src/blue_noise.hpp index ef92206..daeb473 100644 --- a/src/blue_noise.hpp +++ b/src/blue_noise.hpp @@ -28,7 +28,7 @@ namespace dither { image::Bl blue_noise(int width, int height, int threads = 1, - bool use_opencl = true); + bool use_opencl = true, bool use_vulkan = true); namespace internal { std::vector blue_noise_impl(int width, int height, diff --git a/src/main.cpp b/src/main.cpp index 71d5f9a..9f1027a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,7 +41,7 @@ int main(int argc, char **argv) { std::cout << "Generating blue_noise..." << std::endl; image::Bl bl = dither::blue_noise(args.blue_noise_size_, args.blue_noise_size_, - args.threads_, args.use_opencl_); + args.threads_, args.use_opencl_, args.use_vulkan_); if (!bl.writeToFile(image::file_type::PNG, args.overwrite_file_, args.output_filename_)) { std::cout << "ERROR: Failed to write blue-noise to file\n"; diff --git a/src/utility.cpp b/src/utility.cpp new file mode 100644 index 0000000..58dbebb --- /dev/null +++ b/src/utility.cpp @@ -0,0 +1,6 @@ +#include "utility.hpp" + +utility::Cleanup::Cleanup(std::function fn, void *ptr) + : fn(fn), ptr(ptr) {} + +utility::Cleanup::~Cleanup() { this->fn(this->ptr); } diff --git a/src/utility.hpp b/src/utility.hpp index b28feef..6da17d9 100644 --- a/src/utility.hpp +++ b/src/utility.hpp @@ -2,6 +2,7 @@ #define DITHERING_UTILITY_HPP #include +#include #include namespace utility { @@ -28,6 +29,24 @@ inline float dist(int a, int b, int width) { float dy = axy.second - bxy.second; return std::sqrt(dx * dx + dy * dy); } + +class Cleanup { + public: + Cleanup(std::function fn, void *ptr); + ~Cleanup(); + + // allow move + Cleanup(Cleanup &&) = default; + Cleanup &operator=(Cleanup &&) = default; + + // deny copy + Cleanup(const Cleanup &) = delete; + Cleanup &operator=(const Cleanup &) = delete; + + private: + std::function fn; + void *ptr; +}; } // namespace utility #endif