]> git.seodisparate.com - blue_noise_generation/commitdiff
Begin work on Vulkan compute (WIP)
authorStephen Seo <seo.disparate@gmail.com>
Wed, 6 Mar 2024 08:04:08 +0000 (17:04 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Wed, 6 Mar 2024 08:04:08 +0000 (17:04 +0900)
Some Vulkan initialization stuff WIP.

CMakeLists.txt
src/arg_parse.cpp
src/arg_parse.hpp
src/blue_noise.cpp
src/blue_noise.hpp
src/main.cpp
src/utility.cpp [new file with mode: 0644]
src/utility.hpp

index 97f36039002ec34cb35b3d3a6fff7433c9731c17..0eef0029b874919d9780de897e56448784ea69aa 100644 (file)
@@ -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()
index 654fca0c59708887a726bb60fbb7f75f2239d397..b1010d3f7ef75fefadf11d3e1c2e266deee51baf 100644 (file)
@@ -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 <filelname> | --output <filename>\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;
index d6af17639b9be55bc1c4fb020ae8a7b06d857d91..c7fc9e74a02ec7afe914b1a4d4225d39a71a07c1 100644 (file)
@@ -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_;
index c0b62da83b46260d6ef25033e1c7747a018db505..dd9a66455802cf71d2b1100bccb76d5da4fc0f80 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <cassert>
 #include <cstdio>
+#include <cstring>
 #include <fstream>
 #include <iostream>
 #include <memory>
 #include <CL/opencl.h>
 #endif
 
+#if DITHERING_VULKAN_ENABLED == 1
+#include <vulkan/vulkan.h>
+
+static std::vector<const char *> VK_EXTENSIONS = {};
+
+#if VULKAN_VALIDATION == 1
+const std::vector<const char *> 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<VkLayerProperties> 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) {
index ef9220654a46bce5bfb555c8eef05a618a749ace..daeb47385d728856d646fc2fc3f50b492b7ab937 100644 (file)
@@ -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<unsigned int> blue_noise_impl(int width, int height,
index 71d5f9a25b007dfd9f674225339a393549136310..9f1027ab0cc357da9e9e3dbad049c4bf444d42a9 100644 (file)
@@ -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 (file)
index 0000000..58dbebb
--- /dev/null
@@ -0,0 +1,6 @@
+#include "utility.hpp"
+
+utility::Cleanup::Cleanup(std::function<void(void *)> fn, void *ptr)
+    : fn(fn), ptr(ptr) {}
+
+utility::Cleanup::~Cleanup() { this->fn(this->ptr); }
index b28feefd22b54b557f24195eb73450c97f28b85f..6da17d9bf78f2bbe1e0b528fe9eec605ec59b4bf 100644 (file)
@@ -2,6 +2,7 @@
 #define DITHERING_UTILITY_HPP
 
 #include <cmath>
+#include <functional>
 #include <utility>
 
 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<void(void *)> 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<void(void *)> fn;
+  void *ptr;
+};
 }  // namespace utility
 
 #endif