Compare commits

..

No commits in common. "2854aa5104dd1c581515a001db77d6fe7aec51ab" and "0da27b59bee6bd70f2a1ec4f2bebc99e1c66fab2" have entirely different histories.

10 changed files with 30 additions and 1498 deletions

View file

@ -6,13 +6,12 @@ set(blueNoiseGen_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/image.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/image.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/arg_parse.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/arg_parse.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utility.cpp
) )
add_compile_options( add_compile_options(
-Wall -Wextra -Wpedantic -Wall -Wextra -Wpedantic
$<$<COMPILE_LANGUAGE:CXX>:-Weffc++> $<$<COMPILE_LANGUAGE:CXX>:-Weffc++>
$<$<CONFIG:DEBUG>:-Og> $<$<CONFIG:DEBUG>:-O0>
) )
if(NOT DEFINED CMAKE_BUILD_TYPE OR NOT CMAKE_BUILD_TYPE) if(NOT DEFINED CMAKE_BUILD_TYPE OR NOT CMAKE_BUILD_TYPE)
@ -30,48 +29,29 @@ if(NOT DEFINED DISABLE_OPENCL OR NOT DISABLE_OPENCL)
else() else()
message(STATUS "Not checking for OpenCL") message(STATUS "Not checking for OpenCL")
endif() 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) find_package(PNG REQUIRED)
add_executable(blueNoiseGen ${blueNoiseGen_SOURCES}) add_executable(blueNoiseGen ${blueNoiseGen_SOURCES})
target_compile_features(blueNoiseGen PUBLIC cxx_std_17) target_compile_features(blueNoiseGen PUBLIC cxx_std_17)
target_compile_definitions(blueNoiseGen PRIVATE CL_TARGET_OPENCL_VERSION=300) 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) if(DEFINED DISABLE_OPENCL AND DISABLE_OPENCL)
message(STATUS "OpenCL usage is disabled.") 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) target_compile_definitions(blueNoiseGen PRIVATE DITHERING_OPENCL_ENABLED=0)
else() else()
message(STATUS "OpenCL usage is enabled.") message(STATUS "OpenCL usage is enabled.")
target_include_directories(blueNoiseGen PUBLIC target_include_directories(blueNoiseGen PUBLIC
${OpenCL_INCLUDE_DIRS}) Threads::Threads
${OpenCL_INCLUDE_DIRS}
${PNG_INCLUDE_DIRS})
target_link_libraries(blueNoiseGen PUBLIC target_link_libraries(blueNoiseGen PUBLIC
${OpenCL_LIBRARIES}) Threads::Threads
${OpenCL_LIBRARIES}
${PNG_LIBRARIES})
target_compile_definitions(blueNoiseGen PRIVATE DITHERING_OPENCL_ENABLED=1) target_compile_definitions(blueNoiseGen PRIVATE DITHERING_OPENCL_ENABLED=1)
endif() 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()

