diff --git a/desktop/graphics/glfw_mgmt.cpp b/desktop/graphics/glfw_mgmt.cpp index ff4279d..8c9f77d 100644 --- a/desktop/graphics/glfw_mgmt.cpp +++ b/desktop/graphics/glfw_mgmt.cpp @@ -8,61 +8,95 @@ #include "../../main/logging.h" #include "../../main/meta.h" -#include "vulkan_mgmt.h" +#include "../../main/util.h" + using namespace progressia::main::logging; namespace progressia::desktop { -// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables): global variable required for GLFW callbacks -static GLFWwindow *window = nullptr; - static void onGlfwError(int errorCode, const char *description); static void onWindowGeometryChange(GLFWwindow *window, int width, int height); -void initializeGlfw() { - debug("Beginning GLFW init"); +class GlfwManagerImpl : public GlfwManager { + private: + GLFWwindow *window = nullptr; + std::function onScreenResize = nullptr; - glfwSetErrorCallback(onGlfwError); + public: + DISABLE_COPYING(GlfwManagerImpl) + DISABLE_MOVING(GlfwManagerImpl) - if (!glfwInit()) { - fatal("glfwInit() failed"); + GlfwManagerImpl() { + debug("Beginning GLFW init"); + + glfwSetErrorCallback(onGlfwError); + + if (!glfwInit()) { + fatal("glfwInit() failed"); + // REPORT_ERROR + exit(1); + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + + std::string title; + + { + std::stringstream accumulator; + accumulator << progressia::main::meta::NAME << " " + << progressia::main::meta::VERSION << " build " + << progressia::main::meta::BUILD_ID; + title = accumulator.str(); + } + + constexpr auto windowDimensions = 800; + window = glfwCreateWindow(windowDimensions, windowDimensions, + title.c_str(), nullptr, nullptr); + + glfwSetWindowSizeCallback(window, onWindowGeometryChange); + + debug("GLFW init complete"); + } + + ~GlfwManagerImpl() override { glfwTerminate(); } + + void setOnScreenResize(std::function hook) override { + onScreenResize = hook; + } + + void showWindow() override { + glfwShowWindow(window); + debug("Window now visible"); + } + + bool shouldRun() override { return !glfwWindowShouldClose(window); } + + void doGlfwRoutine() override { glfwPollEvents(); } + + friend GLFWwindow *getGLFWWindowHandle(); + friend void onWindowGeometryChange(GLFWwindow *, int, int); +}; + +namespace { +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables): global variables required by GLFW C callbacks +std::weak_ptr theGlfwManager; +} // namespace + +std::shared_ptr makeGlfwManager() { + if (!theGlfwManager.expired()) { + fatal("GlfwManager already exists"); // REPORT_ERROR exit(1); } - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + std::shared_ptr aGlfwManager = + std::make_shared(); - std::string title; - - { - std::stringstream accumulator; - accumulator << progressia::main::meta::NAME << " " - << progressia::main::meta::VERSION << " build " - << progressia::main::meta::BUILD_ID; - title = accumulator.str(); - } - - constexpr auto windowDimensions = 800; - window = glfwCreateWindow(windowDimensions, windowDimensions, title.c_str(), - nullptr, nullptr); - - glfwSetWindowSizeCallback(window, onWindowGeometryChange); - - debug("GLFW init complete"); + theGlfwManager = aGlfwManager; + return aGlfwManager; } -void showWindow() { - glfwShowWindow(window); - debug("Window now visible"); -} - -bool shouldRun() { return !glfwWindowShouldClose(window); } - -void doGlfwRoutine() { glfwPollEvents(); } - -void shutdownGlfw() { glfwTerminate(); } - void onGlfwError(int errorCode, const char *description) { fatal() << "[GLFW] " << description << " (" << errorCode << ")"; // REPORT_ERROR @@ -71,13 +105,25 @@ void onGlfwError(int errorCode, const char *description) { void onWindowGeometryChange(GLFWwindow *window, [[maybe_unused]] int width, [[maybe_unused]] int height) { - if (window != progressia::desktop::window) { + if (auto manager = theGlfwManager.lock()) { + if (manager->window != window) { + return; + } + + if (manager->onScreenResize != nullptr) { + manager->onScreenResize(); + } + } else { return; } - - resizeVulkanSurface(); } -GLFWwindow *getGLFWWindowHandle() { return window; } +GLFWwindow *getGLFWWindowHandle() { + if (auto manager = theGlfwManager.lock()) { + return manager->window; + } + + return nullptr; +} } // namespace progressia::desktop diff --git a/desktop/graphics/glfw_mgmt.h b/desktop/graphics/glfw_mgmt.h index 50b6f04..be1a31f 100644 --- a/desktop/graphics/glfw_mgmt.h +++ b/desktop/graphics/glfw_mgmt.h @@ -1,11 +1,22 @@ #pragma once +#include +#include + namespace progressia::desktop { -void initializeGlfw(); -void showWindow(); -void shutdownGlfw(); -bool shouldRun(); -void doGlfwRoutine(); +class GlfwManager { + + public: + virtual ~GlfwManager(){}; + + virtual void setOnScreenResize(std::function) = 0; + + virtual void showWindow() = 0; + virtual bool shouldRun() = 0; + virtual void doGlfwRoutine() = 0; +}; + +std::shared_ptr makeGlfwManager(); } // namespace progressia::desktop diff --git a/desktop/graphics/glfw_mgmt_details.h b/desktop/graphics/glfw_mgmt_details.h index 1ac290c..f9fa7cd 100644 --- a/desktop/graphics/glfw_mgmt_details.h +++ b/desktop/graphics/glfw_mgmt_details.h @@ -7,6 +7,7 @@ namespace progressia::desktop { +// TODO refactor into OOP GLFWwindow *getGLFWWindowHandle(); } // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_image.cpp b/desktop/graphics/vulkan_image.cpp index b57be5c..d462660 100644 --- a/desktop/graphics/vulkan_image.cpp +++ b/desktop/graphics/vulkan_image.cpp @@ -20,9 +20,7 @@ Image::Image(VkImage vk, VkImageView view, VkFormat format) // do nothing } -Image::~Image() { - // do nothing -} +Image::~Image() = default; /* * ManagedImage @@ -33,7 +31,7 @@ ManagedImage::ManagedImage(std::size_t width, std::size_t height, VkImageUsageFlags usage, Vulkan &vulkan) : - Image(VK_NULL_HANDLE, VK_NULL_HANDLE, format), vulkan(vulkan), + Image(VK_NULL_HANDLE, VK_NULL_HANDLE, format), memory(), vulkan(vulkan), state{VK_IMAGE_LAYOUT_UNDEFINED, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT} { @@ -146,7 +144,8 @@ Texture::Texture(const progressia::main::Image &src, Vulkan &vulkan) ManagedImage(src.width, src.height, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - vulkan) { + vulkan), + sampler() { /* * Create a staging buffer @@ -211,9 +210,9 @@ Texture::Texture(const progressia::main::Image &src, Vulkan &vulkan) samplerInfo.compareEnable = VK_FALSE; samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerInfo.mipLodBias = 0.0f; - samplerInfo.minLod = 0.0f; - samplerInfo.maxLod = 0.0f; + samplerInfo.mipLodBias = 0.0F; + samplerInfo.minLod = 0.0F; + samplerInfo.maxLod = 0.0F; vulkan.handleVkResult( "Could not create texture sampler", @@ -223,6 +222,7 @@ Texture::Texture(const progressia::main::Image &src, Vulkan &vulkan) * Create descriptor set */ + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer): sampler must be set using vkCreateSampler first descriptorSet = vulkan.getTextureDescriptors().addTexture(view, sampler); } @@ -233,8 +233,8 @@ Texture::~Texture() { void Texture::bind() { // REPORT_ERROR if getCurrentFrame() == nullptr - auto commandBuffer = vulkan.getCurrentFrame()->getCommandBuffer(); - auto pipelineLayout = vulkan.getPipeline().getLayout(); + auto *commandBuffer = vulkan.getCurrentFrame()->getCommandBuffer(); + auto *pipelineLayout = vulkan.getPipeline().getLayout(); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, diff --git a/desktop/graphics/vulkan_image.h b/desktop/graphics/vulkan_image.h index 23fb9b8..28ca7a5 100644 --- a/desktop/graphics/vulkan_image.h +++ b/desktop/graphics/vulkan_image.h @@ -35,8 +35,9 @@ class ManagedImage : public Image { State state; public: - ManagedImage(std::size_t width, std::size_t height, VkFormat, - VkImageAspectFlags, VkImageUsageFlags, Vulkan &); + ManagedImage(std::size_t width, std::size_t height, VkFormat format, + VkImageAspectFlags aspect, VkImageUsageFlags usage, + Vulkan &vulkan); ~ManagedImage(); void transition(State); @@ -48,7 +49,7 @@ class Texture : public ManagedImage { VkSampler sampler; VkDescriptorSet descriptorSet; - Texture(const progressia::main::Image &, Vulkan &vulkan); + Texture(const main::Image &src, Vulkan &vulkan); ~Texture(); void bind(); diff --git a/desktop/graphics/vulkan_mgmt.cpp b/desktop/graphics/vulkan_mgmt.cpp index 9406b55..bb831b2 100644 --- a/desktop/graphics/vulkan_mgmt.cpp +++ b/desktop/graphics/vulkan_mgmt.cpp @@ -8,25 +8,25 @@ using namespace progressia::main::logging; namespace progressia::desktop { -Vulkan *vulkan; - -void initializeVulkan() { +VulkanManager::VulkanManager() { debug("Vulkan initializing"); // Instance extensions std::vector instanceExtensions; { - uint32_t glfwExtensionCount; - const char **glfwExtensions; - glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + uint32_t glfwExtensionCount = 0; + const char **glfwExtensions = + glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + instanceExtensions.reserve(instanceExtensions.size() + + glfwExtensionCount); for (std::size_t i = 0; i < glfwExtensionCount; i++) { - instanceExtensions.push_back(glfwExtensions[i]); + instanceExtensions.emplace_back(glfwExtensions[i]); } #ifdef VULKAN_ERROR_CHECKING - instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + instanceExtensions.emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); #endif } @@ -42,28 +42,21 @@ void initializeVulkan() { #endif }; - vulkan = new Vulkan(instanceExtensions, deviceExtensions, validationLayers); + vulkan = std::make_unique(instanceExtensions, deviceExtensions, + validationLayers); debug("Vulkan initialized"); } -Vulkan *getVulkan() { return vulkan; } +VulkanManager::~VulkanManager() { debug("Vulkan terminating"); } -bool startRender() { return vulkan->startRender(); } +Vulkan *VulkanManager::getVulkan() { return vulkan.get(); } +const Vulkan *VulkanManager::getVulkan() const { return vulkan.get(); } -void endRender() { return vulkan->endRender(); } +bool VulkanManager::startRender() { return vulkan->startRender(); } -void resizeVulkanSurface() { vulkan->getSwapChain().recreate(); } +void VulkanManager::endRender() { return vulkan->endRender(); } -void shutdownVulkan() { - debug("Vulkan terminating"); - - if (vulkan != nullptr) { - delete vulkan; - vulkan = nullptr; - } - - debug("Vulkan terminated"); -} +void VulkanManager::resizeSurface() { vulkan->getSwapChain().recreate(); } } // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_mgmt.h b/desktop/graphics/vulkan_mgmt.h index 9475db3..18b4c4f 100644 --- a/desktop/graphics/vulkan_mgmt.h +++ b/desktop/graphics/vulkan_mgmt.h @@ -4,18 +4,25 @@ namespace progressia::desktop { -void initializeVulkan(); +class VulkanManager { -Vulkan *getVulkan(); + private: + std::unique_ptr vulkan; -void resizeVulkanSurface(); + public: + VulkanManager(); + ~VulkanManager(); -/* - * Returns false when the frame should be skipped - */ -bool startRender(); -void endRender(); + Vulkan *getVulkan(); + const Vulkan *getVulkan() const; -void shutdownVulkan(); + void resizeSurface(); + + /* + * Returns false when the frame should be skipped + */ + bool startRender(); + void endRender(); +}; } // namespace progressia::desktop diff --git a/desktop/main.cpp b/desktop/main.cpp index 47b0556..e3c24b8 100644 --- a/desktop/main.cpp +++ b/desktop/main.cpp @@ -13,7 +13,6 @@ int main(int argc, char *argv[]) { using namespace progressia; for (int i = 1; i < argc; i++) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): infeasible to avoid in C++17 char *arg = argv[i]; if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) { std::cout << main::meta::NAME << " " << main::meta::VERSION << "+" @@ -28,30 +27,29 @@ int main(int argc, char *argv[]) { << main::meta::VERSION_NUMBER << ")"; debug("Debug is enabled"); - desktop::initializeGlfw(); - desktop::initializeVulkan(); - desktop::showWindow(); + auto glfwManager = desktop::makeGlfwManager(); + desktop::VulkanManager vulkanManager; + glfwManager->setOnScreenResize([&]() { vulkanManager.resizeSurface(); }); + glfwManager->showWindow(); - main::initialize(desktop::getVulkan()->getGint()); + main::initialize(vulkanManager.getVulkan()->getGint()); info("Loading complete"); - while (desktop::shouldRun()) { - bool abortFrame = !desktop::startRender(); + while (glfwManager->shouldRun()) { + bool abortFrame = !vulkanManager.startRender(); if (abortFrame) { continue; } main::renderTick(); - desktop::endRender(); - desktop::doGlfwRoutine(); + vulkanManager.endRender(); + glfwManager->doGlfwRoutine(); } info("Shutting down"); - desktop::getVulkan()->waitIdle(); + vulkanManager.getVulkan()->waitIdle(); main::shutdown(); - desktop::shutdownVulkan(); - desktop::shutdownGlfw(); return 0; } diff --git a/main/util.h b/main/util.h index 0dff48a..be7f393 100644 --- a/main/util.h +++ b/main/util.h @@ -29,6 +29,18 @@ } // clang-format on +// clang-format off +#define DISABLE_MOVING(CLASS) \ + CLASS &operator=(CLASS &&) = delete; \ + CLASS(CLASS &&) = delete; \ +// clang-format on + +// clang-format off +#define DISABLE_COPYING(CLASS) \ + CLASS &operator=(const CLASS &) = delete; \ + CLASS(const CLASS &) = delete; \ +// clang-format on + namespace progressia::main { struct NonCopyable { diff --git a/tools/clang-tidy/clang-tidy.yml b/tools/clang-tidy/clang-tidy.yml index e229ccd..c0ec6c8 100644 --- a/tools/clang-tidy/clang-tidy.yml +++ b/tools/clang-tidy/clang-tidy.yml @@ -13,7 +13,8 @@ Checks: "-*,\ -*-avoid-c-arrays,\ -readability-else-after-return,\ -readability-named-parameter,\ - -readability-use-anyofallof" + -readability-use-anyofallof,\ + -cppcoreguidelines-pro-bounds-pointer-arithmetic" # modernize-use-trailing-return-type # ignore reason: reduces readability @@ -43,3 +44,6 @@ Checks: "-*,\ # readability-use-anyofallof # ignore reason: these relatively obscure functions reduce readability by # increasing code complexity and verbosity + +# cppcoreguidelines-pro-bounds-pointer-arithmetic +# ignore reason: infeasible to avoid in C++17