mirror of
https://gitea.windcorp.ru/Wind-Corporation/Progressia.git
synced 2025-04-21 07:50: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 directory
|
||||||
run
|
run
|
||||||
|
|
||||||
# Local environment setup file
|
# Local settings for pre-commit.py
|
||||||
tools/private.sh
|
tools/pre-commit-settings.json
|
||||||
|
|
||||||
# Prevent anyone from accidentally uploading CMakeFiles
|
# Prevent anyone from accidentally uploading CMakeFiles
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
|
|
||||||
|
# Visual Studio garbage
|
||||||
|
.vs
|
||||||
|
CMakeSettings.json
|
||||||
|
out
|
||||||
|
|
||||||
# Some weirdos use Kate
|
# Some weirdos use Kate
|
||||||
*.kate-swp
|
*.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)
|
project(progressia)
|
||||||
|
set(VERSION "0.0.1")
|
||||||
|
add_executable(progressia)
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/tools/cmake")
|
# Options
|
||||||
include(embed)
|
|
||||||
|
|
||||||
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/main.cpp
|
||||||
desktop/graphics/glfw_mgmt.cpp
|
desktop/graphics/glfw_mgmt.cpp
|
||||||
desktop/graphics/vulkan_common.cpp
|
desktop/graphics/vulkan_common.cpp
|
||||||
@ -19,6 +45,7 @@ add_executable(progressia
|
|||||||
desktop/graphics/vulkan_texture_descriptors.cpp
|
desktop/graphics/vulkan_texture_descriptors.cpp
|
||||||
desktop/graphics/vulkan_adapter.cpp
|
desktop/graphics/vulkan_adapter.cpp
|
||||||
desktop/graphics/vulkan_swap_chain.cpp
|
desktop/graphics/vulkan_swap_chain.cpp
|
||||||
|
desktop/graphics/vulkan_physical_device.cpp
|
||||||
|
|
||||||
main/game.cpp
|
main/game.cpp
|
||||||
main/logging.cpp
|
main/logging.cpp
|
||||||
@ -26,34 +53,78 @@ add_executable(progressia
|
|||||||
main/rendering/image.cpp
|
main/rendering/image.cpp
|
||||||
|
|
||||||
main/stb_image.c
|
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
|
target_embeds(progressia
|
||||||
if (WIN32)
|
assets/texture.png
|
||||||
set_target_properties(progressia PROPERTIES WIN32_EXECUTABLE true)
|
assets/texture2.png)
|
||||||
target_link_options(progressia PRIVATE -static-libstdc++ -static-libgcc)
|
|
||||||
endif()
|
compile_glsl(progressia)
|
||||||
|
compile_embeds(progressia)
|
||||||
|
target_include_directories(progressia PRIVATE ${generated}/embedded_resources)
|
||||||
|
|
||||||
# Compilation settings
|
# Compilation settings
|
||||||
set_property(TARGET progressia PROPERTY CXX_STANDARD 17)
|
|
||||||
target_compile_options(progressia PRIVATE -Wall -Wextra -Wpedantic -Werror)
|
|
||||||
|
|
||||||
# Version information
|
set_property(TARGET progressia PROPERTY CXX_STANDARD 17)
|
||||||
if (NOT DEFINED BUILD_ID)
|
set_property(TARGET progressia PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||||
set(BUILD_ID "dev")
|
|
||||||
|
# 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()
|
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
|
if (compiler_cl_dialect STREQUAL "GCC")
|
||||||
option(VULKAN_ERROR_CHECKING "Enable Vulkan validation layers to detect Vulkan API usage errors at runtime")
|
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
|
# Pass configuration options
|
||||||
|
file(MAKE_DIRECTORY "${generated}/config")
|
||||||
configure_file(${PROJECT_SOURCE_DIR}/main/config.h.in
|
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
|
# Libraries
|
||||||
|
|
||||||
@ -72,7 +143,3 @@ target_link_libraries(progressia glm::glm)
|
|||||||
# Use STB
|
# Use STB
|
||||||
target_include_directories(progressia PUBLIC
|
target_include_directories(progressia PUBLIC
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/lib/stb/include)
|
${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
|
- [STB (GitHub)](https://github.com/nothings/stb) – collection of various
|
||||||
algorithms
|
algorithms
|
||||||
- `stb_image` – PNG loading
|
- `stb_image` – PNG loading
|
||||||
- [Boost](https://www.boost.org/) – utility library
|
|
||||||
|
@ -8,59 +8,95 @@
|
|||||||
|
|
||||||
#include "../../main/logging.h"
|
#include "../../main/logging.h"
|
||||||
#include "../../main/meta.h"
|
#include "../../main/meta.h"
|
||||||
#include "vulkan_mgmt.h"
|
#include "../../main/util.h"
|
||||||
|
|
||||||
using namespace progressia::main::logging;
|
using namespace progressia::main::logging;
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
static GLFWwindow *window = nullptr;
|
|
||||||
|
|
||||||
static void onGlfwError(int errorCode, const char *description);
|
static void onGlfwError(int errorCode, const char *description);
|
||||||
static void onWindowGeometryChange(GLFWwindow *window, int width, int height);
|
static void onWindowGeometryChange(GLFWwindow *window, int width, int height);
|
||||||
|
|
||||||
void initializeGlfw() {
|
class GlfwManagerImpl : public GlfwManager {
|
||||||
debug("Beginning GLFW init");
|
private:
|
||||||
|
GLFWwindow *window = nullptr;
|
||||||
|
std::function<void()> onScreenResize = nullptr;
|
||||||
|
|
||||||
glfwSetErrorCallback(onGlfwError);
|
public:
|
||||||
|
DISABLE_COPYING(GlfwManagerImpl)
|
||||||
|
DISABLE_MOVING(GlfwManagerImpl)
|
||||||
|
|
||||||
if (!glfwInit()) {
|
GlfwManagerImpl() {
|
||||||
fatal("glfwInit() failed");
|
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
|
// REPORT_ERROR
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
std::shared_ptr<GlfwManagerImpl> aGlfwManager =
|
||||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
std::make_shared<GlfwManagerImpl>();
|
||||||
|
|
||||||
std::string title;
|
theGlfwManager = aGlfwManager;
|
||||||
|
return aGlfwManager;
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
void onGlfwError(int errorCode, const char *description) {
|
||||||
fatal() << "[GLFW] " << description << " (" << errorCode << ")";
|
fatal() << "[GLFW] " << description << " (" << errorCode << ")";
|
||||||
// REPORT_ERROR
|
// REPORT_ERROR
|
||||||
@ -69,14 +105,25 @@ void onGlfwError(int errorCode, const char *description) {
|
|||||||
|
|
||||||
void onWindowGeometryChange(GLFWwindow *window, [[maybe_unused]] int width,
|
void onWindowGeometryChange(GLFWwindow *window, [[maybe_unused]] int width,
|
||||||
[[maybe_unused]] int height) {
|
[[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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
resizeVulkanSurface();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLFWwindow *getGLFWWindowHandle() { return window; }
|
GLFWwindow *getGLFWWindowHandle() {
|
||||||
|
if (auto manager = theGlfwManager.lock()) {
|
||||||
|
return manager->window;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace desktop
|
return nullptr;
|
||||||
} // namespace progressia
|
}
|
||||||
|
|
||||||
|
} // namespace progressia::desktop
|
||||||
|
@ -1,13 +1,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace progressia {
|
#include <functional>
|
||||||
namespace desktop {
|
#include <memory>
|
||||||
|
|
||||||
void initializeGlfw();
|
namespace progressia::desktop {
|
||||||
void showWindow();
|
|
||||||
void shutdownGlfw();
|
|
||||||
bool shouldRun();
|
|
||||||
void doGlfwRoutine();
|
|
||||||
|
|
||||||
} // namespace desktop
|
class GlfwManager {
|
||||||
} // namespace progressia
|
|
||||||
|
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
|
#define GLFW_INCLUDE_VULKAN
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
|
// TODO refactor into OOP
|
||||||
GLFWwindow *getGLFWWindowHandle();
|
GLFWwindow *getGLFWWindowHandle();
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -25,8 +26,7 @@
|
|||||||
|
|
||||||
#include <embedded_resources.h>
|
#include <embedded_resources.h>
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
using progressia::main::Vertex;
|
using progressia::main::Vertex;
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ auto getVertexFieldProperties() {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::vector<char> tmp_readFile(const std::string &path) {
|
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) {
|
if (resource.data == nullptr) {
|
||||||
// REPORT_ERROR
|
// REPORT_ERROR
|
||||||
@ -59,7 +59,7 @@ std::vector<char> tmp_readFile(const std::string &path) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::vector<char>(resource.data, resource.data + resource.length);
|
return {resource.data, resource.data + resource.length};
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -82,25 +82,26 @@ Adapter::Adapter(Vulkan &vulkan)
|
|||||||
VK_ATTACHMENT_LOAD_OP_CLEAR,
|
VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||||
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||||
|
|
||||||
{1.0f, 0},
|
{1.0F, 0},
|
||||||
|
|
||||||
nullptr});
|
nullptr});
|
||||||
}
|
}
|
||||||
|
|
||||||
Adapter::~Adapter() {
|
Adapter::~Adapter() = default;
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Attachment> &Adapter::getAttachments() { return attachments; }
|
std::vector<Attachment> &Adapter::getAttachments() { return attachments; }
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||||
std::vector<char> Adapter::loadVertexShader() {
|
std::vector<char> Adapter::loadVertexShader() {
|
||||||
return tmp_readFile("shader.vert.spv");
|
return tmp_readFile("shader.vert.spv");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||||
std::vector<char> Adapter::loadFragmentShader() {
|
std::vector<char> Adapter::loadFragmentShader() {
|
||||||
return tmp_readFile("shader.frag.spv");
|
return tmp_readFile("shader.frag.spv");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||||
VkVertexInputBindingDescription Adapter::getVertexInputBindingDescription() {
|
VkVertexInputBindingDescription Adapter::getVertexInputBindingDescription() {
|
||||||
VkVertexInputBindingDescription bindingDescription{};
|
VkVertexInputBindingDescription bindingDescription{};
|
||||||
bindingDescription.binding = 0;
|
bindingDescription.binding = 0;
|
||||||
@ -111,6 +112,7 @@ VkVertexInputBindingDescription Adapter::getVertexInputBindingDescription() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<VkVertexInputAttributeDescription>
|
std::vector<VkVertexInputAttributeDescription>
|
||||||
|
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||||
Adapter::getVertexInputAttributeDescriptions() {
|
Adapter::getVertexInputAttributeDescriptions() {
|
||||||
std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
|
std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
|
||||||
|
|
||||||
@ -151,8 +153,8 @@ void Adapter::onPreFrame() {
|
|||||||
* graphics_interface implementation
|
* graphics_interface implementation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
namespace main {
|
namespace progressia::main {
|
||||||
|
|
||||||
using namespace progressia::desktop;
|
using namespace progressia::desktop;
|
||||||
|
|
||||||
@ -163,122 +165,125 @@ struct DrawRequest {
|
|||||||
glm::mat4 modelTransform;
|
glm::mat4 modelTransform;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE: TODO
|
||||||
std::vector<DrawRequest> pendingDrawCommands;
|
std::vector<DrawRequest> pendingDrawCommands;
|
||||||
|
constexpr std::size_t PENDING_DRAW_COMMANDS_MAX_SIZE = 100000;
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE: TODO
|
||||||
glm::mat4 currentModelTransform;
|
glm::mat4 currentModelTransform;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
progressia::main::Texture::Texture(Backend backend) : backend(backend) {}
|
struct progressia::main::Texture::Backend {
|
||||||
|
progressia::desktop::Texture texture;
|
||||||
|
};
|
||||||
|
|
||||||
progressia::main::Texture::~Texture() {
|
progressia::main::Texture::Texture(std::unique_ptr<Backend> backend)
|
||||||
delete static_cast<progressia::desktop::Texture *>(this->backend);
|
: backend(std::move(backend)) {}
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
progressia::main::Texture::~Texture() = default;
|
||||||
struct PrimitiveBackend {
|
|
||||||
|
|
||||||
|
struct Primitive::Backend {
|
||||||
IndexedBuffer<Vertex> buf;
|
IndexedBuffer<Vertex> buf;
|
||||||
progressia::main::Texture *tex;
|
progressia::main::Texture *tex;
|
||||||
};
|
};
|
||||||
} // namespace
|
|
||||||
|
|
||||||
Primitive::Primitive(Backend backend) : backend(backend) {}
|
Primitive::Primitive(std::unique_ptr<Backend> backend)
|
||||||
|
: backend(std::move(backend)) {}
|
||||||
|
|
||||||
Primitive::~Primitive() {
|
Primitive::~Primitive() = default;
|
||||||
delete static_cast<PrimitiveBackend *>(this->backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Primitive::draw() {
|
void Primitive::draw() {
|
||||||
auto backend = static_cast<PrimitiveBackend *>(this->backend);
|
if (pendingDrawCommands.size() > PENDING_DRAW_COMMANDS_MAX_SIZE) {
|
||||||
|
|
||||||
if (pendingDrawCommands.size() > 100000) {
|
|
||||||
backend->buf.getVulkan().getGint().flush();
|
backend->buf.getVulkan().getGint().flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingDrawCommands.push_back(
|
pendingDrawCommands.push_back({&backend->tex->backend->texture,
|
||||||
{static_cast<progressia::desktop::Texture *>(backend->tex->backend),
|
&backend->buf, currentModelTransform});
|
||||||
&backend->buf, currentModelTransform});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const progressia::main::Texture *Primitive::getTexture() const {
|
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() {
|
View::View(std::unique_ptr<Backend> backend) : backend(std::move(backend)) {}
|
||||||
delete static_cast<Adapter::ViewUniform::State *>(this->backend);
|
|
||||||
}
|
View::~View() = default;
|
||||||
|
|
||||||
void View::configure(const glm::mat4 &proj, const glm::mat4 &view) {
|
void View::configure(const glm::mat4 &proj, const glm::mat4 &view) {
|
||||||
|
backend->state.update(proj, view);
|
||||||
static_cast<Adapter::ViewUniform::State *>(this->backend)
|
|
||||||
->update(proj, view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void View::use() {
|
void View::use() {
|
||||||
auto backend = static_cast<Adapter::ViewUniform::State *>(this->backend);
|
backend->state.uniform->getVulkan().getGint().flush();
|
||||||
backend->uniform->getVulkan().getGint().flush();
|
backend->state.bind();
|
||||||
backend->bind();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Light::Light(Backend backend) : backend(backend) {}
|
struct Light::Backend {
|
||||||
|
Adapter::LightUniform::State state;
|
||||||
|
};
|
||||||
|
|
||||||
Light::~Light() {
|
Light::Light(std::unique_ptr<Backend> backend) : backend(std::move(backend)) {}
|
||||||
delete static_cast<Adapter::LightUniform::State *>(this->backend);
|
Light::~Light() = default;
|
||||||
}
|
|
||||||
|
|
||||||
void Light::configure(const glm::vec3 &color, const glm::vec3 &from,
|
void Light::configure(const glm::vec3 &color, const glm::vec3 &from,
|
||||||
float contrast, float softness) {
|
float contrast, float softness) {
|
||||||
|
|
||||||
static_cast<Adapter::LightUniform::State *>(this->backend)
|
backend->state.update(Adapter::Light{glm::vec4(color, 1.0F),
|
||||||
->update(Adapter::Light{glm::vec4(color, 1.0f),
|
glm::vec4(glm::normalize(from), 1.0F),
|
||||||
glm::vec4(glm::normalize(from), 1.0f), contrast,
|
contrast, softness});
|
||||||
softness});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Light::use() {
|
void Light::use() {
|
||||||
auto backend = static_cast<Adapter::LightUniform::State *>(this->backend);
|
backend->state.uniform->getVulkan().getGint().flush();
|
||||||
backend->uniform->getVulkan().getGint().flush();
|
backend->state.bind();
|
||||||
backend->bind();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphicsInterface::GraphicsInterface(Backend backend) : backend(backend) {}
|
GraphicsInterface::GraphicsInterface(Backend backend) : backend(backend) {}
|
||||||
|
|
||||||
GraphicsInterface::~GraphicsInterface() {
|
GraphicsInterface::~GraphicsInterface() = default;
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
progressia::main::Texture *
|
std::unique_ptr<progressia::main::Texture>
|
||||||
GraphicsInterface::newTexture(const progressia::main::Image &src) {
|
GraphicsInterface::newTexture(const progressia::main::Image &src) {
|
||||||
auto backend = new progressia::desktop::Texture(
|
using Backend = progressia::main::Texture::Backend;
|
||||||
src, *static_cast<Vulkan *>(this->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,
|
GraphicsInterface::newPrimitive(const std::vector<Vertex> &vertices,
|
||||||
const std::vector<Vertex::Index> &indices,
|
const std::vector<Vertex::Index> &indices,
|
||||||
progressia::main::Texture *texture) {
|
progressia::main::Texture *texture) {
|
||||||
|
|
||||||
auto backend = new PrimitiveBackend{
|
auto primitive = std::make_unique<Primitive>(
|
||||||
IndexedBuffer<Vertex>(vertices.size(), indices.size(),
|
std::unique_ptr<Primitive::Backend>(new Primitive::Backend{
|
||||||
*static_cast<Vulkan *>(this->backend)),
|
IndexedBuffer<Vertex>(vertices.size(), indices.size(),
|
||||||
texture};
|
*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() {
|
std::unique_ptr<View> GraphicsInterface::newView() {
|
||||||
return new View(new Adapter::ViewUniform::State(
|
return std::make_unique<View>(std::unique_ptr<View::Backend>(
|
||||||
static_cast<Vulkan *>(this->backend)->getAdapter().createView()));
|
new View::Backend{Adapter::ViewUniform::State(
|
||||||
|
static_cast<Vulkan *>(this->backend)->getAdapter().createView())}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Light *GraphicsInterface::newLight() {
|
std::unique_ptr<Light> GraphicsInterface::newLight() {
|
||||||
return new Light(new Adapter::LightUniform::State(
|
return std::make_unique<Light>(
|
||||||
static_cast<Vulkan *>(this->backend)->getAdapter().createLight()));
|
std::unique_ptr<Light::Backend>(new Light::Backend{
|
||||||
|
Adapter::LightUniform::State(static_cast<Vulkan *>(this->backend)
|
||||||
|
->getAdapter()
|
||||||
|
.createLight())}));
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec2 GraphicsInterface::getViewport() const {
|
glm::vec2 GraphicsInterface::getViewport() const {
|
||||||
@ -287,16 +292,17 @@ glm::vec2 GraphicsInterface::getViewport() const {
|
|||||||
return {extent.width, extent.height};
|
return {extent.width, extent.height};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||||
void GraphicsInterface::setModelTransform(const glm::mat4 &m) {
|
void GraphicsInterface::setModelTransform(const glm::mat4 &m) {
|
||||||
currentModelTransform = m;
|
currentModelTransform = m;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsInterface::flush() {
|
void GraphicsInterface::flush() {
|
||||||
|
|
||||||
auto commandBuffer = static_cast<Vulkan *>(this->backend)
|
auto *commandBuffer = static_cast<Vulkan *>(this->backend)
|
||||||
->getCurrentFrame()
|
->getCurrentFrame()
|
||||||
->getCommandBuffer();
|
->getCommandBuffer();
|
||||||
auto pipelineLayout =
|
auto *pipelineLayout =
|
||||||
static_cast<Vulkan *>(this->backend)->getPipeline().getLayout();
|
static_cast<Vulkan *>(this->backend)->getPipeline().getLayout();
|
||||||
|
|
||||||
progressia::desktop::Texture *lastTexture = nullptr;
|
progressia::desktop::Texture *lastTexture = nullptr;
|
||||||
@ -328,11 +334,11 @@ void GraphicsInterface::flush() {
|
|||||||
pendingDrawCommands.clear();
|
pendingDrawCommands.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE: TODO
|
||||||
float GraphicsInterface::tmp_getTime() { return glfwGetTime(); }
|
float GraphicsInterface::tmp_getTime() { return glfwGetTime(); }
|
||||||
|
|
||||||
uint64_t GraphicsInterface::getLastStartedFrame() {
|
uint64_t GraphicsInterface::getLastStartedFrame() {
|
||||||
return static_cast<Vulkan *>(this->backend)->getLastStartedFrame();
|
return static_cast<Vulkan *>(this->backend)->getLastStartedFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace main
|
} // namespace progressia::main
|
||||||
} // namespace progressia
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "boost/core/noncopyable.hpp"
|
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
#include "vulkan_descriptor_set.h"
|
#include "vulkan_descriptor_set.h"
|
||||||
#include "vulkan_image.h"
|
#include "vulkan_image.h"
|
||||||
#include "vulkan_uniform.h"
|
#include "vulkan_uniform.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
class Attachment {
|
class Attachment {
|
||||||
public:
|
public:
|
||||||
@ -68,5 +66,4 @@ class Adapter : public VkObjectWrapper {
|
|||||||
void onPreFrame();
|
void onPreFrame();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/core/noncopyable.hpp>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A single buffer with a chunk of allocated memory.
|
* A single buffer with a chunk of allocated memory.
|
||||||
@ -192,5 +190,4 @@ class IndexedBufferBase : public VkObjectWrapper {
|
|||||||
template <typename Vertex>
|
template <typename Vertex>
|
||||||
using IndexedBuffer = IndexedBufferBase<Vertex, uint16_t, VK_INDEX_TYPE_UINT16>;
|
using IndexedBuffer = IndexedBufferBase<Vertex, uint16_t, VK_INDEX_TYPE_UINT16>;
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
|
|
||||||
#include "../config.h"
|
|
||||||
#include "vulkan_adapter.h"
|
#include "vulkan_adapter.h"
|
||||||
#include "vulkan_frame.h"
|
#include "vulkan_frame.h"
|
||||||
|
#include "vulkan_physical_device.h"
|
||||||
#include "vulkan_pick_device.h"
|
#include "vulkan_pick_device.h"
|
||||||
#include "vulkan_pipeline.h"
|
#include "vulkan_pipeline.h"
|
||||||
#include "vulkan_render_pass.h"
|
#include "vulkan_render_pass.h"
|
||||||
@ -15,8 +15,7 @@
|
|||||||
|
|
||||||
using namespace progressia::main::logging;
|
using namespace progressia::main::logging;
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Vulkan
|
* Vulkan
|
||||||
@ -27,7 +26,7 @@ Vulkan::Vulkan(std::vector<const char *> instanceExtensions,
|
|||||||
std::vector<const char *> validationLayers)
|
std::vector<const char *> validationLayers)
|
||||||
:
|
:
|
||||||
|
|
||||||
frames(MAX_FRAMES_IN_FLIGHT), isRenderingFrame(false),
|
frames(MAX_FRAMES_IN_FLIGHT), currentFrame(0), isRenderingFrame(false),
|
||||||
lastStartedFrame(0) {
|
lastStartedFrame(0) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -58,7 +57,7 @@ Vulkan::Vulkan(std::vector<const char *> instanceExtensions,
|
|||||||
|
|
||||||
// Enable extensions
|
// Enable extensions
|
||||||
{
|
{
|
||||||
uint32_t extensionCount;
|
uint32_t extensionCount = 0;
|
||||||
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount,
|
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount,
|
||||||
nullptr);
|
nullptr);
|
||||||
std::vector<VkExtensionProperties> available(extensionCount);
|
std::vector<VkExtensionProperties> available(extensionCount);
|
||||||
@ -89,7 +88,7 @@ Vulkan::Vulkan(std::vector<const char *> instanceExtensions,
|
|||||||
|
|
||||||
// Enable validation layers
|
// Enable validation layers
|
||||||
{
|
{
|
||||||
uint32_t layerCount;
|
uint32_t layerCount = 0;
|
||||||
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
||||||
std::vector<VkLayerProperties> available(layerCount);
|
std::vector<VkLayerProperties> available(layerCount);
|
||||||
vkEnumerateInstanceLayerProperties(&layerCount, available.data());
|
vkEnumerateInstanceLayerProperties(&layerCount, available.data());
|
||||||
@ -150,31 +149,25 @@ Vulkan::Vulkan(std::vector<const char *> instanceExtensions,
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<VkPhysicalDevice> devices(deviceCount);
|
std::vector<VkPhysicalDevice> vkDevices(deviceCount);
|
||||||
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
|
vkEnumeratePhysicalDevices(instance, &deviceCount, vkDevices.data());
|
||||||
|
|
||||||
std::vector<PhysicalDeviceData> choices;
|
std::vector<PhysicalDevice> choices;
|
||||||
|
choices.reserve(deviceCount);
|
||||||
for (const auto &device : devices) {
|
for (const auto &vkDevice : vkDevices) {
|
||||||
PhysicalDeviceData data = {};
|
choices.emplace_back(PhysicalDevice(vkDevice));
|
||||||
data.device = device;
|
|
||||||
|
|
||||||
vkGetPhysicalDeviceProperties(device, &data.properties);
|
|
||||||
vkGetPhysicalDeviceFeatures(device, &data.features);
|
|
||||||
|
|
||||||
choices.push_back(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &result =
|
const auto &result =
|
||||||
pickPhysicalDevice(choices, *this, deviceExtensions);
|
pickPhysicalDevice(choices, *this, deviceExtensions);
|
||||||
physicalDevice = result.device;
|
physicalDevice = std::make_unique<PhysicalDevice>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setup queues
|
* Setup queues
|
||||||
*/
|
*/
|
||||||
|
|
||||||
queues = std::make_unique<Queues>(physicalDevice, *this);
|
queues = std::make_unique<Queues>(physicalDevice->getVk(), *this);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create logical device
|
* Create logical device
|
||||||
@ -207,9 +200,9 @@ Vulkan::Vulkan(std::vector<const char *> instanceExtensions,
|
|||||||
|
|
||||||
// Create logical device
|
// Create logical device
|
||||||
|
|
||||||
handleVkResult(
|
handleVkResult("Could not create logical device",
|
||||||
"Could not create logical device",
|
vkCreateDevice(physicalDevice->getVk(), &createInfo,
|
||||||
vkCreateDevice(physicalDevice, &createInfo, nullptr, &device));
|
nullptr, &device));
|
||||||
|
|
||||||
// Store queue handles
|
// Store queue handles
|
||||||
|
|
||||||
@ -259,7 +252,6 @@ Vulkan::Vulkan(std::vector<const char *> instanceExtensions,
|
|||||||
for (auto &container : frames) {
|
for (auto &container : frames) {
|
||||||
container.emplace(*this);
|
container.emplace(*this);
|
||||||
}
|
}
|
||||||
currentFrame = 0;
|
|
||||||
|
|
||||||
gint = std::make_unique<progressia::main::GraphicsInterface>(this);
|
gint = std::make_unique<progressia::main::GraphicsInterface>(this);
|
||||||
}
|
}
|
||||||
@ -275,13 +267,16 @@ Vulkan::~Vulkan() {
|
|||||||
commandPool.reset();
|
commandPool.reset();
|
||||||
vkDestroyDevice(device, nullptr);
|
vkDestroyDevice(device, nullptr);
|
||||||
surface.reset();
|
surface.reset();
|
||||||
|
physicalDevice.reset();
|
||||||
errorHandler.reset();
|
errorHandler.reset();
|
||||||
vkDestroyInstance(instance, nullptr);
|
vkDestroyInstance(instance, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
VkInstance Vulkan::getInstance() const { return instance; }
|
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; }
|
VkDevice Vulkan::getDevice() const { return device; }
|
||||||
|
|
||||||
@ -333,7 +328,8 @@ VkFormat Vulkan::findSupportedFormat(const std::vector<VkFormat> &candidates,
|
|||||||
|
|
||||||
for (VkFormat format : candidates) {
|
for (VkFormat format : candidates) {
|
||||||
VkFormatProperties props;
|
VkFormatProperties props;
|
||||||
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);
|
vkGetPhysicalDeviceFormatProperties(physicalDevice->getVk(), format,
|
||||||
|
&props);
|
||||||
|
|
||||||
if (tiling == VK_IMAGE_TILING_LINEAR &&
|
if (tiling == VK_IMAGE_TILING_LINEAR &&
|
||||||
(props.linearTilingFeatures & features) == features) {
|
(props.linearTilingFeatures & features) == features) {
|
||||||
@ -351,8 +347,7 @@ VkFormat Vulkan::findSupportedFormat(const std::vector<VkFormat> &candidates,
|
|||||||
|
|
||||||
uint32_t Vulkan::findMemoryType(uint32_t allowedByDevice,
|
uint32_t Vulkan::findMemoryType(uint32_t allowedByDevice,
|
||||||
VkMemoryPropertyFlags desiredProperties) {
|
VkMemoryPropertyFlags desiredProperties) {
|
||||||
VkPhysicalDeviceMemoryProperties memProperties;
|
auto memProperties = physicalDevice->getMemory();
|
||||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
|
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
|
||||||
if (((1 << i) & allowedByDevice) == 0) {
|
if (((1 << i) & allowedByDevice) == 0) {
|
||||||
@ -383,9 +378,9 @@ Frame *Vulkan::getCurrentFrame() {
|
|||||||
return nullptr;
|
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() {
|
bool Vulkan::startRender() {
|
||||||
if (currentFrame >= MAX_FRAMES_IN_FLIGHT - 1) {
|
if (currentFrame >= MAX_FRAMES_IN_FLIGHT - 1) {
|
||||||
@ -421,16 +416,21 @@ void Vulkan::waitIdle() {
|
|||||||
* VulkanErrorHandler
|
* VulkanErrorHandler
|
||||||
*/
|
*/
|
||||||
|
|
||||||
VulkanErrorHandler::VulkanErrorHandler(Vulkan &vulkan) : vulkan(vulkan) {
|
VulkanErrorHandler::VulkanErrorHandler(Vulkan &vulkan)
|
||||||
|
: debugMessenger(nullptr), vulkan(vulkan) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanErrorHandler::~VulkanErrorHandler() {
|
|
||||||
#ifdef VULKAN_ERROR_CHECKING
|
#ifdef VULKAN_ERROR_CHECKING
|
||||||
vulkan.callVoid("vkDestroyDebugUtilsMessengerEXT",
|
VulkanErrorHandler::~VulkanErrorHandler() {
|
||||||
(VkDebugUtilsMessengerEXT)debugMessenger, nullptr);
|
if (debugMessenger != nullptr) {
|
||||||
#endif
|
vulkan.callVoid("vkDestroyDebugUtilsMessengerEXT",
|
||||||
|
(VkDebugUtilsMessengerEXT)debugMessenger, nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
VulkanErrorHandler::~VulkanErrorHandler() = default;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef VULKAN_ERROR_CHECKING
|
#ifdef VULKAN_ERROR_CHECKING
|
||||||
namespace {
|
namespace {
|
||||||
@ -445,7 +445,8 @@ debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
|||||||
return VK_FALSE;
|
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 =
|
const char *severityStr =
|
||||||
messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
|
messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
|
||||||
@ -456,7 +457,7 @@ debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
|||||||
? "info"
|
? "info"
|
||||||
: "verbose";
|
: "verbose";
|
||||||
|
|
||||||
const char *typeStr;
|
const char *typeStr = "";
|
||||||
switch (messageType) {
|
switch (messageType) {
|
||||||
case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT:
|
case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT:
|
||||||
typeStr = "general";
|
typeStr = "general";
|
||||||
@ -513,8 +514,9 @@ VulkanErrorHandler::attachDebugProbe(VkInstanceCreateInfo &createInfo) {
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
(void)createInfo;
|
(void)createInfo; // unused argument
|
||||||
return std::unique_ptr<VkDebugUtilsMessengerCreateInfoEXT>();
|
(void)this; // not static
|
||||||
|
return {};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -533,6 +535,7 @@ void VulkanErrorHandler::onInstanceReady() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||||
void VulkanErrorHandler::handleVkResult(const char *errorMessage,
|
void VulkanErrorHandler::handleVkResult(const char *errorMessage,
|
||||||
VkResult result) {
|
VkResult result) {
|
||||||
if (result == VK_SUCCESS) {
|
if (result == VK_SUCCESS) {
|
||||||
@ -548,7 +551,7 @@ void VulkanErrorHandler::handleVkResult(const char *errorMessage,
|
|||||||
* Surface
|
* Surface
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Surface::Surface(Vulkan &vulkan) : vulkan(vulkan) {
|
Surface::Surface(Vulkan &vulkan) : vk(), vulkan(vulkan) {
|
||||||
vulkan.handleVkResult("Could not create window surface (what?)",
|
vulkan.handleVkResult("Could not create window surface (what?)",
|
||||||
glfwCreateWindowSurface(vulkan.getInstance(),
|
glfwCreateWindowSurface(vulkan.getInstance(),
|
||||||
getGLFWWindowHandle(),
|
getGLFWWindowHandle(),
|
||||||
@ -563,7 +566,7 @@ VkSurfaceKHR Surface::getVk() { return vk; }
|
|||||||
* Queue
|
* Queue
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Queue::Queue(Test test) : test(test) {
|
Queue::Queue(Test test) : test(std::move(test)), vk() {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,7 +622,7 @@ Queues::Queues(VkPhysicalDevice physicalDevice, Vulkan &vulkan)
|
|||||||
|
|
||||||
for (std::size_t index = 0; index < queueFamilyCount; index++) {
|
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,
|
if (!queue->isSuitable(physicalDevice, index, vulkan,
|
||||||
properties[index])) {
|
properties[index])) {
|
||||||
continue;
|
continue;
|
||||||
@ -634,12 +637,10 @@ Queues::Queues(VkPhysicalDevice physicalDevice, Vulkan &vulkan)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Queues::~Queues() {
|
Queues::~Queues() = default;
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
void Queues::storeHandles(VkDevice device) {
|
void Queues::storeHandles(VkDevice device) {
|
||||||
for (auto queue : {&graphicsQueue, &presentQueue}) {
|
for (auto *queue : {&graphicsQueue, &presentQueue}) {
|
||||||
vkGetDeviceQueue(device, queue->getFamilyIndex(), 0, &queue->vk);
|
vkGetDeviceQueue(device, queue->getFamilyIndex(), 0, &queue->vk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -648,7 +649,7 @@ std::unique_ptr<Queues::CreationRequest>
|
|||||||
Queues::requestCreation(VkDeviceCreateInfo &createInfo) const {
|
Queues::requestCreation(VkDeviceCreateInfo &createInfo) const {
|
||||||
|
|
||||||
std::unique_ptr result = std::make_unique<CreationRequest>();
|
std::unique_ptr result = std::make_unique<CreationRequest>();
|
||||||
result->priority = 1.0f;
|
result->priority = 1.0F;
|
||||||
|
|
||||||
std::unordered_set<uint32_t> uniqueQueues;
|
std::unordered_set<uint32_t> uniqueQueues;
|
||||||
for (const auto *queue : {&graphicsQueue, &presentQueue}) {
|
for (const auto *queue : {&graphicsQueue, &presentQueue}) {
|
||||||
@ -673,7 +674,7 @@ Queues::requestCreation(VkDeviceCreateInfo &createInfo) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Queues::isComplete() const {
|
bool Queues::isComplete() const {
|
||||||
for (auto queue : {&graphicsQueue, &presentQueue}) {
|
for (const auto *queue : {&graphicsQueue, &presentQueue}) {
|
||||||
if (!queue->familyIndex.has_value()) {
|
if (!queue->familyIndex.has_value()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -691,7 +692,7 @@ const Queue &Queues::getPresentQueue() const { return presentQueue; }
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
CommandPool::CommandPool(Vulkan &vulkan, const Queue &queue)
|
CommandPool::CommandPool(Vulkan &vulkan, const Queue &queue)
|
||||||
: queue(queue), vulkan(vulkan) {
|
: pool(), queue(queue), vulkan(vulkan) {
|
||||||
|
|
||||||
VkCommandPoolCreateInfo poolInfo{};
|
VkCommandPoolCreateInfo poolInfo{};
|
||||||
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||||
@ -714,12 +715,13 @@ VkCommandBuffer CommandPool::allocateCommandBuffer() {
|
|||||||
allocInfo.commandPool = pool;
|
allocInfo.commandPool = pool;
|
||||||
allocInfo.commandBufferCount = 1;
|
allocInfo.commandBufferCount = 1;
|
||||||
|
|
||||||
VkCommandBuffer commandBuffer;
|
auto *commandBuffer = VkCommandBuffer();
|
||||||
vkAllocateCommandBuffers(vulkan.getDevice(), &allocInfo, &commandBuffer);
|
vkAllocateCommandBuffers(vulkan.getDevice(), &allocInfo, &commandBuffer);
|
||||||
|
|
||||||
return commandBuffer;
|
return commandBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||||
void CommandPool::beginCommandBuffer(VkCommandBuffer commandBuffer,
|
void CommandPool::beginCommandBuffer(VkCommandBuffer commandBuffer,
|
||||||
VkCommandBufferUsageFlags usage) {
|
VkCommandBufferUsageFlags usage) {
|
||||||
VkCommandBufferBeginInfo beginInfo{};
|
VkCommandBufferBeginInfo beginInfo{};
|
||||||
@ -773,5 +775,4 @@ void CommandPool::freeMultiUse(VkCommandBuffer buffer) {
|
|||||||
vkFreeCommandBuffers(vulkan.getDevice(), pool, 1, &buffer);
|
vkFreeCommandBuffers(vulkan.getDevice(), pool, 1, &buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -17,13 +17,12 @@
|
|||||||
#include <glm/vec3.hpp>
|
#include <glm/vec3.hpp>
|
||||||
#include <glm/vec4.hpp>
|
#include <glm/vec4.hpp>
|
||||||
|
|
||||||
#include <boost/core/noncopyable.hpp>
|
#include "../../main/util.h"
|
||||||
|
|
||||||
#include "../../main/logging.h"
|
#include "../../main/logging.h"
|
||||||
#include "../../main/rendering/graphics_interface.h"
|
#include "../../main/rendering/graphics_interface.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
namespace CstrUtils {
|
namespace CstrUtils {
|
||||||
struct CstrHash {
|
struct CstrHash {
|
||||||
@ -54,13 +53,14 @@ struct CstrCompare {
|
|||||||
using CstrHashSet = std::unordered_set<const char *, CstrHash, CstrEqual>;
|
using CstrHashSet = std::unordered_set<const char *, CstrHash, CstrEqual>;
|
||||||
} // namespace CstrUtils
|
} // namespace CstrUtils
|
||||||
|
|
||||||
class VkObjectWrapper : private boost::noncopyable {
|
class VkObjectWrapper : private progressia::main::NonCopyable {
|
||||||
// empty
|
// empty
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr std::size_t MAX_FRAMES_IN_FLIGHT = 2;
|
constexpr std::size_t MAX_FRAMES_IN_FLIGHT = 2;
|
||||||
|
|
||||||
class VulkanErrorHandler;
|
class VulkanErrorHandler;
|
||||||
|
class PhysicalDevice;
|
||||||
class Surface;
|
class Surface;
|
||||||
class Queue;
|
class Queue;
|
||||||
class Queues;
|
class Queues;
|
||||||
@ -75,10 +75,10 @@ class Frame;
|
|||||||
class Vulkan : public VkObjectWrapper {
|
class Vulkan : public VkObjectWrapper {
|
||||||
private:
|
private:
|
||||||
VkInstance instance = VK_NULL_HANDLE;
|
VkInstance instance = VK_NULL_HANDLE;
|
||||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
|
||||||
VkDevice device = VK_NULL_HANDLE;
|
VkDevice device = VK_NULL_HANDLE;
|
||||||
|
|
||||||
std::unique_ptr<VulkanErrorHandler> errorHandler;
|
std::unique_ptr<VulkanErrorHandler> errorHandler;
|
||||||
|
std::unique_ptr<PhysicalDevice> physicalDevice;
|
||||||
std::unique_ptr<Surface> surface;
|
std::unique_ptr<Surface> surface;
|
||||||
std::unique_ptr<Queues> queues;
|
std::unique_ptr<Queues> queues;
|
||||||
std::unique_ptr<CommandPool> commandPool;
|
std::unique_ptr<CommandPool> commandPool;
|
||||||
@ -103,9 +103,9 @@ class Vulkan : public VkObjectWrapper {
|
|||||||
~Vulkan();
|
~Vulkan();
|
||||||
|
|
||||||
VkInstance getInstance() const;
|
VkInstance getInstance() const;
|
||||||
VkPhysicalDevice getPhysicalDevice() const;
|
|
||||||
VkDevice getDevice() const;
|
VkDevice getDevice() const;
|
||||||
|
|
||||||
|
const PhysicalDevice &getPhysicalDevice() const;
|
||||||
Surface &getSurface();
|
Surface &getSurface();
|
||||||
const Surface &getSurface() const;
|
const Surface &getSurface() const;
|
||||||
Queues &getQueues();
|
Queues &getQueues();
|
||||||
@ -135,8 +135,8 @@ class Vulkan : public VkObjectWrapper {
|
|||||||
bool startRender();
|
bool startRender();
|
||||||
void endRender();
|
void endRender();
|
||||||
|
|
||||||
uint64_t getLastStartedFrame();
|
uint64_t getLastStartedFrame() const;
|
||||||
std::size_t getFrameInFlightIndex();
|
std::size_t getFrameInFlightIndex() const;
|
||||||
|
|
||||||
void waitIdle();
|
void waitIdle();
|
||||||
|
|
||||||
@ -192,12 +192,13 @@ class VulkanErrorHandler : public VkObjectWrapper {
|
|||||||
Vulkan &vulkan;
|
Vulkan &vulkan;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VulkanErrorHandler(Vulkan &);
|
VulkanErrorHandler(Vulkan &vulkan);
|
||||||
|
|
||||||
std::unique_ptr<VkDebugUtilsMessengerCreateInfoEXT>
|
std::unique_ptr<VkDebugUtilsMessengerCreateInfoEXT>
|
||||||
attachDebugProbe(VkInstanceCreateInfo &);
|
attachDebugProbe(VkInstanceCreateInfo &);
|
||||||
void onInstanceReady();
|
void onInstanceReady();
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(performance-trivially-destructible): fixing this makes code less readable due to use of macros in implementation
|
||||||
~VulkanErrorHandler();
|
~VulkanErrorHandler();
|
||||||
|
|
||||||
void handleVkResult(const char *errorMessage, VkResult result);
|
void handleVkResult(const char *errorMessage, VkResult result);
|
||||||
@ -209,7 +210,7 @@ class Surface : public VkObjectWrapper {
|
|||||||
Vulkan &vulkan;
|
Vulkan &vulkan;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Surface(Vulkan &);
|
Surface(Vulkan &vulkan);
|
||||||
~Surface();
|
~Surface();
|
||||||
|
|
||||||
VkSurfaceKHR getVk();
|
VkSurfaceKHR getVk();
|
||||||
@ -226,7 +227,7 @@ class Queue {
|
|||||||
|
|
||||||
friend class Queues;
|
friend class Queues;
|
||||||
|
|
||||||
Queue(Test);
|
Queue(Test test);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool isSuitable(VkPhysicalDevice, uint32_t familyIndex, Vulkan &,
|
bool isSuitable(VkPhysicalDevice, uint32_t familyIndex, Vulkan &,
|
||||||
@ -275,7 +276,7 @@ class CommandPool : public VkObjectWrapper {
|
|||||||
VkCommandBufferUsageFlags usage);
|
VkCommandBufferUsageFlags usage);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CommandPool(Vulkan &, const Queue &);
|
CommandPool(Vulkan &vulkan, const Queue &queue);
|
||||||
~CommandPool();
|
~CommandPool();
|
||||||
|
|
||||||
VkCommandBuffer beginSingleUse();
|
VkCommandBuffer beginSingleUse();
|
||||||
@ -287,5 +288,4 @@ class CommandPool : public VkObjectWrapper {
|
|||||||
void freeMultiUse(VkCommandBuffer);
|
void freeMultiUse(VkCommandBuffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
#include "vulkan_descriptor_set.h"
|
#include "vulkan_descriptor_set.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
DescriptorSetInterface::DescriptorSetInterface(uint32_t setNumber,
|
DescriptorSetInterface::DescriptorSetInterface(uint32_t setNumber,
|
||||||
Vulkan &vulkan)
|
Vulkan &vulkan)
|
||||||
: setNumber(setNumber), vulkan(vulkan) {}
|
: layout(), setNumber(setNumber), vulkan(vulkan) {}
|
||||||
|
|
||||||
VkDescriptorSetLayout DescriptorSetInterface::getLayout() const {
|
VkDescriptorSetLayout DescriptorSetInterface::getLayout() const {
|
||||||
return layout;
|
return layout;
|
||||||
@ -15,5 +14,4 @@ uint32_t DescriptorSetInterface::getSetNumber() const { return setNumber; }
|
|||||||
|
|
||||||
Vulkan &DescriptorSetInterface::getVulkan() { return vulkan; }
|
Vulkan &DescriptorSetInterface::getVulkan() { return vulkan; }
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
class DescriptorSetInterface : public VkObjectWrapper {
|
class DescriptorSetInterface : public VkObjectWrapper {
|
||||||
protected:
|
protected:
|
||||||
@ -19,5 +18,4 @@ class DescriptorSetInterface : public VkObjectWrapper {
|
|||||||
Vulkan &getVulkan();
|
Vulkan &getVulkan();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -8,12 +8,11 @@
|
|||||||
#include "vulkan_render_pass.h"
|
#include "vulkan_render_pass.h"
|
||||||
#include "vulkan_swap_chain.h"
|
#include "vulkan_swap_chain.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
Frame::Frame(Vulkan &vulkan)
|
Frame::Frame(Vulkan &vulkan)
|
||||||
: vulkan(vulkan),
|
: vulkan(vulkan), commandBuffer(vulkan.getCommandPool().allocateMultiUse()),
|
||||||
commandBuffer(vulkan.getCommandPool().allocateMultiUse()) {
|
imageAvailableSemaphore(), renderFinishedSemaphore(), inFlightFence() {
|
||||||
|
|
||||||
VkSemaphoreCreateInfo semaphoreInfo{};
|
VkSemaphoreCreateInfo semaphoreInfo{};
|
||||||
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||||
@ -98,12 +97,12 @@ bool Frame::startRender() {
|
|||||||
vulkan.getPipeline().getVk());
|
vulkan.getPipeline().getVk());
|
||||||
|
|
||||||
VkViewport viewport{};
|
VkViewport viewport{};
|
||||||
viewport.x = 0.0f;
|
viewport.x = 0.0F;
|
||||||
viewport.y = 0.0f;
|
viewport.y = 0.0F;
|
||||||
viewport.width = (float)extent.width;
|
viewport.width = (float)extent.width;
|
||||||
viewport.height = (float)extent.height;
|
viewport.height = (float)extent.height;
|
||||||
viewport.minDepth = 0.0f;
|
viewport.minDepth = 0.0F;
|
||||||
viewport.maxDepth = 1.0f;
|
viewport.maxDepth = 1.0F;
|
||||||
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
|
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
|
||||||
|
|
||||||
VkRect2D scissor{};
|
VkRect2D scissor{};
|
||||||
@ -170,5 +169,4 @@ void Frame::endRender() {
|
|||||||
|
|
||||||
VkCommandBuffer Frame::getCommandBuffer() { return commandBuffer; }
|
VkCommandBuffer Frame::getCommandBuffer() { return commandBuffer; }
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
class Frame : public VkObjectWrapper {
|
class Frame : public VkObjectWrapper {
|
||||||
private:
|
private:
|
||||||
@ -32,5 +31,4 @@ class Frame : public VkObjectWrapper {
|
|||||||
VkCommandBuffer getCommandBuffer();
|
VkCommandBuffer getCommandBuffer();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -9,8 +9,7 @@
|
|||||||
#include "vulkan_pipeline.h"
|
#include "vulkan_pipeline.h"
|
||||||
#include "vulkan_texture_descriptors.h"
|
#include "vulkan_texture_descriptors.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Image
|
* Image
|
||||||
@ -21,9 +20,7 @@ Image::Image(VkImage vk, VkImageView view, VkFormat format)
|
|||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::~Image() {
|
Image::~Image() = default;
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ManagedImage
|
* ManagedImage
|
||||||
@ -34,7 +31,7 @@ ManagedImage::ManagedImage(std::size_t width, std::size_t height,
|
|||||||
VkImageUsageFlags usage, Vulkan &vulkan)
|
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} {
|
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,
|
ManagedImage(src.width, src.height, VK_FORMAT_R8G8B8A8_SRGB,
|
||||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||||
vulkan) {
|
vulkan),
|
||||||
|
sampler() {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a staging buffer
|
* Create a staging buffer
|
||||||
@ -212,9 +210,9 @@ Texture::Texture(const progressia::main::Image &src, Vulkan &vulkan)
|
|||||||
samplerInfo.compareEnable = VK_FALSE;
|
samplerInfo.compareEnable = VK_FALSE;
|
||||||
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
|
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
|
||||||
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||||
samplerInfo.mipLodBias = 0.0f;
|
samplerInfo.mipLodBias = 0.0F;
|
||||||
samplerInfo.minLod = 0.0f;
|
samplerInfo.minLod = 0.0F;
|
||||||
samplerInfo.maxLod = 0.0f;
|
samplerInfo.maxLod = 0.0F;
|
||||||
|
|
||||||
vulkan.handleVkResult(
|
vulkan.handleVkResult(
|
||||||
"Could not create texture sampler",
|
"Could not create texture sampler",
|
||||||
@ -224,6 +222,7 @@ Texture::Texture(const progressia::main::Image &src, Vulkan &vulkan)
|
|||||||
* Create descriptor set
|
* Create descriptor set
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer): sampler must be set using vkCreateSampler first
|
||||||
descriptorSet = vulkan.getTextureDescriptors().addTexture(view, sampler);
|
descriptorSet = vulkan.getTextureDescriptors().addTexture(view, sampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,8 +233,8 @@ Texture::~Texture() {
|
|||||||
|
|
||||||
void Texture::bind() {
|
void Texture::bind() {
|
||||||
// REPORT_ERROR if getCurrentFrame() == nullptr
|
// REPORT_ERROR if getCurrentFrame() == nullptr
|
||||||
auto commandBuffer = vulkan.getCurrentFrame()->getCommandBuffer();
|
auto *commandBuffer = vulkan.getCurrentFrame()->getCommandBuffer();
|
||||||
auto pipelineLayout = vulkan.getPipeline().getLayout();
|
auto *pipelineLayout = vulkan.getPipeline().getLayout();
|
||||||
|
|
||||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||||
pipelineLayout,
|
pipelineLayout,
|
||||||
@ -243,5 +242,4 @@ void Texture::bind() {
|
|||||||
&descriptorSet, 0, nullptr);
|
&descriptorSet, 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/core/noncopyable.hpp>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "vulkan_buffer.h"
|
#include "vulkan_buffer.h"
|
||||||
@ -8,8 +7,7 @@
|
|||||||
|
|
||||||
#include "../../main/rendering/image.h"
|
#include "../../main/rendering/image.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
class Image : public VkObjectWrapper {
|
class Image : public VkObjectWrapper {
|
||||||
public:
|
public:
|
||||||
@ -37,8 +35,9 @@ class ManagedImage : public Image {
|
|||||||
State state;
|
State state;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ManagedImage(std::size_t width, std::size_t height, VkFormat,
|
ManagedImage(std::size_t width, std::size_t height, VkFormat format,
|
||||||
VkImageAspectFlags, VkImageUsageFlags, Vulkan &);
|
VkImageAspectFlags aspect, VkImageUsageFlags usage,
|
||||||
|
Vulkan &vulkan);
|
||||||
~ManagedImage();
|
~ManagedImage();
|
||||||
|
|
||||||
void transition(State);
|
void transition(State);
|
||||||
@ -50,11 +49,10 @@ class Texture : public ManagedImage {
|
|||||||
VkSampler sampler;
|
VkSampler sampler;
|
||||||
VkDescriptorSet descriptorSet;
|
VkDescriptorSet descriptorSet;
|
||||||
|
|
||||||
Texture(const progressia::main::Image &, Vulkan &vulkan);
|
Texture(const main::Image &src, Vulkan &vulkan);
|
||||||
~Texture();
|
~Texture();
|
||||||
|
|
||||||
void bind();
|
void bind();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -1,34 +1,32 @@
|
|||||||
#include "vulkan_mgmt.h"
|
#include "vulkan_mgmt.h"
|
||||||
|
|
||||||
#include "../config.h"
|
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
#include "vulkan_swap_chain.h"
|
#include "vulkan_swap_chain.h"
|
||||||
|
|
||||||
#include "../../main/logging.h"
|
#include "../../main/logging.h"
|
||||||
using namespace progressia::main::logging;
|
using namespace progressia::main::logging;
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
Vulkan *vulkan;
|
VulkanManager::VulkanManager() {
|
||||||
|
|
||||||
void initializeVulkan() {
|
|
||||||
debug("Vulkan initializing");
|
debug("Vulkan initializing");
|
||||||
|
|
||||||
// Instance extensions
|
// Instance extensions
|
||||||
|
|
||||||
std::vector<const char *> instanceExtensions;
|
std::vector<const char *> instanceExtensions;
|
||||||
{
|
{
|
||||||
uint32_t glfwExtensionCount;
|
uint32_t glfwExtensionCount = 0;
|
||||||
const char **glfwExtensions;
|
const char **glfwExtensions =
|
||||||
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
||||||
|
|
||||||
|
instanceExtensions.reserve(instanceExtensions.size() +
|
||||||
|
glfwExtensionCount);
|
||||||
for (std::size_t i = 0; i < glfwExtensionCount; i++) {
|
for (std::size_t i = 0; i < glfwExtensionCount; i++) {
|
||||||
instanceExtensions.push_back(glfwExtensions[i]);
|
instanceExtensions.emplace_back(glfwExtensions[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef VULKAN_ERROR_CHECKING
|
#ifdef VULKAN_ERROR_CHECKING
|
||||||
instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
instanceExtensions.emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,29 +42,21 @@ void initializeVulkan() {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
vulkan = new Vulkan(instanceExtensions, deviceExtensions, validationLayers);
|
vulkan = std::make_unique<Vulkan>(instanceExtensions, deviceExtensions,
|
||||||
|
validationLayers);
|
||||||
|
|
||||||
debug("Vulkan initialized");
|
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() {
|
void VulkanManager::resizeSurface() { vulkan->getSwapChain().recreate(); }
|
||||||
debug("Vulkan terminating");
|
|
||||||
|
|
||||||
if (vulkan != nullptr) {
|
} // namespace progressia::desktop
|
||||||
delete vulkan;
|
|
||||||
vulkan = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("Vulkan terminated");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace desktop
|
|
||||||
} // namespace progressia
|
|
||||||
|
@ -2,22 +2,27 @@
|
|||||||
|
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
void initializeVulkan();
|
class VulkanManager {
|
||||||
|
|
||||||
Vulkan *getVulkan();
|
private:
|
||||||
|
std::unique_ptr<Vulkan> vulkan;
|
||||||
|
|
||||||
void resizeVulkanSurface();
|
public:
|
||||||
|
VulkanManager();
|
||||||
|
~VulkanManager();
|
||||||
|
|
||||||
/*
|
Vulkan *getVulkan();
|
||||||
* Returns false when the frame should be skipped
|
const Vulkan *getVulkan() const;
|
||||||
*/
|
|
||||||
bool startRender();
|
|
||||||
void endRender();
|
|
||||||
|
|
||||||
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"
|
#include "vulkan_swap_chain.h"
|
||||||
using namespace progressia::main::logging;
|
using namespace progressia::main::logging;
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -14,7 +13,7 @@ bool checkDeviceExtensions(VkPhysicalDevice device,
|
|||||||
CstrUtils::CstrHashSet toFind(deviceExtensions.cbegin(),
|
CstrUtils::CstrHashSet toFind(deviceExtensions.cbegin(),
|
||||||
deviceExtensions.cend());
|
deviceExtensions.cend());
|
||||||
|
|
||||||
uint32_t extensionCount;
|
uint32_t extensionCount = 0;
|
||||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount,
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount,
|
||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
@ -29,20 +28,24 @@ bool checkDeviceExtensions(VkPhysicalDevice device,
|
|||||||
return toFind.empty();
|
return toFind.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDeviceSuitable(const PhysicalDeviceData &data, Vulkan &vulkan,
|
bool isDeviceSuitable(const PhysicalDevice &data, Vulkan &vulkan,
|
||||||
const std::vector<const char *> &deviceExtensions) {
|
const std::vector<const char *> &deviceExtensions) {
|
||||||
|
|
||||||
if (!Queues(data.device, vulkan).isComplete()) {
|
if (!data.isSuitable()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checkDeviceExtensions(data.device, deviceExtensions)) {
|
if (!Queues(data.getVk(), vulkan).isComplete()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkDeviceExtensions(data.getVk(), deviceExtensions)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check requires that the swap chain extension is present
|
// Check requires that the swap chain extension is present
|
||||||
if (!SwapChain::isSwapChainSuitable(
|
if (!SwapChain::isSwapChainSuitable(
|
||||||
SwapChain::querySwapChainSupport(data.device, vulkan))) {
|
SwapChain::querySwapChainSupport(data.getVk(), vulkan))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,8 +54,8 @@ bool isDeviceSuitable(const PhysicalDeviceData &data, Vulkan &vulkan,
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
const PhysicalDeviceData &
|
const PhysicalDevice &
|
||||||
pickPhysicalDevice(std::vector<PhysicalDeviceData> &choices, Vulkan &vulkan,
|
pickPhysicalDevice(std::vector<PhysicalDevice> &choices, Vulkan &vulkan,
|
||||||
const std::vector<const char *> &deviceExtensions) {
|
const std::vector<const char *> &deviceExtensions) {
|
||||||
|
|
||||||
// Remove unsuitable devices
|
// Remove unsuitable devices
|
||||||
@ -82,20 +85,17 @@ pickPhysicalDevice(std::vector<PhysicalDeviceData> &choices, Vulkan &vulkan,
|
|||||||
{"Virtual GPU", +1},
|
{"Virtual GPU", +1},
|
||||||
{"CPU", -1}};
|
{"CPU", -1}};
|
||||||
|
|
||||||
auto type = option.properties.deviceType;
|
auto type = option.getType();
|
||||||
m << "\n\t- " << opinions[type].description << " "
|
m << "\n\t- " << opinions[type].description << " " << option.getName();
|
||||||
<< option.properties.deviceName;
|
|
||||||
|
|
||||||
if (opinions[pick->properties.deviceType].value <
|
if (opinions[pick->getType()].value < opinions[type].value) {
|
||||||
opinions[type].value) {
|
|
||||||
pick = &option;
|
pick = &option;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m << "\n";
|
m << "\n";
|
||||||
|
|
||||||
m << "Picked device " << pick->properties.deviceName;
|
m << "Picked device " << pick->getName();
|
||||||
return *pick;
|
return *pick;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -1,21 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
|
#include "vulkan_physical_device.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
struct PhysicalDeviceData {
|
const PhysicalDevice &
|
||||||
VkPhysicalDevice device;
|
pickPhysicalDevice(std::vector<PhysicalDevice> &, Vulkan &,
|
||||||
VkPhysicalDeviceProperties properties;
|
|
||||||
VkPhysicalDeviceFeatures features;
|
|
||||||
};
|
|
||||||
|
|
||||||
const PhysicalDeviceData &
|
|
||||||
pickPhysicalDevice(std::vector<PhysicalDeviceData> &, Vulkan &,
|
|
||||||
const std::vector<const char *> &deviceExtensions);
|
const std::vector<const char *> &deviceExtensions);
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -5,17 +5,16 @@
|
|||||||
#include "vulkan_descriptor_set.h"
|
#include "vulkan_descriptor_set.h"
|
||||||
#include "vulkan_render_pass.h"
|
#include "vulkan_render_pass.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
Pipeline::Pipeline(Vulkan &vulkan) : vulkan(vulkan) {
|
Pipeline::Pipeline(Vulkan &vulkan) : layout(), vk(), vulkan(vulkan) {
|
||||||
|
|
||||||
auto &adapter = vulkan.getAdapter();
|
auto &adapter = vulkan.getAdapter();
|
||||||
|
|
||||||
// Shaders
|
// Shaders
|
||||||
|
|
||||||
auto vertShader = createShaderModule(adapter.loadVertexShader());
|
auto *vertShader = createShaderModule(adapter.loadVertexShader());
|
||||||
auto fragShader = createShaderModule(adapter.loadFragmentShader());
|
auto *fragShader = createShaderModule(adapter.loadFragmentShader());
|
||||||
|
|
||||||
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
|
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
|
||||||
vertShaderStageInfo.sType =
|
vertShaderStageInfo.sType =
|
||||||
@ -81,13 +80,13 @@ Pipeline::Pipeline(Vulkan &vulkan) : vulkan(vulkan) {
|
|||||||
rasterizer.depthClampEnable = VK_FALSE;
|
rasterizer.depthClampEnable = VK_FALSE;
|
||||||
rasterizer.rasterizerDiscardEnable = VK_FALSE;
|
rasterizer.rasterizerDiscardEnable = VK_FALSE;
|
||||||
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
|
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
|
||||||
rasterizer.lineWidth = 1.0f;
|
rasterizer.lineWidth = 1.0F;
|
||||||
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
|
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
|
||||||
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||||
rasterizer.depthBiasEnable = VK_FALSE;
|
rasterizer.depthBiasEnable = VK_FALSE;
|
||||||
rasterizer.depthBiasConstantFactor = 0.0f; // Optional
|
rasterizer.depthBiasConstantFactor = 0.0F; // Optional
|
||||||
rasterizer.depthBiasClamp = 0.0f; // Optional
|
rasterizer.depthBiasClamp = 0.0F; // Optional
|
||||||
rasterizer.depthBiasSlopeFactor = 0.0f; // Optional
|
rasterizer.depthBiasSlopeFactor = 0.0F; // Optional
|
||||||
|
|
||||||
// Multisampling (disabled)
|
// Multisampling (disabled)
|
||||||
|
|
||||||
@ -96,7 +95,7 @@ Pipeline::Pipeline(Vulkan &vulkan) : vulkan(vulkan) {
|
|||||||
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||||
multisampling.sampleShadingEnable = VK_FALSE;
|
multisampling.sampleShadingEnable = VK_FALSE;
|
||||||
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
multisampling.minSampleShading = 1.0f; // Optional
|
multisampling.minSampleShading = 1.0F; // Optional
|
||||||
multisampling.pSampleMask = nullptr; // Optional
|
multisampling.pSampleMask = nullptr; // Optional
|
||||||
multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
|
multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
|
||||||
multisampling.alphaToOneEnable = 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.logicOp = VK_LOGIC_OP_COPY; // Optional
|
||||||
colorBlending.attachmentCount = 1;
|
colorBlending.attachmentCount = 1;
|
||||||
colorBlending.pAttachments = &colorBlendAttachment;
|
colorBlending.pAttachments = &colorBlendAttachment;
|
||||||
colorBlending.blendConstants[0] = 0.0f; // Optional
|
colorBlending.blendConstants[0] = 0.0F; // Optional
|
||||||
colorBlending.blendConstants[1] = 0.0f; // Optional
|
colorBlending.blendConstants[1] = 0.0F; // Optional
|
||||||
colorBlending.blendConstants[2] = 0.0f; // Optional
|
colorBlending.blendConstants[2] = 0.0F; // Optional
|
||||||
colorBlending.blendConstants[3] = 0.0f; // Optional
|
colorBlending.blendConstants[3] = 0.0F; // Optional
|
||||||
|
|
||||||
// Pipeline
|
// Pipeline
|
||||||
|
|
||||||
@ -202,7 +201,7 @@ VkShaderModule Pipeline::createShaderModule(const std::vector<char> &bytecode) {
|
|||||||
// Important - the buffer must be aligned properly. std::vector does that.
|
// Important - the buffer must be aligned properly. std::vector does that.
|
||||||
createInfo.pCode = reinterpret_cast<const uint32_t *>(bytecode.data());
|
createInfo.pCode = reinterpret_cast<const uint32_t *>(bytecode.data());
|
||||||
|
|
||||||
VkShaderModule shaderModule;
|
VkShaderModule shaderModule = nullptr;
|
||||||
vulkan.handleVkResult("Could not load shader",
|
vulkan.handleVkResult("Could not load shader",
|
||||||
vkCreateShaderModule(vulkan.getDevice(), &createInfo,
|
vkCreateShaderModule(vulkan.getDevice(), &createInfo,
|
||||||
nullptr, &shaderModule));
|
nullptr, &shaderModule));
|
||||||
@ -219,5 +218,4 @@ VkPipeline Pipeline::getVk() { return vk; }
|
|||||||
|
|
||||||
VkPipelineLayout Pipeline::getLayout() { return layout; }
|
VkPipelineLayout Pipeline::getLayout() { return layout; }
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
class Pipeline : public VkObjectWrapper {
|
class Pipeline : public VkObjectWrapper {
|
||||||
|
|
||||||
@ -23,5 +22,4 @@ class Pipeline : public VkObjectWrapper {
|
|||||||
VkPipelineLayout getLayout();
|
VkPipelineLayout getLayout();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -3,10 +3,9 @@
|
|||||||
#include "vulkan_adapter.h"
|
#include "vulkan_adapter.h"
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
RenderPass::RenderPass(Vulkan &vulkan) : vulkan(vulkan) {
|
RenderPass::RenderPass(Vulkan &vulkan) : vk(), vulkan(vulkan) {
|
||||||
|
|
||||||
std::vector<VkAttachmentDescription> attachmentDescriptions;
|
std::vector<VkAttachmentDescription> attachmentDescriptions;
|
||||||
std::vector<VkAttachmentReference> attachmentReferences;
|
std::vector<VkAttachmentReference> attachmentReferences;
|
||||||
@ -15,8 +14,8 @@ RenderPass::RenderPass(Vulkan &vulkan) : vulkan(vulkan) {
|
|||||||
|
|
||||||
for (std::size_t i = 0; i < attachments.size(); i++) {
|
for (std::size_t i = 0; i < attachments.size(); i++) {
|
||||||
const auto &attachment = attachments[i];
|
const auto &attachment = attachments[i];
|
||||||
VkAttachmentDescription *desc;
|
VkAttachmentDescription *desc = nullptr;
|
||||||
VkAttachmentReference *ref;
|
VkAttachmentReference *ref = nullptr;
|
||||||
|
|
||||||
attachmentDescriptions.push_back({});
|
attachmentDescriptions.push_back({});
|
||||||
desc = &attachmentDescriptions.back();
|
desc = &attachmentDescriptions.back();
|
||||||
@ -79,5 +78,4 @@ RenderPass::~RenderPass() {
|
|||||||
|
|
||||||
VkRenderPass RenderPass::getVk() { return vk; }
|
VkRenderPass RenderPass::getVk() { return vk; }
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
class RenderPass : public VkObjectWrapper {
|
class RenderPass : public VkObjectWrapper {
|
||||||
|
|
||||||
@ -19,5 +18,4 @@ class RenderPass : public VkObjectWrapper {
|
|||||||
VkRenderPass getVk();
|
VkRenderPass getVk();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -7,23 +7,23 @@
|
|||||||
#include "glfw_mgmt_details.h"
|
#include "glfw_mgmt_details.h"
|
||||||
#include "vulkan_adapter.h"
|
#include "vulkan_adapter.h"
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
|
#include "vulkan_physical_device.h"
|
||||||
#include "vulkan_render_pass.h"
|
#include "vulkan_render_pass.h"
|
||||||
|
|
||||||
#include "../../main/logging.h"
|
#include "../../main/logging.h"
|
||||||
using namespace progressia::main::logging;
|
using namespace progressia::main::logging;
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
SwapChain::SupportDetails
|
SwapChain::SupportDetails
|
||||||
SwapChain::querySwapChainSupport(VkPhysicalDevice device, Vulkan &vulkan) {
|
SwapChain::querySwapChainSupport(VkPhysicalDevice device, Vulkan &vulkan) {
|
||||||
SupportDetails details;
|
SupportDetails details;
|
||||||
auto surface = vulkan.getSurface().getVk();
|
auto *surface = vulkan.getSurface().getVk();
|
||||||
|
|
||||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface,
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface,
|
||||||
&details.capabilities);
|
&details.capabilities);
|
||||||
|
|
||||||
uint32_t formatCount;
|
uint32_t formatCount = 0;
|
||||||
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount,
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount,
|
||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ SwapChain::querySwapChainSupport(VkPhysicalDevice device, Vulkan &vulkan) {
|
|||||||
details.formats.data());
|
details.formats.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t presentModeCount;
|
uint32_t presentModeCount = 0;
|
||||||
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface,
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface,
|
||||||
&presentModeCount, nullptr);
|
&presentModeCount, nullptr);
|
||||||
|
|
||||||
@ -51,7 +51,8 @@ bool SwapChain::isSwapChainSuitable(const SupportDetails &details) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SwapChain::create() {
|
void SwapChain::create() {
|
||||||
auto details = querySwapChainSupport(vulkan.getPhysicalDevice(), vulkan);
|
auto details =
|
||||||
|
querySwapChainSupport(vulkan.getPhysicalDevice().getVk(), vulkan);
|
||||||
auto surfaceFormat = chooseSurfaceFormat(details.formats);
|
auto surfaceFormat = chooseSurfaceFormat(details.formats);
|
||||||
auto presentMode = choosePresentMode(details.presentModes, true);
|
auto presentMode = choosePresentMode(details.presentModes, true);
|
||||||
this->extent = chooseExtent(details.capabilities);
|
this->extent = chooseExtent(details.capabilities);
|
||||||
@ -188,6 +189,7 @@ void SwapChain::create() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||||
VkSurfaceFormatKHR SwapChain::chooseSurfaceFormat(
|
VkSurfaceFormatKHR SwapChain::chooseSurfaceFormat(
|
||||||
const std::vector<VkSurfaceFormatKHR> &supported) {
|
const std::vector<VkSurfaceFormatKHR> &supported) {
|
||||||
for (const auto &option : supported) {
|
for (const auto &option : supported) {
|
||||||
@ -202,6 +204,7 @@ VkSurfaceFormatKHR SwapChain::chooseSurfaceFormat(
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||||
bool SwapChain::isTripleBufferingSupported(
|
bool SwapChain::isTripleBufferingSupported(
|
||||||
const std::vector<VkPresentModeKHR> &supported) {
|
const std::vector<VkPresentModeKHR> &supported) {
|
||||||
return std::find(supported.begin(), supported.end(),
|
return std::find(supported.begin(), supported.end(),
|
||||||
@ -219,13 +222,15 @@ SwapChain::choosePresentMode(const std::vector<VkPresentModeKHR> &supported,
|
|||||||
}
|
}
|
||||||
|
|
||||||
VkExtent2D
|
VkExtent2D
|
||||||
|
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): future-proofing
|
||||||
SwapChain::chooseExtent(const VkSurfaceCapabilitiesKHR &capabilities) {
|
SwapChain::chooseExtent(const VkSurfaceCapabilitiesKHR &capabilities) {
|
||||||
if (capabilities.currentExtent.width !=
|
if (capabilities.currentExtent.width !=
|
||||||
std::numeric_limits<uint32_t>::max()) {
|
std::numeric_limits<uint32_t>::max()) {
|
||||||
return capabilities.currentExtent;
|
return capabilities.currentExtent;
|
||||||
}
|
}
|
||||||
|
|
||||||
int width, height;
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
glfwGetFramebufferSize(getGLFWWindowHandle(), &width, &height);
|
glfwGetFramebufferSize(getGLFWWindowHandle(), &width, &height);
|
||||||
|
|
||||||
VkExtent2D actualExtent = {static_cast<uint32_t>(width),
|
VkExtent2D actualExtent = {static_cast<uint32_t>(width),
|
||||||
@ -242,7 +247,7 @@ SwapChain::chooseExtent(const VkSurfaceCapabilitiesKHR &capabilities) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SwapChain::destroy() {
|
void SwapChain::destroy() {
|
||||||
for (auto framebuffer : framebuffers) {
|
for (auto *framebuffer : framebuffers) {
|
||||||
vkDestroyFramebuffer(vulkan.getDevice(), framebuffer, nullptr);
|
vkDestroyFramebuffer(vulkan.getDevice(), framebuffer, nullptr);
|
||||||
}
|
}
|
||||||
framebuffers.clear();
|
framebuffers.clear();
|
||||||
@ -259,7 +264,7 @@ void SwapChain::destroy() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto colorBufferView : colorBufferViews) {
|
for (auto *colorBufferView : colorBufferViews) {
|
||||||
vkDestroyImageView(vulkan.getDevice(), colorBufferView, nullptr);
|
vkDestroyImageView(vulkan.getDevice(), colorBufferView, nullptr);
|
||||||
}
|
}
|
||||||
colorBufferViews.clear();
|
colorBufferViews.clear();
|
||||||
@ -271,10 +276,10 @@ void SwapChain::destroy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SwapChain::SwapChain(Vulkan &vulkan)
|
SwapChain::SwapChain(Vulkan &vulkan)
|
||||||
: vk(VK_NULL_HANDLE), colorBuffer(nullptr),
|
: vk(VK_NULL_HANDLE), colorBuffer(nullptr), extent{0, 0},
|
||||||
colorBufferViews(), extent{0, 0}, depthBuffer(nullptr), framebuffers(),
|
depthBuffer(nullptr), vulkan(vulkan) {
|
||||||
vulkan(vulkan) {
|
auto details =
|
||||||
auto details = querySwapChainSupport(vulkan.getPhysicalDevice(), vulkan);
|
querySwapChainSupport(vulkan.getPhysicalDevice().getVk(), vulkan);
|
||||||
auto surfaceFormat = chooseSurfaceFormat(details.formats);
|
auto surfaceFormat = chooseSurfaceFormat(details.formats);
|
||||||
|
|
||||||
vulkan.getAdapter().getAttachments().push_back(
|
vulkan.getAdapter().getAttachments().push_back(
|
||||||
@ -289,7 +294,7 @@ SwapChain::SwapChain(Vulkan &vulkan)
|
|||||||
VK_ATTACHMENT_LOAD_OP_CLEAR,
|
VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||||
VK_ATTACHMENT_STORE_OP_STORE,
|
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),
|
std::make_unique<Image>(static_cast<VkImage>(VK_NULL_HANDLE),
|
||||||
static_cast<VkImageView>(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; }
|
VkExtent2D SwapChain::getExtent() const { return extent; }
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
#include "vulkan_adapter.h"
|
#include "vulkan_adapter.h"
|
||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
class SwapChain : public VkObjectWrapper {
|
class SwapChain : public VkObjectWrapper {
|
||||||
|
|
||||||
@ -54,5 +53,4 @@ class SwapChain : public VkObjectWrapper {
|
|||||||
VkExtent2D getExtent() const;
|
VkExtent2D getExtent() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include "vulkan_texture_descriptors.h"
|
#include "vulkan_texture_descriptors.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
void TextureDescriptors::allocatePool() {
|
void TextureDescriptors::allocatePool() {
|
||||||
pools.resize(pools.size() + 1);
|
pools.resize(pools.size() + 1);
|
||||||
@ -16,7 +15,7 @@ void TextureDescriptors::allocatePool() {
|
|||||||
poolInfo.pPoolSizes = &poolSize;
|
poolInfo.pPoolSizes = &poolSize;
|
||||||
poolInfo.maxSets = POOL_SIZE;
|
poolInfo.maxSets = POOL_SIZE;
|
||||||
|
|
||||||
auto output = &pools[pools.size() - 1];
|
auto *output = &pools[pools.size() - 1];
|
||||||
vulkan.handleVkResult(
|
vulkan.handleVkResult(
|
||||||
"Could not create texture descriptor pool",
|
"Could not create texture descriptor pool",
|
||||||
vkCreateDescriptorPool(vulkan.getDevice(), &poolInfo, nullptr, output));
|
vkCreateDescriptorPool(vulkan.getDevice(), &poolInfo, nullptr, output));
|
||||||
@ -25,7 +24,7 @@ void TextureDescriptors::allocatePool() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TextureDescriptors::TextureDescriptors(Vulkan &vulkan)
|
TextureDescriptors::TextureDescriptors(Vulkan &vulkan)
|
||||||
: DescriptorSetInterface(SET_NUMBER, vulkan) {
|
: DescriptorSetInterface(SET_NUMBER, vulkan), lastPoolCapacity(0) {
|
||||||
VkDescriptorSetLayoutCreateInfo layoutInfo{};
|
VkDescriptorSetLayoutCreateInfo layoutInfo{};
|
||||||
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||||
|
|
||||||
@ -48,7 +47,7 @@ TextureDescriptors::TextureDescriptors(Vulkan &vulkan)
|
|||||||
}
|
}
|
||||||
|
|
||||||
TextureDescriptors::~TextureDescriptors() {
|
TextureDescriptors::~TextureDescriptors() {
|
||||||
for (auto pool : pools) {
|
for (auto *pool : pools) {
|
||||||
vkDestroyDescriptorPool(vulkan.getDevice(), pool, nullptr);
|
vkDestroyDescriptorPool(vulkan.getDevice(), pool, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +71,7 @@ VkDescriptorSet TextureDescriptors::addTexture(VkImageView view,
|
|||||||
allocInfo.descriptorSetCount = 1;
|
allocInfo.descriptorSetCount = 1;
|
||||||
allocInfo.pSetLayouts = &layout;
|
allocInfo.pSetLayouts = &layout;
|
||||||
|
|
||||||
VkDescriptorSet descriptorSet;
|
VkDescriptorSet descriptorSet = nullptr;
|
||||||
vulkan.handleVkResult("Could not create texture descriptor set",
|
vulkan.handleVkResult("Could not create texture descriptor set",
|
||||||
vkAllocateDescriptorSets(vulkan.getDevice(),
|
vkAllocateDescriptorSets(vulkan.getDevice(),
|
||||||
&allocInfo, &descriptorSet));
|
&allocInfo, &descriptorSet));
|
||||||
@ -102,5 +101,4 @@ VkDescriptorSet TextureDescriptors::addTexture(VkImageView view,
|
|||||||
return descriptorSet;
|
return descriptorSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -5,8 +5,7 @@
|
|||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
#include "vulkan_descriptor_set.h"
|
#include "vulkan_descriptor_set.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
class TextureDescriptors : public DescriptorSetInterface {
|
class TextureDescriptors : public DescriptorSetInterface {
|
||||||
private:
|
private:
|
||||||
@ -25,5 +24,4 @@ class TextureDescriptors : public DescriptorSetInterface {
|
|||||||
VkDescriptorSet addTexture(VkImageView, VkSampler);
|
VkDescriptorSet addTexture(VkImageView, VkSampler);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -7,8 +8,7 @@
|
|||||||
#include "vulkan_common.h"
|
#include "vulkan_common.h"
|
||||||
#include "vulkan_descriptor_set.h"
|
#include "vulkan_descriptor_set.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace desktop {
|
|
||||||
|
|
||||||
template <typename... Entries> class Uniform : public DescriptorSetInterface {
|
template <typename... Entries> class Uniform : public DescriptorSetInterface {
|
||||||
|
|
||||||
@ -70,7 +70,6 @@ template <typename... Entries> class Uniform : public DescriptorSetInterface {
|
|||||||
void doUpdates();
|
void doUpdates();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
|
||||||
#include "vulkan_uniform.inl"
|
#include "vulkan_uniform.inl"
|
||||||
|
@ -5,14 +5,29 @@
|
|||||||
#include "../../main/util.h"
|
#include "../../main/util.h"
|
||||||
#include "vulkan_frame.h"
|
#include "vulkan_frame.h"
|
||||||
#include "vulkan_pipeline.h"
|
#include "vulkan_pipeline.h"
|
||||||
|
#include "vulkan_physical_device.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::desktop {
|
||||||
namespace 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>
|
template <typename... Entries>
|
||||||
Uniform<Entries...>::StateImpl::Set::Set(VkDescriptorSet vk, Vulkan &vulkan)
|
Uniform<Entries...>::StateImpl::Set::Set(VkDescriptorSet vk, Vulkan &vulkan)
|
||||||
: vk(vk),
|
: 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_VISIBLE_BIT |
|
||||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||||
vulkan) {}
|
vulkan) {}
|
||||||
@ -48,7 +63,7 @@ Uniform<Entries...>::StateImpl::StateImpl(
|
|||||||
writes[index].descriptorCount = 1;
|
writes[index].descriptorCount = 1;
|
||||||
writes[index].pBufferInfo = &bufferInfos[index];
|
writes[index].pBufferInfo = &bufferInfos[index];
|
||||||
|
|
||||||
offset += sizeof(Entry);
|
offset += detail::offsetOf<Entry>(vulkan);
|
||||||
index++;
|
index++;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -71,7 +86,7 @@ void Uniform<Entries...>::State::update(const Entries &...entries) {
|
|||||||
auto *dst = state.newContents.data();
|
auto *dst = state.newContents.data();
|
||||||
FOR_PACK(Entries, entries, e, {
|
FOR_PACK(Entries, entries, e, {
|
||||||
std::memcpy(dst, &e, sizeof(e));
|
std::memcpy(dst, &e, sizeof(e));
|
||||||
dst += sizeof(e);
|
dst += detail::offsetOf(uniform->getVulkan(), e);
|
||||||
})
|
})
|
||||||
state.setsToUpdate = state.sets.size();
|
state.setsToUpdate = state.sets.size();
|
||||||
}
|
}
|
||||||
@ -190,5 +205,4 @@ template <typename... Entries> void Uniform<Entries...>::doUpdates() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace desktop
|
} // namespace progressia::desktop
|
||||||
} // namespace progressia
|
|
||||||
|
@ -27,30 +27,28 @@ int main(int argc, char *argv[]) {
|
|||||||
<< main::meta::VERSION_NUMBER << ")";
|
<< main::meta::VERSION_NUMBER << ")";
|
||||||
debug("Debug is enabled");
|
debug("Debug is enabled");
|
||||||
|
|
||||||
desktop::initializeGlfw();
|
auto glfwManager = desktop::makeGlfwManager();
|
||||||
desktop::initializeVulkan();
|
desktop::VulkanManager vulkanManager;
|
||||||
desktop::showWindow();
|
glfwManager->setOnScreenResize([&]() { vulkanManager.resizeSurface(); });
|
||||||
|
glfwManager->showWindow();
|
||||||
|
|
||||||
main::initialize(desktop::getVulkan()->getGint());
|
auto game = main::makeGame(vulkanManager.getVulkan()->getGint());
|
||||||
|
|
||||||
info("Loading complete");
|
info("Loading complete");
|
||||||
while (desktop::shouldRun()) {
|
while (glfwManager->shouldRun()) {
|
||||||
bool abortFrame = !desktop::startRender();
|
bool abortFrame = !vulkanManager.startRender();
|
||||||
if (abortFrame) {
|
if (abortFrame) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
main::renderTick();
|
game->renderTick();
|
||||||
|
|
||||||
desktop::endRender();
|
vulkanManager.endRender();
|
||||||
desktop::doGlfwRoutine();
|
glfwManager->doGlfwRoutine();
|
||||||
}
|
}
|
||||||
info("Shutting down");
|
info("Shutting down");
|
||||||
|
|
||||||
desktop::getVulkan()->waitIdle();
|
vulkanManager.getVulkan()->waitIdle();
|
||||||
main::shutdown();
|
|
||||||
desktop::shutdownVulkan();
|
|
||||||
desktop::shutdownGlfw();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,85 +1,192 @@
|
|||||||
# Building guide
|
# Building Guide
|
||||||
|
|
||||||
At this time, building is only supported in GNU/Linux targeting GNU/Linux with
|
This document provides instructions for building Progressia from source code.
|
||||||
X11/Wayland and Windows (cross-compilation). See also
|
See also
|
||||||
[Development Setup Guide](DevelopmentSetupGuide.md)
|
[Development Setup Guide](DevelopmentSetupGuide.md)
|
||||||
if you want to make git commits.
|
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
|
## Prerequisites
|
||||||
|
|
||||||
Install the following software:
|
### C++ compiler
|
||||||
- a C++ compiler (GCC or clang preferably),
|
|
||||||
- CMake,
|
|
||||||
- Python 3,
|
|
||||||
- glslc.
|
|
||||||
|
|
||||||
Install the following libraries with headers:
|
Project explicitly fully supports GCC, MinGW and Clang. Compilation with MSVC
|
||||||
- Vulkan (loader library and headers),
|
is also supported, but it can't be used for release builds and its use is
|
||||||
- GLFW3,
|
generally discouraged.
|
||||||
- GLM,
|
|
||||||
- Boost (only core library required).
|
|
||||||
|
|
||||||
### 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
|
Cross-compilation from Linux to Windows is also explicitly supported with
|
||||||
required software:
|
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
|
```bash
|
||||||
apt-get install \
|
apt install libvulkan-dev glslc
|
||||||
g++ \
|
|
||||||
cmake \
|
|
||||||
python3 &&
|
|
||||||
apt-get install --no-install-recommends \
|
|
||||||
libvulkan-dev \
|
|
||||||
libglfw3-dev \
|
|
||||||
libglm-dev \
|
|
||||||
libboost-dev
|
|
||||||
```
|
```
|
||||||
|
|
||||||
However, glslc, the shader compiler, is not available as a Debian package at the
|
Fedora users can install this dependency using dnf:
|
||||||
moment. You can install it manually from official sources or use the download it
|
|
||||||
from windcorp.ru by running these commands as root:
|
|
||||||
```bash
|
```bash
|
||||||
apt-get install wget &&
|
dnf install vulkan-devel glslc
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, packages provided by LunarG are available for Ubuntu. Follow the
|
Windows users using vcpkg should install the LunarG distribution, then install
|
||||||
instructions on [LunarG.com](https://vulkan.lunarg.com/sdk/home) to install
|
the `vulkan` vcpkg package:
|
||||||
`vulkan-sdk`.
|
```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
|
```bash
|
||||||
git clone <clone url>
|
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
|
## 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
|
```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
|
## Running
|
||||||
|
|
||||||
```bash
|
Executable file will be located directly inside the CMake binary directory.
|
||||||
tools/build.sh -R
|
|
||||||
```
|
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
|
# Development setup guide
|
||||||
|
|
||||||
To make development easier, contributors should be using a few tools. Included
|
This document provides instructions for setting up a development environment
|
||||||
with the project are configurations and scripts for these tools:
|
for Progressia.
|
||||||
- [cppcheck](http://cppcheck.net/) – performs static code analysis for C++
|
See also
|
||||||
- [clang-format](https://clang.llvm.org/docs/ClangFormat.html) – automatically
|
[Building Guide](BuildingGuide.md)
|
||||||
formats C++ source code
|
and
|
||||||
- [memcheck](https://valgrind.org/docs/manual/mc-manual.html)
|
[IDE setup guides](ide_setup).
|
||||||
(part of [valgrind](https://valgrind.org/)) – performs runtime memory
|
|
||||||
error detection
|
|
||||||
|
|
||||||
Additionally, git hooks prevent committing code that is formatted incorrectly,
|
To make development easier, contributors should be using a few tools. Included
|
||||||
does not compile or produces warnings. You can bypass this check using
|
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`
|
`git commit --no-verify`
|
||||||
in case of dire need.
|
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:
|
## Vulkan validation layers
|
||||||
- cppcheck,
|
|
||||||
- clang-format (version 13 is recommended)
|
|
||||||
- valgrind
|
|
||||||
|
|
||||||
### Debian
|
LunarG validation layers are extremely useful when debugging Vulkan code.
|
||||||
|
|
||||||
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.
|
|
||||||
The official
|
The official
|
||||||
[Vulkan tutorial](https://vulkan-tutorial.com/Development_environment)
|
[Vulkan tutorial](https://vulkan-tutorial.com/Development_environment)
|
||||||
has detailed instructions for all platforms.
|
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
|
```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 "game.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#define GLM_FORCE_RADIANS
|
#define GLM_FORCE_RADIANS
|
||||||
@ -15,167 +16,181 @@
|
|||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
using namespace progressia::main::logging;
|
using namespace progressia::main::logging;
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::main {
|
||||||
namespace main {
|
|
||||||
|
|
||||||
std::unique_ptr<Primitive> cube1, cube2;
|
class GameImpl : public Game {
|
||||||
std::unique_ptr<Texture> texture1, texture2;
|
|
||||||
std::unique_ptr<View> perspective;
|
|
||||||
std::unique_ptr<Light> light;
|
|
||||||
|
|
||||||
GraphicsInterface *gint;
|
DISABLE_COPYING(GameImpl)
|
||||||
|
DISABLE_MOVING(GameImpl)
|
||||||
|
|
||||||
void addRect(glm::vec3 origin, glm::vec3 width, glm::vec3 height,
|
public:
|
||||||
glm::vec4 color, std::vector<Vertex> &vertices,
|
std::unique_ptr<Primitive> cube1;
|
||||||
std::vector<Vertex::Index> &indices) {
|
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}});
|
static void addRect(glm::vec3 origin, glm::vec3 width, glm::vec3 height,
|
||||||
vertices.push_back({origin + width, color, {}, {0, 1}});
|
glm::vec4 color, std::vector<Vertex> &vertices,
|
||||||
vertices.push_back({origin + width + height, color, {}, {1, 1}});
|
std::vector<Vertex::Index> &indices) {
|
||||||
vertices.push_back({origin + height, color, {}, {1, 0}});
|
|
||||||
|
|
||||||
indices.push_back(offset + 0);
|
Vertex::Index offset = vertices.size();
|
||||||
indices.push_back(offset + 1);
|
|
||||||
indices.push_back(offset + 2);
|
|
||||||
|
|
||||||
indices.push_back(offset + 0);
|
vertices.push_back({origin, color, {}, {0, 0}});
|
||||||
indices.push_back(offset + 2);
|
vertices.push_back({origin + width, color, {}, {0, 1}});
|
||||||
indices.push_back(offset + 3);
|
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,
|
indices.push_back(offset + 0);
|
||||||
glm::vec3 depth, std::array<glm::vec4, 6> colors,
|
indices.push_back(offset + 1);
|
||||||
std::vector<Vertex> &vertices,
|
indices.push_back(offset + 2);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void initialize(GraphicsInterface &gintp) {
|
indices.push_back(offset + 0);
|
||||||
|
indices.push_back(offset + 2);
|
||||||
|
indices.push_back(offset + 3);
|
||||||
|
}
|
||||||
|
|
||||||
debug("game init begin");
|
static void addBox(glm::vec3 origin, glm::vec3 length, glm::vec3 height,
|
||||||
gint = &gintp;
|
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(
|
GameImpl(GraphicsInterface &gintp) {
|
||||||
progressia::main::loadImage(u"../assets/texture.png")));
|
|
||||||
texture2.reset(gint->newTexture(
|
|
||||||
progressia::main::loadImage(u"../assets/texture2.png")));
|
|
||||||
|
|
||||||
// Cube 1
|
debug("game init begin");
|
||||||
{
|
gint = &gintp;
|
||||||
std::vector<Vertex> vertices;
|
|
||||||
std::vector<Vertex::Index> indices;
|
|
||||||
auto white = glm::vec4(1, 1, 1, 1);
|
|
||||||
|
|
||||||
addBox({-0.5, -0.5, -0.5}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1},
|
texture1 =
|
||||||
{white, white, white, white, white, white}, vertices, indices);
|
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) {
|
// Cube 1
|
||||||
Vertex &a = vertices[indices[i + 0]];
|
{
|
||||||
Vertex &b = vertices[indices[i + 1]];
|
std::vector<Vertex> vertices;
|
||||||
Vertex &c = vertices[indices[i + 2]];
|
std::vector<Vertex::Index> indices;
|
||||||
|
auto white = glm::vec4(1, 1, 1, 1);
|
||||||
|
|
||||||
glm::vec3 x = b.position - a.position;
|
addBox({-0.5, -0.5, -0.5}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1},
|
||||||
glm::vec3 y = c.position - a.position;
|
{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;
|
glm::vec3 x = b.position - a.position;
|
||||||
b.normal = normal;
|
glm::vec3 y = c.position - a.position;
|
||||||
c.normal = normal;
|
|
||||||
|
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
|
addBox({-0.5, -2.5, -0.5}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1},
|
||||||
{
|
{white, white, white, white, white, white}, vertices,
|
||||||
std::vector<Vertex> vertices;
|
indices);
|
||||||
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},
|
for (std::size_t i = 0; i < indices.size(); i += 3) {
|
||||||
{white, white, white, white, white, white}, vertices, indices);
|
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) {
|
glm::vec3 x = b.position - a.position;
|
||||||
Vertex &a = vertices[indices[i + 0]];
|
glm::vec3 y = c.position - a.position;
|
||||||
Vertex &b = vertices[indices[i + 1]];
|
|
||||||
Vertex &c = vertices[indices[i + 2]];
|
|
||||||
|
|
||||||
glm::vec3 x = b.position - a.position;
|
glm::vec3 normal = glm::normalize(glm::cross(x, y));
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
a.normal = normal;
|
cube2 = gint->newPrimitive(vertices, indices, &*texture2);
|
||||||
b.normal = normal;
|
|
||||||
c.normal = normal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cube2.reset(gint->newPrimitive(vertices, indices, &*texture2));
|
perspective = gint->newView();
|
||||||
|
light = gint->newLight();
|
||||||
|
|
||||||
|
debug("game init complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
perspective.reset(gint->newView());
|
void renderTick() override {
|
||||||
light.reset(gint->newLight());
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
{
|
auto view = glm::lookAt(glm::vec3(2.0F, 2.0F, 2.0F),
|
||||||
float fov = 70.0f;
|
glm::vec3(0.0F, 0.0F, 0.0F),
|
||||||
|
glm::vec3(0.0F, 0.0F, 1.0F));
|
||||||
|
|
||||||
auto extent = gint->getViewport();
|
perspective->configure(proj, view);
|
||||||
auto proj = glm::perspective(glm::radians(fov),
|
}
|
||||||
extent.x / (float)extent.y, 0.1f, 10.0f);
|
|
||||||
proj[1][1] *= -1;
|
|
||||||
|
|
||||||
auto view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f),
|
perspective->use();
|
||||||
glm::vec3(0.0f, 0.0f, 0.0f),
|
|
||||||
glm::vec3(0.0f, 0.0f, 1.0f));
|
|
||||||
|
|
||||||
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;
|
cube1.reset();
|
||||||
glm::vec3 color0(0.60f, 0.60f, 0.70f);
|
cube2.reset();
|
||||||
glm::vec3 color1(1.10f, 1.05f, 0.70f);
|
texture1.reset();
|
||||||
|
texture2.reset();
|
||||||
|
|
||||||
float m = glm::sin(gint->tmp_getTime() / 3) * 0.5 + 0.5;
|
light.reset();
|
||||||
glm::vec3 color = m * color1 + (1 - m) * color0;
|
perspective.reset();
|
||||||
|
|
||||||
light->configure(color, glm::vec3(1.0f, -2.0f, 1.0f), contrast, 0.1f);
|
debug("game shutdown complete");
|
||||||
light->use();
|
}
|
||||||
|
};
|
||||||
|
|
||||||
auto model = glm::eulerAngleYXZ(0.0f, 0.0f, gint->tmp_getTime() * 0.1f);
|
std::unique_ptr<Game> makeGame(GraphicsInterface &gint) {
|
||||||
|
return std::make_unique<GameImpl>(gint);
|
||||||
gint->setModelTransform(model);
|
|
||||||
cube1->draw();
|
|
||||||
cube2->draw();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void shutdown() {
|
} // namespace progressia::main
|
||||||
debug("game shutdown begin");
|
|
||||||
|
|
||||||
cube1.reset();
|
|
||||||
cube2.reset();
|
|
||||||
texture1.reset();
|
|
||||||
texture2.reset();
|
|
||||||
|
|
||||||
light.reset();
|
|
||||||
perspective.reset();
|
|
||||||
|
|
||||||
debug("game shutdown complete");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace main
|
|
||||||
} // namespace progressia
|
|
||||||
|
17
main/game.h
17
main/game.h
@ -1,13 +1,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "rendering.h"
|
#include "rendering.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::main {
|
||||||
namespace main {
|
|
||||||
|
|
||||||
void initialize(GraphicsInterface &);
|
class Game : private NonCopyable {
|
||||||
void renderTick();
|
public:
|
||||||
void shutdown();
|
virtual ~Game() = default;
|
||||||
|
virtual void renderTick() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace main
|
std::unique_ptr<Game> makeGame(GraphicsInterface &);
|
||||||
} // namespace progressia
|
|
||||||
|
} // namespace progressia::main
|
||||||
|
@ -8,8 +8,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::main {
|
||||||
namespace main {
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ class LogSinkBackend {
|
|||||||
void flush();
|
void flush();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LogSinkBackend() {}
|
LogSinkBackend() = default;
|
||||||
|
|
||||||
std::ostream &getOutput() { return buffer; }
|
std::ostream &getOutput() { return buffer; }
|
||||||
|
|
||||||
@ -46,14 +45,17 @@ class LogSinkBackend {
|
|||||||
namespace {
|
namespace {
|
||||||
std::ofstream openLogFile() {
|
std::ofstream openLogFile() {
|
||||||
// FIXME this is relative to bin, not root dir
|
// FIXME this is relative to bin, not root dir
|
||||||
std::filesystem::create_directories("../run");
|
std::filesystem::create_directories("run");
|
||||||
std::filesystem::create_directories("../run/logs");
|
std::filesystem::create_directories("run/logs");
|
||||||
return std::ofstream("../run/logs/latest.log");
|
return std::ofstream("run/logs/latest.log");
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables): TODO
|
||||||
std::mutex logFileMutex;
|
std::mutex logFileMutex;
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
std::ofstream logFile = openLogFile();
|
std::ofstream logFile = openLogFile();
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
thread_local detail::LogSinkBackend theBackend;
|
thread_local detail::LogSinkBackend theBackend;
|
||||||
|
|
||||||
std::ostream &detail::LogSink::getStream() const {
|
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) {
|
: isCurrentSink(moveFrom.isCurrentSink) {
|
||||||
moveFrom.isCurrentSink = false;
|
moveFrom.isCurrentSink = false;
|
||||||
}
|
}
|
||||||
@ -99,6 +101,7 @@ void detail::LogSinkBackend::flush() {
|
|||||||
namespace {
|
namespace {
|
||||||
// FIXME This approach is horribly inefficient. It is also unsafe if any
|
// FIXME This approach is horribly inefficient. It is also unsafe if any
|
||||||
// other piece of code wants access to std::localtime.
|
// other piece of code wants access to std::localtime.
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
std::mutex getLocalTimeMutex;
|
std::mutex getLocalTimeMutex;
|
||||||
std::tm getLocalTimeAndDontExplodePlease() {
|
std::tm getLocalTimeAndDontExplodePlease() {
|
||||||
std::lock_guard<std::mutex> lock(getLocalTimeMutex);
|
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 logging
|
||||||
|
|
||||||
} // namespace main
|
} // namespace progressia::main
|
||||||
} // namespace progressia
|
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "boost/core/noncopyable.hpp"
|
#include "util.h"
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::main {
|
||||||
namespace main {
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class LogSink : private boost::noncopyable {
|
class LogSink : private progressia::main::NonCopyable {
|
||||||
private:
|
private:
|
||||||
bool isCurrentSink;
|
bool isCurrentSink;
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ class LogSink : private boost::noncopyable {
|
|||||||
LogSink(bool isCurrentSink);
|
LogSink(bool isCurrentSink);
|
||||||
~LogSink();
|
~LogSink();
|
||||||
|
|
||||||
LogSink(LogSink &&);
|
LogSink(LogSink &&) noexcept;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
friend const LogSink &operator<<(const LogSink &sink, const T &x) {
|
friend const LogSink &operator<<(const LogSink &sink, const T &x) {
|
||||||
@ -60,5 +59,4 @@ detail::LogSink fatal(const char *start = nullptr);
|
|||||||
void initializeLogging();
|
void initializeLogging();
|
||||||
void shutdownLogging();
|
void shutdownLogging();
|
||||||
|
|
||||||
} // namespace main
|
} // namespace progressia::main
|
||||||
} // namespace progressia
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../config.h"
|
#include <config.h>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::main {
|
||||||
namespace main {
|
|
||||||
namespace meta {
|
namespace meta {
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
@ -37,5 +36,4 @@ constexpr uint32_t VERSION_MINOR = (VERSION_NUMBER & 0x00FF00) >> 8;
|
|||||||
constexpr uint32_t VERSION_PATCH = (VERSION_NUMBER & 0x0000FF) >> 0;
|
constexpr uint32_t VERSION_PATCH = (VERSION_NUMBER & 0x0000FF) >> 0;
|
||||||
|
|
||||||
} // namespace meta
|
} // namespace meta
|
||||||
} // namespace main
|
} // namespace progressia::main
|
||||||
} // namespace progressia
|
|
||||||
|
@ -3,6 +3,4 @@
|
|||||||
#include "rendering/graphics_interface.h"
|
#include "rendering/graphics_interface.h"
|
||||||
#include "rendering/image.h"
|
#include "rendering/image.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::main {} // namespace progressia::main
|
||||||
namespace main {} // namespace main
|
|
||||||
} // namespace progressia
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "boost/core/noncopyable.hpp"
|
#include "../util.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define GLM_FORCE_RADIANS
|
#define GLM_FORCE_RADIANS
|
||||||
@ -12,8 +12,7 @@
|
|||||||
|
|
||||||
#include "image.h"
|
#include "image.h"
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::main {
|
||||||
namespace main {
|
|
||||||
|
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
|
|
||||||
@ -25,31 +24,27 @@ struct Vertex {
|
|||||||
glm::vec2 texCoord;
|
glm::vec2 texCoord;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Texture : private boost::noncopyable {
|
class Texture : private progressia::main::NonCopyable {
|
||||||
public:
|
|
||||||
using Backend = void *;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Backend backend;
|
struct Backend;
|
||||||
|
std::unique_ptr<Backend> backend;
|
||||||
|
friend class GraphicsInterface;
|
||||||
|
|
||||||
friend class Primitive;
|
friend class Primitive;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Texture(Backend);
|
Texture(std::unique_ptr<Backend>);
|
||||||
~Texture();
|
~Texture();
|
||||||
};
|
};
|
||||||
|
|
||||||
class Primitive : private boost::noncopyable {
|
class Primitive : private progressia::main::NonCopyable {
|
||||||
public:
|
|
||||||
using Backend = void *;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Backend backend;
|
struct Backend;
|
||||||
|
std::unique_ptr<Backend> backend;
|
||||||
friend class GraphicsInterface;
|
friend class GraphicsInterface;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Primitive(Backend);
|
Primitive(std::unique_ptr<Backend>);
|
||||||
~Primitive();
|
~Primitive();
|
||||||
|
|
||||||
void draw();
|
void draw();
|
||||||
@ -57,30 +52,28 @@ class Primitive : private boost::noncopyable {
|
|||||||
const Texture *getTexture() const;
|
const Texture *getTexture() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class View : private boost::noncopyable {
|
class View : private progressia::main::NonCopyable {
|
||||||
public:
|
|
||||||
using Backend = void *;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Backend backend;
|
struct Backend;
|
||||||
|
std::unique_ptr<Backend> backend;
|
||||||
|
friend class GraphicsInterface;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
View(Backend);
|
View(std::unique_ptr<Backend>);
|
||||||
~View();
|
~View();
|
||||||
|
|
||||||
void configure(const glm::mat4 &proj, const glm::mat4 &view);
|
void configure(const glm::mat4 &proj, const glm::mat4 &view);
|
||||||
void use();
|
void use();
|
||||||
};
|
};
|
||||||
|
|
||||||
class Light : private boost::noncopyable {
|
class Light : private progressia::main::NonCopyable {
|
||||||
public:
|
|
||||||
using Backend = void *;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Backend backend;
|
struct Backend;
|
||||||
|
std::unique_ptr<Backend> backend;
|
||||||
|
friend class GraphicsInterface;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Light(Backend);
|
Light(std::unique_ptr<Backend>);
|
||||||
~Light();
|
~Light();
|
||||||
|
|
||||||
void configure(const glm::vec3 &color, const glm::vec3 &from,
|
void configure(const glm::vec3 &color, const glm::vec3 &from,
|
||||||
@ -88,7 +81,7 @@ class Light : private boost::noncopyable {
|
|||||||
void use();
|
void use();
|
||||||
};
|
};
|
||||||
|
|
||||||
class GraphicsInterface : private boost::noncopyable {
|
class GraphicsInterface : private progressia::main::NonCopyable {
|
||||||
public:
|
public:
|
||||||
using Backend = void *;
|
using Backend = void *;
|
||||||
|
|
||||||
@ -99,18 +92,18 @@ class GraphicsInterface : private boost::noncopyable {
|
|||||||
GraphicsInterface(Backend);
|
GraphicsInterface(Backend);
|
||||||
~GraphicsInterface();
|
~GraphicsInterface();
|
||||||
|
|
||||||
Texture *newTexture(const Image &);
|
std::unique_ptr<Texture> newTexture(const Image &);
|
||||||
|
|
||||||
Primitive *newPrimitive(const std::vector<Vertex> &,
|
std::unique_ptr<Primitive> newPrimitive(const std::vector<Vertex> &,
|
||||||
const std::vector<Vertex::Index> &,
|
const std::vector<Vertex::Index> &,
|
||||||
Texture *texture);
|
Texture *texture);
|
||||||
|
|
||||||
glm::vec2 getViewport() const;
|
glm::vec2 getViewport() const;
|
||||||
|
|
||||||
void setModelTransform(const glm::mat4 &);
|
void setModelTransform(const glm::mat4 &);
|
||||||
|
|
||||||
View *newView();
|
std::unique_ptr<View> newView();
|
||||||
Light *newLight();
|
std::unique_ptr<Light> newLight();
|
||||||
|
|
||||||
void flush();
|
void flush();
|
||||||
void startNextLayer();
|
void startNextLayer();
|
||||||
@ -119,5 +112,4 @@ class GraphicsInterface : private boost::noncopyable {
|
|||||||
uint64_t getLastStartedFrame();
|
uint64_t getLastStartedFrame();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace main
|
} // namespace progressia::main
|
||||||
} // namespace progressia
|
|
||||||
|
@ -4,15 +4,17 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "stb/stb_image.h"
|
#include "stb/stb_image.h"
|
||||||
|
|
||||||
|
#include <embedded_resources.h>
|
||||||
|
|
||||||
#include "../logging.h"
|
#include "../logging.h"
|
||||||
using namespace progressia::main::logging;
|
using namespace progressia::main::logging;
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::main {
|
||||||
namespace main {
|
|
||||||
|
|
||||||
std::size_t Image::getSize() const { return data.size(); }
|
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::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()) {
|
if (resource.data == nullptr) {
|
||||||
fatal() << "Could not access a PNG image in file " << path;
|
|
||||||
// REPORT_ERROR
|
// REPORT_ERROR
|
||||||
|
progressia::main::logging::fatal()
|
||||||
|
<< "Could not find resource \"" << path << "\"";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t fileSize = static_cast<std::size_t>(file.tellg());
|
std::vector<Image::Byte> png(resource.data,
|
||||||
std::vector<Image::Byte> png(fileSize);
|
resource.data + resource.length);
|
||||||
|
|
||||||
file.seekg(0);
|
if (png.size() > std::numeric_limits<int>::max()) {
|
||||||
file.read(reinterpret_cast<char *>(png.data()), fileSize);
|
// 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;
|
Image::Byte *stbAllocatedData = stbi_load_from_memory(
|
||||||
int height;
|
png.data(), dataSize, &width, &height, &channelsInFile, STBI_rgb_alpha);
|
||||||
int channelsInFile;
|
|
||||||
|
|
||||||
Image::Byte *stbAllocatedData =
|
if (stbAllocatedData == nullptr) {
|
||||||
stbi_load_from_memory(png.data(), png.size(), &width, &height,
|
|
||||||
&channelsInFile, STBI_rgb_alpha);
|
|
||||||
|
|
||||||
if (stbAllocatedData == NULL) {
|
|
||||||
fatal() << "Could not decode a PNG image from file " << path;
|
fatal() << "Could not decode a PNG image from file " << path;
|
||||||
// REPORT_ERROR
|
// REPORT_ERROR
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -61,5 +66,4 @@ Image loadImage(const std::filesystem::path &path) {
|
|||||||
data};
|
data};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace main
|
} // namespace progressia::main
|
||||||
} // namespace progressia
|
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace progressia {
|
namespace progressia::main {
|
||||||
namespace main {
|
|
||||||
|
|
||||||
class Image {
|
class Image {
|
||||||
public:
|
public:
|
||||||
@ -19,7 +18,6 @@ class Image {
|
|||||||
Byte *getData();
|
Byte *getData();
|
||||||
};
|
};
|
||||||
|
|
||||||
Image loadImage(const std::filesystem::path &);
|
Image loadImage(const std::string &);
|
||||||
|
|
||||||
} // namespace main
|
} // namespace progressia::main
|
||||||
} // namespace progressia
|
|
||||||
|
22
main/util.h
22
main/util.h
@ -28,3 +28,25 @@
|
|||||||
}; \
|
}; \
|
||||||
}
|
}
|
||||||
// clang-format on
|
// 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
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
usage = \
|
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.
|
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.
|
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}'")
|
fail(f"Unknown option '{arg}'")
|
||||||
|
|
||||||
elif considerOptions and (arg == '-h' or arg == '--help'):
|
elif considerOptions and (arg == '-h' or arg == '--help'):
|
||||||
|
print(usage % {'me': os.path.basename(sys.argv[0])})
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
elif considerOptions and arg == '--':
|
elif considerOptions and arg == '--':
|
||||||
@ -237,8 +238,8 @@ namespace {
|
|||||||
|
|
||||||
mid=\
|
mid=\
|
||||||
'''
|
'''
|
||||||
std::unordered_map<std::string,
|
const std::unordered_map<std::string,
|
||||||
__embedded_resources::EmbeddedResource>
|
__embedded_resources::EmbeddedResource>
|
||||||
EMBEDDED_RESOURCES =
|
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