mirror of
https://gitea.windcorp.ru/Wind-Corporation/Progressia.git
synced 2025-04-20 21:40:46 +03:00
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
This commit is contained in:
parent
189d19fd4a
commit
ae4e265a90
5
.clang-format
Normal file
5
.clang-format
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"BasedOnStyle": "LLVM",
|
||||
"IndentWidth": 4,
|
||||
"CommentPragmas": "NOLINT",
|
||||
}
|
78
.clang-tidy
Normal file
78
.clang-tidy
Normal file
@ -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<S>(new S { 1, 2 }) // works
|
||||
# std::make_unique<S>(1, 2) // error
|
||||
# std::make_unique<S>({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
|
12
.gitignore
vendored
12
.gitignore
vendored
@ -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*
|
||||
|
115
CMakeLists.txt
115
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)
|
||||
|
@ -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
|
||||
|
@ -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<void()> 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<void()> 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<GlfwManagerImpl> theGlfwManager;
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<GlfwManager> 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<GlfwManagerImpl> aGlfwManager =
|
||||
std::make_shared<GlfwManagerImpl>();
|
||||
|
||||
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
|
||||
|
@ -1,13 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
namespace progressia {
|
||||
namespace desktop {
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
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<void()>) = 0;
|
||||
|
||||
virtual void showWindow() = 0;
|
||||
virtual bool shouldRun() = 0;
|
||||
virtual void doGlfwRoutine() = 0;
|
||||
};
|
||||
|
||||
std::shared_ptr<GlfwManager> makeGlfwManager();
|
||||
|
||||
} // namespace progressia::desktop
|
||||
|
@ -5,10 +5,9 @@
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
namespace progressia {
|
||||
namespace desktop {
|
||||
namespace progressia::desktop {
|
||||
|
||||
// TODO refactor into OOP
|
||||
GLFWwindow *getGLFWWindowHandle();
|
||||
|
||||
} // namespace desktop
|
||||
} // namespace progressia
|
||||
} // namespace progressia::desktop
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "vulkan_common.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
@ -25,8 +26,7 @@
|
||||
|
||||
#include <embedded_resources.h>
|
||||
|
||||
namespace progressia {
|
||||
namespace desktop {
|
||||
namespace progressia::desktop {
|
||||
|
||||
using progressia::main::Vertex;
|
||||
|
||||
@ -50,7 +50,7 @@ auto getVertexFieldProperties() {
|
||||
|
||||
namespace {
|
||||
std::vector<char> 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<char> tmp_readFile(const std::string &path) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return std::vector<char>(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<Attachment> &Adapter::getAttachments() { return attachments; }
|
||||
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||
std::vector<char> Adapter::loadVertexShader() {
|
||||
return tmp_readFile("shader.vert.spv");
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||
std::vector<char> 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<VkVertexInputAttributeDescription>
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||
Adapter::getVertexInputAttributeDescriptions() {
|
||||
std::vector<VkVertexInputAttributeDescription> 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<DrawRequest> 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<progressia::desktop::Texture *>(this->backend);
|
||||
}
|
||||
progressia::main::Texture::Texture(std::unique_ptr<Backend> backend)
|
||||
: backend(std::move(backend)) {}
|
||||
|
||||
namespace {
|
||||
struct PrimitiveBackend {
|
||||
progressia::main::Texture::~Texture() = default;
|
||||
|
||||
struct Primitive::Backend {
|
||||
IndexedBuffer<Vertex> buf;
|
||||
progressia::main::Texture *tex;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Primitive::Primitive(Backend backend) : backend(backend) {}
|
||||
Primitive::Primitive(std::unique_ptr<Backend> backend)
|
||||
: backend(std::move(backend)) {}
|
||||
|
||||
Primitive::~Primitive() {
|
||||
delete static_cast<PrimitiveBackend *>(this->backend);
|
||||
}
|
||||
Primitive::~Primitive() = default;
|
||||
|
||||
void Primitive::draw() {
|
||||
auto backend = static_cast<PrimitiveBackend *>(this->backend);
|
||||
|
||||
if (pendingDrawCommands.size() > 100000) {
|
||||
if (pendingDrawCommands.size() > PENDING_DRAW_COMMANDS_MAX_SIZE) {
|
||||
backend->buf.getVulkan().getGint().flush();
|
||||
}
|
||||
|
||||
pendingDrawCommands.push_back(
|
||||
{static_cast<progressia::desktop::Texture *>(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<PrimitiveBackend *>(this->backend)->tex;
|
||||
return backend->tex;
|
||||
}
|
||||
|
||||
View::View(Backend backend) : backend(backend) {}
|
||||
struct View::Backend {
|
||||
Adapter::ViewUniform::State state;
|
||||
};
|
||||
|
||||
View::~View() {
|
||||
delete static_cast<Adapter::ViewUniform::State *>(this->backend);
|
||||
}
|
||||
View::View(std::unique_ptr<Backend> backend) : backend(std::move(backend)) {}
|
||||
|
||||
View::~View() = default;
|
||||
|
||||
void View::configure(const glm::mat4 &proj, const glm::mat4 &view) {
|
||||
|
||||
static_cast<Adapter::ViewUniform::State *>(this->backend)
|
||||
->update(proj, view);
|
||||
backend->state.update(proj, view);
|
||||
}
|
||||
|
||||
void View::use() {
|
||||
auto backend = static_cast<Adapter::ViewUniform::State *>(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<Adapter::LightUniform::State *>(this->backend);
|
||||
}
|
||||
Light::Light(std::unique_ptr<Backend> 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<Adapter::LightUniform::State *>(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<Adapter::LightUniform::State *>(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<progressia::main::Texture>
|
||||
GraphicsInterface::newTexture(const progressia::main::Image &src) {
|
||||
auto backend = new progressia::desktop::Texture(
|
||||
src, *static_cast<Vulkan *>(this->backend));
|
||||
using Backend = progressia::main::Texture::Backend;
|
||||
|
||||
return new Texture(backend);
|
||||
return std::make_unique<progressia::main::Texture>(
|
||||
std::unique_ptr<Backend>(new Backend{progressia::desktop::Texture(
|
||||
src, *static_cast<Vulkan *>(this->backend))}));
|
||||
}
|
||||
|
||||
Primitive *
|
||||
std::unique_ptr<Primitive>
|
||||
GraphicsInterface::newPrimitive(const std::vector<Vertex> &vertices,
|
||||
const std::vector<Vertex::Index> &indices,
|
||||
progressia::main::Texture *texture) {
|
||||
|
||||
auto backend = new PrimitiveBackend{
|
||||
IndexedBuffer<Vertex>(vertices.size(), indices.size(),
|
||||
*static_cast<Vulkan *>(this->backend)),
|
||||
texture};
|
||||
auto primitive = std::make_unique<Primitive>(
|
||||
std::unique_ptr<Primitive::Backend>(new Primitive::Backend{
|
||||
IndexedBuffer<Vertex>(vertices.size(), indices.size(),
|
||||
*static_cast<Vulkan *>(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<Vulkan *>(this->backend)->getAdapter().createView()));
|
||||
std::unique_ptr<View> GraphicsInterface::newView() {
|
||||
return std::make_unique<View>(std::unique_ptr<View::Backend>(
|
||||
new View::Backend{Adapter::ViewUniform::State(
|
||||
static_cast<Vulkan *>(this->backend)->getAdapter().createView())}));
|
||||
}
|
||||
|
||||
Light *GraphicsInterface::newLight() {
|
||||
return new Light(new Adapter::LightUniform::State(
|
||||
static_cast<Vulkan *>(this->backend)->getAdapter().createLight()));
|
||||
std::unique_ptr<Light> GraphicsInterface::newLight() {
|
||||
return std::make_unique<Light>(
|
||||
std::unique_ptr<Light::Backend>(new Light::Backend{
|
||||
Adapter::LightUniform::State(static_cast<Vulkan *>(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<Vulkan *>(this->backend)
|
||||
->getCurrentFrame()
|
||||
->getCommandBuffer();
|
||||
auto pipelineLayout =
|
||||
auto *commandBuffer = static_cast<Vulkan *>(this->backend)
|
||||
->getCurrentFrame()
|
||||
->getCommandBuffer();
|
||||
auto *pipelineLayout =
|
||||
static_cast<Vulkan *>(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<Vulkan *>(this->backend)->getLastStartedFrame();
|
||||
}
|
||||
|
||||
} // namespace main
|
||||
} // namespace progressia
|
||||
} // namespace progressia::main
|
||||
|
@ -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
|
||||
|
@ -1,12 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#include <vector>
|
||||
|
||||
#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 <typename Vertex>
|
||||
using IndexedBuffer = IndexedBufferBase<Vertex, uint16_t, VK_INDEX_TYPE_UINT16>;
|
||||
|
||||
} // namespace desktop
|
||||
} // namespace progressia
|
||||
} // namespace progressia::desktop
|
||||
|
@ -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<const char *> instanceExtensions,
|
||||
std::vector<const char *> 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<const char *> instanceExtensions,
|
||||
|
||||
// Enable extensions
|
||||
{
|
||||
uint32_t extensionCount;
|
||||
uint32_t extensionCount = 0;
|
||||
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount,
|
||||
nullptr);
|
||||
std::vector<VkExtensionProperties> available(extensionCount);
|
||||
@ -89,7 +88,7 @@ Vulkan::Vulkan(std::vector<const char *> instanceExtensions,
|
||||
|
||||
// Enable validation layers
|
||||
{
|
||||
uint32_t layerCount;
|
||||
uint32_t layerCount = 0;
|
||||
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
||||
std::vector<VkLayerProperties> available(layerCount);
|
||||
vkEnumerateInstanceLayerProperties(&layerCount, available.data());
|
||||
@ -150,31 +149,25 @@ Vulkan::Vulkan(std::vector<const char *> instanceExtensions,
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::vector<VkPhysicalDevice> devices(deviceCount);
|
||||
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
|
||||
std::vector<VkPhysicalDevice> vkDevices(deviceCount);
|
||||
vkEnumeratePhysicalDevices(instance, &deviceCount, vkDevices.data());
|
||||
|
||||
std::vector<PhysicalDeviceData> 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<PhysicalDevice> 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<PhysicalDevice>(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup queues
|
||||
*/
|
||||
|
||||
queues = std::make_unique<Queues>(physicalDevice, *this);
|
||||
queues = std::make_unique<Queues>(physicalDevice->getVk(), *this);
|
||||
|
||||
/*
|
||||
* Create logical device
|
||||
@ -207,9 +200,9 @@ Vulkan::Vulkan(std::vector<const char *> 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<const char *> instanceExtensions,
|
||||
for (auto &container : frames) {
|
||||
container.emplace(*this);
|
||||
}
|
||||
currentFrame = 0;
|
||||
|
||||
gint = std::make_unique<progressia::main::GraphicsInterface>(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<VkFormat> &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<VkFormat> &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<const Vulkan *>(pUserData);
|
||||
[[maybe_unused]] const auto &vk =
|
||||
*reinterpret_cast<const Vulkan *>(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<VkDebugUtilsMessengerCreateInfoEXT>();
|
||||
(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::CreationRequest>
|
||||
Queues::requestCreation(VkDeviceCreateInfo &createInfo) const {
|
||||
|
||||
std::unique_ptr result = std::make_unique<CreationRequest>();
|
||||
result->priority = 1.0f;
|
||||
result->priority = 1.0F;
|
||||
|
||||
std::unordered_set<uint32_t> 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
|
||||
|
@ -17,13 +17,12 @@
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#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<const char *, CstrHash, CstrEqual>;
|
||||
} // 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<VulkanErrorHandler> errorHandler;
|
||||
std::unique_ptr<PhysicalDevice> physicalDevice;
|
||||
std::unique_ptr<Surface> surface;
|
||||
std::unique_ptr<Queues> queues;
|
||||
std::unique_ptr<CommandPool> 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<VkDebugUtilsMessengerCreateInfoEXT>
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
|
@ -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<const char *> 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<Vulkan>(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
|
||||
|
@ -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> 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
|
||||
|
51
desktop/graphics/vulkan_physical_device.cpp
Normal file
51
desktop/graphics/vulkan_physical_device.cpp
Normal file
@ -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
|
33
desktop/graphics/vulkan_physical_device.h
Normal file
33
desktop/graphics/vulkan_physical_device.h
Normal file
@ -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
|
@ -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<const char *> &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<PhysicalDeviceData> &choices, Vulkan &vulkan,
|
||||
const PhysicalDevice &
|
||||
pickPhysicalDevice(std::vector<PhysicalDevice> &choices, Vulkan &vulkan,
|
||||
const std::vector<const char *> &deviceExtensions) {
|
||||
|
||||
// Remove unsuitable devices
|
||||
@ -82,20 +85,17 @@ pickPhysicalDevice(std::vector<PhysicalDeviceData> &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
|
||||
|
@ -1,21 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_common.h"
|
||||
#include "vulkan_physical_device.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace progressia {
|
||||
namespace desktop {
|
||||
namespace progressia::desktop {
|
||||
|
||||
struct PhysicalDeviceData {
|
||||
VkPhysicalDevice device;
|
||||
VkPhysicalDeviceProperties properties;
|
||||
VkPhysicalDeviceFeatures features;
|
||||
};
|
||||
|
||||
const PhysicalDeviceData &
|
||||
pickPhysicalDevice(std::vector<PhysicalDeviceData> &, Vulkan &,
|
||||
const PhysicalDevice &
|
||||
pickPhysicalDevice(std::vector<PhysicalDevice> &, Vulkan &,
|
||||
const std::vector<const char *> &deviceExtensions);
|
||||
|
||||
} // namespace desktop
|
||||
} // namespace progressia
|
||||
} // namespace progressia::desktop
|
||||
|
@ -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<char> &bytecode) {
|
||||
// Important - the buffer must be aligned properly. std::vector does that.
|
||||
createInfo.pCode = reinterpret_cast<const uint32_t *>(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
|
||||
|
@ -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
|
||||
|
@ -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<VkAttachmentDescription> attachmentDescriptions;
|
||||
std::vector<VkAttachmentReference> 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
|
||||
|
@ -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
|
||||
|
@ -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<VkSurfaceFormatKHR> &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<VkPresentModeKHR> &supported) {
|
||||
return std::find(supported.begin(), supported.end(),
|
||||
@ -219,13 +222,15 @@ SwapChain::choosePresentMode(const std::vector<VkPresentModeKHR> &supported,
|
||||
}
|
||||
|
||||
VkExtent2D
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||
SwapChain::chooseExtent(const VkSurfaceCapabilitiesKHR &capabilities) {
|
||||
if (capabilities.currentExtent.width !=
|
||||
std::numeric_limits<uint32_t>::max()) {
|
||||
return capabilities.currentExtent;
|
||||
}
|
||||
|
||||
int width, height;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
glfwGetFramebufferSize(getGLFWWindowHandle(), &width, &height);
|
||||
|
||||
VkExtent2D actualExtent = {static_cast<uint32_t>(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<Image>(static_cast<VkImage>(VK_NULL_HANDLE),
|
||||
static_cast<VkImageView>(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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@ -7,8 +8,7 @@
|
||||
#include "vulkan_common.h"
|
||||
#include "vulkan_descriptor_set.h"
|
||||
|
||||
namespace progressia {
|
||||
namespace desktop {
|
||||
namespace progressia::desktop {
|
||||
|
||||
template <typename... Entries> class Uniform : public DescriptorSetInterface {
|
||||
|
||||
@ -70,7 +70,6 @@ template <typename... Entries> class Uniform : public DescriptorSetInterface {
|
||||
void doUpdates();
|
||||
};
|
||||
|
||||
} // namespace desktop
|
||||
} // namespace progressia
|
||||
} // namespace progressia::desktop
|
||||
|
||||
#include "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 <typename T>
|
||||
std::size_t offsetOf(Vulkan &vulkan) {
|
||||
auto step = vulkan.getPhysicalDevice().getMinUniformOffset();
|
||||
return ((sizeof(T) - 1) / step + 1) * step; // Round up to multiple
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::size_t offsetOf(Vulkan &vulkan, const T&) {
|
||||
return offsetOf<T>(vulkan);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename... Entries>
|
||||
Uniform<Entries...>::StateImpl::Set::Set(VkDescriptorSet vk, Vulkan &vulkan)
|
||||
: vk(vk),
|
||||
contents((sizeof(Entries) + ...), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
contents((detail::offsetOf<Entries>(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<Entries...>::StateImpl::StateImpl(
|
||||
writes[index].descriptorCount = 1;
|
||||
writes[index].pBufferInfo = &bufferInfos[index];
|
||||
|
||||
offset += sizeof(Entry);
|
||||
offset += detail::offsetOf<Entry>(vulkan);
|
||||
index++;
|
||||
})
|
||||
}
|
||||
@ -71,7 +86,7 @@ void Uniform<Entries...>::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 <typename... Entries> void Uniform<Entries...>::doUpdates() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace desktop
|
||||
} // namespace progressia
|
||||
} // namespace progressia::desktop
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 <clone url>
|
||||
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.
|
||||
|
@ -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.
|
||||
|
100
docs/ide_setup/WindowsCLion.md
Normal file
100
docs/ide_setup/WindowsCLion.md
Normal file
@ -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.
|
97
docs/ide_setup/WindowsVisualStudio.md
Normal file
97
docs/ide_setup/WindowsVisualStudio.md
Normal file
@ -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._
|
257
main/game.cpp
257
main/game.cpp
@ -1,5 +1,6 @@
|
||||
#include "game.h"
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
#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<Primitive> cube1, cube2;
|
||||
std::unique_ptr<Texture> texture1, texture2;
|
||||
std::unique_ptr<View> perspective;
|
||||
std::unique_ptr<Light> 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<Vertex> &vertices,
|
||||
std::vector<Vertex::Index> &indices) {
|
||||
public:
|
||||
std::unique_ptr<Primitive> cube1;
|
||||
std::unique_ptr<Primitive> cube2;
|
||||
std::unique_ptr<Texture> texture1;
|
||||
std::unique_ptr<Texture> texture2;
|
||||
std::unique_ptr<View> perspective;
|
||||
std::unique_ptr<Light> 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<Vertex> &vertices,
|
||||
std::vector<Vertex::Index> &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<glm::vec4, 6> colors,
|
||||
std::vector<Vertex> &vertices,
|
||||
std::vector<Vertex::Index> &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<glm::vec4, 6> colors,
|
||||
std::vector<Vertex> &vertices,
|
||||
std::vector<Vertex::Index> &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<Vertex> vertices;
|
||||
std::vector<Vertex::Index> 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<Vertex> vertices;
|
||||
std::vector<Vertex::Index> 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<Vertex> vertices;
|
||||
std::vector<Vertex::Index> indices;
|
||||
auto white = glm::vec4(1, 1, 1, 1);
|
||||
|
||||
// Cube 2
|
||||
{
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<Vertex::Index> 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<float>(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<Game> makeGame(GraphicsInterface &gint) {
|
||||
return std::make_unique<GameImpl>(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
|
||||
|
17
main/game.h
17
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<Game> makeGame(GraphicsInterface &);
|
||||
|
||||
} // namespace progressia::main
|
||||
|
@ -8,8 +8,7 @@
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
|
||||
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<std::mutex> 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
|
||||
|
@ -1,14 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost/core/noncopyable.hpp"
|
||||
#include "util.h"
|
||||
#include <ostream>
|
||||
|
||||
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 <typename T>
|
||||
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
|
||||
|
@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include <config.h>
|
||||
#include <cstdlib>
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost/core/noncopyable.hpp"
|
||||
#include "../util.h"
|
||||
#include <vector>
|
||||
|
||||
#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> backend;
|
||||
friend class GraphicsInterface;
|
||||
|
||||
friend class Primitive;
|
||||
|
||||
public:
|
||||
Texture(Backend);
|
||||
Texture(std::unique_ptr<Backend>);
|
||||
~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> backend;
|
||||
friend class GraphicsInterface;
|
||||
|
||||
public:
|
||||
Primitive(Backend);
|
||||
Primitive(std::unique_ptr<Backend>);
|
||||
~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> backend;
|
||||
friend class GraphicsInterface;
|
||||
|
||||
public:
|
||||
View(Backend);
|
||||
View(std::unique_ptr<Backend>);
|
||||
~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> backend;
|
||||
friend class GraphicsInterface;
|
||||
|
||||
public:
|
||||
Light(Backend);
|
||||
Light(std::unique_ptr<Backend>);
|
||||
~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<Texture> newTexture(const Image &);
|
||||
|
||||
Primitive *newPrimitive(const std::vector<Vertex> &,
|
||||
const std::vector<Vertex::Index> &,
|
||||
Texture *texture);
|
||||
std::unique_ptr<Primitive> newPrimitive(const std::vector<Vertex> &,
|
||||
const std::vector<Vertex::Index> &,
|
||||
Texture *texture);
|
||||
|
||||
glm::vec2 getViewport() const;
|
||||
|
||||
void setModelTransform(const glm::mat4 &);
|
||||
|
||||
View *newView();
|
||||
Light *newLight();
|
||||
std::unique_ptr<View> newView();
|
||||
std::unique_ptr<Light> newLight();
|
||||
|
||||
void flush();
|
||||
void startNextLayer();
|
||||
@ -119,5 +112,4 @@ class GraphicsInterface : private boost::noncopyable {
|
||||
uint64_t getLastStartedFrame();
|
||||
};
|
||||
|
||||
} // namespace main
|
||||
} // namespace progressia
|
||||
} // namespace progressia::main
|
||||
|
@ -4,15 +4,17 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "stb/stb_image.h"
|
||||
|
||||
#include <embedded_resources.h>
|
||||
|
||||
#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<std::size_t>(file.tellg());
|
||||
std::vector<Image::Byte> png(fileSize);
|
||||
std::vector<Image::Byte> png(resource.data,
|
||||
resource.data + resource.length);
|
||||
|
||||
file.seekg(0);
|
||||
file.read(reinterpret_cast<char *>(png.data()), fileSize);
|
||||
if (png.size() > std::numeric_limits<int>::max()) {
|
||||
// REPORT_ERROR
|
||||
progressia::main::logging::fatal()
|
||||
<< "Could not load \"" << path << "\": image file too large";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
file.close();
|
||||
int dataSize = static_cast<int>(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
|
||||
|
@ -3,8 +3,7 @@
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
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
|
||||
|
22
main/util.h
22
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
|
||||
|
@ -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
|
199
tools/build.sh
199
tools/build.sh
@ -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
|
@ -1,4 +0,0 @@
|
||||
BasedOnStyle: LLVM
|
||||
|
||||
# Use larger indentation
|
||||
IndentWidth: 4
|
@ -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=<style> ARGUMENTS...\`.
|
||||
|
||||
Environment variables:
|
||||
CLANG_FORMAT clang-format executable
|
||||
CLANG_FORMAT_DIFF clang-format-diff script"
|
||||
|
||||
rsrc="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
|
||||
source "$rsrc/../bashlib.sh"
|
||||
|
||||
case "$1" in
|
||||
git )
|
||||
find_cmd CLANG_FORMAT_DIFF \
|
||||
clang-format-diff-13 \
|
||||
clang-format-diff \
|
||||
clang-format-diff.py
|
||||
;;
|
||||
files | raw )
|
||||
find_cmd CLANG_FORMAT \
|
||||
clang-format-13 \
|
||||
clang-format
|
||||
;;
|
||||
-h | --help | '' )
|
||||
echo "$usage"
|
||||
exit
|
||||
;;
|
||||
* )
|
||||
fail "Unknown option '$1'"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Generate style argument
|
||||
style=''
|
||||
while IFS='' read line; do
|
||||
[ -z "$line" ] && continue
|
||||
[ "${line:0:1}" = '#' ] && continue
|
||||
[ -n "$style" ] && style+=', '
|
||||
style+="$line"
|
||||
done < "$rsrc/clang-format.yml"
|
||||
style="{$style}" # Not typo
|
||||
|
||||
case "$1" in
|
||||
git )
|
||||
unstaged_changes="`git diff --name-only`"
|
||||
if [ -n "$unstaged_changes" ]; then
|
||||
fail "Refusing to operate in git repository with unstaged changes:
|
||||
$unstaged_changes"
|
||||
fi
|
||||
|
||||
git diff -U0 --no-color --relative HEAD \
|
||||
{desktop,main}/{'*.cpp','*.h','*.inl'} \
|
||||
| command "$CLANG_FORMAT_DIFF" -p1 -style="$style" -i --verbose
|
||||
exit_code="$?"
|
||||
git add "$root_dir"
|
||||
exit "$exit_code"
|
||||
;;
|
||||
|
||||
raw )
|
||||
command "$CLANG_FORMAT" -style="$style" "$@"
|
||||
;;
|
||||
|
||||
files )
|
||||
files=()
|
||||
|
||||
for input in "${@:2}"; do
|
||||
if [ -d "$input" ]; then
|
||||
readarray -d '' current_files < <(
|
||||
find "$input" \
|
||||
\( -name '*.cpp' -o -name '*.h' -o -name '*.inl' \) \
|
||||
-type f \
|
||||
-print0 \
|
||||
)
|
||||
|
||||
[ "${#current_files[@]}" -eq 0 ] \
|
||||
&& fail "No suitable files found in directory $input"
|
||||
|
||||
files+=("${current_files[@]}")
|
||||
else
|
||||
case "$input" in
|
||||
*.cpp | *.h | *.inl )
|
||||
files+=("$input")
|
||||
;;
|
||||
* )
|
||||
error "Refusing to format file '$input': `
|
||||
`only .cpp, .h and .inl supported"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
|
||||
[ "${#files[@]}" -eq 0 ] && fail "No files to format"
|
||||
|
||||
command "$CLANG_FORMAT" -style="$style" -i --verbose "${files[@]}"
|
||||
;;
|
||||
esac
|
@ -1,56 +0,0 @@
|
||||
# Global variables. Yikes. FIXME
|
||||
set(tools ${PROJECT_SOURCE_DIR}/tools)
|
||||
set(generated ${PROJECT_BINARY_DIR}/generated)
|
||||
set(assets_to_embed "")
|
||||
set(assets_to_embed_args "")
|
||||
|
||||
file(MAKE_DIRECTORY ${generated})
|
||||
|
||||
find_package(Vulkan COMPONENTS glslc REQUIRED)
|
||||
find_program(glslc_executable NAMES glslc HINTS Vulkan::glslc)
|
||||
set(shaders ${generated}/shaders)
|
||||
file(MAKE_DIRECTORY ${shaders})
|
||||
|
||||
# Shedules compilation of shaders
|
||||
# Adapted from https://stackoverflow.com/a/60472877/4463352
|
||||
macro(compile_shader)
|
||||
foreach(source ${ARGV})
|
||||
get_filename_component(source_basename ${source} NAME)
|
||||
set(tmp "${shaders}/${source_basename}.spv")
|
||||
add_custom_command(
|
||||
OUTPUT ${tmp}
|
||||
DEPENDS ${source}
|
||||
COMMAND ${glslc_executable}
|
||||
-o ${tmp}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${source}
|
||||
COMMENT "Compiling shader ${source}"
|
||||
)
|
||||
list(APPEND assets_to_embed_args "${tmp};as;${source_basename}.spv")
|
||||
list(APPEND assets_to_embed "${tmp}")
|
||||
unset(tmp)
|
||||
unset(source_basename)
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
compile_shader(
|
||||
desktop/graphics/shaders/shader.frag
|
||||
desktop/graphics/shaders/shader.vert
|
||||
)
|
||||
|
||||
# Generate embed files
|
||||
add_custom_command(
|
||||
OUTPUT ${generated}/embedded_resources.cpp
|
||||
${generated}/embedded_resources.h
|
||||
|
||||
COMMAND ${tools}/embed/embed.py
|
||||
--cpp ${generated}/embedded_resources.cpp
|
||||
--header ${generated}/embedded_resources.h
|
||||
--
|
||||
${assets_to_embed_args}
|
||||
|
||||
DEPENDS "${assets_to_embed}"
|
||||
${tools}/embed/embed.py
|
||||
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
COMMENT "Embedding assets"
|
||||
)
|
@ -1,28 +0,0 @@
|
||||
# CppCheck command line arguments
|
||||
# Each line is treated as one argument, unless it is empty or it starts with #.
|
||||
#
|
||||
# Available variables:
|
||||
# ${CMAKE_SOURCE_DIR} project root
|
||||
# ${CMAKE_BINARY_DIR} CMake build directory
|
||||
|
||||
--enable=warning,style,information
|
||||
#--enable=unusedFunction # Unused functions are often OK since they are intended
|
||||
# # to be used later
|
||||
#--enable=missingInclude # Very prone to false positives; system-dependent
|
||||
--inconclusive
|
||||
|
||||
# SUPPRESSIONS
|
||||
# Warnings that are suppressed on a case-by-case basis should be suppressed
|
||||
# using inline suppressions.
|
||||
# Warnings that were decided to be generally inapplicable should be suppressed
|
||||
# using suppressions.txt.
|
||||
# Warnings that result from the way cppcheck is invoked should be suppressed
|
||||
# using this file.
|
||||
|
||||
--inline-suppr
|
||||
--suppressions-list=${CMAKE_SOURCE_DIR}/tools/cppcheck/suppressions.txt
|
||||
|
||||
# N.B.: this path is also mentioned in use scripts
|
||||
--cppcheck-build-dir=${CMAKE_BINARY_DIR}/cppcheck
|
||||
|
||||
--error-exitcode=2
|
@ -1,18 +0,0 @@
|
||||
# CppCheck global suppressions
|
||||
# Do not use this file for suppressions that could easily be declared inline.
|
||||
|
||||
# Allow the use of implicit constructors.
|
||||
noExplicitConstructor:*
|
||||
|
||||
# In most cases using STL algorithm functions causes unnecessary code bloat.
|
||||
useStlAlgorithm:*
|
||||
|
||||
# cppcheck trips on #include <embedded_resources.h> and there's no way to
|
||||
# suppress that exlusively
|
||||
missingInclude:*
|
||||
|
||||
# Shut up. Just shut up.
|
||||
unmatchedSuppression:*
|
||||
|
||||
# Do not check third-party libraries
|
||||
*:*lib*
|
@ -1,65 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
usage=\
|
||||
"Usage: use-cppcheck.sh
|
||||
Run cppcheck with correct options.
|
||||
|
||||
Environment variables:
|
||||
PARALLELISM threads to use, default is 1
|
||||
|
||||
CPPCHECK cppcheck executable
|
||||
CMAKE cmake executable"
|
||||
|
||||
rsrc="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
|
||||
source "$rsrc/../bashlib.sh"
|
||||
|
||||
find_cmd CPPCHECK cppcheck
|
||||
find_cmd CMAKE cmake
|
||||
|
||||
case "$1" in
|
||||
-h | --help )
|
||||
echo "$usage"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
|
||||
# Generate compile database for CppCheck
|
||||
command "$CMAKE" \
|
||||
-B "$build_dir" \
|
||||
-S "$source_dir" \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||
|
||||
compile_database="$build_dir/compile_commands.json"
|
||||
|
||||
mkdir -p "$build_dir/cppcheck"
|
||||
|
||||
options=()
|
||||
|
||||
while IFS='' read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
[ "${line:0:1}" = '#' ] && continue
|
||||
|
||||
option="$(
|
||||
CMAKE_SOURCE_DIR="$source_dir" \
|
||||
CMAKE_BINARY_DIR="$build_dir" \
|
||||
envsubst <<<"$line"
|
||||
)"
|
||||
|
||||
options+=("$option")
|
||||
done < "$tools_dir/cppcheck/options.txt"
|
||||
|
||||
[ -n "${PARALLELISM+x}" ] && options+=(-j "$PARALLELISM")
|
||||
|
||||
errors="`
|
||||
echo_and_run "$CPPCHECK" \
|
||||
--project="$compile_database" \
|
||||
-D__CPPCHECK__ \
|
||||
"${options[@]}" \
|
||||
2>&1 >/dev/fd/0 # Store stderr into variable, pass stdout to our stdout
|
||||
`"
|
||||
|
||||
exit_code="$?"
|
||||
if [ "$exit_code" -eq 2 ]; then
|
||||
less - <<<"$errors"
|
||||
exit "$exit_code"
|
||||
fi
|
57
tools/dev-mode.cmake
Normal file
57
tools/dev-mode.cmake
Normal file
@ -0,0 +1,57 @@
|
||||
if (DEV_MODE)
|
||||
find_program(clang_tidy_EXECUTABLE NAMES clang-tidy-13 clang-tidy REQUIRED)
|
||||
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||
|
||||
# Setup clang-tidy
|
||||
list(APPEND clang_tidy_command "${clang_tidy_EXECUTABLE}"
|
||||
"--warnings-as-errors=*"
|
||||
"--use-color")
|
||||
|
||||
set_target_properties(progressia
|
||||
PROPERTIES CXX_CLANG_TIDY "${clang_tidy_command}")
|
||||
|
||||
# Display the marker for pre-commit.py at build time
|
||||
add_custom_target(clang_tidy_marker ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E echo
|
||||
"Clang-tidy is enabled. This is a marker for pre-commit.py")
|
||||
|
||||
# Notify pre-commit.py about CMake settings
|
||||
execute_process(COMMAND ${Python3_EXECUTABLE} ${tools}/pre-commit.py
|
||||
set-build-info -- "${CMAKE_COMMAND}" "${CMAKE_BINARY_DIR}"
|
||||
RESULT_VARIABLE set_build_info_RESULT)
|
||||
|
||||
if(${set_build_info_RESULT})
|
||||
message(FATAL_ERROR "pre-commit.py set-build-info failed")
|
||||
endif()
|
||||
|
||||
# Setup pre-commit git hook
|
||||
if (IS_DIRECTORY "${CMAKE_SOURCE_DIR}/.git/hooks")
|
||||
set(pre_commit_hook "${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit")
|
||||
if (NOT EXISTS "${pre_commit_hook}")
|
||||
file(WRITE "${pre_commit_hook}"
|
||||
"#!/bin/sh\n"
|
||||
"# Progressia autogenerated pre-commit hook\n"
|
||||
"# You may modify this hook freely "
|
||||
"(just make sure the checks run)\n"
|
||||
"/bin/env python3 ${CMAKE_SOURCE_DIR}/tools/pre-commit.py run")
|
||||
|
||||
if (${CMAKE_VERSION} VERSION_LESS "3.19.0")
|
||||
if (${CMAKE_HOST_UNIX})
|
||||
execute_process(COMMAND chmod "755" "${pre_commit_hook}"
|
||||
RESULT_VARIABLE chmod_RESULT)
|
||||
if (${chmod_RESULT})
|
||||
message(FATAL_ERROR "Could not make git pre-commit hook executable")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
file(CHMOD "${pre_commit_hook}"
|
||||
PERMISSIONS
|
||||
OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
||||
GROUP_READ GROUP_EXECUTE
|
||||
WORLD_READ WORLD_EXECUTE)
|
||||
endif()
|
||||
endif()
|
||||
unset(pre_commit_hook)
|
||||
endif()
|
||||
|
||||
endif()
|
89
tools/embed/embed.cmake
Normal file
89
tools/embed/embed.cmake
Normal file
@ -0,0 +1,89 @@
|
||||
# embed.cmake
|
||||
# Generates embedded_resources.h and embedded_resources.cpp
|
||||
|
||||
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||
|
||||
macro (get_target_property_or var target prop default)
|
||||
get_property(__is_set TARGET ${target} PROPERTY ${prop} SET)
|
||||
if (__is_set)
|
||||
get_property(${var} TARGET ${target} PROPERTY ${prop})
|
||||
else()
|
||||
set(${var} "${default}")
|
||||
endif()
|
||||
unset(__is_set)
|
||||
endmacro()
|
||||
|
||||
function (target_embeds)
|
||||
set(expecting_name FALSE)
|
||||
set(target "")
|
||||
set(current_asset "")
|
||||
|
||||
foreach (word ${ARGV})
|
||||
|
||||
# First argument is target name
|
||||
if (target STREQUAL "")
|
||||
set(target "${word}")
|
||||
get_target_property_or(script_args "${target}" EMBED_ARGS "")
|
||||
get_target_property_or(embeds "${target}" EMBEDS "")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
if (current_asset STREQUAL "")
|
||||
# Beginning of asset declaration (1/2)
|
||||
set(current_asset "${word}")
|
||||
|
||||
elseif (expecting_name)
|
||||
# End of "asset AS asset_name"
|
||||
list(APPEND script_args "${current_asset};as;${word}")
|
||||
list(APPEND embeds ${current_asset})
|
||||
set(current_asset "")
|
||||
set(expecting_name FALSE)
|
||||
|
||||
elseif ("${word}" STREQUAL "AS")
|
||||
# Keyword AS in "asset AS asset_name"
|
||||
set(expecting_name TRUE)
|
||||
|
||||
else()
|
||||
# End of asset without AS, beginning of asset declaration (2/2)
|
||||
list(APPEND script_args "${current_asset};as;${current_asset}")
|
||||
list(APPEND embeds ${current_asset})
|
||||
set(current_asset "${word}")
|
||||
endif()
|
||||
|
||||
endforeach()
|
||||
|
||||
if (expecting_name)
|
||||
message(FATAL_ERROR "No name given for asset \"${current_asset}\"")
|
||||
endif()
|
||||
|
||||
if (NOT current_asset STREQUAL "")
|
||||
list(APPEND script_args "${current_asset};as;${current_asset}")
|
||||
endif()
|
||||
|
||||
set_target_properties("${target}" PROPERTIES EMBED_ARGS "${script_args}")
|
||||
set_target_properties("${target}" PROPERTIES EMBEDS "${embeds}")
|
||||
endfunction()
|
||||
|
||||
file(MAKE_DIRECTORY "${generated}/embedded_resources")
|
||||
|
||||
function(compile_embeds target)
|
||||
get_target_property(script_args "${target}" EMBED_ARGS)
|
||||
get_target_property(embeds "${target}" EMBEDS)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${generated}/embedded_resources/embedded_resources.cpp
|
||||
${generated}/embedded_resources/embedded_resources.h
|
||||
|
||||
COMMAND ${Python3_EXECUTABLE} ${tools}/embed/embed.py
|
||||
--cpp ${generated}/embedded_resources/embedded_resources.cpp
|
||||
--header ${generated}/embedded_resources/embedded_resources.h
|
||||
--
|
||||
${script_args}
|
||||
|
||||
DEPENDS ${embeds}
|
||||
${tools}/embed/embed.py
|
||||
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
COMMENT "Embedding assets"
|
||||
)
|
||||
endfunction()
|
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
usage = \
|
||||
'''Usage: embed.py --cpp OUT_CPP --header OUT_H [--] [INPUT as PATH]...
|
||||
'''Usage: %(me)s --cpp OUT_CPP --header OUT_H [--] [INPUT as PATH]...
|
||||
Generate C++ source code that includes binary contents of INPUT files.
|
||||
|
||||
Each file in INPUT is stored as a resource: a static array of unsigned char.
|
||||
@ -79,6 +79,7 @@ def main():
|
||||
fail(f"Unknown option '{arg}'")
|
||||
|
||||
elif considerOptions and (arg == '-h' or arg == '--help'):
|
||||
print(usage % {'me': os.path.basename(sys.argv[0])})
|
||||
sys.exit(0)
|
||||
|
||||
elif considerOptions and arg == '--':
|
||||
@ -237,8 +238,8 @@ namespace {
|
||||
|
||||
mid=\
|
||||
'''
|
||||
std::unordered_map<std::string,
|
||||
__embedded_resources::EmbeddedResource>
|
||||
const std::unordered_map<std::string,
|
||||
__embedded_resources::EmbeddedResource>
|
||||
EMBEDDED_RESOURCES =
|
||||
{
|
||||
''',
|
||||
|
@ -1,51 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
me="$(realpath "${BASH_SOURCE[0]}")"
|
||||
if [ "$(basename "$me")" = 'pre-commit' ]; then
|
||||
# i write good shell scripts - Javapony 2022-10-07
|
||||
root_dir="$(realpath "$(dirname "$me")/../../")"
|
||||
|
||||
hook_source="$root_dir/tools/git/hook_pre_commit.sh"
|
||||
if [ "$hook_source" -nt "$me" ]; then
|
||||
if [ -n "${ALREADY_UPDATED+x}" ]; then
|
||||
echo >&2 "git pre-commit hook: Attempted recursive hook update. `
|
||||
`Something is very wrong."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ''
|
||||
echo "===== tools/git/hook_pre_commit.sh updated; `
|
||||
`replacing pre-commit hook ====="
|
||||
echo ''
|
||||
|
||||
cp "$hook_source" "$me" &&
|
||||
chmod +x "$me" \
|
||||
|| fail 'Update failed'
|
||||
|
||||
ALREADY_UPDATED=true "$me"
|
||||
exit $?
|
||||
fi
|
||||
|
||||
source "$root_dir/tools/bashlib.sh"
|
||||
else
|
||||
rsrc="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
|
||||
source "$rsrc/../bashlib.sh"
|
||||
fi
|
||||
|
||||
unstaged_changes="`git diff --name-only`"
|
||||
if [ -n "$unstaged_changes" ]; then
|
||||
fail "Please stage all stash all unstaged changes in the following files:
|
||||
$unstaged_changes"
|
||||
fi
|
||||
|
||||
echo_and_run "$tools_dir/cppcheck/use-cppcheck.sh" \
|
||||
|| fail "Cppcheck has generated warnings, aborting commit"
|
||||
|
||||
echo_and_run "$tools_dir/clang-format/use-clang-format.sh" git \
|
||||
|| fail "clang-format has failed, aborting commit"
|
||||
|
||||
echo_and_run "$tools_dir/build.sh" --dont-generate \
|
||||
|| fail "Could not build project, aborting commit"
|
||||
|
||||
echo 'All checks passed'
|
||||
|
53
tools/glslc.cmake
Normal file
53
tools/glslc.cmake
Normal file
@ -0,0 +1,53 @@
|
||||
# glslc.cmake
|
||||
# Compiles GLSL shaders to SPV files
|
||||
|
||||
find_package(Vulkan COMPONENTS glslc REQUIRED)
|
||||
find_program(glslc_EXECUTABLE NAMES glslc HINTS Vulkan::glslc REQUIRED)
|
||||
|
||||
macro (get_target_property_or var target prop default)
|
||||
get_property(__is_set TARGET ${target} PROPERTY ${prop} SET)
|
||||
if (__is_set)
|
||||
get_property(${var} TARGET ${target} PROPERTY ${prop})
|
||||
else()
|
||||
set(${var} "${default}")
|
||||
endif()
|
||||
unset(__is_set)
|
||||
endmacro()
|
||||
|
||||
function (target_glsl_shaders)
|
||||
set(target "")
|
||||
|
||||
foreach (word ${ARGV})
|
||||
# First argument is target name
|
||||
if (target STREQUAL "")
|
||||
set(target ${word})
|
||||
get_target_property_or(glsl_shaders ${target} GLSL_SHADERS "")
|
||||
else()
|
||||
list(APPEND glsl_shaders ${word})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set_target_properties(${target} PROPERTIES GLSL_SHADERS "${glsl_shaders}")
|
||||
endfunction()
|
||||
|
||||
file(MAKE_DIRECTORY "${generated}/compiled_glsl_shaders")
|
||||
|
||||
function(compile_glsl target)
|
||||
get_target_property(glsl_shaders ${target} GLSL_SHADERS)
|
||||
|
||||
foreach (source_path ${glsl_shaders})
|
||||
get_filename_component(source_basename ${source_path} NAME)
|
||||
set(spv_path
|
||||
"${generated}/compiled_glsl_shaders/${source_basename}.spv")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${spv_path}
|
||||
DEPENDS ${source_path}
|
||||
COMMAND ${glslc_EXECUTABLE}
|
||||
-o ${spv_path}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${source_path}
|
||||
COMMENT "Compiling shader ${source_path}"
|
||||
)
|
||||
target_embeds(${target} ${spv_path} AS "${source_basename}.spv")
|
||||
endforeach()
|
||||
endfunction()
|
416
tools/pre-commit.py
Executable file
416
tools/pre-commit.py
Executable file
@ -0,0 +1,416 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
usage = \
|
||||
'''Usage: %(me)s run [OPTIONS...]
|
||||
or: %(me)s restore [OPTIONS...]
|
||||
or: %(me)s set-build-info CMAKE_EXECUTABLE CMAKE_BINARY_DIR
|
||||
In the 1st form, run standard pre-commit procedure for Progressia.
|
||||
In the 2nd form, attempt to restore workspace if the pre-commit hook failed.
|
||||
In the 3rd form, update cached build settings.
|
||||
|
||||
--dry-run do not change anything in git or in the filesystem;
|
||||
implies --verbose
|
||||
--verbose print commands and diagnostics
|
||||
--help display this help and exit
|
||||
--version display version information and exit
|
||||
|
||||
Currently, the pre-commit procedure performs the following:
|
||||
1. format staged changes
|
||||
2. attempt to compile with staged changes only
|
||||
|
||||
pre-commit-settings.json values:
|
||||
build-root CMake binary dir to use (filled in by CMake)
|
||||
parallelism threads to use, default is 1
|
||||
git git command, default is null
|
||||
cmake cmake command, default is null (filled in by CMake)
|
||||
clang-format-diff clang-format-diff command, default is null
|
||||
|
||||
Use semicolons to separate arguments in git, cmake and clang-format-diff'''
|
||||
|
||||
# Script version. Increment when script logic changes significantly.
|
||||
# Commit change separately.
|
||||
version = 1
|
||||
|
||||
# Source directories to format
|
||||
src_dirs = ['desktop', 'main']
|
||||
|
||||
# File extensions to format
|
||||
exts = ['cpp', 'h', 'inl']
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import shutil
|
||||
import json
|
||||
|
||||
|
||||
STASH_NAME = 'progressia_pre_commit_stash'
|
||||
# Paths are relative to this script's directory, tools/
|
||||
SETTINGS_PATH = 'pre-commit-settings.json'
|
||||
CLANG_TIDY_CHECK_MARKER = 'Clang-tidy is enabled. ' \
|
||||
'This is a marker for pre-commit.py'
|
||||
|
||||
|
||||
def fail(*args, code=1):
|
||||
"""Print an error message and exit with given code (default 1)"""
|
||||
print(my_name + ':', *args, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def verbose(*args):
|
||||
"""Print a message in verbose mode only."""
|
||||
if verbose_mode:
|
||||
print(my_name + ':', *args)
|
||||
|
||||
|
||||
def long_print_iter(title, it):
|
||||
"""Print contents of iterable titled as specified. If iterable is empty,
|
||||
print the string (nothing) instead.
|
||||
"""
|
||||
print(title + ':')
|
||||
|
||||
if len(it) > 0:
|
||||
print('\t' + '\n\t'.join(it) + '\n')
|
||||
else:
|
||||
print('\t(nothing)\n')
|
||||
|
||||
|
||||
def invoke(*cmd, result_when_dry=None, quiet=True, text=True, stdin=None):
|
||||
"""Execute given system command and return its stdout. If command fails,
|
||||
throw CalledProcessError.
|
||||
|
||||
When in verbose mode, log command before execution. If in dry-run mode and
|
||||
result_when_dry is not None, skip execution and return result_when_dry
|
||||
instead.
|
||||
|
||||
Keyword arguments:
|
||||
result_when_dry -- unless None (default), skip execution and return this
|
||||
quiet -- if False, print stdout (default True)
|
||||
text -- treat stdin and stdout as text rather than bytes (default False)
|
||||
stdin -- unless None (default), send this to stdin of spawned process
|
||||
"""
|
||||
verbose('command', *(repr(c) for c in cmd))
|
||||
|
||||
if dry_run and result_when_dry is not None:
|
||||
print(my_name + ': skipped: --dry-run')
|
||||
return result_when_dry
|
||||
|
||||
popen = subprocess.Popen(cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
text=text,
|
||||
universal_newlines=text,
|
||||
stdin=subprocess.PIPE if stdin else subprocess.DEVNULL)
|
||||
|
||||
stdout, _ = popen.communicate(input=stdin)
|
||||
|
||||
if text and not quiet:
|
||||
print(stdout, end='')
|
||||
|
||||
return_code = popen.wait()
|
||||
if return_code != 0:
|
||||
raise subprocess.CalledProcessError(return_code, cmd)
|
||||
|
||||
return stdout
|
||||
|
||||
|
||||
def get_file_sets():
|
||||
"""Return sets of indexed and unindexed files according to Git"""
|
||||
def git_z(*cmd):
|
||||
raw = invoke(*git, *cmd, '-z', text=False)
|
||||
return set(f.decode() for f in raw.split(b'\x00') if len(f) != 0)
|
||||
|
||||
indexed = git_z('diff', '--name-only', '--cached')
|
||||
unindexed = git_z('diff', '--name-only') | \
|
||||
git_z('ls-files', '--other', '--exclude-standard')
|
||||
|
||||
return indexed, unindexed
|
||||
|
||||
|
||||
def run_safety_checks(indexed, unindexed):
|
||||
if invoke(*git, 'stash', 'list', '--grep', f"\\b{STASH_NAME}$") != '':
|
||||
fail(f"Cannot run pre-commit checks: stash {STASH_NAME} exists. "
|
||||
f"Use `{my_name} restore` to restore workspace and repository "
|
||||
f"state")
|
||||
|
||||
both_changes = indexed & unindexed
|
||||
|
||||
if len(both_changes) != 0:
|
||||
fail(f"Cannot run pre-commit checks: files with indexed and unindexed "
|
||||
'changes exist:\n\n\t' +
|
||||
'\n\t'.join(both_changes) +
|
||||
'\n')
|
||||
|
||||
|
||||
def do_restore():
|
||||
"""Restore repository and filesystem. Fail if stash not found."""
|
||||
print('Redoing rolled back changes')
|
||||
|
||||
git_list = invoke(*git, 'stash', 'list', '--grep', f"\\b{STASH_NAME}$")
|
||||
|
||||
if len(git_list) == 0:
|
||||
if dry_run:
|
||||
stash_name = 'stash@{0}'
|
||||
else:
|
||||
fail(f"Cannot restore repository: stash {STASH_NAME} not found")
|
||||
else:
|
||||
stash_name, _, _ = git_list.partition(':')
|
||||
|
||||
invoke(*git, 'stash', 'pop', '--index', '--quiet', stash_name,
|
||||
result_when_dry='', quiet=False)
|
||||
|
||||
|
||||
def format_project():
|
||||
"""Format staged files with clang-format-diff."""
|
||||
diff = invoke(*git, 'diff', '-U0', '--no-color', '--relative', 'HEAD',
|
||||
*(f"{d}/*.{e}" for d in src_dirs for e in exts))
|
||||
|
||||
invoke(*clang_format_diff, '-p1', '-i', '--verbose',
|
||||
stdin=diff, result_when_dry='', quiet=False)
|
||||
|
||||
|
||||
def unformat_project(indexed_existing):
|
||||
"""Undo formatting changes introduced by format_project()."""
|
||||
print('Undoing formatting changes')
|
||||
|
||||
if len(indexed_existing) == 0:
|
||||
print('Nothing to do: all indexed changes are deletions')
|
||||
return
|
||||
|
||||
invoke(*git, 'restore', '--', *indexed_existing)
|
||||
|
||||
|
||||
def build_project():
|
||||
"""Build project with cmake."""
|
||||
print('Building project')
|
||||
build_log = invoke(*cmake,
|
||||
'--build', build_root,
|
||||
'--parallel', str(parallelism),
|
||||
result_when_dry=CLANG_TIDY_CHECK_MARKER,
|
||||
quiet=False)
|
||||
|
||||
if CLANG_TIDY_CHECK_MARKER not in build_log.splitlines():
|
||||
fail('Project build was successful, but clang-tidy did not run. '
|
||||
'Please make sure DEV_MODE is ON and regenerate CMake cache.')
|
||||
|
||||
print('Success')
|
||||
|
||||
|
||||
def pre_commit():
|
||||
"""Run pre-commit checks."""
|
||||
|
||||
if build_root is None:
|
||||
fail(f"build-root is not set in {SETTINGS_PATH}. Compile project "
|
||||
'manually to set this variable properly.')
|
||||
if not os.path.exists(build_root):
|
||||
fail(f"build-root {build_root} does not exist. Compile project "
|
||||
'manually to set this variable properly.')
|
||||
|
||||
cmakeCache = os.path.join(build_root, 'CMakeCache.txt')
|
||||
if not os.path.exists(cmakeCache):
|
||||
fail(f"{cmakeCache} does not exist. build-root is likely invalid. "
|
||||
'Compile project manually to set this variable properly.')
|
||||
|
||||
indexed, unindexed = get_file_sets()
|
||||
indexed_existing = [f for f in indexed if os.path.exists(f)]
|
||||
if verbose_mode:
|
||||
long_print_iter('Indexed changes', indexed)
|
||||
long_print_iter('Unindexed changes', unindexed)
|
||||
long_print_iter('Indexed changes without deletions', indexed_existing)
|
||||
|
||||
if len(indexed) == 0:
|
||||
fail('No indexed changes. You probably forgot to run `git add .`')
|
||||
|
||||
run_safety_checks(indexed, unindexed)
|
||||
|
||||
undo_formatting = False
|
||||
restore = False
|
||||
try:
|
||||
if len(unindexed) != 0:
|
||||
long_print_iter('Unindexed changes found in files', unindexed)
|
||||
print('These changes will be rolled back temporarily')
|
||||
|
||||
invoke(*git, 'stash', 'push',
|
||||
'--keep-index',
|
||||
'--include-untracked',
|
||||
'--message', STASH_NAME,
|
||||
result_when_dry='', quiet=False)
|
||||
restore = True
|
||||
|
||||
format_project()
|
||||
undo_formatting = True
|
||||
build_project()
|
||||
undo_formatting = False
|
||||
|
||||
finally:
|
||||
if undo_formatting:
|
||||
unformat_project(indexed_existing)
|
||||
if restore:
|
||||
do_restore()
|
||||
|
||||
print('Staging formatting changes')
|
||||
|
||||
if len(indexed_existing) == 0:
|
||||
print('Nothing to do: all indexed changes are deletions')
|
||||
else:
|
||||
invoke(*git, 'add', '--', *indexed_existing,
|
||||
result_when_dry='', quiet=False)
|
||||
|
||||
|
||||
def get_settings_path():
|
||||
return os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
SETTINGS_PATH))
|
||||
|
||||
|
||||
def save_settings():
|
||||
"""Save tools/pre-commit-settings.json."""
|
||||
path = get_settings_path()
|
||||
verbose(f"Saving settings into {path}")
|
||||
if not dry_run:
|
||||
with open(path, mode='w') as f:
|
||||
json.dump(settings, f, indent=4)
|
||||
else:
|
||||
verbose(' skipped: --dry-run')
|
||||
|
||||
|
||||
def set_build_info():
|
||||
"""Set build info in tools/pre-commit-settings.json."""
|
||||
settings['build_root'] = arg_build_root
|
||||
settings['cmake'] = arg_cmake_executable
|
||||
save_settings()
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""Parse sys.argv and environment variables; set corresponding globals.
|
||||
Return (action, arguments for set-build-root).
|
||||
"""
|
||||
global action
|
||||
global verbose_mode
|
||||
global dry_run
|
||||
global allow_update
|
||||
|
||||
consider_options = True
|
||||
action = None
|
||||
arg_cmake_executable = None
|
||||
arg_build_root = None
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
if arg == 'restore' or arg == 'set-build-info' or arg == 'run':
|
||||
if action is not None:
|
||||
fail(f"Cannot use '{arg}' and '{action}' together")
|
||||
action = arg
|
||||
elif consider_options and arg.startswith('-'):
|
||||
if arg == '-h' or arg == '--help' or arg == 'help' or arg == '/?':
|
||||
print(usage % {'me': my_name})
|
||||
sys.exit(0)
|
||||
elif arg == '--version':
|
||||
print(f"Progressia pre-commit script, version {version}")
|
||||
sys.exit(0)
|
||||
elif arg == '--verbose':
|
||||
verbose_mode = True
|
||||
elif arg == '--dry-run':
|
||||
dry_run = True
|
||||
verbose_mode = True
|
||||
elif arg == '--':
|
||||
consider_options = False
|
||||
else:
|
||||
fail(f"Unknown option '{arg}'")
|
||||
elif action == 'set-build-info' and arg_cmake_executable is None:
|
||||
arg_cmake_executable = arg
|
||||
elif action == 'set-build-info' and arg_build_root is None:
|
||||
arg_build_root = arg
|
||||
else:
|
||||
fail(f"Unknown or unexpected argument '{arg}'")
|
||||
|
||||
if action is None:
|
||||
fail('No action specified')
|
||||
|
||||
if action == 'set-build-info' and arg_cmake_executable is None:
|
||||
fail('No CMake executable given')
|
||||
|
||||
if action == 'set-build-info' and arg_build_root is None:
|
||||
fail('No build root given')
|
||||
|
||||
return action, arg_build_root, arg_cmake_executable
|
||||
|
||||
|
||||
def load_settings():
|
||||
"""Ensure pre-commit-settings.json exists and is loaded into memory."""
|
||||
global settings
|
||||
|
||||
path = get_settings_path()
|
||||
if os.path.exists(path):
|
||||
with open(path, mode='r') as f:
|
||||
settings = json.load(f)
|
||||
else:
|
||||
verbose(f"{path} not found, using defaults")
|
||||
settings = {
|
||||
"__comment": "See `pre-commit.py --help` for documentation",
|
||||
"build_root": None,
|
||||
"git": None,
|
||||
"cmake": None,
|
||||
"clang_format_diff": None,
|
||||
"parallelism": 1
|
||||
}
|
||||
save_settings()
|
||||
|
||||
def parse_settings():
|
||||
"""Load values from settings and check their validity."""
|
||||
global settings
|
||||
global build_root
|
||||
global git
|
||||
global cmake
|
||||
global clang_format_diff
|
||||
global parallelism
|
||||
|
||||
build_root = settings['build_root']
|
||||
parallelism = settings['parallelism']
|
||||
|
||||
def find_command(hints, settings_name):
|
||||
if settings[settings_name] is not None:
|
||||
hints = [settings[settings_name]]
|
||||
|
||||
cmds = (hint.split(';') for hint in hints)
|
||||
res = next((cmd for cmd in cmds if shutil.which(cmd[0])), None) \
|
||||
or fail(f"Command {hints[0]} not found. Set {settings_name} " +
|
||||
f"in {path} or check PATH")
|
||||
|
||||
verbose(f"Found command {hints[0]}:", *(repr(c) for c in res))
|
||||
return res
|
||||
|
||||
git = find_command(['git'], 'git')
|
||||
cmake = find_command(['cmake'], 'cmake')
|
||||
clang_format_diff = find_command(['clang-format-diff-13',
|
||||
'clang-format-diff',
|
||||
'clang-format-diff.py'],
|
||||
'clang_format_diff')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
my_name = os.path.basename(sys.argv[0])
|
||||
verbose_mode = False
|
||||
dry_run = False
|
||||
allow_update = True
|
||||
|
||||
action, arg_build_root, arg_cmake_executable = parse_args()
|
||||
load_settings()
|
||||
|
||||
if dry_run:
|
||||
print('Running in dry mode: no changes to filesystem or git will '
|
||||
'actually be performed')
|
||||
|
||||
try:
|
||||
if action == 'set-build-info':
|
||||
set_build_info()
|
||||
elif action == 'restore':
|
||||
parse_settings()
|
||||
do_restore()
|
||||
indexed, unindexed = get_file_sets()
|
||||
if indexed & unindexed:
|
||||
unformat_project(indexed)
|
||||
else:
|
||||
parse_settings()
|
||||
pre_commit()
|
||||
except subprocess.CalledProcessError as e:
|
||||
fail('Command', *(repr(c) for c in e.cmd),
|
||||
f"exited with code {e.returncode}")
|
146
tools/setup.sh
146
tools/setup.sh
@ -1,146 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
usage=\
|
||||
"Usage: setup.sh [--for-development]
|
||||
Set up the development environment after \`git clone\`
|
||||
|
||||
Options:
|
||||
--for-development perform additional setup only necessary for developers
|
||||
|
||||
-h, --help display this help and exit"
|
||||
|
||||
rsrc="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
|
||||
source "$rsrc/bashlib.sh" || {
|
||||
echo >&2 'Could not load bashlib'
|
||||
exit 1
|
||||
}
|
||||
cd "$root_dir"
|
||||
|
||||
|
||||
|
||||
# Parse arguments
|
||||
|
||||
for_development=''
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-h | --help )
|
||||
echo "$usage"
|
||||
exit
|
||||
;;
|
||||
--for-development )
|
||||
for_development=true
|
||||
;;
|
||||
* )
|
||||
fail "Unknown option '$arg'"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
|
||||
# Сreate private.sh
|
||||
|
||||
if [ ! -e "$private_sh" ]; then
|
||||
echo '#!/bin/bash
|
||||
|
||||
# This file is ignored by git. Use it to configure shell scripts in tools/
|
||||
# for your development environment.
|
||||
|
||||
PARALLELISM=1
|
||||
#PATH="$PATH:/opt/whatever"
|
||||
' >"$private_sh" &&
|
||||
chmod +x "$private_sh" ||
|
||||
fail "tools/private.sh was not found; could not create it"
|
||||
|
||||
echo "Created tools/private.sh"
|
||||
else
|
||||
echo "Found and loaded private.sh"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Check available commands
|
||||
|
||||
failed=()
|
||||
|
||||
function check_cmd() {
|
||||
if FAIL_SILENTLY=true find_cmd found "$@"; then
|
||||
echo "Found command $found"
|
||||
else
|
||||
failed+=("command $1")
|
||||
echo "Could not find command $1"
|
||||
fi
|
||||
unset found
|
||||
}
|
||||
|
||||
check_cmd cmake
|
||||
check_cmd python3
|
||||
check_cmd glslc
|
||||
|
||||
if [ $for_development ]; then
|
||||
check_cmd git
|
||||
check_cmd cppcheck
|
||||
check_cmd clang-format-13 clang-format
|
||||
check_cmd clang-format-diff-13 clang-format-diff clang-format-diff.py
|
||||
check_cmd valgrind
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Try generating build files
|
||||
|
||||
if FAIL_SILENTLY=true find_cmd CMAKE cmake; then
|
||||
if CMAKE="$CMAKE" "$tools_dir/build.sh" --dont-build; then
|
||||
echo 'CMake did not encounter any problems'
|
||||
else
|
||||
echo 'Could not generate build files; libraries are probably missing'
|
||||
failed+=('some libraries, probably (see CMake messages for details)')
|
||||
fi
|
||||
else
|
||||
echo 'Skipping CMake test because cmake was not found'
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Display accumulated errors
|
||||
|
||||
[ ${#failed[@]} -ne 0 ] &&
|
||||
fail "Could not find the following required commands or libraries:
|
||||
|
||||
`for f in "${failed[@]}"; do echo " $f"; done`
|
||||
|
||||
You can resolve these errors in the following ways:
|
||||
1. Install required software packages. See README for specific instructions.
|
||||
2. Edit PATH or CMAKE_MODULE_PATH environment variables in tools/private.sh
|
||||
to include your installation directories.
|
||||
"
|
||||
|
||||
|
||||
|
||||
# Set executable flags
|
||||
|
||||
chmod -v +x tools/build.sh \
|
||||
tools/embed/embed.py \
|
||||
|| fail 'Could not make scripts executable'
|
||||
|
||||
if [ $for_development ]; then
|
||||
chmod -v +x tools/clang-format/use-clang-format.sh \
|
||||
tools/cppcheck/use-cppcheck.sh \
|
||||
|| fail 'Could not make developer scripts executable'
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Set git hook
|
||||
|
||||
if [ $for_development ]; then
|
||||
mkdir -vp .git/hooks &&
|
||||
cp -v tools/git/hook_pre_commit.sh .git/hooks/pre-commit &&
|
||||
chmod -v +x .git/hooks/pre-commit \
|
||||
|| fail 'Could not setup git pre-commit hook'
|
||||
fi
|
||||
|
||||
|
||||
|
||||
echo 'Setup complete'
|
Loading…
x
Reference in New Issue
Block a user