View file

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2021,2023-2024 Stephen Seo Copyright (c) 2021,2023 Stephen Seo
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -7,7 +7,6 @@ Args::Args()
: generate_blue_noise_(false), : generate_blue_noise_(false),
use_opencl_(true), use_opencl_(true),
overwrite_file_(false), overwrite_file_(false),
use_vulkan_(true),
blue_noise_size_(32), blue_noise_size_(32),
threads_(4), threads_(4),
output_filename_("output.png") {} output_filename_("output.png") {}
@ -24,12 +23,10 @@ void Args::DisplayHelp() {
" -t <int> | --threads <int>\t\tUse CPU thread count when " " -t <int> | --threads <int>\t\tUse CPU thread count when "
"not using " "not using "
"OpenCL\n" "OpenCL\n"
" -o <filename> | --output <filename>\tOutput filename to " " -o <filelname> | --output <filename>\tOutput filename to "
"use\n" "use\n"
" --overwrite\t\t\t\tEnable overwriting of file (default " " --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) { bool Args::ParseArgs(int argc, char **argv) {
@ -72,10 +69,6 @@ bool Args::ParseArgs(int argc, char **argv) {
output_filename_ = std::string(argv[1]); output_filename_ = std::string(argv[1]);
--argc; --argc;
++argv; ++argv;
} else if (std::strcmp(argv[0], "--usevulkan") == 0) {
use_vulkan_ = true;
} else if (std::strcmp(argv[0], "--nousevulkan") == 0) {
use_vulkan_ = false;
} else { } else {
std::cout << "WARNING: Ignoring invalid input \"" << argv[0] << "\"" std::cout << "WARNING: Ignoring invalid input \"" << argv[0] << "\""
<< std::endl; << std::endl;

View file

@ -14,7 +14,6 @@ struct Args {
bool generate_blue_noise_; bool generate_blue_noise_;
bool use_opencl_; bool use_opencl_;
bool overwrite_file_; bool overwrite_file_;
bool use_vulkan_;
unsigned int blue_noise_size_; unsigned int blue_noise_size_;
unsigned int threads_; unsigned int threads_;
std::string output_filename_; std::string output_filename_;

File diff suppressed because it is too large Load diff

View file

@ -1,50 +0,0 @@
#version 450
int twoToOne(int x, int y, int width, int height) {
while (x < 0) {
x += width;
}
while (y < 0) {
y += height;
}
x = x % width;
y = y % height;
return x + y * width;
}
layout(binding = 0) readonly buffer PreComputed { float precomputed[]; };
layout(binding = 1) writeonly buffer FilterOut { float filter_out[]; };
layout(binding = 2) readonly buffer PBP { int pbp[]; };
layout(binding = 3) readonly buffer Other {
int width;
int height;
int filter_size;
};
layout(local_size_x = 256) in;
void main() {
uint index = gl_GlobalInvocationID.x;
if (index >= width * height) {
return;
}
int x = int(index % width);
int y = int(index / width);
float sum = 0.0F;
for (int q = 0; q < filter_size; ++q) {
int q_prime = height - filter_size / 2 + y + q;
for (int p = 0; p < filter_size; ++p) {
int p_prime = width - filter_size / 2 + x + p;
if (pbp[twoToOne(p_prime, q_prime, width, height)] != 0) {
sum += precomputed[twoToOne(p, q, filter_size, filter_size)];
}
}
}
filter_out[index] = sum;
}

View file

@ -4,9 +4,7 @@
#if DITHERING_OPENCL_ENABLED == 1 #if DITHERING_OPENCL_ENABLED == 1
#include <CL/opencl.h> #include <CL/opencl.h>
#endif #endif
#if DITHERING_VULKAN_ENABLED == 1 #include <sys/sysinfo.h>
#include <vulkan/vulkan.h>
#endif
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
@ -30,123 +28,11 @@
namespace dither { namespace dither {
image::Bl blue_noise(int width, int height, int threads = 1, image::Bl blue_noise(int width, int height, int threads = 1,
bool use_opencl = true, bool use_vulkan = true); bool use_opencl = true);
namespace internal { namespace internal {
std::vector<unsigned int> blue_noise_impl(int width, int height, std::vector<unsigned int> blue_noise_impl(int width, int height,
int threads = 1); int threads = 1);
#if DITHERING_VULKAN_ENABLED == 1
struct QueueFamilyIndices {
QueueFamilyIndices();
std::optional<uint32_t> computeFamily;
bool isComplete();
};
QueueFamilyIndices vulkan_find_queue_families(VkPhysicalDevice device);
std::optional<uint32_t> vulkan_find_memory_type(VkPhysicalDevice phys_dev,
uint32_t t_filter,
VkMemoryPropertyFlags props);
bool vulkan_create_buffer(VkDevice device, VkPhysicalDevice phys_dev,
VkDeviceSize size, VkBufferUsageFlags usage,
VkMemoryPropertyFlags props, VkBuffer &buf,
VkDeviceMemory &buf_mem);
void vulkan_copy_buffer(VkDevice device, VkCommandPool command_pool,
VkQueue queue, VkBuffer src_buf, VkBuffer dst_buf,
VkDeviceSize size);
void vulkan_flush_buffer(VkDevice device, VkDeviceMemory memory);
void vulkan_invalidate_buffer(VkDevice device, VkDeviceMemory memory);
std::vector<unsigned int> blue_noise_vulkan_impl(
VkDevice device, VkPhysicalDevice phys_device,
VkCommandBuffer command_buffer, VkCommandPool command_pool, VkQueue queue,
VkBuffer pbp_buf, VkPipeline pipeline, VkPipelineLayout pipeline_layout,
VkDescriptorSet descriptor_set, VkBuffer filter_out_buf, const int width,
const int height);
std::vector<float> vulkan_buf_to_vec(float *mapped, unsigned int size);
inline bool vulkan_get_filter(
VkDevice device, VkCommandBuffer command_buffer, VkCommandPool command_pool,
VkQueue queue, VkBuffer pbp_buf, VkPipeline pipeline,
VkPipelineLayout pipeline_layout, VkDescriptorSet descriptor_set,
VkBuffer filter_out_buf, const int size, std::vector<bool> &pbp,
bool reversed_pbp, const std::size_t global_size, int *pbp_mapped_int,
VkBuffer staging_pbp_buffer, VkDeviceMemory staging_pbp_buffer_mem,
VkDeviceMemory staging_filter_buffer_mem, VkBuffer staging_filter_buffer) {
vkResetCommandBuffer(command_buffer, 0);
if (reversed_pbp) {
for (unsigned int i = 0; i < pbp.size(); ++i) {
pbp_mapped_int[i] = pbp[i] ? 0 : 1;
}
} else {
for (unsigned int i = 0; i < pbp.size(); ++i) {
pbp_mapped_int[i] = pbp[i] ? 1 : 0;
}
}
vulkan_flush_buffer(device, staging_pbp_buffer_mem);
// Copy pbp buffer.
vulkan_copy_buffer(device, command_pool, queue, staging_pbp_buffer, pbp_buf,
size * sizeof(int));
VkCommandBufferBeginInfo begin_info{};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {
std::clog << "get_filter ERROR: Failed to begin recording compute "
"command buffer!\n";
return false;
}
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE,
pipeline_layout, 0, 1, &descriptor_set, 0, nullptr);
vkCmdDispatch(command_buffer, global_size, 1, 1);
if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) {
std::clog << "get_filter ERROR: Failed to record compute command buffer!\n";
return false;
}
{
VkSubmitInfo submit_info{};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
submit_info.signalSemaphoreCount = 0;
submit_info.pSignalSemaphores = nullptr;
if (vkQueueSubmit(queue, 1, &submit_info, nullptr) != VK_SUCCESS) {
std::clog
<< "get_filter ERROR: Failed to submit compute command buffer!\n";
return false;
}
}
if (vkDeviceWaitIdle(device) != VK_SUCCESS) {
std::clog << "get_filter ERROR: Failed to vkDeviceWaitIdle!\n";
return false;
}
// Copy back filter_out buffer.
vulkan_copy_buffer(device, command_pool, queue, filter_out_buf,
staging_filter_buffer, size * sizeof(float));
vulkan_invalidate_buffer(device, staging_filter_buffer_mem);
return true;
}
#endif
#if DITHERING_OPENCL_ENABLED == 1 #if DITHERING_OPENCL_ENABLED == 1
std::vector<unsigned int> blue_noise_cl_impl(const int width, const int height, std::vector<unsigned int> blue_noise_cl_impl(const int width, const int height,
const int filter_size, const int filter_size,
@ -379,42 +265,6 @@ inline std::pair<int, int> filter_minmax(const std::vector<float> &filter,
return {min_index, max_index}; return {min_index, max_index};
} }
inline std::pair<int, int> filter_minmax_raw_array(const float *const filter,
unsigned int size,
std::vector<bool> pbp) {
// ensure minority pixel is "true"
unsigned int count = 0;
for (bool value : pbp) {
if (value) {
++count;
}
}
if (count * 2 >= pbp.size()) {
// std::cout << "MINMAX flip\n"; // DEBUG
for (unsigned int i = 0; i < pbp.size(); ++i) {
pbp[i] = !pbp[i];
}
}
float min = std::numeric_limits<float>::infinity();
float max = -std::numeric_limits<float>::infinity();
int min_index = -1;
int max_index = -1;
for (unsigned int i = 0; i < size; ++i) {
if (!pbp[i] && filter[i] < min) {
min_index = i;
min = filter[i];
}
if (pbp[i] && filter[i] > max) {
max_index = i;
max = filter[i];
}
}
return {min_index, max_index};
}
inline std::pair<int, int> filter_abs_minmax(const std::vector<float> &filter) { inline std::pair<int, int> filter_abs_minmax(const std::vector<float> &filter) {
float min = std::numeric_limits<float>::infinity(); float min = std::numeric_limits<float>::infinity();
float max = -std::numeric_limits<float>::infinity(); float max = -std::numeric_limits<float>::infinity();

View file

@ -41,7 +41,7 @@ int main(int argc, char **argv) {
std::cout << "Generating blue_noise..." << std::endl; std::cout << "Generating blue_noise..." << std::endl;
image::Bl bl = image::Bl bl =
dither::blue_noise(args.blue_noise_size_, args.blue_noise_size_, dither::blue_noise(args.blue_noise_size_, args.blue_noise_size_,
args.threads_, args.use_opencl_, args.use_vulkan_); args.threads_, args.use_opencl_);
if (!bl.writeToFile(image::file_type::PNG, args.overwrite_file_, if (!bl.writeToFile(image::file_type::PNG, args.overwrite_file_,
args.output_filename_)) { args.output_filename_)) {
std::cout << "ERROR: Failed to write blue-noise to file\n"; std::cout << "ERROR: Failed to write blue-noise to file\n";

View file

@ -1,31 +0,0 @@
#include "utility.hpp"
utility::Cleanup::Cleanup(std::function<void(void *)> fn, void *ptr)
: fn(fn), ptr(ptr) {}
utility::Cleanup::Cleanup() : fn(), ptr(nullptr) {}
utility::Cleanup::~Cleanup() {
if (this->fn.has_value()) {
this->fn.value()(this->ptr);
}
}
utility::Cleanup::Cleanup(Cleanup &&other) : fn(other.fn), ptr(other.ptr) {
other.fn = std::nullopt;
other.ptr = nullptr;
}
utility::Cleanup &utility::Cleanup::operator=(utility::Cleanup &&other) {
if (this->fn.has_value()) {
this->fn.value()(this->ptr);
}
this->fn = other.fn;
this->ptr = other.ptr;
other.fn = std::nullopt;
other.ptr = nullptr;
return *this;
}

View file

@ -2,8 +2,6 @@
#define DITHERING_UTILITY_HPP #define DITHERING_UTILITY_HPP
#include <cmath> #include <cmath>
#include <functional>
#include <optional>
#include <utility> #include <utility>
namespace utility { namespace utility {
@ -30,25 +28,6 @@ inline float dist(int a, int b, int width) {
float dy = axy.second - bxy.second; float dy = axy.second - bxy.second;
return std::sqrt(dx * dx + dy * dy); return std::sqrt(dx * dx + dy * dy);
} }
class Cleanup {
public:
Cleanup();
Cleanup(std::function<void(void *)> fn, void *ptr);
~Cleanup();
// allow move
Cleanup(Cleanup &&);
Cleanup &operator=(Cleanup &&);
// deny copy
Cleanup(const Cleanup &) = delete;
Cleanup &operator=(const Cleanup &) = delete;
private:
std::optional<std::function<void(void *)>> fn;
void *ptr;
};
} // namespace utility } // namespace utility
#endif #endif