From ae4e265a90b27d6e29f067a9de939daf2c147551 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Fri, 10 Nov 2023 21:30:55 +0100 Subject: [PATCH] Squash improve-ide-compat into main Fixes GH-5 - cppcheck replaced with clang-tidy - clang-tidy lint warnings fixed - Reworked build tools from scratch to make IDE setup easier - Added 1.5 IDE setup guides --- .clang-format | 5 + .clang-tidy | 78 ++++ .gitignore | 12 +- CMakeLists.txt | 115 ++++- README.md | 1 - desktop/graphics/glfw_mgmt.cpp | 135 ++++-- desktop/graphics/glfw_mgmt.h | 27 +- desktop/graphics/glfw_mgmt_details.h | 7 +- desktop/graphics/vulkan_adapter.cpp | 156 +++---- desktop/graphics/vulkan_adapter.h | 7 +- desktop/graphics/vulkan_buffer.h | 7 +- desktop/graphics/vulkan_common.cpp | 105 ++--- desktop/graphics/vulkan_common.h | 28 +- desktop/graphics/vulkan_descriptor_set.cpp | 8 +- desktop/graphics/vulkan_descriptor_set.h | 6 +- desktop/graphics/vulkan_frame.cpp | 18 +- desktop/graphics/vulkan_frame.h | 6 +- desktop/graphics/vulkan_image.cpp | 26 +- desktop/graphics/vulkan_image.h | 14 +- desktop/graphics/vulkan_mgmt.cpp | 46 +- desktop/graphics/vulkan_mgmt.h | 31 +- desktop/graphics/vulkan_physical_device.cpp | 51 +++ desktop/graphics/vulkan_physical_device.h | 33 ++ desktop/graphics/vulkan_pick_device.cpp | 34 +- desktop/graphics/vulkan_pick_device.h | 17 +- desktop/graphics/vulkan_pipeline.cpp | 32 +- desktop/graphics/vulkan_pipeline.h | 6 +- desktop/graphics/vulkan_render_pass.cpp | 12 +- desktop/graphics/vulkan_render_pass.h | 6 +- desktop/graphics/vulkan_swap_chain.cpp | 36 +- desktop/graphics/vulkan_swap_chain.h | 6 +- .../graphics/vulkan_texture_descriptors.cpp | 14 +- desktop/graphics/vulkan_texture_descriptors.h | 6 +- desktop/graphics/vulkan_uniform.h | 7 +- desktop/graphics/vulkan_uniform.inl | 28 +- desktop/main.cpp | 24 +- docs/BuildingGuide.md | 225 +++++++--- docs/DevelopmentSetupGuide.md | 126 ++++-- docs/ide_setup/WindowsCLion.md | 100 +++++ docs/ide_setup/WindowsVisualStudio.md | 97 ++++ main/game.cpp | 257 ++++++----- main/game.h | 17 +- main/logging.cpp | 20 +- main/logging.h | 12 +- main/meta.h | 8 +- main/rendering.h | 4 +- main/rendering/graphics_interface.h | 66 ++- main/rendering/image.cpp | 46 +- main/rendering/image.h | 8 +- main/util.h | 22 + tools/bashlib.sh | 66 --- tools/build.sh | 199 --------- tools/clang-format/clang-format.yml | 4 - tools/clang-format/use-clang-format.sh | 102 ----- tools/cmake/embed.cmake | 56 --- tools/cppcheck/options.txt | 28 -- tools/cppcheck/suppressions.txt | 18 - tools/cppcheck/use-cppcheck.sh | 65 --- tools/dev-mode.cmake | 57 +++ tools/embed/embed.cmake | 89 ++++ tools/embed/embed.py | 7 +- tools/git/hook_pre_commit.sh | 51 --- tools/glslc.cmake | 53 +++ tools/pre-commit.py | 416 ++++++++++++++++++ tools/setup.sh | 146 ------ ...pp => valgrind-memcheck-suppressions.supp} | 0 66 files changed, 2017 insertions(+), 1498 deletions(-) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 desktop/graphics/vulkan_physical_device.cpp create mode 100644 desktop/graphics/vulkan_physical_device.h create mode 100644 docs/ide_setup/WindowsCLion.md create mode 100644 docs/ide_setup/WindowsVisualStudio.md delete mode 100644 tools/bashlib.sh delete mode 100755 tools/build.sh delete mode 100644 tools/clang-format/clang-format.yml delete mode 100755 tools/clang-format/use-clang-format.sh delete mode 100644 tools/cmake/embed.cmake delete mode 100644 tools/cppcheck/options.txt delete mode 100644 tools/cppcheck/suppressions.txt delete mode 100755 tools/cppcheck/use-cppcheck.sh create mode 100644 tools/dev-mode.cmake create mode 100644 tools/embed/embed.cmake delete mode 100755 tools/git/hook_pre_commit.sh create mode 100644 tools/glslc.cmake create mode 100755 tools/pre-commit.py delete mode 100755 tools/setup.sh rename tools/{memcheck/suppressions.supp => valgrind-memcheck-suppressions.supp} (100%) diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..4556c43 --- /dev/null +++ b/.clang-format @@ -0,0 +1,5 @@ +{ + "BasedOnStyle": "LLVM", + "IndentWidth": 4, + "CommentPragmas": "NOLINT", +} diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..243f9dc --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,78 @@ +Checks: "-*,\ + clang-analyzer-*,\ + cppcoreguidelines-*,\ + modernize-*,\ + performance-*,\ + readability-*,\ + clang-diagnostic-*,\ + -modernize-use-trailing-return-type,\ + -readability-implicit-bool-conversion,\ + -cppcoreguidelines-pro-bounds-array-to-pointer-decay,\ + -cppcoreguidelines-pro-type-reinterpret-cast,\ + -cppcoreguidelines-pro-bounds-constant-array-index,\ + -*-avoid-c-arrays,\ + -readability-else-after-return,\ + -readability-named-parameter,\ + -readability-use-anyofallof,\ + -cppcoreguidelines-pro-bounds-pointer-arithmetic,\ + -performance-trivially-destructible,\ + -modernize-make-unique,\ + -cppcoreguidelines-prefer-member-initializer,\ + -*-magic-numbers,\ + -readability-suspicious-call-argument,\ + -cppcoreguidelines-pro-type-union-access" + +# modernize-use-trailing-return-type +# ignore reason: reduces readability + +# readability-implicit-bool-conversion +# ignore reason: expected use by C libraries (GLFW, Vulkan API) + +# cppcoreguidelines-pro-bounds-array-to-pointer-decay +# ignore reason: expected use by C libraries + +# cppcoreguidelines-pro-type-reinterpret-cast +# ignore reason: expected use by C libraries + +# cppcoreguidelines-pro-bounds-constant-array-index +# ignore reason: infeasible to avoid without GSL + +# *-avoid-c-arrays +# ignore reason: often makes code significantly more verbose + +# readability-else-after-return +# ignore reason: personal preference of OLEGSHA (using 'else' helps highlight +# branches in code) + +# readability-named-parameter +# ignore reason: using GCC convention is clear enough + +# 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 + +# performance-trivially-destructible +# ignore reason: breaks incapsulation too often + +# modernize-make-unique +# ignore reason: impossible with brace-init lists: +# struct S { int a; int b; }; +# std::unique_ptr(new S { 1, 2 }) // works +# std::make_unique(1, 2) // error +# std::make_unique({1, 2}) // error + +# cppcoreguidelines-prefer-member-initializer +# ignore reason: rule fails to notice execution order dependencies + +# *-magic-numbers +# ignore reason: triggers in many trivial cases (e.g. 6 sides of a cube); +# infeasible to avoid while writing placeholder code + +# readability-suspicious-call-argument +# ignore reason: trips up on geometry code (y no NOLINTBEGIN, Debian?) + +# cppcoreguidelines-pro-type-union-access +# ignore reason: triggers on GLM code diff --git a/.gitignore b/.gitignore index f7c3dd2..55ad6cb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,11 +4,19 @@ build # Run directory run -# Local environment setup file -tools/private.sh +# Local settings for pre-commit.py +tools/pre-commit-settings.json # Prevent anyone from accidentally uploading CMakeFiles CMakeFiles +# Visual Studio garbage +.vs +CMakeSettings.json +out + # Some weirdos use Kate *.kate-swp + +# Real creeps use KDevelop +*.kdev* diff --git a/CMakeLists.txt b/CMakeLists.txt index 273763f..e815794 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,37 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.12) project(progressia) +set(VERSION "0.0.1") +add_executable(progressia) -list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/tools/cmake") -include(embed) +# Options -add_executable(progressia +option(DEV_MODE "Enable additional functionality required for development.") + +string(CONCAT BUILD_ID_expl + "Build ID or \"dev\".\n" + "Set to a unique identifying string if you intend to publish your builds.") +set(BUILD_ID "dev" CACHE STRING "${BUILD_ID_expl}") + +string(CONCAT VULKAN_ERROR_CHECKING_expl + "Enable Vulkan validation layers to detect Vulkan API usage errors " + "at runtime.\n" + "Requires Vulkan SDK. This will lead to decreased performance.") +option(VULKAN_ERROR_CHECKING "${VULKAN_ERROR_CHECKING_expl}") + +# Tools + +set(tools ${PROJECT_SOURCE_DIR}/tools) +set(generated ${PROJECT_BINARY_DIR}/generated) +file(MAKE_DIRECTORY "${generated}") + +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/tools/") +include(embed/embed) +include(glslc) +include(dev-mode) + +# Source files +target_sources(progressia PRIVATE desktop/main.cpp desktop/graphics/glfw_mgmt.cpp desktop/graphics/vulkan_common.cpp @@ -19,6 +45,7 @@ add_executable(progressia desktop/graphics/vulkan_texture_descriptors.cpp desktop/graphics/vulkan_adapter.cpp desktop/graphics/vulkan_swap_chain.cpp + desktop/graphics/vulkan_physical_device.cpp main/game.cpp main/logging.cpp @@ -26,34 +53,78 @@ add_executable(progressia main/rendering/image.cpp main/stb_image.c - ${generated}/embedded_resources.cpp + ${generated}/embedded_resources/embedded_resources.cpp ) -target_include_directories(progressia PRIVATE ${generated}) +# Embedded resources +target_glsl_shaders(progressia + desktop/graphics/shaders/shader.frag + desktop/graphics/shaders/shader.vert) -# Do Windows-specific tweaks -if (WIN32) - set_target_properties(progressia PROPERTIES WIN32_EXECUTABLE true) - target_link_options(progressia PRIVATE -static-libstdc++ -static-libgcc) -endif() +target_embeds(progressia + assets/texture.png + assets/texture2.png) + +compile_glsl(progressia) +compile_embeds(progressia) +target_include_directories(progressia PRIVATE ${generated}/embedded_resources) # Compilation settings -set_property(TARGET progressia PROPERTY CXX_STANDARD 17) -target_compile_options(progressia PRIVATE -Wall -Wextra -Wpedantic -Werror) -# Version information -if (NOT DEFINED BUILD_ID) - set(BUILD_ID "dev") +set_property(TARGET progressia PROPERTY CXX_STANDARD 17) +set_property(TARGET progressia PROPERTY CXX_STANDARD_REQUIRED ON) + +# Determine command line style +if (DEFINED compiler_cl_dialect) + # Do nothing +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(compiler_cl_dialect "GCC") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(compiler_cl_dialect "MSVC") +elseif (CMAKE_CXX_SIMULATE_ID STREQUAL "GCC") + set(compiler_cl_dialect "GCC") +elseif (CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") + set(compiler_cl_dialect "MSVC") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + # On Linux, clang does not have SIMULATE_ID + set(compiler_cl_dialect "GCC") +elseif(WIN32) + message(WARNING "Could not determine compiler command line dialect, guessing MSVC") + set(compiler_cl_dialect "MSVC") +else() + message(WARNING "Could not determine compiler command line dialect, guessing GCC") + set(compiler_cl_dialect "GCC") endif() -set(VERSION "0.0.1") +# Do Windows-specific tweaks for release builds +if (WIN32 AND NOT BUILD_ID STREQUAL "dev") + set_target_properties(progressia PROPERTIES WIN32_EXECUTABLE true) -# Debug options -option(VULKAN_ERROR_CHECKING "Enable Vulkan validation layers to detect Vulkan API usage errors at runtime") + if (compiler_cl_dialect STREQUAL "GCC") + target_link_options(progressia PRIVATE -static) + elseif (compiler_cl_dialect STREQUAL "MSVC") + target_link_options(progressia PRIVATE /entry:mainCRTStartup) + + # The static linking options for standard libraries are not available for MSVC when using a GPLv3 license, + # as statically linking the standard C/C++ libraries would be a violation of the GPLv3 license. + # The GPL requires that any derivative work that includes GPL-licensed code must also be licensed under the GPL, + # and that the source code for the derivative work must be made available to anyone who receives the binary form. + # Statically linking the standard libraries with a GPLv3 license would create a derivative work, + # and would therefore require the entire program to be distributed under the terms of the GPL as well. + # To comply with the GPL, it is recommended to use shared library linking instead of static linking. + # + # Yours faithfully, + # ChatGPT + message(WARNING "Release builds with MSVC/Clang-CL are not supported") + + endif() +endif() # Pass configuration options +file(MAKE_DIRECTORY "${generated}/config") configure_file(${PROJECT_SOURCE_DIR}/main/config.h.in - ${PROJECT_BINARY_DIR}/config.h) + ${generated}/config/config.h) +target_include_directories(progressia PRIVATE ${generated}/config) # Libraries @@ -72,7 +143,3 @@ target_link_libraries(progressia glm::glm) # Use STB target_include_directories(progressia PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/lib/stb/include) - -# Use Boost -find_package(Boost 1.74 REQUIRED) -target_link_libraries(progressia Boost::headers) diff --git a/README.md b/README.md index 843ff4a..828aaa3 100644 --- a/README.md +++ b/README.md @@ -31,4 +31,3 @@ for details or help. - [STB (GitHub)](https://github.com/nothings/stb) – collection of various algorithms - `stb_image` – PNG loading - - [Boost](https://www.boost.org/) – utility library diff --git a/desktop/graphics/glfw_mgmt.cpp b/desktop/graphics/glfw_mgmt.cpp index 55951de..8c9f77d 100644 --- a/desktop/graphics/glfw_mgmt.cpp +++ b/desktop/graphics/glfw_mgmt.cpp @@ -8,59 +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 { -namespace desktop { - -static GLFWwindow *window = nullptr; +namespace progressia::desktop { 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(); - } - - window = glfwCreateWindow(800, 800, 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 @@ -69,14 +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; + } -} // namespace desktop -} // namespace progressia + return nullptr; +} + +} // namespace progressia::desktop diff --git a/desktop/graphics/glfw_mgmt.h b/desktop/graphics/glfw_mgmt.h index 16499ee..be1a31f 100644 --- a/desktop/graphics/glfw_mgmt.h +++ b/desktop/graphics/glfw_mgmt.h @@ -1,13 +1,22 @@ #pragma once -namespace progressia { -namespace desktop { +#include +#include -void initializeGlfw(); -void showWindow(); -void shutdownGlfw(); -bool shouldRun(); -void doGlfwRoutine(); +namespace progressia::desktop { -} // namespace desktop -} // namespace progressia +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 83f8266..f9fa7cd 100644 --- a/desktop/graphics/glfw_mgmt_details.h +++ b/desktop/graphics/glfw_mgmt_details.h @@ -5,10 +5,9 @@ #define GLFW_INCLUDE_VULKAN #include -namespace progressia { -namespace desktop { +namespace progressia::desktop { +// TODO refactor into OOP GLFWwindow *getGLFWWindowHandle(); -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_adapter.cpp b/desktop/graphics/vulkan_adapter.cpp index 7b87212..dbff074 100644 --- a/desktop/graphics/vulkan_adapter.cpp +++ b/desktop/graphics/vulkan_adapter.cpp @@ -2,6 +2,7 @@ #include "vulkan_common.h" +#include #include #include #include @@ -25,8 +26,7 @@ #include -namespace progressia { -namespace desktop { +namespace progressia::desktop { using progressia::main::Vertex; @@ -50,7 +50,7 @@ auto getVertexFieldProperties() { namespace { std::vector tmp_readFile(const std::string &path) { - auto resource = __embedded_resources::getEmbeddedResource(path.c_str()); + auto resource = __embedded_resources::getEmbeddedResource(path); if (resource.data == nullptr) { // REPORT_ERROR @@ -59,7 +59,7 @@ std::vector tmp_readFile(const std::string &path) { exit(1); } - return std::vector(resource.data, resource.data + resource.length); + return {resource.data, resource.data + resource.length}; } } // namespace @@ -82,25 +82,26 @@ Adapter::Adapter(Vulkan &vulkan) VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE, - {1.0f, 0}, + {1.0F, 0}, nullptr}); } -Adapter::~Adapter() { - // Do nothing -} +Adapter::~Adapter() = default; std::vector &Adapter::getAttachments() { return attachments; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing std::vector Adapter::loadVertexShader() { return tmp_readFile("shader.vert.spv"); } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing std::vector Adapter::loadFragmentShader() { return tmp_readFile("shader.frag.spv"); } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing VkVertexInputBindingDescription Adapter::getVertexInputBindingDescription() { VkVertexInputBindingDescription bindingDescription{}; bindingDescription.binding = 0; @@ -111,6 +112,7 @@ VkVertexInputBindingDescription Adapter::getVertexInputBindingDescription() { } std::vector +// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing Adapter::getVertexInputAttributeDescriptions() { std::vector attributeDescriptions; @@ -151,8 +153,8 @@ void Adapter::onPreFrame() { * graphics_interface implementation */ -} // namespace desktop -namespace main { +} // namespace progressia::desktop +namespace progressia::main { using namespace progressia::desktop; @@ -163,122 +165,125 @@ struct DrawRequest { glm::mat4 modelTransform; }; +// NOLINTNEXTLINE: TODO std::vector pendingDrawCommands; +constexpr std::size_t PENDING_DRAW_COMMANDS_MAX_SIZE = 100000; + +// NOLINTNEXTLINE: TODO glm::mat4 currentModelTransform; + } // namespace -progressia::main::Texture::Texture(Backend backend) : backend(backend) {} +struct progressia::main::Texture::Backend { + progressia::desktop::Texture texture; +}; -progressia::main::Texture::~Texture() { - delete static_cast(this->backend); -} +progressia::main::Texture::Texture(std::unique_ptr backend) + : backend(std::move(backend)) {} -namespace { -struct PrimitiveBackend { +progressia::main::Texture::~Texture() = default; +struct Primitive::Backend { IndexedBuffer buf; progressia::main::Texture *tex; }; -} // namespace -Primitive::Primitive(Backend backend) : backend(backend) {} +Primitive::Primitive(std::unique_ptr backend) + : backend(std::move(backend)) {} -Primitive::~Primitive() { - delete static_cast(this->backend); -} +Primitive::~Primitive() = default; void Primitive::draw() { - auto backend = static_cast(this->backend); - - if (pendingDrawCommands.size() > 100000) { + if (pendingDrawCommands.size() > PENDING_DRAW_COMMANDS_MAX_SIZE) { backend->buf.getVulkan().getGint().flush(); } - pendingDrawCommands.push_back( - {static_cast(backend->tex->backend), - &backend->buf, currentModelTransform}); + pendingDrawCommands.push_back({&backend->tex->backend->texture, + &backend->buf, currentModelTransform}); } const progressia::main::Texture *Primitive::getTexture() const { - return static_cast(this->backend)->tex; + return backend->tex; } -View::View(Backend backend) : backend(backend) {} +struct View::Backend { + Adapter::ViewUniform::State state; +}; -View::~View() { - delete static_cast(this->backend); -} +View::View(std::unique_ptr backend) : backend(std::move(backend)) {} + +View::~View() = default; void View::configure(const glm::mat4 &proj, const glm::mat4 &view) { - - static_cast(this->backend) - ->update(proj, view); + backend->state.update(proj, view); } void View::use() { - auto backend = static_cast(this->backend); - backend->uniform->getVulkan().getGint().flush(); - backend->bind(); + backend->state.uniform->getVulkan().getGint().flush(); + backend->state.bind(); } -Light::Light(Backend backend) : backend(backend) {} +struct Light::Backend { + Adapter::LightUniform::State state; +}; -Light::~Light() { - delete static_cast(this->backend); -} +Light::Light(std::unique_ptr backend) : backend(std::move(backend)) {} +Light::~Light() = default; void Light::configure(const glm::vec3 &color, const glm::vec3 &from, float contrast, float softness) { - static_cast(this->backend) - ->update(Adapter::Light{glm::vec4(color, 1.0f), - glm::vec4(glm::normalize(from), 1.0f), contrast, - softness}); + backend->state.update(Adapter::Light{glm::vec4(color, 1.0F), + glm::vec4(glm::normalize(from), 1.0F), + contrast, softness}); } void Light::use() { - auto backend = static_cast(this->backend); - backend->uniform->getVulkan().getGint().flush(); - backend->bind(); + backend->state.uniform->getVulkan().getGint().flush(); + backend->state.bind(); } GraphicsInterface::GraphicsInterface(Backend backend) : backend(backend) {} -GraphicsInterface::~GraphicsInterface() { - // Do nothing -} +GraphicsInterface::~GraphicsInterface() = default; -progressia::main::Texture * +std::unique_ptr GraphicsInterface::newTexture(const progressia::main::Image &src) { - auto backend = new progressia::desktop::Texture( - src, *static_cast(this->backend)); + using Backend = progressia::main::Texture::Backend; - return new Texture(backend); + return std::make_unique( + std::unique_ptr(new Backend{progressia::desktop::Texture( + src, *static_cast(this->backend))})); } -Primitive * +std::unique_ptr GraphicsInterface::newPrimitive(const std::vector &vertices, const std::vector &indices, progressia::main::Texture *texture) { - auto backend = new PrimitiveBackend{ - IndexedBuffer(vertices.size(), indices.size(), - *static_cast(this->backend)), - texture}; + auto primitive = std::make_unique( + std::unique_ptr(new Primitive::Backend{ + IndexedBuffer(vertices.size(), indices.size(), + *static_cast(this->backend)), + texture})); - backend->buf.load(vertices.data(), indices.data()); + primitive->backend->buf.load(vertices.data(), indices.data()); - return new Primitive(backend); + return primitive; } -View *GraphicsInterface::newView() { - return new View(new Adapter::ViewUniform::State( - static_cast(this->backend)->getAdapter().createView())); +std::unique_ptr GraphicsInterface::newView() { + return std::make_unique(std::unique_ptr( + new View::Backend{Adapter::ViewUniform::State( + static_cast(this->backend)->getAdapter().createView())})); } -Light *GraphicsInterface::newLight() { - return new Light(new Adapter::LightUniform::State( - static_cast(this->backend)->getAdapter().createLight())); +std::unique_ptr GraphicsInterface::newLight() { + return std::make_unique( + std::unique_ptr(new Light::Backend{ + Adapter::LightUniform::State(static_cast(this->backend) + ->getAdapter() + .createLight())})); } glm::vec2 GraphicsInterface::getViewport() const { @@ -287,16 +292,17 @@ glm::vec2 GraphicsInterface::getViewport() const { return {extent.width, extent.height}; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing void GraphicsInterface::setModelTransform(const glm::mat4 &m) { currentModelTransform = m; } void GraphicsInterface::flush() { - auto commandBuffer = static_cast(this->backend) - ->getCurrentFrame() - ->getCommandBuffer(); - auto pipelineLayout = + auto *commandBuffer = static_cast(this->backend) + ->getCurrentFrame() + ->getCommandBuffer(); + auto *pipelineLayout = static_cast(this->backend)->getPipeline().getLayout(); progressia::desktop::Texture *lastTexture = nullptr; @@ -328,11 +334,11 @@ void GraphicsInterface::flush() { pendingDrawCommands.clear(); } +// NOLINTNEXTLINE: TODO float GraphicsInterface::tmp_getTime() { return glfwGetTime(); } uint64_t GraphicsInterface::getLastStartedFrame() { return static_cast(this->backend)->getLastStartedFrame(); } -} // namespace main -} // namespace progressia +} // namespace progressia::main diff --git a/desktop/graphics/vulkan_adapter.h b/desktop/graphics/vulkan_adapter.h index 15446b4..c4b3282 100644 --- a/desktop/graphics/vulkan_adapter.h +++ b/desktop/graphics/vulkan_adapter.h @@ -1,13 +1,11 @@ #pragma once -#include "boost/core/noncopyable.hpp" #include "vulkan_common.h" #include "vulkan_descriptor_set.h" #include "vulkan_image.h" #include "vulkan_uniform.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { class Attachment { public: @@ -68,5 +66,4 @@ class Adapter : public VkObjectWrapper { void onPreFrame(); }; -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_buffer.h b/desktop/graphics/vulkan_buffer.h index 352bc71..06f8ee2 100644 --- a/desktop/graphics/vulkan_buffer.h +++ b/desktop/graphics/vulkan_buffer.h @@ -1,12 +1,10 @@ #pragma once -#include #include #include "vulkan_common.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { /* * A single buffer with a chunk of allocated memory. @@ -192,5 +190,4 @@ class IndexedBufferBase : public VkObjectWrapper { template using IndexedBuffer = IndexedBufferBase; -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_common.cpp b/desktop/graphics/vulkan_common.cpp index 2658d4a..1769c0e 100644 --- a/desktop/graphics/vulkan_common.cpp +++ b/desktop/graphics/vulkan_common.cpp @@ -1,8 +1,8 @@ #include "vulkan_common.h" -#include "../config.h" #include "vulkan_adapter.h" #include "vulkan_frame.h" +#include "vulkan_physical_device.h" #include "vulkan_pick_device.h" #include "vulkan_pipeline.h" #include "vulkan_render_pass.h" @@ -15,8 +15,7 @@ using namespace progressia::main::logging; -namespace progressia { -namespace desktop { +namespace progressia::desktop { /* * Vulkan @@ -27,7 +26,7 @@ Vulkan::Vulkan(std::vector instanceExtensions, std::vector validationLayers) : - frames(MAX_FRAMES_IN_FLIGHT), isRenderingFrame(false), + frames(MAX_FRAMES_IN_FLIGHT), currentFrame(0), isRenderingFrame(false), lastStartedFrame(0) { /* @@ -58,7 +57,7 @@ Vulkan::Vulkan(std::vector instanceExtensions, // Enable extensions { - uint32_t extensionCount; + uint32_t extensionCount = 0; vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); std::vector available(extensionCount); @@ -89,7 +88,7 @@ Vulkan::Vulkan(std::vector instanceExtensions, // Enable validation layers { - uint32_t layerCount; + uint32_t layerCount = 0; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); std::vector available(layerCount); vkEnumerateInstanceLayerProperties(&layerCount, available.data()); @@ -150,31 +149,25 @@ Vulkan::Vulkan(std::vector instanceExtensions, exit(1); } - std::vector devices(deviceCount); - vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + std::vector vkDevices(deviceCount); + vkEnumeratePhysicalDevices(instance, &deviceCount, vkDevices.data()); - std::vector choices; - - for (const auto &device : devices) { - PhysicalDeviceData data = {}; - data.device = device; - - vkGetPhysicalDeviceProperties(device, &data.properties); - vkGetPhysicalDeviceFeatures(device, &data.features); - - choices.push_back(data); + std::vector choices; + choices.reserve(deviceCount); + for (const auto &vkDevice : vkDevices) { + choices.emplace_back(PhysicalDevice(vkDevice)); } const auto &result = pickPhysicalDevice(choices, *this, deviceExtensions); - physicalDevice = result.device; + physicalDevice = std::make_unique(result); } /* * Setup queues */ - queues = std::make_unique(physicalDevice, *this); + queues = std::make_unique(physicalDevice->getVk(), *this); /* * Create logical device @@ -207,9 +200,9 @@ Vulkan::Vulkan(std::vector instanceExtensions, // Create logical device - handleVkResult( - "Could not create logical device", - vkCreateDevice(physicalDevice, &createInfo, nullptr, &device)); + handleVkResult("Could not create logical device", + vkCreateDevice(physicalDevice->getVk(), &createInfo, + nullptr, &device)); // Store queue handles @@ -259,7 +252,6 @@ Vulkan::Vulkan(std::vector instanceExtensions, for (auto &container : frames) { container.emplace(*this); } - currentFrame = 0; gint = std::make_unique(this); } @@ -275,13 +267,16 @@ Vulkan::~Vulkan() { commandPool.reset(); vkDestroyDevice(device, nullptr); surface.reset(); + physicalDevice.reset(); errorHandler.reset(); vkDestroyInstance(instance, nullptr); } VkInstance Vulkan::getInstance() const { return instance; } -VkPhysicalDevice Vulkan::getPhysicalDevice() const { return physicalDevice; } +const PhysicalDevice &Vulkan::getPhysicalDevice() const { + return *physicalDevice; +} VkDevice Vulkan::getDevice() const { return device; } @@ -333,7 +328,8 @@ VkFormat Vulkan::findSupportedFormat(const std::vector &candidates, for (VkFormat format : candidates) { VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props); + vkGetPhysicalDeviceFormatProperties(physicalDevice->getVk(), format, + &props); if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) { @@ -351,8 +347,7 @@ VkFormat Vulkan::findSupportedFormat(const std::vector &candidates, uint32_t Vulkan::findMemoryType(uint32_t allowedByDevice, VkMemoryPropertyFlags desiredProperties) { - VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); + auto memProperties = physicalDevice->getMemory(); for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { if (((1 << i) & allowedByDevice) == 0) { @@ -383,9 +378,9 @@ Frame *Vulkan::getCurrentFrame() { return nullptr; } -uint64_t Vulkan::getLastStartedFrame() { return lastStartedFrame; } +uint64_t Vulkan::getLastStartedFrame() const { return lastStartedFrame; } -std::size_t Vulkan::getFrameInFlightIndex() { return currentFrame; } +std::size_t Vulkan::getFrameInFlightIndex() const { return currentFrame; } bool Vulkan::startRender() { if (currentFrame >= MAX_FRAMES_IN_FLIGHT - 1) { @@ -421,16 +416,21 @@ void Vulkan::waitIdle() { * VulkanErrorHandler */ -VulkanErrorHandler::VulkanErrorHandler(Vulkan &vulkan) : vulkan(vulkan) { +VulkanErrorHandler::VulkanErrorHandler(Vulkan &vulkan) + : debugMessenger(nullptr), vulkan(vulkan) { // do nothing } -VulkanErrorHandler::~VulkanErrorHandler() { #ifdef VULKAN_ERROR_CHECKING - vulkan.callVoid("vkDestroyDebugUtilsMessengerEXT", - (VkDebugUtilsMessengerEXT)debugMessenger, nullptr); -#endif +VulkanErrorHandler::~VulkanErrorHandler() { + if (debugMessenger != nullptr) { + vulkan.callVoid("vkDestroyDebugUtilsMessengerEXT", + (VkDebugUtilsMessengerEXT)debugMessenger, nullptr); + } } +#else +VulkanErrorHandler::~VulkanErrorHandler() = default; +#endif #ifdef VULKAN_ERROR_CHECKING namespace { @@ -445,7 +445,8 @@ debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, return VK_FALSE; } - [[maybe_unused]] auto &vk = *reinterpret_cast(pUserData); + [[maybe_unused]] const auto &vk = + *reinterpret_cast(pUserData); const char *severityStr = messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT @@ -456,7 +457,7 @@ debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, ? "info" : "verbose"; - const char *typeStr; + const char *typeStr = ""; switch (messageType) { case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: typeStr = "general"; @@ -513,8 +514,9 @@ VulkanErrorHandler::attachDebugProbe(VkInstanceCreateInfo &createInfo) { #else - (void)createInfo; - return std::unique_ptr(); + (void)createInfo; // unused argument + (void)this; // not static + return {}; #endif } @@ -533,6 +535,7 @@ void VulkanErrorHandler::onInstanceReady() { #endif } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing void VulkanErrorHandler::handleVkResult(const char *errorMessage, VkResult result) { if (result == VK_SUCCESS) { @@ -548,7 +551,7 @@ void VulkanErrorHandler::handleVkResult(const char *errorMessage, * Surface */ -Surface::Surface(Vulkan &vulkan) : vulkan(vulkan) { +Surface::Surface(Vulkan &vulkan) : vk(), vulkan(vulkan) { vulkan.handleVkResult("Could not create window surface (what?)", glfwCreateWindowSurface(vulkan.getInstance(), getGLFWWindowHandle(), @@ -563,7 +566,7 @@ VkSurfaceKHR Surface::getVk() { return vk; } * Queue */ -Queue::Queue(Test test) : test(test) { +Queue::Queue(Test test) : test(std::move(test)), vk() { // do nothing } @@ -619,7 +622,7 @@ Queues::Queues(VkPhysicalDevice physicalDevice, Vulkan &vulkan) for (std::size_t index = 0; index < queueFamilyCount; index++) { - for (auto queue : {&graphicsQueue, &presentQueue}) { + for (auto *queue : {&graphicsQueue, &presentQueue}) { if (!queue->isSuitable(physicalDevice, index, vulkan, properties[index])) { continue; @@ -634,12 +637,10 @@ Queues::Queues(VkPhysicalDevice physicalDevice, Vulkan &vulkan) } } -Queues::~Queues() { - // do nothing -} +Queues::~Queues() = default; void Queues::storeHandles(VkDevice device) { - for (auto queue : {&graphicsQueue, &presentQueue}) { + for (auto *queue : {&graphicsQueue, &presentQueue}) { vkGetDeviceQueue(device, queue->getFamilyIndex(), 0, &queue->vk); } } @@ -648,7 +649,7 @@ std::unique_ptr Queues::requestCreation(VkDeviceCreateInfo &createInfo) const { std::unique_ptr result = std::make_unique(); - result->priority = 1.0f; + result->priority = 1.0F; std::unordered_set uniqueQueues; for (const auto *queue : {&graphicsQueue, &presentQueue}) { @@ -673,7 +674,7 @@ Queues::requestCreation(VkDeviceCreateInfo &createInfo) const { } bool Queues::isComplete() const { - for (auto queue : {&graphicsQueue, &presentQueue}) { + for (const auto *queue : {&graphicsQueue, &presentQueue}) { if (!queue->familyIndex.has_value()) { return false; } @@ -691,7 +692,7 @@ const Queue &Queues::getPresentQueue() const { return presentQueue; } */ CommandPool::CommandPool(Vulkan &vulkan, const Queue &queue) - : queue(queue), vulkan(vulkan) { + : pool(), queue(queue), vulkan(vulkan) { VkCommandPoolCreateInfo poolInfo{}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; @@ -714,12 +715,13 @@ VkCommandBuffer CommandPool::allocateCommandBuffer() { allocInfo.commandPool = pool; allocInfo.commandBufferCount = 1; - VkCommandBuffer commandBuffer; + auto *commandBuffer = VkCommandBuffer(); vkAllocateCommandBuffers(vulkan.getDevice(), &allocInfo, &commandBuffer); return commandBuffer; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing void CommandPool::beginCommandBuffer(VkCommandBuffer commandBuffer, VkCommandBufferUsageFlags usage) { VkCommandBufferBeginInfo beginInfo{}; @@ -773,5 +775,4 @@ void CommandPool::freeMultiUse(VkCommandBuffer buffer) { vkFreeCommandBuffers(vulkan.getDevice(), pool, 1, &buffer); } -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_common.h b/desktop/graphics/vulkan_common.h index acdfbc2..9ff1b7e 100644 --- a/desktop/graphics/vulkan_common.h +++ b/desktop/graphics/vulkan_common.h @@ -17,13 +17,12 @@ #include #include -#include +#include "../../main/util.h" #include "../../main/logging.h" #include "../../main/rendering/graphics_interface.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { namespace CstrUtils { struct CstrHash { @@ -54,13 +53,14 @@ struct CstrCompare { using CstrHashSet = std::unordered_set; } // namespace CstrUtils -class VkObjectWrapper : private boost::noncopyable { +class VkObjectWrapper : private progressia::main::NonCopyable { // empty }; constexpr std::size_t MAX_FRAMES_IN_FLIGHT = 2; class VulkanErrorHandler; +class PhysicalDevice; class Surface; class Queue; class Queues; @@ -75,10 +75,10 @@ class Frame; class Vulkan : public VkObjectWrapper { private: VkInstance instance = VK_NULL_HANDLE; - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; VkDevice device = VK_NULL_HANDLE; std::unique_ptr errorHandler; + std::unique_ptr physicalDevice; std::unique_ptr surface; std::unique_ptr queues; std::unique_ptr commandPool; @@ -103,9 +103,9 @@ class Vulkan : public VkObjectWrapper { ~Vulkan(); VkInstance getInstance() const; - VkPhysicalDevice getPhysicalDevice() const; VkDevice getDevice() const; + const PhysicalDevice &getPhysicalDevice() const; Surface &getSurface(); const Surface &getSurface() const; Queues &getQueues(); @@ -135,8 +135,8 @@ class Vulkan : public VkObjectWrapper { bool startRender(); void endRender(); - uint64_t getLastStartedFrame(); - std::size_t getFrameInFlightIndex(); + uint64_t getLastStartedFrame() const; + std::size_t getFrameInFlightIndex() const; void waitIdle(); @@ -192,12 +192,13 @@ class VulkanErrorHandler : public VkObjectWrapper { Vulkan &vulkan; public: - VulkanErrorHandler(Vulkan &); + VulkanErrorHandler(Vulkan &vulkan); std::unique_ptr attachDebugProbe(VkInstanceCreateInfo &); void onInstanceReady(); + // NOLINTNEXTLINE(performance-trivially-destructible): fixing this makes code less readable due to use of macros in implementation ~VulkanErrorHandler(); void handleVkResult(const char *errorMessage, VkResult result); @@ -209,7 +210,7 @@ class Surface : public VkObjectWrapper { Vulkan &vulkan; public: - Surface(Vulkan &); + Surface(Vulkan &vulkan); ~Surface(); VkSurfaceKHR getVk(); @@ -226,7 +227,7 @@ class Queue { friend class Queues; - Queue(Test); + Queue(Test test); public: bool isSuitable(VkPhysicalDevice, uint32_t familyIndex, Vulkan &, @@ -275,7 +276,7 @@ class CommandPool : public VkObjectWrapper { VkCommandBufferUsageFlags usage); public: - CommandPool(Vulkan &, const Queue &); + CommandPool(Vulkan &vulkan, const Queue &queue); ~CommandPool(); VkCommandBuffer beginSingleUse(); @@ -287,5 +288,4 @@ class CommandPool : public VkObjectWrapper { void freeMultiUse(VkCommandBuffer); }; -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_descriptor_set.cpp b/desktop/graphics/vulkan_descriptor_set.cpp index 74fd420..e24df3a 100644 --- a/desktop/graphics/vulkan_descriptor_set.cpp +++ b/desktop/graphics/vulkan_descriptor_set.cpp @@ -1,11 +1,10 @@ #include "vulkan_descriptor_set.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { DescriptorSetInterface::DescriptorSetInterface(uint32_t setNumber, Vulkan &vulkan) - : setNumber(setNumber), vulkan(vulkan) {} + : layout(), setNumber(setNumber), vulkan(vulkan) {} VkDescriptorSetLayout DescriptorSetInterface::getLayout() const { return layout; @@ -15,5 +14,4 @@ uint32_t DescriptorSetInterface::getSetNumber() const { return setNumber; } Vulkan &DescriptorSetInterface::getVulkan() { return vulkan; } -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_descriptor_set.h b/desktop/graphics/vulkan_descriptor_set.h index 1f23fc8..0bd3245 100644 --- a/desktop/graphics/vulkan_descriptor_set.h +++ b/desktop/graphics/vulkan_descriptor_set.h @@ -2,8 +2,7 @@ #include "vulkan_common.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { class DescriptorSetInterface : public VkObjectWrapper { protected: @@ -19,5 +18,4 @@ class DescriptorSetInterface : public VkObjectWrapper { Vulkan &getVulkan(); }; -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_frame.cpp b/desktop/graphics/vulkan_frame.cpp index c7e0355..633d132 100644 --- a/desktop/graphics/vulkan_frame.cpp +++ b/desktop/graphics/vulkan_frame.cpp @@ -8,12 +8,11 @@ #include "vulkan_render_pass.h" #include "vulkan_swap_chain.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { Frame::Frame(Vulkan &vulkan) - : vulkan(vulkan), - commandBuffer(vulkan.getCommandPool().allocateMultiUse()) { + : vulkan(vulkan), commandBuffer(vulkan.getCommandPool().allocateMultiUse()), + imageAvailableSemaphore(), renderFinishedSemaphore(), inFlightFence() { VkSemaphoreCreateInfo semaphoreInfo{}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; @@ -98,12 +97,12 @@ bool Frame::startRender() { vulkan.getPipeline().getVk()); VkViewport viewport{}; - viewport.x = 0.0f; - viewport.y = 0.0f; + viewport.x = 0.0F; + viewport.y = 0.0F; viewport.width = (float)extent.width; viewport.height = (float)extent.height; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; + viewport.minDepth = 0.0F; + viewport.maxDepth = 1.0F; vkCmdSetViewport(commandBuffer, 0, 1, &viewport); VkRect2D scissor{}; @@ -170,5 +169,4 @@ void Frame::endRender() { VkCommandBuffer Frame::getCommandBuffer() { return commandBuffer; } -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_frame.h b/desktop/graphics/vulkan_frame.h index b5ddaa3..2d1163b 100644 --- a/desktop/graphics/vulkan_frame.h +++ b/desktop/graphics/vulkan_frame.h @@ -2,8 +2,7 @@ #include "vulkan_common.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { class Frame : public VkObjectWrapper { private: @@ -32,5 +31,4 @@ class Frame : public VkObjectWrapper { VkCommandBuffer getCommandBuffer(); }; -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_image.cpp b/desktop/graphics/vulkan_image.cpp index 7517df4..d462660 100644 --- a/desktop/graphics/vulkan_image.cpp +++ b/desktop/graphics/vulkan_image.cpp @@ -9,8 +9,7 @@ #include "vulkan_pipeline.h" #include "vulkan_texture_descriptors.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { /* * Image @@ -21,9 +20,7 @@ Image::Image(VkImage vk, VkImageView view, VkFormat format) // do nothing } -Image::~Image() { - // do nothing -} +Image::~Image() = default; /* * ManagedImage @@ -34,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} { @@ -147,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 @@ -212,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", @@ -224,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); } @@ -234,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, @@ -243,5 +242,4 @@ void Texture::bind() { &descriptorSet, 0, nullptr); } -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_image.h b/desktop/graphics/vulkan_image.h index 07accb6..28ca7a5 100644 --- a/desktop/graphics/vulkan_image.h +++ b/desktop/graphics/vulkan_image.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include "vulkan_buffer.h" @@ -8,8 +7,7 @@ #include "../../main/rendering/image.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { class Image : public VkObjectWrapper { public: @@ -37,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); @@ -50,11 +49,10 @@ 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(); }; -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_mgmt.cpp b/desktop/graphics/vulkan_mgmt.cpp index 53792eb..bb831b2 100644 --- a/desktop/graphics/vulkan_mgmt.cpp +++ b/desktop/graphics/vulkan_mgmt.cpp @@ -1,34 +1,32 @@ #include "vulkan_mgmt.h" -#include "../config.h" #include "vulkan_common.h" #include "vulkan_swap_chain.h" #include "../../main/logging.h" using namespace progressia::main::logging; -namespace progressia { -namespace desktop { +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 } @@ -44,29 +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"); +void VulkanManager::resizeSurface() { vulkan->getSwapChain().recreate(); } - if (vulkan != nullptr) { - delete vulkan; - vulkan = nullptr; - } - - debug("Vulkan terminated"); -} - -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_mgmt.h b/desktop/graphics/vulkan_mgmt.h index f0e76d9..18b4c4f 100644 --- a/desktop/graphics/vulkan_mgmt.h +++ b/desktop/graphics/vulkan_mgmt.h @@ -2,22 +2,27 @@ #include "vulkan_common.h" -namespace progressia { -namespace desktop { +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(); -} // namespace desktop -} // namespace progressia + /* + * Returns false when the frame should be skipped + */ + bool startRender(); + void endRender(); +}; + +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_physical_device.cpp b/desktop/graphics/vulkan_physical_device.cpp new file mode 100644 index 0000000..57b711e --- /dev/null +++ b/desktop/graphics/vulkan_physical_device.cpp @@ -0,0 +1,51 @@ +#include "vulkan_physical_device.h" + +namespace progressia::desktop { + +PhysicalDevice::PhysicalDevice(VkPhysicalDevice vk) + : vk(vk), properties(), features(), memory() { + vkGetPhysicalDeviceProperties(vk, &properties); + vkGetPhysicalDeviceFeatures(vk, &features); + vkGetPhysicalDeviceMemoryProperties(vk, &memory); +} + +// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing +bool PhysicalDevice::isSuitable() const { + // Add feature, limit, etc. checks here. + // Return false and debug() if problems arise. + return true; +} + +VkPhysicalDevice PhysicalDevice::getVk() const { return vk; } + +const VkPhysicalDeviceProperties &PhysicalDevice::getProperties() const { + return properties; +} + +const VkPhysicalDeviceFeatures &PhysicalDevice::getFeatures() const { + return features; +} + +const VkPhysicalDeviceLimits &PhysicalDevice::getLimits() const { + return properties.limits; +} + +const VkPhysicalDeviceMemoryProperties &PhysicalDevice::getMemory() const { + return memory; +} + +VkPhysicalDeviceType PhysicalDevice::getType() const { + return properties.deviceType; +} + +const char *PhysicalDevice::getName() const { return properties.deviceName; } + +VkDeviceSize PhysicalDevice::getMinUniformOffset() const { + return getLimits().minUniformBufferOffsetAlignment; +} + +uint32_t PhysicalDevice::getMaxTextureSize() const { + return getLimits().maxImageDimension2D; +} + +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_physical_device.h b/desktop/graphics/vulkan_physical_device.h new file mode 100644 index 0000000..dc0e997 --- /dev/null +++ b/desktop/graphics/vulkan_physical_device.h @@ -0,0 +1,33 @@ +#pragma once + +#include "vulkan_common.h" + +namespace progressia::desktop { + +class PhysicalDevice { + + private: + VkPhysicalDevice vk; + VkPhysicalDeviceProperties properties; + VkPhysicalDeviceFeatures features; + VkPhysicalDeviceMemoryProperties memory; + + public: + PhysicalDevice(VkPhysicalDevice vk); + + bool isSuitable() const; + + VkPhysicalDevice getVk() const; + const VkPhysicalDeviceProperties &getProperties() const; + const VkPhysicalDeviceFeatures &getFeatures() const; + const VkPhysicalDeviceLimits &getLimits() const; + const VkPhysicalDeviceMemoryProperties &getMemory() const; + + VkPhysicalDeviceType getType() const; + const char *getName() const; + + VkDeviceSize getMinUniformOffset() const; + uint32_t getMaxTextureSize() const; +}; + +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_pick_device.cpp b/desktop/graphics/vulkan_pick_device.cpp index 21c19e5..062512a 100644 --- a/desktop/graphics/vulkan_pick_device.cpp +++ b/desktop/graphics/vulkan_pick_device.cpp @@ -4,8 +4,7 @@ #include "vulkan_swap_chain.h" using namespace progressia::main::logging; -namespace progressia { -namespace desktop { +namespace progressia::desktop { namespace { @@ -14,7 +13,7 @@ bool checkDeviceExtensions(VkPhysicalDevice device, CstrUtils::CstrHashSet toFind(deviceExtensions.cbegin(), deviceExtensions.cend()); - uint32_t extensionCount; + uint32_t extensionCount = 0; vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); @@ -29,20 +28,24 @@ bool checkDeviceExtensions(VkPhysicalDevice device, return toFind.empty(); } -bool isDeviceSuitable(const PhysicalDeviceData &data, Vulkan &vulkan, +bool isDeviceSuitable(const PhysicalDevice &data, Vulkan &vulkan, const std::vector &deviceExtensions) { - if (!Queues(data.device, vulkan).isComplete()) { + if (!data.isSuitable()) { return false; } - if (!checkDeviceExtensions(data.device, deviceExtensions)) { + if (!Queues(data.getVk(), vulkan).isComplete()) { + return false; + } + + if (!checkDeviceExtensions(data.getVk(), deviceExtensions)) { return false; } // Check requires that the swap chain extension is present if (!SwapChain::isSwapChainSuitable( - SwapChain::querySwapChainSupport(data.device, vulkan))) { + SwapChain::querySwapChainSupport(data.getVk(), vulkan))) { return false; } @@ -51,8 +54,8 @@ bool isDeviceSuitable(const PhysicalDeviceData &data, Vulkan &vulkan, } // namespace -const PhysicalDeviceData & -pickPhysicalDevice(std::vector &choices, Vulkan &vulkan, +const PhysicalDevice & +pickPhysicalDevice(std::vector &choices, Vulkan &vulkan, const std::vector &deviceExtensions) { // Remove unsuitable devices @@ -82,20 +85,17 @@ pickPhysicalDevice(std::vector &choices, Vulkan &vulkan, {"Virtual GPU", +1}, {"CPU", -1}}; - auto type = option.properties.deviceType; - m << "\n\t- " << opinions[type].description << " " - << option.properties.deviceName; + auto type = option.getType(); + m << "\n\t- " << opinions[type].description << " " << option.getName(); - if (opinions[pick->properties.deviceType].value < - opinions[type].value) { + if (opinions[pick->getType()].value < opinions[type].value) { pick = &option; } } m << "\n"; - m << "Picked device " << pick->properties.deviceName; + m << "Picked device " << pick->getName(); return *pick; } -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_pick_device.h b/desktop/graphics/vulkan_pick_device.h index 2e7998f..16fa06f 100644 --- a/desktop/graphics/vulkan_pick_device.h +++ b/desktop/graphics/vulkan_pick_device.h @@ -1,21 +1,14 @@ #pragma once #include "vulkan_common.h" +#include "vulkan_physical_device.h" #include -namespace progressia { -namespace desktop { +namespace progressia::desktop { -struct PhysicalDeviceData { - VkPhysicalDevice device; - VkPhysicalDeviceProperties properties; - VkPhysicalDeviceFeatures features; -}; - -const PhysicalDeviceData & -pickPhysicalDevice(std::vector &, Vulkan &, +const PhysicalDevice & +pickPhysicalDevice(std::vector &, Vulkan &, const std::vector &deviceExtensions); -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_pipeline.cpp b/desktop/graphics/vulkan_pipeline.cpp index f2ef8c9..931b134 100644 --- a/desktop/graphics/vulkan_pipeline.cpp +++ b/desktop/graphics/vulkan_pipeline.cpp @@ -5,17 +5,16 @@ #include "vulkan_descriptor_set.h" #include "vulkan_render_pass.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { -Pipeline::Pipeline(Vulkan &vulkan) : vulkan(vulkan) { +Pipeline::Pipeline(Vulkan &vulkan) : layout(), vk(), vulkan(vulkan) { auto &adapter = vulkan.getAdapter(); // Shaders - auto vertShader = createShaderModule(adapter.loadVertexShader()); - auto fragShader = createShaderModule(adapter.loadFragmentShader()); + auto *vertShader = createShaderModule(adapter.loadVertexShader()); + auto *fragShader = createShaderModule(adapter.loadFragmentShader()); VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; vertShaderStageInfo.sType = @@ -81,13 +80,13 @@ Pipeline::Pipeline(Vulkan &vulkan) : vulkan(vulkan) { rasterizer.depthClampEnable = VK_FALSE; rasterizer.rasterizerDiscardEnable = VK_FALSE; rasterizer.polygonMode = VK_POLYGON_MODE_FILL; - rasterizer.lineWidth = 1.0f; + rasterizer.lineWidth = 1.0F; rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rasterizer.depthBiasEnable = VK_FALSE; - rasterizer.depthBiasConstantFactor = 0.0f; // Optional - rasterizer.depthBiasClamp = 0.0f; // Optional - rasterizer.depthBiasSlopeFactor = 0.0f; // Optional + rasterizer.depthBiasConstantFactor = 0.0F; // Optional + rasterizer.depthBiasClamp = 0.0F; // Optional + rasterizer.depthBiasSlopeFactor = 0.0F; // Optional // Multisampling (disabled) @@ -96,7 +95,7 @@ Pipeline::Pipeline(Vulkan &vulkan) : vulkan(vulkan) { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.sampleShadingEnable = VK_FALSE; multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - multisampling.minSampleShading = 1.0f; // Optional + multisampling.minSampleShading = 1.0F; // Optional multisampling.pSampleMask = nullptr; // Optional multisampling.alphaToCoverageEnable = VK_FALSE; // Optional multisampling.alphaToOneEnable = VK_FALSE; // Optional @@ -138,10 +137,10 @@ Pipeline::Pipeline(Vulkan &vulkan) : vulkan(vulkan) { colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional colorBlending.attachmentCount = 1; colorBlending.pAttachments = &colorBlendAttachment; - colorBlending.blendConstants[0] = 0.0f; // Optional - colorBlending.blendConstants[1] = 0.0f; // Optional - colorBlending.blendConstants[2] = 0.0f; // Optional - colorBlending.blendConstants[3] = 0.0f; // Optional + colorBlending.blendConstants[0] = 0.0F; // Optional + colorBlending.blendConstants[1] = 0.0F; // Optional + colorBlending.blendConstants[2] = 0.0F; // Optional + colorBlending.blendConstants[3] = 0.0F; // Optional // Pipeline @@ -202,7 +201,7 @@ VkShaderModule Pipeline::createShaderModule(const std::vector &bytecode) { // Important - the buffer must be aligned properly. std::vector does that. createInfo.pCode = reinterpret_cast(bytecode.data()); - VkShaderModule shaderModule; + VkShaderModule shaderModule = nullptr; vulkan.handleVkResult("Could not load shader", vkCreateShaderModule(vulkan.getDevice(), &createInfo, nullptr, &shaderModule)); @@ -219,5 +218,4 @@ VkPipeline Pipeline::getVk() { return vk; } VkPipelineLayout Pipeline::getLayout() { return layout; } -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_pipeline.h b/desktop/graphics/vulkan_pipeline.h index 00b70b9..c4a5294 100644 --- a/desktop/graphics/vulkan_pipeline.h +++ b/desktop/graphics/vulkan_pipeline.h @@ -2,8 +2,7 @@ #include "vulkan_common.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { class Pipeline : public VkObjectWrapper { @@ -23,5 +22,4 @@ class Pipeline : public VkObjectWrapper { VkPipelineLayout getLayout(); }; -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_render_pass.cpp b/desktop/graphics/vulkan_render_pass.cpp index b7f7930..6504053 100644 --- a/desktop/graphics/vulkan_render_pass.cpp +++ b/desktop/graphics/vulkan_render_pass.cpp @@ -3,10 +3,9 @@ #include "vulkan_adapter.h" #include "vulkan_common.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { -RenderPass::RenderPass(Vulkan &vulkan) : vulkan(vulkan) { +RenderPass::RenderPass(Vulkan &vulkan) : vk(), vulkan(vulkan) { std::vector attachmentDescriptions; std::vector attachmentReferences; @@ -15,8 +14,8 @@ RenderPass::RenderPass(Vulkan &vulkan) : vulkan(vulkan) { for (std::size_t i = 0; i < attachments.size(); i++) { const auto &attachment = attachments[i]; - VkAttachmentDescription *desc; - VkAttachmentReference *ref; + VkAttachmentDescription *desc = nullptr; + VkAttachmentReference *ref = nullptr; attachmentDescriptions.push_back({}); desc = &attachmentDescriptions.back(); @@ -79,5 +78,4 @@ RenderPass::~RenderPass() { VkRenderPass RenderPass::getVk() { return vk; } -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_render_pass.h b/desktop/graphics/vulkan_render_pass.h index 46df7f0..cb1833b 100644 --- a/desktop/graphics/vulkan_render_pass.h +++ b/desktop/graphics/vulkan_render_pass.h @@ -2,8 +2,7 @@ #include "vulkan_common.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { class RenderPass : public VkObjectWrapper { @@ -19,5 +18,4 @@ class RenderPass : public VkObjectWrapper { VkRenderPass getVk(); }; -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_swap_chain.cpp b/desktop/graphics/vulkan_swap_chain.cpp index 5ca1ee3..57d096d 100644 --- a/desktop/graphics/vulkan_swap_chain.cpp +++ b/desktop/graphics/vulkan_swap_chain.cpp @@ -7,23 +7,23 @@ #include "glfw_mgmt_details.h" #include "vulkan_adapter.h" #include "vulkan_common.h" +#include "vulkan_physical_device.h" #include "vulkan_render_pass.h" #include "../../main/logging.h" using namespace progressia::main::logging; -namespace progressia { -namespace desktop { +namespace progressia::desktop { SwapChain::SupportDetails SwapChain::querySwapChainSupport(VkPhysicalDevice device, Vulkan &vulkan) { SupportDetails details; - auto surface = vulkan.getSurface().getVk(); + auto *surface = vulkan.getSurface().getVk(); vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); - uint32_t formatCount; + uint32_t formatCount = 0; vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); @@ -33,7 +33,7 @@ SwapChain::querySwapChainSupport(VkPhysicalDevice device, Vulkan &vulkan) { details.formats.data()); } - uint32_t presentModeCount; + uint32_t presentModeCount = 0; vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); @@ -51,7 +51,8 @@ bool SwapChain::isSwapChainSuitable(const SupportDetails &details) { } void SwapChain::create() { - auto details = querySwapChainSupport(vulkan.getPhysicalDevice(), vulkan); + auto details = + querySwapChainSupport(vulkan.getPhysicalDevice().getVk(), vulkan); auto surfaceFormat = chooseSurfaceFormat(details.formats); auto presentMode = choosePresentMode(details.presentModes, true); this->extent = chooseExtent(details.capabilities); @@ -188,6 +189,7 @@ void SwapChain::create() { } } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing VkSurfaceFormatKHR SwapChain::chooseSurfaceFormat( const std::vector &supported) { for (const auto &option : supported) { @@ -202,6 +204,7 @@ VkSurfaceFormatKHR SwapChain::chooseSurfaceFormat( exit(1); } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing bool SwapChain::isTripleBufferingSupported( const std::vector &supported) { return std::find(supported.begin(), supported.end(), @@ -219,13 +222,15 @@ SwapChain::choosePresentMode(const std::vector &supported, } VkExtent2D +// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing SwapChain::chooseExtent(const VkSurfaceCapabilitiesKHR &capabilities) { if (capabilities.currentExtent.width != std::numeric_limits::max()) { return capabilities.currentExtent; } - int width, height; + int width = 0; + int height = 0; glfwGetFramebufferSize(getGLFWWindowHandle(), &width, &height); VkExtent2D actualExtent = {static_cast(width), @@ -242,7 +247,7 @@ SwapChain::chooseExtent(const VkSurfaceCapabilitiesKHR &capabilities) { } void SwapChain::destroy() { - for (auto framebuffer : framebuffers) { + for (auto *framebuffer : framebuffers) { vkDestroyFramebuffer(vulkan.getDevice(), framebuffer, nullptr); } framebuffers.clear(); @@ -259,7 +264,7 @@ void SwapChain::destroy() { } } - for (auto colorBufferView : colorBufferViews) { + for (auto *colorBufferView : colorBufferViews) { vkDestroyImageView(vulkan.getDevice(), colorBufferView, nullptr); } colorBufferViews.clear(); @@ -271,10 +276,10 @@ void SwapChain::destroy() { } SwapChain::SwapChain(Vulkan &vulkan) - : vk(VK_NULL_HANDLE), colorBuffer(nullptr), - colorBufferViews(), extent{0, 0}, depthBuffer(nullptr), framebuffers(), - vulkan(vulkan) { - auto details = querySwapChainSupport(vulkan.getPhysicalDevice(), vulkan); + : vk(VK_NULL_HANDLE), colorBuffer(nullptr), extent{0, 0}, + depthBuffer(nullptr), vulkan(vulkan) { + auto details = + querySwapChainSupport(vulkan.getPhysicalDevice().getVk(), vulkan); auto surfaceFormat = chooseSurfaceFormat(details.formats); vulkan.getAdapter().getAttachments().push_back( @@ -289,7 +294,7 @@ SwapChain::SwapChain(Vulkan &vulkan) VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE, - {{{0.0f, 0.0f, 0.0f, 1.0f}}}, + {{{0.0F, 0.0F, 0.0F, 1.0F}}}, std::make_unique(static_cast(VK_NULL_HANDLE), static_cast(VK_NULL_HANDLE), @@ -325,5 +330,4 @@ VkFramebuffer SwapChain::getFramebuffer(std::size_t index) const { VkExtent2D SwapChain::getExtent() const { return extent; } -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_swap_chain.h b/desktop/graphics/vulkan_swap_chain.h index f938afc..682be8b 100644 --- a/desktop/graphics/vulkan_swap_chain.h +++ b/desktop/graphics/vulkan_swap_chain.h @@ -3,8 +3,7 @@ #include "vulkan_adapter.h" #include "vulkan_common.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { class SwapChain : public VkObjectWrapper { @@ -54,5 +53,4 @@ class SwapChain : public VkObjectWrapper { VkExtent2D getExtent() const; }; -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_texture_descriptors.cpp b/desktop/graphics/vulkan_texture_descriptors.cpp index d1c81a9..efd7b33 100644 --- a/desktop/graphics/vulkan_texture_descriptors.cpp +++ b/desktop/graphics/vulkan_texture_descriptors.cpp @@ -1,7 +1,6 @@ #include "vulkan_texture_descriptors.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { void TextureDescriptors::allocatePool() { pools.resize(pools.size() + 1); @@ -16,7 +15,7 @@ void TextureDescriptors::allocatePool() { poolInfo.pPoolSizes = &poolSize; poolInfo.maxSets = POOL_SIZE; - auto output = &pools[pools.size() - 1]; + auto *output = &pools[pools.size() - 1]; vulkan.handleVkResult( "Could not create texture descriptor pool", vkCreateDescriptorPool(vulkan.getDevice(), &poolInfo, nullptr, output)); @@ -25,7 +24,7 @@ void TextureDescriptors::allocatePool() { } TextureDescriptors::TextureDescriptors(Vulkan &vulkan) - : DescriptorSetInterface(SET_NUMBER, vulkan) { + : DescriptorSetInterface(SET_NUMBER, vulkan), lastPoolCapacity(0) { VkDescriptorSetLayoutCreateInfo layoutInfo{}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; @@ -48,7 +47,7 @@ TextureDescriptors::TextureDescriptors(Vulkan &vulkan) } TextureDescriptors::~TextureDescriptors() { - for (auto pool : pools) { + for (auto *pool : pools) { vkDestroyDescriptorPool(vulkan.getDevice(), pool, nullptr); } @@ -72,7 +71,7 @@ VkDescriptorSet TextureDescriptors::addTexture(VkImageView view, allocInfo.descriptorSetCount = 1; allocInfo.pSetLayouts = &layout; - VkDescriptorSet descriptorSet; + VkDescriptorSet descriptorSet = nullptr; vulkan.handleVkResult("Could not create texture descriptor set", vkAllocateDescriptorSets(vulkan.getDevice(), &allocInfo, &descriptorSet)); @@ -102,5 +101,4 @@ VkDescriptorSet TextureDescriptors::addTexture(VkImageView view, return descriptorSet; } -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_texture_descriptors.h b/desktop/graphics/vulkan_texture_descriptors.h index 9a82462..0b04c82 100644 --- a/desktop/graphics/vulkan_texture_descriptors.h +++ b/desktop/graphics/vulkan_texture_descriptors.h @@ -5,8 +5,7 @@ #include "vulkan_common.h" #include "vulkan_descriptor_set.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { class TextureDescriptors : public DescriptorSetInterface { private: @@ -25,5 +24,4 @@ class TextureDescriptors : public DescriptorSetInterface { VkDescriptorSet addTexture(VkImageView, VkSampler); }; -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/graphics/vulkan_uniform.h b/desktop/graphics/vulkan_uniform.h index 91a8f8f..0f29ac3 100644 --- a/desktop/graphics/vulkan_uniform.h +++ b/desktop/graphics/vulkan_uniform.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -7,8 +8,7 @@ #include "vulkan_common.h" #include "vulkan_descriptor_set.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { template class Uniform : public DescriptorSetInterface { @@ -70,7 +70,6 @@ template class Uniform : public DescriptorSetInterface { void doUpdates(); }; -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop #include "vulkan_uniform.inl" diff --git a/desktop/graphics/vulkan_uniform.inl b/desktop/graphics/vulkan_uniform.inl index 79e99b6..5fa2e13 100644 --- a/desktop/graphics/vulkan_uniform.inl +++ b/desktop/graphics/vulkan_uniform.inl @@ -5,14 +5,29 @@ #include "../../main/util.h" #include "vulkan_frame.h" #include "vulkan_pipeline.h" +#include "vulkan_physical_device.h" -namespace progressia { -namespace desktop { +namespace progressia::desktop { + +namespace detail { + + template + std::size_t offsetOf(Vulkan &vulkan) { + auto step = vulkan.getPhysicalDevice().getMinUniformOffset(); + return ((sizeof(T) - 1) / step + 1) * step; // Round up to multiple + } + + template + std::size_t offsetOf(Vulkan &vulkan, const T&) { + return offsetOf(vulkan); + } + +} template Uniform::StateImpl::Set::Set(VkDescriptorSet vk, Vulkan &vulkan) : vk(vk), - contents((sizeof(Entries) + ...), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + contents((detail::offsetOf(vulkan) + ...), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, vulkan) {} @@ -48,7 +63,7 @@ Uniform::StateImpl::StateImpl( writes[index].descriptorCount = 1; writes[index].pBufferInfo = &bufferInfos[index]; - offset += sizeof(Entry); + offset += detail::offsetOf(vulkan); index++; }) } @@ -71,7 +86,7 @@ void Uniform::State::update(const Entries &...entries) { auto *dst = state.newContents.data(); FOR_PACK(Entries, entries, e, { std::memcpy(dst, &e, sizeof(e)); - dst += sizeof(e); + dst += detail::offsetOf(uniform->getVulkan(), e); }) state.setsToUpdate = state.sets.size(); } @@ -190,5 +205,4 @@ template void Uniform::doUpdates() { } } -} // namespace desktop -} // namespace progressia +} // namespace progressia::desktop diff --git a/desktop/main.cpp b/desktop/main.cpp index 1ac6a22..4cd9e2c 100644 --- a/desktop/main.cpp +++ b/desktop/main.cpp @@ -27,30 +27,28 @@ 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()); + auto game = main::makeGame(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(); + game->renderTick(); - desktop::endRender(); - desktop::doGlfwRoutine(); + vulkanManager.endRender(); + glfwManager->doGlfwRoutine(); } info("Shutting down"); - desktop::getVulkan()->waitIdle(); - main::shutdown(); - desktop::shutdownVulkan(); - desktop::shutdownGlfw(); + vulkanManager.getVulkan()->waitIdle(); return 0; } diff --git a/docs/BuildingGuide.md b/docs/BuildingGuide.md index 8c637c5..3e9a9dd 100644 --- a/docs/BuildingGuide.md +++ b/docs/BuildingGuide.md @@ -1,85 +1,192 @@ -# Building guide +# Building Guide -At this time, building is only supported in GNU/Linux targeting GNU/Linux with -X11/Wayland and Windows (cross-compilation). See also -[Development Setup Guide](DevelopmentSetupGuide.md) -if you want to make git commits. +This document provides instructions for building Progressia from source code. +See also + [Development Setup Guide](DevelopmentSetupGuide.md) +and + [IDE setup guides](ide_setup). + +MacOS targets are not supported at this moment. + +## Short version +Debian/Ubuntu: +```bash +# Install GCC, CMake, Python 3, glslc, git, Vulkan, GLFW and GLM +sudo apt update && sudo apt install -y \ + g++ cmake python3 glslc git libvulkan-dev libglfw3-dev libglm-dev + +# Clone project +git clone https://github.com/Wind-Corporation/Progressia.git +cd Progressia + +# Generate build files for release (MY-1 is the build ID) +cmake -S . -B build -DBUILD_ID=MY-1 -DCMAKE_BUILD_TYPE=Release + +# Compile +cmake --build build + +# Run +build/progressia +``` + +Fedora: +```bash +# Install GCC, CMake, Python 3, glslc, git, Vulkan, GLFW and GLM +sudo dnf install -y \ + gcc-c++ cmake python3 glslc git vulkan-devel glfw-devel glm-devel + +# Clone project +git clone https://github.com/Wind-Corporation/Progressia.git +cd Progressia + +# Generate build files for release (MY-1 is the build ID) +cmake -S . -B build -DBUILD_ID=MY-1 -DCMAKE_BUILD_TYPE=Release + +# Compile +cmake --build build + +# Run +build/progressia +``` + +Windows: _see [IDE setup guides](ide_setup)._ ## Prerequisites -Install the following software: - - a C++ compiler (GCC or clang preferably), - - CMake, - - Python 3, - - glslc. +### C++ compiler -Install the following libraries with headers: - - Vulkan (loader library and headers), - - GLFW3, - - GLM, - - Boost (only core library required). +Project explicitly fully supports GCC, MinGW and Clang. Compilation with MSVC +is also supported, but it can't be used for release builds and its use is +generally discouraged. -### Debian +On Windows, + [w64devkit](https://github.com/skeeto/w64devkit/releases) +distribution of MinGW was tested. -On Debian, you can run the following commands as root to install almost all -required software: +Cross-compilation from Linux to Windows is also explicitly supported with +MinGW-w64 as provided by Debian. + +### CMake + +[CMake](https://cmake.org/) version 3.12 or higher is required. + +### Python 3 + +[Python 3](https://www.python.org/downloads/) is required. + +### Vulkan + +The following Vulkan components are strictly necessary for builds: + - Header files + - Loader static library (`vulkan-1.lib`) + - `glslc` ([standalone downloads](https://github.com/google/shaderc/blob/main/downloads.md)) + +However, it is usually easier to install a complete Vulkan SDK. An open-source +Vulkan SDK can be downloaded from + [LunarG](https://www.lunarg.com/vulkan-sdk/) +for all platforms. + +Debian/Ubuntu users can install this dependency using APT: ```bash -apt-get install \ - g++ \ - cmake \ - python3 && -apt-get install --no-install-recommends \ - libvulkan-dev \ - libglfw3-dev \ - libglm-dev \ - libboost-dev +apt install libvulkan-dev glslc ``` -However, glslc, the shader compiler, is not available as a Debian package at the -moment. You can install it manually from official sources or use the download it -from windcorp.ru by running these commands as root: +Fedora users can install this dependency using dnf: ```bash -apt-get install wget && -mkdir -p /opt/glslc && -wget --output-file=/opt/glslc/glslc \ - 'https://windcorp.ru/other/glslc-v2022.1-6-ga0a247d-static' && -chmod +x /opt/glslc/glslc +dnf install vulkan-devel glslc ``` -Alternatively, packages provided by LunarG are available for Ubuntu. Follow the -instructions on [LunarG.com](https://vulkan.lunarg.com/sdk/home) to install -`vulkan-sdk`. +Windows users using vcpkg should install the LunarG distribution, then install +the `vulkan` vcpkg package: +```cmd +vcpkg install vulkan:x64-mingw-static +``` -## Setup +### Other libraries +The following libraries are additionally required: + - [GLFW](https://www.glfw.org/download.html) version 3.3.2 or higher + - [GLM](https://glm.g-truc.net/) + +Debian/Ubuntu users can install these dependencies using APT: +```bash +apt install libglfw3-dev libglm-dev +``` + +Fedora users can install these dependencies using dnf: +```bash +dnf install glfw-devel glm-devel +``` + +Windows users can install these dependencies using vcpkg: +```cmd +vcpkg install glfw3:x64-mingw-static glm:x64-mingw-static +``` + +## Downloading source code + +Clone this git repository. + +Command line users: run ```bash git clone -cd Progressia -chmod +x tools/setup.sh -tools/setup.sh -``` - -`tools/setup.sh` will check the availability of all required commands and -libraries. - -Build tools use enviroment variables `PATH`, `VULKAN_SDK`, `CMAKE_MODULE_PATH` -and `CMAKE_PREFIX_PATH` to locate the various components; you can edit these -variables system-wide or use `tools/private.sh` to amend them for build tools. -(Your changes to `tools/private.sh` are ignored by git.) -For example, of you ran the script to download glslc on Debian, you will need to -add the following line to `tools/private.sh`: -```bash -PATH="$PATH:/opt/glslc" ``` ## Building +### CMake + +Use CMake to generate build files. There are a few options available: + - **`BUILD_ID`** enables release builds and specifies visible unique build + identifier string. + - `DEV_MODE`, `VULKAN_ERROR_CHECKING`: + see [Development Setup Guide](DevelopmentSetupGuide.md). + - `VULKAN_ERROR_CHECKING` enables Vulkan debug features. This requires Vulkan + validation layers (available as part of LunarG Vulkan SDK, + `vulkan-validationlayers-dev` Debian package and `vulkan-devel` Fedora + package). + +Directory `build` in project root is ignored by git for convenience. + +This step is usually performed in the IDE. + +Command line users: run ```bash -tools/build.sh +cd /path/to/project +# Routine (debug) build +cmake -S . -B build +# Release build +cmake -S . -B build -DBUILD_ID=MY-1 -DCMAKE_BUILD_TYPE=Release +``` + +> **Note** +> +> Use proper build IDs if distribution is expected. Convention is two-letter +> builder identifier, a dash and a unique ascending build number. +> +> For example, automated builds at windcorp.ru use IDs `WA-1`, `WA-2`, etc. + +> **Note** +> +> Release builds with MSVC are not supported. +> The standard library used by MSVC poses a problem: +> - it cannot be statically linked with Progressia due to GPL restrictions, +> - it cannot be bundled with Progressia for the same reason, +> - asking the user to install Visual C++ Runtime manually would introduce +> unnecessary confusion because official builds do not require it. + +### Compiling + +This step is usually performed in the IDE. + +Command line users: run +```bash +cmake --build build ``` ## Running -```bash -tools/build.sh -R -``` +Executable file will be located directly inside the CMake binary directory. + +Directory `run` in project root is ignored by git for convenience; using +project root as working directory is safe for debug builds. diff --git a/docs/DevelopmentSetupGuide.md b/docs/DevelopmentSetupGuide.md index c96db26..195dcc8 100644 --- a/docs/DevelopmentSetupGuide.md +++ b/docs/DevelopmentSetupGuide.md @@ -1,63 +1,93 @@ # Development setup guide -To make development easier, contributors should be using a few tools. Included -with the project are configurations and scripts for these tools: - - [cppcheck](http://cppcheck.net/) – performs static code analysis for C++ - - [clang-format](https://clang.llvm.org/docs/ClangFormat.html) – automatically - formats C++ source code - - [memcheck](https://valgrind.org/docs/manual/mc-manual.html) - (part of [valgrind](https://valgrind.org/)) – performs runtime memory - error detection +This document provides instructions for setting up a development environment +for Progressia. +See also + [Building Guide](BuildingGuide.md) +and + [IDE setup guides](ide_setup). -Additionally, git hooks prevent committing code that is formatted incorrectly, -does not compile or produces warnings. You can bypass this check using +To make development easier, contributors should be using a few tools. Included +with the project are configurations and scripts for these tools: + - [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) – performs static + code analysis for C++ + - [clang-format](https://clang.llvm.org/docs/ClangFormat.html) – formats C++ + source code + - Vulkan validation layers – checks for errors in Vulkan API usage at runtime + (see below for details) + - [memcheck](https://valgrind.org/docs/manual/mc-manual.html) + (part of [valgrind](https://valgrind.org/)) – performs runtime memory + error detection on Linux + +Additionally, a git pre-commit hook prevents committing code that is formatted +incorrectly, does not compile or produces warnings. + +## Basic setup +Follow [Building Guide](BuildingGuide.md) instructions before proceeding. + +Debian/Ubuntu: +```bash +# Install clang-tidy and clang-format-diff +sudo apt update && sudo apt install -y \ + clang-tidy clang-format + +# Enable DEV_MODE (sets up git pre-commit hook) +cmake -S . -B build -DDEV_MODE=ON +``` + +Fedora: +```bash +# Install clang-tidy and clang-format-diff +sudo dnf install -y \ + clang-tools-extra clang + +# Enable DEV_MODE (sets up git pre-commit hook) +cmake -S . -B build -DDEV_MODE=ON +``` + +Windows: _see [IDE setup guides](ide_setup)._ + +## Pre-commit git hook + +A +[git pre-commit hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) +is installed to correct formatting and check for compilation/linting issues. +This check can be bypassed with `git commit --no-verify` in case of dire need. -## Prerequisites +The hook runs `tools/pre-commit.py`, which formats modified files and ensures +that `cmake --build build` executes without errors. Several git operations are +performed by `pre-commit.py`; run + `tools/pre-commit.py restore` +to restore the state of the repository in case the hook crashes. -Perform the setup described in the [Building Guide](BuildingGuide.md) first. +The list of directories with source code to format and the list of source code +filename extensions are hard-coded into the Python script. -Install the following software: - - cppcheck, - - clang-format (version 13 is recommended) - - valgrind +## Vulkan validation layers -### Debian - -On Debian, you can run the following commands as root to install all required -software: -```bash -apt-get install \ - cppcheck \ - clang-format-13 \ - valgrind -``` - -## Setup - -```bash -tools/setup.sh --for-development -``` - -With `--for-development` flag, `tools/setup.sh` will check the development tools -and install git pre-commit hook in addition to its normal duties. - -## Notes - -Developers will find it useful to read through the following help pages: -```bash -tools/build.sh --help -tools/cppcheck/use-cppcheck.sh --help -tools/clang-format/use-clang-format.sh --help -``` - -LunarG validation layers are extremely useful when writing and debugging Vulkan. +LunarG validation layers are extremely useful when debugging Vulkan code. The official [Vulkan tutorial](https://vulkan-tutorial.com/Development_environment) has detailed instructions for all platforms. -In particular, Debian users can run the following command as root: +Use CMake option `VULKAN_ERROR_CHECKING` to enable the use of validation +layers. + +Debian/Ubuntu users can install this dependency using APT: ```bash -apt-get install vulkan-validationlayers-dev +apt install vulkan-validationlayers-dev ``` + +Fedora users can install this dependency using dnf: +```bash +dnf install vulkan-validation-layers-devel +``` + +Windows users can install this dependency when installing LunarG distribution. + +## memcheck + +`tools/valgrind-memcheck-suppressions.supp` contains useful suppressions for +memcheck. diff --git a/docs/ide_setup/WindowsCLion.md b/docs/ide_setup/WindowsCLion.md new file mode 100644 index 0000000..6739b09 --- /dev/null +++ b/docs/ide_setup/WindowsCLion.md @@ -0,0 +1,100 @@ +# IDE setup guide: Windows / CLion + +> **Note** +> +> This guide has not been tested sufficiently because currently none of the +> developers use CLion to develop Progressia. Please let us know if this guide +> requires corrections or updates. + +This document is an IDE setup guide for CLion with MinGW, the recommended +compiler for Windows. + +Compilation with MSVC and clang-cl is supported; however, these compilers may +generate warnings. Additionally, release builds compiled with MSVC or clang-cl +are strongly discouraged, see [Building Guide](../BuildingGuide.md). + +## Installing CLion + +Install CLion as usual. Close CLion for the following steps. + +> **Note** +> +> Native vcpkg support has been added to CLion in version 2023.1. At the time +> of writing this is a recent update. Make sure you use the latest version. +> +> Workaround for older versions: add +> `-DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake` +> to CMake options. + +## Installing build tools + +### Python 3 +Install Python 3 (available from +[the official website](https://www.python.org/downloads/) +and Microsoft Store). Make sure `python` or `python3` is available in PATH: +```cmd +:: This command must work in a fresh CMD: +python3 --version +``` +Note that if running this command launches Microsoft Store, Python was not +installed correctly. + +### MinGW +Install MinGW. There are many distributions of MinGW available; this guide was +tested with [w64devkit](https://github.com/skeeto/w64devkit). + +To install w64devkit, go to the +[Releases](https://github.com/skeeto/w64devkit/releases) +section of the official repository. Download the `w64devkit-XXX.zip` file and +extract it into `C:\msys64\mingw64\`. If extracted correctly, + `C:\msys64\mingw64\bin\gcc.exe` +should exist. Directory + `C:\msys64\mingw64\bin\` +should be added to system PATH +([instructions for Windows 10](https://stackoverflow.com/a/44272417/4463352)). +Proper installation can be verified like so: +```cmd +:: This command must work in a fresh CMD: +gcc --version +``` + +## Installing libraries + +Several third party libraries are used by the project. With Windows, installing +them manually can be a hassle, so the developers recommend using vcpkg. + +A Vulkan SDK has to be installed before vcpkg can install `vulkan` package. +[LunarG](https://www.lunarg.com/vulkan-sdk/) distribution is recommended: +download and run the SDK installer. "Validation layer" errors are common on +Windows and can usually be safely ignored; they are typically caused by third- +party software such as GPU drivers, OBS or Steam. + +To install vcpkg, go to the +[Releases](https://github.com/microsoft/vcpkg/releases) section of the official +repository. Download and extract "Source code" ZIP file to a directory of your +choice. Run the following commands inside the resulting folder: +```cmd +:: Perform initial setup +bootstrap-vcpkg + +:: Setup Visual Studio integration +vcpkg integrate install + +:: Install libraries +vcpkg install vulkan:x64-mingw-static glfw3:x64-mingw-static glm:x64-mingw-static +``` + +## Project setup + +Uhm... how do I put it... I could not get my hands on a Windows install of +CLion in a reasonable time and so I will have to leave this blank for now. If +you have CLion on Windows, please contact the devs so we can do the setup +together and this doc can be completed. + +In general, from this point you should clone the git repo and open the project +as a CMake project. + +## Developer setup + +To enable features useful for developers, set CMake option `DEV_MODE` to `ON`. +See [Development Setup Guide](../DevelopmentSetupGuide.md) for more details. diff --git a/docs/ide_setup/WindowsVisualStudio.md b/docs/ide_setup/WindowsVisualStudio.md new file mode 100644 index 0000000..85403de --- /dev/null +++ b/docs/ide_setup/WindowsVisualStudio.md @@ -0,0 +1,97 @@ +# IDE setup guide: Windows / Visual Studio + +This document is an IDE setup guide for Visual Studio with MinGW, the +recommended compiler for Windows. + +Compilation with MSVC and clang-cl is supported; however, these compilers may +generate warnings. Additionally, release builds compiled with MSVC or clang-cl +are strongly discouraged, see [Building Guide](../BuildingGuide.md). + +## Installing Visual Studio + +This guide was tested with Visual Studio 2022. "Desktop Development with C++" +workload is required to work with C++ projects. Launch Visual Studio at least +once with this configuration and close it for the next steps. + +## Installing build tools + +### Python 3 +Install Python 3 (available from +[the official website](https://www.python.org/downloads/) +and Microsoft Store). Make sure `python` or `python3` is available in PATH: +```cmd +:: This command must work in a fresh CMD: +python3 --version +``` +Note that if running this command launches Microsoft Store, Python was not +installed correctly. + +### MinGW +Install MinGW. There are many distributions of MinGW available; this guide was +tested with [w64devkit](https://github.com/skeeto/w64devkit). + +To install w64devkit, go to the +[Releases](https://github.com/skeeto/w64devkit/releases) +section of the official repository. Download the `w64devkit-XXX.zip` file and +extract it into `C:\msys64\mingw64\`. If extracted correctly, + `C:\msys64\mingw64\bin\gcc.exe` +should exist. Directory + `C:\msys64\mingw64\bin\` +should be added to system PATH +([instructions for Windows 10](https://stackoverflow.com/a/44272417/4463352)). +Proper installation can be verified like so: +```cmd +:: This command must work in a fresh CMD: +gcc --version +``` + +## Installing libraries + +Several third party libraries are used by the project. With Windows, installing +them manually can be a hassle, so the developers recommend using vcpkg. + +A Vulkan SDK has to be installed before vcpkg can install `vulkan` package. +[LunarG](https://www.lunarg.com/vulkan-sdk/) distribution is recommended: +download and run the SDK installer. "Validation layer" errors are common on +Windows and can usually be safely ignored; they are typically caused by third- +party software such as GPU drivers, OBS or Steam. + +To install vcpkg, go to the +[Releases](https://github.com/microsoft/vcpkg/releases) section of the official +repository. Download and extract "Source code" ZIP file to a directory of your +choice. Run the following commands inside the resulting folder: +```cmd +:: Perform initial setup +bootstrap-vcpkg + +:: Setup Visual Studio integration +vcpkg integrate install + +:: Install libraries +vcpkg install vulkan:x64-mingw-static glfw3:x64-mingw-static glm:x64-mingw-static +``` + +## Project setup + +Start Visual Studio. Use "Clone a Repository" to download sources and create a +project. Select the project in Solution Explorer and wait for CMake +initialization to complete. + +Next, click on "x64-Debug" in the toolbar. Click on "Manage Configurations..." +to open CMake Settings. Use the plus button to add a new configuration; select +"Mingw64-Debug" when prompted. Select the new configuration and add the +following parameter to "CMake command arguments": +``` +-DVCPKG_TARGET_TRIPLET=x64-mingw-static +``` + +Remove "x64-Debug" configuration by selecting it and pressing the cross button. + +Finally click "▶ Select startup item" in the toolbar and choose progressia.exe. + +## Developer setup + +To enable features useful for developers, set CMake option `DEV_MODE` to `ON`. +See [Development Setup Guide](../DevelopmentSetupGuide.md) for more details. + +TODO: _include step-by-step instructions for this section._ diff --git a/main/game.cpp b/main/game.cpp index 1cd42d6..ca5ac4a 100644 --- a/main/game.cpp +++ b/main/game.cpp @@ -1,5 +1,6 @@ #include "game.h" +#include #include #define GLM_FORCE_RADIANS @@ -15,167 +16,181 @@ #include "logging.h" using namespace progressia::main::logging; -namespace progressia { -namespace main { +namespace progressia::main { -std::unique_ptr cube1, cube2; -std::unique_ptr texture1, texture2; -std::unique_ptr perspective; -std::unique_ptr light; +class GameImpl : public Game { -GraphicsInterface *gint; + DISABLE_COPYING(GameImpl) + DISABLE_MOVING(GameImpl) -void addRect(glm::vec3 origin, glm::vec3 width, glm::vec3 height, - glm::vec4 color, std::vector &vertices, - std::vector &indices) { + public: + std::unique_ptr cube1; + std::unique_ptr cube2; + std::unique_ptr texture1; + std::unique_ptr texture2; + std::unique_ptr perspective; + std::unique_ptr light; - Vertex::Index offset = vertices.size(); + GraphicsInterface *gint; - vertices.push_back({origin, color, {}, {0, 0}}); - vertices.push_back({origin + width, color, {}, {0, 1}}); - vertices.push_back({origin + width + height, color, {}, {1, 1}}); - vertices.push_back({origin + height, color, {}, {1, 0}}); + static void addRect(glm::vec3 origin, glm::vec3 width, glm::vec3 height, + glm::vec4 color, std::vector &vertices, + std::vector &indices) { - indices.push_back(offset + 0); - indices.push_back(offset + 1); - indices.push_back(offset + 2); + Vertex::Index offset = vertices.size(); - indices.push_back(offset + 0); - indices.push_back(offset + 2); - indices.push_back(offset + 3); -} + vertices.push_back({origin, color, {}, {0, 0}}); + vertices.push_back({origin + width, color, {}, {0, 1}}); + vertices.push_back({origin + width + height, color, {}, {1, 1}}); + vertices.push_back({origin + height, color, {}, {1, 0}}); -void addBox(glm::vec3 origin, glm::vec3 length, glm::vec3 height, - glm::vec3 depth, std::array colors, - std::vector &vertices, - std::vector &indices) { - addRect(origin, height, length, colors[0], vertices, indices); - addRect(origin, length, depth, colors[1], vertices, indices); - addRect(origin, depth, height, colors[2], vertices, indices); - addRect(origin + height, depth, length, colors[3], vertices, indices); - addRect(origin + length, height, depth, colors[4], vertices, indices); - addRect(origin + depth, length, height, colors[5], vertices, indices); -} + indices.push_back(offset + 0); + indices.push_back(offset + 1); + indices.push_back(offset + 2); -void initialize(GraphicsInterface &gintp) { + indices.push_back(offset + 0); + indices.push_back(offset + 2); + indices.push_back(offset + 3); + } - debug("game init begin"); - gint = &gintp; + static void addBox(glm::vec3 origin, glm::vec3 length, glm::vec3 height, + glm::vec3 depth, std::array colors, + std::vector &vertices, + std::vector &indices) { + addRect(origin, height, length, colors[0], vertices, indices); + addRect(origin, length, depth, colors[1], vertices, indices); + addRect(origin, depth, height, colors[2], vertices, indices); + addRect(origin + height, depth, length, colors[3], vertices, indices); + addRect(origin + length, height, depth, colors[4], vertices, indices); + addRect(origin + depth, length, height, colors[5], vertices, indices); + } - texture1.reset(gint->newTexture( - progressia::main::loadImage(u"../assets/texture.png"))); - texture2.reset(gint->newTexture( - progressia::main::loadImage(u"../assets/texture2.png"))); + GameImpl(GraphicsInterface &gintp) { - // Cube 1 - { - std::vector vertices; - std::vector indices; - auto white = glm::vec4(1, 1, 1, 1); + debug("game init begin"); + gint = &gintp; - addBox({-0.5, -0.5, -0.5}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, - {white, white, white, white, white, white}, vertices, indices); + texture1 = + gint->newTexture(progressia::main::loadImage("assets/texture.png")); + texture2 = gint->newTexture( + progressia::main::loadImage("assets/texture2.png")); - for (std::size_t i = 0; i < indices.size(); i += 3) { - Vertex &a = vertices[indices[i + 0]]; - Vertex &b = vertices[indices[i + 1]]; - Vertex &c = vertices[indices[i + 2]]; + // Cube 1 + { + std::vector vertices; + std::vector indices; + auto white = glm::vec4(1, 1, 1, 1); - glm::vec3 x = b.position - a.position; - glm::vec3 y = c.position - a.position; + addBox({-0.5, -0.5, -0.5}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, + {white, white, white, white, white, white}, vertices, + indices); - glm::vec3 normal = glm::normalize(glm::cross(x, y)); + for (std::size_t i = 0; i < indices.size(); i += 3) { + Vertex &a = vertices[indices[i + 0]]; + Vertex &b = vertices[indices[i + 1]]; + Vertex &c = vertices[indices[i + 2]]; - a.normal = normal; - b.normal = normal; - c.normal = normal; + glm::vec3 x = b.position - a.position; + glm::vec3 y = c.position - a.position; + + glm::vec3 normal = glm::normalize(glm::cross(x, y)); + + a.normal = normal; + b.normal = normal; + c.normal = normal; + } + + cube1 = gint->newPrimitive(vertices, indices, &*texture1); } - cube1.reset(gint->newPrimitive(vertices, indices, &*texture1)); - } + // Cube 2 + { + std::vector vertices; + std::vector indices; + auto white = glm::vec4(1, 1, 1, 1); - // Cube 2 - { - std::vector vertices; - std::vector indices; - auto white = glm::vec4(1, 1, 1, 1); + addBox({-0.5, -2.5, -0.5}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, + {white, white, white, white, white, white}, vertices, + indices); - addBox({-0.5, -2.5, -0.5}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, - {white, white, white, white, white, white}, vertices, indices); + for (std::size_t i = 0; i < indices.size(); i += 3) { + Vertex &a = vertices[indices[i + 0]]; + Vertex &b = vertices[indices[i + 1]]; + Vertex &c = vertices[indices[i + 2]]; - for (std::size_t i = 0; i < indices.size(); i += 3) { - Vertex &a = vertices[indices[i + 0]]; - Vertex &b = vertices[indices[i + 1]]; - Vertex &c = vertices[indices[i + 2]]; + glm::vec3 x = b.position - a.position; + glm::vec3 y = c.position - a.position; - glm::vec3 x = b.position - a.position; - glm::vec3 y = c.position - a.position; + glm::vec3 normal = glm::normalize(glm::cross(x, y)); - glm::vec3 normal = glm::normalize(glm::cross(x, y)); + a.normal = normal; + b.normal = normal; + c.normal = normal; + } - a.normal = normal; - b.normal = normal; - c.normal = normal; + cube2 = gint->newPrimitive(vertices, indices, &*texture2); } - cube2.reset(gint->newPrimitive(vertices, indices, &*texture2)); + perspective = gint->newView(); + light = gint->newLight(); + + debug("game init complete"); } - perspective.reset(gint->newView()); - light.reset(gint->newLight()); + void renderTick() override { - debug("game init complete"); -} + { + float fov = 70.0F; -void renderTick() { + auto extent = gint->getViewport(); + auto proj = glm::perspective( + glm::radians(fov), extent.x / (float)extent.y, 0.1F, 10.0F); + proj[1][1] *= -1; - { - float fov = 70.0f; + auto view = glm::lookAt(glm::vec3(2.0F, 2.0F, 2.0F), + glm::vec3(0.0F, 0.0F, 0.0F), + glm::vec3(0.0F, 0.0F, 1.0F)); - auto extent = gint->getViewport(); - auto proj = glm::perspective(glm::radians(fov), - extent.x / (float)extent.y, 0.1f, 10.0f); - proj[1][1] *= -1; + perspective->configure(proj, view); + } - auto view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), - glm::vec3(0.0f, 0.0f, 0.0f), - glm::vec3(0.0f, 0.0f, 1.0f)); + perspective->use(); - perspective->configure(proj, view); + float contrast = glm::sin(gint->tmp_getTime() / 3) * 0.18F + 0.18F; + glm::vec3 color0(0.60F, 0.60F, 0.70F); + glm::vec3 color1(1.10F, 1.05F, 0.70F); + + auto m = + static_cast(glm::sin(gint->tmp_getTime() / 3) * 0.5 + 0.5); + glm::vec3 color = m * color1 + (1 - m) * color0; + + light->configure(color, glm::vec3(1.0F, -2.0F, 1.0F), contrast, 0.1F); + light->use(); + + auto model = glm::eulerAngleYXZ(0.0F, 0.0F, gint->tmp_getTime() * 0.1F); + + gint->setModelTransform(model); + cube1->draw(); + cube2->draw(); } - perspective->use(); + ~GameImpl() override { + debug("game shutdown begin"); - float contrast = glm::sin(gint->tmp_getTime() / 3) * 0.18f + 0.18f; - glm::vec3 color0(0.60f, 0.60f, 0.70f); - glm::vec3 color1(1.10f, 1.05f, 0.70f); + cube1.reset(); + cube2.reset(); + texture1.reset(); + texture2.reset(); - float m = glm::sin(gint->tmp_getTime() / 3) * 0.5 + 0.5; - glm::vec3 color = m * color1 + (1 - m) * color0; + light.reset(); + perspective.reset(); - light->configure(color, glm::vec3(1.0f, -2.0f, 1.0f), contrast, 0.1f); - light->use(); + debug("game shutdown complete"); + } +}; - auto model = glm::eulerAngleYXZ(0.0f, 0.0f, gint->tmp_getTime() * 0.1f); - - gint->setModelTransform(model); - cube1->draw(); - cube2->draw(); +std::unique_ptr makeGame(GraphicsInterface &gint) { + return std::make_unique(gint); } -void shutdown() { - debug("game shutdown begin"); - - cube1.reset(); - cube2.reset(); - texture1.reset(); - texture2.reset(); - - light.reset(); - perspective.reset(); - - debug("game shutdown complete"); -} - -} // namespace main -} // namespace progressia +} // namespace progressia::main diff --git a/main/game.h b/main/game.h index 8a1b65e..b0fcdf4 100644 --- a/main/game.h +++ b/main/game.h @@ -1,13 +1,16 @@ #pragma once #include "rendering.h" +#include "util.h" -namespace progressia { -namespace main { +namespace progressia::main { -void initialize(GraphicsInterface &); -void renderTick(); -void shutdown(); +class Game : private NonCopyable { + public: + virtual ~Game() = default; + virtual void renderTick() = 0; +}; -} // namespace main -} // namespace progressia +std::unique_ptr makeGame(GraphicsInterface &); + +} // namespace progressia::main diff --git a/main/logging.cpp b/main/logging.cpp index c017250..2bdb40d 100644 --- a/main/logging.cpp +++ b/main/logging.cpp @@ -8,8 +8,7 @@ #include #include -namespace progressia { -namespace main { +namespace progressia::main { namespace detail { @@ -21,7 +20,7 @@ class LogSinkBackend { void flush(); public: - LogSinkBackend() {} + LogSinkBackend() = default; std::ostream &getOutput() { return buffer; } @@ -46,14 +45,17 @@ class LogSinkBackend { namespace { std::ofstream openLogFile() { // FIXME this is relative to bin, not root dir - std::filesystem::create_directories("../run"); - std::filesystem::create_directories("../run/logs"); - return std::ofstream("../run/logs/latest.log"); + std::filesystem::create_directories("run"); + std::filesystem::create_directories("run/logs"); + return std::ofstream("run/logs/latest.log"); } } // namespace +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables): TODO std::mutex logFileMutex; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) std::ofstream logFile = openLogFile(); +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) thread_local detail::LogSinkBackend theBackend; std::ostream &detail::LogSink::getStream() const { @@ -78,7 +80,7 @@ detail::LogSink::~LogSink() { } } -detail::LogSink::LogSink(LogSink &&moveFrom) +detail::LogSink::LogSink(LogSink &&moveFrom) noexcept : isCurrentSink(moveFrom.isCurrentSink) { moveFrom.isCurrentSink = false; } @@ -99,6 +101,7 @@ void detail::LogSinkBackend::flush() { namespace { // FIXME This approach is horribly inefficient. It is also unsafe if any // other piece of code wants access to std::localtime. +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) std::mutex getLocalTimeMutex; std::tm getLocalTimeAndDontExplodePlease() { std::lock_guard lock(getLocalTimeMutex); @@ -165,5 +168,4 @@ detail::LogSink fatal(const char *start) { return log(LogLevel::FATAL, start); } } // namespace logging -} // namespace main -} // namespace progressia +} // namespace progressia::main diff --git a/main/logging.h b/main/logging.h index f6bc072..de8322f 100644 --- a/main/logging.h +++ b/main/logging.h @@ -1,14 +1,13 @@ #pragma once -#include "boost/core/noncopyable.hpp" +#include "util.h" #include -namespace progressia { -namespace main { +namespace progressia::main { namespace detail { -class LogSink : private boost::noncopyable { +class LogSink : private progressia::main::NonCopyable { private: bool isCurrentSink; @@ -24,7 +23,7 @@ class LogSink : private boost::noncopyable { LogSink(bool isCurrentSink); ~LogSink(); - LogSink(LogSink &&); + LogSink(LogSink &&) noexcept; template friend const LogSink &operator<<(const LogSink &sink, const T &x) { @@ -60,5 +59,4 @@ detail::LogSink fatal(const char *start = nullptr); void initializeLogging(); void shutdownLogging(); -} // namespace main -} // namespace progressia +} // namespace progressia::main diff --git a/main/meta.h b/main/meta.h index be8a203..5a77700 100644 --- a/main/meta.h +++ b/main/meta.h @@ -1,10 +1,9 @@ #pragma once -#include "../config.h" +#include #include -namespace progressia { -namespace main { +namespace progressia::main { namespace meta { namespace detail { @@ -37,5 +36,4 @@ constexpr uint32_t VERSION_MINOR = (VERSION_NUMBER & 0x00FF00) >> 8; constexpr uint32_t VERSION_PATCH = (VERSION_NUMBER & 0x0000FF) >> 0; } // namespace meta -} // namespace main -} // namespace progressia +} // namespace progressia::main diff --git a/main/rendering.h b/main/rendering.h index 0348577..79fa6f2 100644 --- a/main/rendering.h +++ b/main/rendering.h @@ -3,6 +3,4 @@ #include "rendering/graphics_interface.h" #include "rendering/image.h" -namespace progressia { -namespace main {} // namespace main -} // namespace progressia +namespace progressia::main {} // namespace progressia::main diff --git a/main/rendering/graphics_interface.h b/main/rendering/graphics_interface.h index 8b44b9e..c1b13d5 100644 --- a/main/rendering/graphics_interface.h +++ b/main/rendering/graphics_interface.h @@ -1,6 +1,6 @@ #pragma once -#include "boost/core/noncopyable.hpp" +#include "../util.h" #include #define GLM_FORCE_RADIANS @@ -12,8 +12,7 @@ #include "image.h" -namespace progressia { -namespace main { +namespace progressia::main { struct Vertex { @@ -25,31 +24,27 @@ struct Vertex { glm::vec2 texCoord; }; -class Texture : private boost::noncopyable { - public: - using Backend = void *; - +class Texture : private progressia::main::NonCopyable { private: - Backend backend; + struct Backend; + std::unique_ptr backend; + friend class GraphicsInterface; friend class Primitive; public: - Texture(Backend); + Texture(std::unique_ptr); ~Texture(); }; -class Primitive : private boost::noncopyable { - public: - using Backend = void *; - +class Primitive : private progressia::main::NonCopyable { private: - Backend backend; - + struct Backend; + std::unique_ptr backend; friend class GraphicsInterface; public: - Primitive(Backend); + Primitive(std::unique_ptr); ~Primitive(); void draw(); @@ -57,30 +52,28 @@ class Primitive : private boost::noncopyable { const Texture *getTexture() const; }; -class View : private boost::noncopyable { - public: - using Backend = void *; - +class View : private progressia::main::NonCopyable { private: - Backend backend; + struct Backend; + std::unique_ptr backend; + friend class GraphicsInterface; public: - View(Backend); + View(std::unique_ptr); ~View(); void configure(const glm::mat4 &proj, const glm::mat4 &view); void use(); }; -class Light : private boost::noncopyable { - public: - using Backend = void *; - +class Light : private progressia::main::NonCopyable { private: - Backend backend; + struct Backend; + std::unique_ptr backend; + friend class GraphicsInterface; public: - Light(Backend); + Light(std::unique_ptr); ~Light(); void configure(const glm::vec3 &color, const glm::vec3 &from, @@ -88,7 +81,7 @@ class Light : private boost::noncopyable { void use(); }; -class GraphicsInterface : private boost::noncopyable { +class GraphicsInterface : private progressia::main::NonCopyable { public: using Backend = void *; @@ -99,18 +92,18 @@ class GraphicsInterface : private boost::noncopyable { GraphicsInterface(Backend); ~GraphicsInterface(); - Texture *newTexture(const Image &); + std::unique_ptr newTexture(const Image &); - Primitive *newPrimitive(const std::vector &, - const std::vector &, - Texture *texture); + std::unique_ptr newPrimitive(const std::vector &, + const std::vector &, + Texture *texture); glm::vec2 getViewport() const; void setModelTransform(const glm::mat4 &); - View *newView(); - Light *newLight(); + std::unique_ptr newView(); + std::unique_ptr newLight(); void flush(); void startNextLayer(); @@ -119,5 +112,4 @@ class GraphicsInterface : private boost::noncopyable { uint64_t getLastStartedFrame(); }; -} // namespace main -} // namespace progressia +} // namespace progressia::main diff --git a/main/rendering/image.cpp b/main/rendering/image.cpp index c606710..5ddaf88 100644 --- a/main/rendering/image.cpp +++ b/main/rendering/image.cpp @@ -4,15 +4,17 @@ #include #include #include +#include #include #include "stb/stb_image.h" +#include + #include "../logging.h" using namespace progressia::main::logging; -namespace progressia { -namespace main { +namespace progressia::main { std::size_t Image::getSize() const { return data.size(); } @@ -20,33 +22,36 @@ const Image::Byte *Image::getData() const { return data.data(); } Image::Byte *Image::getData() { return data.data(); } -Image loadImage(const std::filesystem::path &path) { +Image loadImage(const std::string &path) { - std::ifstream file(path, std::ios::ate | std::ios::binary); + auto resource = __embedded_resources::getEmbeddedResource(path); - if (!file.is_open()) { - fatal() << "Could not access a PNG image in file " << path; + if (resource.data == nullptr) { // REPORT_ERROR + progressia::main::logging::fatal() + << "Could not find resource \"" << path << "\""; exit(1); } - std::size_t fileSize = static_cast(file.tellg()); - std::vector png(fileSize); + std::vector png(resource.data, + resource.data + resource.length); - file.seekg(0); - file.read(reinterpret_cast(png.data()), fileSize); + if (png.size() > std::numeric_limits::max()) { + // REPORT_ERROR + progressia::main::logging::fatal() + << "Could not load \"" << path << "\": image file too large"; + exit(1); + } - file.close(); + int dataSize = static_cast(png.size()); + int width = 0; + int height = 0; + int channelsInFile = 0; - int width; - int height; - int channelsInFile; + Image::Byte *stbAllocatedData = stbi_load_from_memory( + png.data(), dataSize, &width, &height, &channelsInFile, STBI_rgb_alpha); - Image::Byte *stbAllocatedData = - stbi_load_from_memory(png.data(), png.size(), &width, &height, - &channelsInFile, STBI_rgb_alpha); - - if (stbAllocatedData == NULL) { + if (stbAllocatedData == nullptr) { fatal() << "Could not decode a PNG image from file " << path; // REPORT_ERROR exit(1); @@ -61,5 +66,4 @@ Image loadImage(const std::filesystem::path &path) { data}; } -} // namespace main -} // namespace progressia +} // namespace progressia::main diff --git a/main/rendering/image.h b/main/rendering/image.h index a49e4b7..ec7482e 100644 --- a/main/rendering/image.h +++ b/main/rendering/image.h @@ -3,8 +3,7 @@ #include #include -namespace progressia { -namespace main { +namespace progressia::main { class Image { public: @@ -19,7 +18,6 @@ class Image { Byte *getData(); }; -Image loadImage(const std::filesystem::path &); +Image loadImage(const std::string &); -} // namespace main -} // namespace progressia +} // namespace progressia::main diff --git a/main/util.h b/main/util.h index 34f4ccc..be7f393 100644 --- a/main/util.h +++ b/main/util.h @@ -28,3 +28,25 @@ }; \ } // 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 { + NonCopyable &operator=(const NonCopyable &) = delete; + NonCopyable(const NonCopyable &) = delete; + NonCopyable() = default; +}; + +} // namespace progressia::main diff --git a/tools/bashlib.sh b/tools/bashlib.sh deleted file mode 100644 index 2c75f2e..0000000 --- a/tools/bashlib.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/false - -# Writes a message to stderr. -# Parameters: -# $@ - the message to display -function error() { - echo >&2 "`basename "$0"`: $@" -} - -# Writes a message to stderr and exits with code 1. -# Parameters: -# $@ - the message to display -function fail() { - error "$@" - exit 1; -} - -# Ensures that a variable with name $1 has a valid executable. If it does not, -# this function attempts to find an executable with a name suggested in $2...$n. -# In either way, if the variable does not end up naming an executable, fail() is -# called. -# Parameters: -# $1 - name of the variable to check and modify -# $2...$n - suggested executables (at least one) -# $FAIL_SILENTLY - if set, don't call exit and don't print anything on failure -function find_cmd() { - declare -n target="$1" - - if [ -z "${target+x}" ]; then - for candidate in "${@:2}"; do - if command -v "$candidate" >/dev/null; then - target="$candidate" - break - fi - done - fi - - if ! command -v "$target" >/dev/null; then - [ -n "${FAIL_SILENTLY+x}" ] && return 1 - fail "Command $2 is not available. Check \$PATH or set \$$1." - fi - - unset -n target - return 0 -} - -# Displays the command and then runs it. -# Parameters: -# $@ - the command to run -function echo_and_run() { - echo " > $*" - command "$@" -} - -root_dir="$(dirname "$(dirname "$(realpath "${BASH_SOURCE[0]}")")")" -source_dir="$root_dir" -build_dir="$root_dir/build" -tools_dir="$root_dir/tools" - -# Load private.sh -private_sh="$tools_dir/private.sh" -if [ -f "$private_sh" ]; then - [ -x "$private_sh" ] \ - || fail 'tools/private.sh exists but it is not executable' - source "$private_sh" -fi diff --git a/tools/build.sh b/tools/build.sh deleted file mode 100755 index a8466b3..0000000 --- a/tools/build.sh +++ /dev/null @@ -1,199 +0,0 @@ -#!/bin/bash - -usage=\ -"Usage: build.sh [OPTIONS...] -Build and run the game. - -Options: - --debug make a debug build (default) - --release make a release build - --build-id=ID set the build ID. Default is dev. - --cmake-gen=ARGS pass additional arguments to pass to cmake when - generating build files. ARGS is the ;-separated list. - --dont-generate don't generate build instructions; use existing - configuration if building - --dont-build don't build; run existing binaries or generate build - instructions only - --debug-vulkan enable Vulkan validation layers from LunarG - -R, --run run the game after building - --memcheck[=ARGS] run the game using valgrind's memcheck dynamic memory - analysis tool. Implies -R. ARGS is the ;-separated - list of arguments to pass to valgrind/memcheck. - - -h, --help display this help and exit - -Environment variables: - PARALLELISM threads to use, default is 1 - - CMAKE cmake executable - VALGRIND valgrind executable - -private.sh variables: - private_cmake_gen_args array of additional arguments to pass to cmake when - generating build files - -See also: tools/cppcheck/use-cppcheck.sh --help - tools/clang-format/use-clang-format.sh --help - tools/setup.sh --help" - -rsrc="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" -source "$rsrc/bashlib.sh" - - - -# Parse arguments - -build_type=Debug -do_generate=true -cmake_gen_args=() -do_build=true - -run_type=Normal -do_run='' - -debug_vulkan='' -memcheck_args=() - -for arg in "$@"; do - case "$arg" in - -h | --help ) - echo "$usage" - exit - ;; - --debug ) - build_type=Debug - ;; - --release ) - build_type=Release - ;; - --build-id ) - fail "Option --build-id=ID requires a parameter" - ;; - --build-id=* ) - build_id="${arg#*=}" - ;; - --cmake-gen ) - fail "Option --cmake-gen=ARGS requires a parameter" - ;; - --cmake-gen=* ) - readarray -t -d ';' new_cmake_gen_args <<<"${arg#*=};" - unset new_cmake_gen_args[-1] - cmake_gen_args+=("${new_cmake_gen_args[@]}") - unset new_cmake_gen_args - ;; - --debug-vulkan ) - debug_vulkan=true - ;; - -R | --run ) - do_run=true - ;; - --memcheck ) - do_run=true - run_type=memcheck - ;; - --memcheck=* ) - do_run=true - run_type=memcheck - readarray -t -d ';' new_memcheck_args <<<"${arg#*=};" - unset new_memcheck_args[-1] - memcheck_args+=("${new_memcheck_args[@]}") - unset new_memcheck_args - ;; - --dont-generate ) - do_generate='' - ;; - --dont-build ) - do_build='' - ;; - * ) - fail "Unknown option '$arg'" - ;; - esac -done - -if [ -z "$do_build" -a -z "$do_generate" -a ${#cmake_gen_args[@]} != 0 ]; then - fail "CMake arguments are set, but no build is requested. Aborting" -fi - -if [ -z "$do_build" -a -z "$do_generate" -a -z "$do_run" ]; then - fail "Nothing to do" -fi - - - -# Generate build files - -find_cmd CMAKE cmake -if [ $do_generate ]; then - - cmake_gen_managed_args=( - -DCMAKE_BUILD_TYPE=$build_type - -DVULKAN_ERROR_CHECKING=`[ $debug_vulkan ] && echo ON || echo OFF` - -UBUILD_ID - ) - - [ -n "${build_id+x}" ] && cmake_gen_managed_args+=( - -DBUILD_ID="$build_id" - ) - - echo_and_run "$CMAKE" \ - -B "$build_dir" \ - -S "$source_dir" \ - "${cmake_gen_managed_args[@]}" \ - "${private_cmake_gen_args[@]}" \ - "${cmake_gen_args[@]}" \ - || fail "Could not generate build files" -fi - - - -# Build - -find_cmd CMAKE cmake -if [ $do_build ]; then - options=() - - [ -n "${PARALLELISM+x}" ] && options+=(-j "$PARALLELISM") - - echo_and_run "$CMAKE" \ - --build "$build_dir" \ - "${options[@]}" \ - || fail "Build failed" - - unset options -fi - - - -# Run - -if [ $do_run ]; then - - run_command=() - - if [ $run_type == memcheck ]; then - find_cmd VALGRIND valgrind - - run_command+=( - "$VALGRIND" - --tool=memcheck - --suppressions="$tools_dir"/memcheck/suppressions.supp - "${memcheck_args[@]}" - -- - ) - fi - - run_command+=( - "$build_dir/progressia" - ) - - run_dir="$root_dir/run" - mkdir -p "$run_dir" - - ( - cd "$run_dir" - echo_and_run "${run_command[@]}" - echo "Process exited with code $?" - ) - -fi diff --git a/tools/clang-format/clang-format.yml b/tools/clang-format/clang-format.yml deleted file mode 100644 index 6d3cedb..0000000 --- a/tools/clang-format/clang-format.yml +++ /dev/null @@ -1,4 +0,0 @@ -BasedOnStyle: LLVM - -# Use larger indentation -IndentWidth: 4 diff --git a/tools/clang-format/use-clang-format.sh b/tools/clang-format/use-clang-format.sh deleted file mode 100755 index 24bdbe9..0000000 --- a/tools/clang-format/use-clang-format.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash - -usage=\ -"Usage: use-clang-format.sh git - or: use-clang-format.sh files FILES... - or: use-clang-format.sh raw ARGUMENTS... -In the 1st form, format all files that have changed since last git commit. -In the 2nd form, format all FILES, treating directories recursively. -In the 3rd form, run \`clang-format --style=