Added logging, refactored versioning; STB is now included

- Added logging
- Rewrote versioning code
- Refactored dependency management
  - STB (stb_image.h) is now included
  - All other dependencies now use find_package
- Cross-compilation from Linux to Windows is now possible
This commit is contained in:
OLEGSHA 2022-10-31 21:12:48 +03:00
parent da10f7c5cd
commit a110c9de03
Signed by: OLEGSHA
GPG Key ID: E57A4B08D64AFF7A
21 changed files with 3217 additions and 198 deletions

View File

@ -21,6 +21,7 @@ add_executable(progressia
desktop/graphics/vulkan_swap_chain.cpp
main/game.cpp
main/logging.cpp
main/rendering/image.cpp
@ -30,45 +31,48 @@ add_executable(progressia
target_include_directories(progressia PRIVATE ${generated})
# Do Windows-specific tweaks
if (WIN32)
set_target_properties(progressia PROPERTIES WIN32_EXECUTABLE true)
target_link_options(progressia PRIVATE -static-libstdc++ -static-libgcc)
endif()
# Compilation settings
set_property(TARGET progressia PROPERTY CXX_STANDARD 17)
target_compile_options(progressia PRIVATE -Wall -Wextra -Wpedantic -Werror)
# Pass version information
target_compile_definitions(progressia PRIVATE
_MAJOR=0 _MINOR=0 _PATCH=0 _BUILD=1)
# Version information
if (NOT DEFINED BUILD_ID)
set(BUILD_ID "dev")
endif()
set(VERSION "0.0.1")
# Debug options
option(VULKAN_ERROR_CHECKING "Enable Vulkan validation layers to detect Vulkan API usage errors at runtime")
if (VULKAN_ERROR_CHECKING)
target_compile_definitions(progressia PRIVATE VULKAN_ERROR_CHECKING)
endif()
# Pass configuration options
configure_file(${PROJECT_SOURCE_DIR}/main/config.h.in
${PROJECT_BINARY_DIR}/config.h)
# Libraries
find_package(PkgConfig REQUIRED)
# Use Vulkan
find_package(Vulkan REQUIRED)
target_link_libraries(progressia ${Vulkan_LIBRARIES})
target_include_directories(progressia PUBLIC ${Vulkan_INCLUDE_DIRS})
find_package(Vulkan 1.0 REQUIRED)
target_link_libraries(progressia Vulkan::Vulkan)
# Use GLFW3
find_package(glfw3 REQUIRED)
find_package(glfw3 3.3.2 REQUIRED)
target_link_libraries(progressia glfw)
# Use GLM
pkg_check_modules(GLM REQUIRED glm)
target_link_libraries(progressia ${GLM_LIBRARIES})
target_include_directories(progressia PUBLIC ${GLM_INCLUDE_DIRS})
target_compile_options(progressia PUBLIC ${GLM_CFLAGS_OTHER})
find_package(glm REQUIRED) # glmConfig-version.cmake is broken
target_link_libraries(progressia glm::glm)
# Use STB
pkg_check_modules(STB REQUIRED stb)
target_link_libraries(progressia ${STB_LIBRARIES})
target_include_directories(progressia PUBLIC ${STB_INCLUDE_DIRS})
target_compile_options(progressia PUBLIC ${STB_CFLAGS_OTHER})
target_include_directories(progressia PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/lib/stb/include)
# Use Boost (header only)
find_package(Boost REQUIRED)
target_include_directories(progressia PUBLIC ${Boost_INCLUDE_DIRS})
# Use Boost
find_package(Boost 1.74 REQUIRED)
target_link_libraries(progressia Boost::headers)

View File

@ -4,8 +4,12 @@
#include <GLFW/glfw3.h>
#include <iostream>
#include <sstream>
#include "../../main/logging.h"
#include "../../main/meta.h"
#include "vulkan_mgmt.h"
using namespace progressia::main::logging;
namespace progressia {
namespace desktop {
@ -16,12 +20,12 @@ static void onGlfwError(int errorCode, const char *description);
static void onWindowGeometryChange(GLFWwindow *window, int width, int height);
void initializeGlfw() {
std::cout << "Beginning GLFW init" << std::endl;
debug("Beginning GLFW init");
glfwSetErrorCallback(onGlfwError);
if (!glfwInit()) {
std::cout << "glfwInit() failed" << std::endl;
fatal("glfwInit() failed");
// REPORT_ERROR
exit(1);
}
@ -29,16 +33,26 @@ void initializeGlfw() {
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
window = glfwCreateWindow(800, 800, "Progressia", nullptr, nullptr);
std::string title;
{
std::stringstream accumulator;
accumulator << progressia::main::meta::NAME << " "
<< progressia::main::meta::VERSION << " build "
<< progressia::main::meta::BUILD_ID;
title = accumulator.str();
}
window = glfwCreateWindow(800, 800, title.c_str(), nullptr, nullptr);
glfwSetWindowSizeCallback(window, onWindowGeometryChange);
std::cout << "GLFW init complete" << std::endl;
debug("GLFW init complete");
}
void showWindow() {
glfwShowWindow(window);
std::cout << "Window now visible" << std::endl;
debug("Window now visible");
}
bool shouldRun() { return !glfwWindowShouldClose(window); }
@ -48,8 +62,7 @@ void doGlfwRoutine() { glfwPollEvents(); }
void shutdownGlfw() { glfwTerminate(); }
void onGlfwError(int errorCode, const char *description) {
std::cout << "[GLFW] " << description << " (" << errorCode << ")"
<< std::endl;
fatal() << "[GLFW] " << description << " (" << errorCode << ")";
// REPORT_ERROR
exit(1);
}

View File

@ -15,6 +15,7 @@
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include "../../main/logging.h"
#include "../../main/rendering.h"
#include "vulkan_buffer.h"
#include "vulkan_frame.h"
@ -53,7 +54,8 @@ std::vector<char> tmp_readFile(const std::string &path) {
if (resource.data == nullptr) {
// REPORT_ERROR
std::cerr << "Could not find resource \"" << path << "\"" << std::endl;
progressia::main::logging::fatal()
<< "Could not find resource \"" << path << "\"";
exit(1);
}

View File

@ -1,5 +1,6 @@
#include "vulkan_common.h"
#include "../config.h"
#include "vulkan_adapter.h"
#include "vulkan_frame.h"
#include "vulkan_pick_device.h"
@ -8,9 +9,12 @@
#include "vulkan_swap_chain.h"
#include "vulkan_texture_descriptors.h"
#include "../../main/logging.h"
#include "../../main/meta.h"
#include "glfw_mgmt_details.h"
using namespace progressia::main::logging;
namespace progressia {
namespace desktop {
@ -46,7 +50,7 @@ Vulkan::Vulkan(std::vector<const char *> instanceExtensions,
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = NAME;
appInfo.applicationVersion =
VK_MAKE_VERSION(VERSION.major, VERSION.minor, VERSION.patch);
VK_MAKE_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
appInfo.pEngineName = nullptr;
appInfo.engineVersion = 0;
appInfo.apiVersion = VK_API_VERSION_1_0;
@ -69,12 +73,11 @@ Vulkan::Vulkan(std::vector<const char *> instanceExtensions,
}
if (!toFind.empty()) {
std::cout << "Could not locate following requested Vulkan "
"extensions:";
auto m = fatal(
"Could not locate following requested Vulkan extensions:");
for (const auto &extension : toFind) {
std::cout << "\n\t- " << extension;
m << "\n\t- " << extension;
}
std::cout << std::endl;
// REPORT_ERROR
exit(1);
}
@ -99,12 +102,11 @@ Vulkan::Vulkan(std::vector<const char *> instanceExtensions,
}
if (!toFind.empty()) {
std::cout << "Could not locate following requested Vulkan "
"validation layers:";
auto m = fatal("Could not locate following requested Vulkan "
"validation layers:");
for (const auto &layer : toFind) {
std::cout << "\n\t- " << layer;
m << "\n\t- " << layer;
}
std::cout << std::endl;
// REPORT_ERROR
exit(1);
}
@ -143,7 +145,7 @@ Vulkan::Vulkan(std::vector<const char *> instanceExtensions,
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
if (deviceCount == 0) {
std::cout << "No GPUs with Vulkan support found" << std::endl;
fatal("No GPUs with Vulkan support found");
// REPORT_ERROR
exit(1);
}
@ -342,7 +344,7 @@ VkFormat Vulkan::findSupportedFormat(const std::vector<VkFormat> &candidates,
}
}
std::cout << "Could not find a suitable format" << std::endl;
fatal("Could not find a suitable format");
// REPORT_ERROR
exit(1);
}
@ -364,7 +366,7 @@ uint32_t Vulkan::findMemoryType(uint32_t allowedByDevice,
return i;
}
std::cout << "Could not find suitable memory type" << std::endl;
fatal("Could not find suitable memory type");
// REPORT_ERROR
exit(1);
return -1;
@ -467,8 +469,8 @@ debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
break;
}
std::cout << "[Vulkan] [" << typeStr << " / " << severityStr << "]\t"
<< pCallbackData->pMessage << std::endl;
error() << "[Vulkan] [" << typeStr << " / " << severityStr << "]\t"
<< pCallbackData->pMessage;
// REPORT_ERROR
return VK_FALSE;
}
@ -519,7 +521,7 @@ VulkanErrorHandler::attachDebugProbe(VkInstanceCreateInfo &createInfo) {
void VulkanErrorHandler::onInstanceReady() {
#ifdef VULKAN_ERROR_CHECKING
std::cout << "Registering debug callback" << std::endl;
debug("Registering debug callback");
VkDebugUtilsMessengerCreateInfoEXT createInfo{};
populateDebugMessengerCreateInfo(createInfo, vulkan);
@ -537,8 +539,7 @@ void VulkanErrorHandler::handleVkResult(const char *errorMessage,
return;
}
std::cout << "Vulkan error (" << result << "): " << errorMessage
<< std::endl;
fatal() << "Vulkan error (" << result << "): " << errorMessage;
// REPORT_ERROR
exit(1);
}

View File

@ -19,6 +19,7 @@
#include <boost/core/noncopyable.hpp>
#include "../../main/logging.h"
#include "../../main/rendering/graphics_interface.h"
namespace progressia {
@ -154,9 +155,10 @@ class Vulkan : public VkObjectWrapper {
if (func != nullptr) {
return func(instance, std::forward<Args>(args)...);
} else {
std::cout << "[Vulkan] [dynVkCall / VkResult]\tFunction not found "
"for name \""
<< functionName << "\"" << std::endl;
progressia::main::logging::error()
<< "[Vulkan] [dynVkCall / VkResult]\tFunction not found for "
"name \""
<< functionName << "\"";
// REPORT_ERROR
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
@ -173,9 +175,9 @@ class Vulkan : public VkObjectWrapper {
func(instance, std::forward<Args>(args)...);
return VK_SUCCESS;
} else {
std::cout
progressia::main::logging::error()
<< "[Vulkan] [dynVkCall / void]\tFunction not found for name \""
<< functionName << "\"" << std::endl;
<< functionName << "\"";
// REPORT_ERROR
return VK_ERROR_EXTENSION_NOT_PRESENT;
}

View File

@ -1,15 +1,19 @@
#include "vulkan_mgmt.h"
#include "../config.h"
#include "vulkan_common.h"
#include "vulkan_swap_chain.h"
#include "../../main/logging.h"
using namespace progressia::main::logging;
namespace progressia {
namespace desktop {
Vulkan *vulkan;
void initializeVulkan() {
std::cout << "Vulkan initializing" << std::endl;
debug("Vulkan initializing");
// Instance extensions
@ -42,7 +46,7 @@ void initializeVulkan() {
vulkan = new Vulkan(instanceExtensions, deviceExtensions, validationLayers);
std::cout << "Vulkan initialized" << std::endl;
debug("Vulkan initialized");
}
Vulkan *getVulkan() { return vulkan; }
@ -54,14 +58,14 @@ void endRender() { return vulkan->endRender(); }
void resizeVulkanSurface() { vulkan->getSwapChain().recreate(); }
void shutdownVulkan() {
std::cout << "Vulkan terminating" << std::endl;
debug("Vulkan terminating");
if (vulkan != nullptr) {
delete vulkan;
vulkan = nullptr;
}
std::cout << "Vulkan terminated" << std::endl;
debug("Vulkan terminated");
}
} // namespace desktop

View File

@ -1,6 +1,8 @@
#include "vulkan_pick_device.h"
#include "../../main/logging.h"
#include "vulkan_swap_chain.h"
using namespace progressia::main::logging;
namespace progressia {
namespace desktop {
@ -60,14 +62,15 @@ pickPhysicalDevice(std::vector<PhysicalDeviceData> &choices, Vulkan &vulkan,
choices.erase(it, choices.end());
if (choices.empty()) {
std::cout << "No suitable GPUs found" << std::endl;
fatal("No suitable GPUs found");
// REPORT_ERROR
exit(1);
}
const auto *pick = &choices.front();
std::cout << "Suitable devices:";
auto m = info("\n");
m << "Suitable devices:";
for (const auto &option : choices) {
struct {
@ -80,7 +83,7 @@ pickPhysicalDevice(std::vector<PhysicalDeviceData> &choices, Vulkan &vulkan,
{"CPU", -1}};
auto type = option.properties.deviceType;
std::cout << "\n\t- " << opinions[type].description << " "
m << "\n\t- " << opinions[type].description << " "
<< option.properties.deviceName;
if (opinions[pick->properties.deviceType].value <
@ -88,9 +91,9 @@ pickPhysicalDevice(std::vector<PhysicalDeviceData> &choices, Vulkan &vulkan,
pick = &option;
}
}
std::cout << std::endl;
m << "\n";
std::cout << "Picked device " << pick->properties.deviceName << std::endl;
m << "Picked device " << pick->properties.deviceName;
return *pick;
}

View File

@ -9,6 +9,9 @@
#include "vulkan_common.h"
#include "vulkan_render_pass.h"
#include "../../main/logging.h"
using namespace progressia::main::logging;
namespace progressia {
namespace desktop {
@ -136,10 +139,9 @@ void SwapChain::create() {
for (auto &attachment : vulkan.getAdapter().getAttachments()) {
if (attachment.format == VK_FORMAT_UNDEFINED) {
if (!attachment.image) {
std::cout << "Attachment " << attachment.name
fatal() << "Attachment " << attachment.name
<< " format is VK_FORMAT_UNDEFINED but it does not "
"have an image"
<< std::endl;
"have an image";
// REPORT_ERROR
exit(1);
}
@ -162,9 +164,8 @@ void SwapChain::create() {
} else if (attachment.image) {
attachmentViews.push_back(attachment.image->view);
} else {
std::cout << "Attachment " << attachment.name
<< " is not colorBuffer but it does not have an image"
<< std::endl;
fatal() << "Attachment " << attachment.name
<< " is not colorBuffer but it does not have an image";
// REPORT_ERROR
exit(1);
}
@ -196,7 +197,7 @@ VkSurfaceFormatKHR SwapChain::chooseSurfaceFormat(
}
}
std::cout << "No suitable formats available" << std::endl;
fatal("No suitable formats available");
// REPORT_ERROR
exit(1);
}

View File

@ -1,19 +1,39 @@
#include <iostream>
#include "../main/game.h"
#include "../main/logging.h"
#include "../main/meta.h"
#include "graphics/glfw_mgmt.h"
#include "graphics/vulkan_mgmt.h"
int main() {
using namespace progressia::main::logging;
int main(int argc, char *argv[]) {
using namespace progressia;
for (int i = 1; i < argc; i++) {
char *arg = argv[i];
if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
std::cout << main::meta::NAME << " " << main::meta::VERSION << "+"
<< main::meta::BUILD_ID << " (version number "
<< main::meta::VERSION_NUMBER << ")" << std::endl;
return 0;
}
}
info() << "Starting " << main::meta::NAME << " " << main::meta::VERSION
<< "+" << main::meta::BUILD_ID << " (version number "
<< main::meta::VERSION_NUMBER << ")";
debug("Debug is enabled");
desktop::initializeGlfw();
desktop::initializeVulkan();
desktop::showWindow();
main::initialize(desktop::getVulkan()->getGint());
info("Loading complete");
while (desktop::shouldRun()) {
bool abortFrame = !desktop::startRender();
if (abortFrame) {
@ -25,6 +45,7 @@ int main() {
desktop::endRender();
desktop::doGlfwRoutine();
}
info("Shutting down");
desktop::getVulkan()->waitIdle();
main::shutdown();

View File

@ -1,15 +1,15 @@
# Building guide
At this time, building is only supported in GNU/Linux targeting GNU/Linux X11.
See also [Development Setup Guide](DevelopmentSetupGuide.md) if you want to make
git commits.
At this time, building is only supported in GNU/Linux targeting GNU/Linux with
X11/Wayland and Windows (cross-compilation). See also
[Development Setup Guide](DevelopmentSetupGuide.md)
if you want to make git commits.
## Prerequisites
Install the following software:
- a C++ compiler (GCC or clang preferably),
- CMake,
- pkg-config,
- Python 3,
- glslc.
@ -17,7 +17,6 @@ Install the following libraries with headers:
- Vulkan (loader library and headers),
- GLFW3,
- GLM,
- STB,
- Boost (only core library required).
### Debian
@ -28,13 +27,11 @@ required software:
apt-get install \
g++ \
cmake \
pkg-config \
python3 &&
apt-get install --no-install-recommends \
libvulkan-dev \
libglfw3-dev \
libglm-dev \
libstb-dev \
libboost-dev
```
@ -65,9 +62,9 @@ tools/setup.sh
`tools/setup.sh` will check the availability of all required commands and
libraries.
Build tools use enviroment variables `PATH`, `PKG_CONFIG_PATH` and
`CMAKE_MODULE_PATH`; you can edit these variables system-wide or use
`tools/private.sh` to make project-specific changes.
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`:

File diff suppressed because it is too large Load Diff

5
main/config.h.in Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#define _VERSION "@VERSION@"
#define _BUILD_ID "@BUILD_ID@"
#cmakedefine VULKAN_ERROR_CHECKING

View File

@ -12,6 +12,9 @@
#include "rendering.h"
#include "logging.h"
using namespace progressia::main::logging;
namespace progressia {
namespace main {
@ -56,13 +59,13 @@ void addBox(glm::vec3 origin, glm::vec3 length, glm::vec3 height,
void initialize(GraphicsInterface &gintp) {
std::cout << "game init begin" << std::endl;
debug("game init begin");
gint = &gintp;
texture1.reset(
gint->newTexture(progressia::main::loadImage(u"../assets/texture.png")));
texture2.reset(
gint->newTexture(progressia::main::loadImage(u"../assets/texture2.png")));
texture1.reset(gint->newTexture(
progressia::main::loadImage(u"../assets/texture.png")));
texture2.reset(gint->newTexture(
progressia::main::loadImage(u"../assets/texture2.png")));
// Cube 1
{
@ -121,7 +124,7 @@ void initialize(GraphicsInterface &gintp) {
perspective.reset(gint->newView());
light.reset(gint->newLight());
std::cout << "game init complete" << std::endl;
debug("game init complete");
}
void renderTick() {
@ -161,7 +164,7 @@ void renderTick() {
}
void shutdown() {
std::cout << "game shutdown begin" << std::endl;
debug("game shutdown begin");
cube1.reset();
cube2.reset();
@ -171,7 +174,7 @@ void shutdown() {
light.reset();
perspective.reset();
std::cout << "game shutdown complete" << std::endl;
debug("game shutdown complete");
}
} // namespace main

169
main/logging.cpp Normal file
View File

@ -0,0 +1,169 @@
#include "logging.h"
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <sstream>
namespace progressia {
namespace main {
namespace detail {
class LogSinkBackend {
private:
bool writing = false;
std::stringstream buffer;
void flush();
public:
LogSinkBackend() {}
std::ostream &getOutput() { return buffer; }
void setWriting(bool writing) {
if (this->writing == writing) {
std::cerr << "Attempting to write two log messages at once"
<< std::endl;
// REPORT_ERROR
exit(1);
}
this->writing = writing;
if (!writing) {
flush();
}
}
};
} // namespace detail
namespace {
std::ofstream openLogFile() {
// FIXME this is relative to bin, not root dir
std::filesystem::create_directories("../run");
std::filesystem::create_directories("../run/logs");
return std::ofstream("../run/logs/latest.log");
}
} // namespace
std::mutex logFileMutex;
std::ofstream logFile = openLogFile();
thread_local detail::LogSinkBackend theBackend;
std::ostream &detail::LogSink::getStream() const {
if (!isCurrentSink) {
std::cerr << "LogSink::getStream() while !isCurrentSink" << std::endl;
// REPORT_ERROR
exit(1);
}
return theBackend.getOutput();
}
detail::LogSink::LogSink(bool isCurrentSink) : isCurrentSink(isCurrentSink) {
if (isCurrentSink) {
theBackend.setWriting(true);
}
}
detail::LogSink::~LogSink() {
if (isCurrentSink) {
theBackend.setWriting(false);
}
}
detail::LogSink::LogSink(LogSink &&moveFrom)
: isCurrentSink(moveFrom.isCurrentSink) {
moveFrom.isCurrentSink = false;
}
void detail::LogSinkBackend::flush() {
auto message = buffer.str();
buffer.str("");
buffer.clear();
{
std::lock_guard<std::mutex> lock(logFileMutex);
// TODO flush less often?
logFile << message << std::endl;
std::cout << message << std::endl;
}
}
namespace {
// FIXME This approach is horribly inefficient. It is also unsafe if any
// other piece of code wants access to std::localtime.
std::mutex getLocalTimeMutex;
std::tm getLocalTimeAndDontExplodePlease() {
std::lock_guard<std::mutex> lock(getLocalTimeMutex);
std::time_t t = std::time(nullptr);
return *std::localtime(&t);
}
} // namespace
detail::LogSink log(LogLevel level, const char *start) {
#ifdef NDEBUG
if (level == LogLevel::DEBUG) {
return detail::LogSink(false);
}
#endif
detail::LogSink sink(true);
auto tm = getLocalTimeAndDontExplodePlease();
sink << std::put_time(&tm, "%T") << " ";
switch (level) {
case LogLevel::DEBUG:
sink << "DEBUG";
break;
case LogLevel::INFO:
sink << "INFO ";
break;
case LogLevel::WARN:
sink << "WARN ";
break;
case LogLevel::ERROR:
sink << "ERROR";
break;
default:
sink << "FATAL";
break;
}
sink << " ";
if (start != nullptr) {
sink << start;
}
return sink;
}
namespace logging {
#ifdef NDEBUG
detail::LogSink debug(const char *) { return detail::LogSink(false); }
#else
detail::LogSink debug(const char *start) { return log(LogLevel::DEBUG, start); }
#endif
detail::LogSink info(const char *start) { return log(LogLevel::INFO, start); }
detail::LogSink warn(const char *start) { return log(LogLevel::WARN, start); }
detail::LogSink error(const char *start) { return log(LogLevel::ERROR, start); }
detail::LogSink fatal(const char *start) { return log(LogLevel::FATAL, start); }
} // namespace logging
} // namespace main
} // namespace progressia

64
main/logging.h Normal file
View File

@ -0,0 +1,64 @@
#pragma once
#include "boost/core/noncopyable.hpp"
#include <ostream>
namespace progressia {
namespace main {
namespace detail {
class LogSink : private boost::noncopyable {
private:
bool isCurrentSink;
std::ostream &getStream() const;
template <typename T> void put(const T &x) const {
if (isCurrentSink) {
getStream() << x;
}
}
public:
LogSink(bool isCurrentSink);
~LogSink();
LogSink(LogSink &&);
template <typename T>
friend const LogSink &operator<<(const LogSink &sink, const T &x) {
sink.put(x);
return sink;
}
};
} // namespace detail
enum class LogLevel {
// Only interesting when investigating a problem.
DEBUG,
// A regular user may want to see this.
INFO,
// All users should read this; if message persists, something is wrong.
WARN,
// Something is definitely wrong, but we're not crashing (yet).
ERROR,
// This is why the game is about to crash.
FATAL
};
detail::LogSink log(LogLevel, const char *start = nullptr);
namespace logging {
detail::LogSink debug(const char *start = nullptr);
detail::LogSink info(const char *start = nullptr);
detail::LogSink warn(const char *start = nullptr);
detail::LogSink error(const char *start = nullptr);
detail::LogSink fatal(const char *start = nullptr);
} // namespace logging
void initializeLogging();
void shutdownLogging();
} // namespace main
} // namespace progressia

View File

@ -1,43 +1,40 @@
#pragma once
#include "../config.h"
#include <cstdlib>
namespace progressia {
namespace main {
namespace meta {
namespace detail {
constexpr static uint32_t getVersionNumber(const char *versionStr) {
uint32_t parts[] = {0, 0, 0};
std::size_t partCount = sizeof(parts) / sizeof(parts[0]);
std::size_t currentPart = 0;
for (const char *cp = versionStr; *cp != '\0'; cp++) {
char c = *cp;
if (c == '.') {
currentPart++;
} else if (currentPart < partCount && c >= '0' && c <= '9') {
parts[currentPart] =
parts[currentPart] * 10 + static_cast<uint32_t>(c - '0');
}
}
return (parts[0] << 16) | (parts[1] << 8) | (parts[2] << 0);
}
} // namespace detail
constexpr const char *NAME = "Progressia";
constexpr const char *VERSION = _VERSION;
constexpr const char *BUILD_ID = _BUILD_ID;
#ifndef _MAJOR
#warning Version number (_MAJOR _MINOR _PATCH _BUILD) not set, using 0.0.0+1
#define _MAJOR 0
#define _MINOR 0
#define _PATCH 0
#define _BUILD 1
#endif
using VersionUnit = uint8_t;
using VersionInt = uint32_t;
constexpr struct {
VersionUnit major, minor, patch, build;
VersionInt number;
bool isRelease;
} VERSION{_MAJOR,
_MINOR,
_PATCH,
_BUILD,
(static_cast<VersionInt>(_MAJOR) << 24) |
(static_cast<VersionInt>(_MINOR) << 16) |
(static_cast<VersionInt>(_PATCH) << 8) |
(static_cast<VersionInt>(_BUILD) << 0),
_BUILD == 0};
constexpr uint32_t VERSION_NUMBER = detail::getVersionNumber(_VERSION);
constexpr uint32_t VERSION_MAJOR = (VERSION_NUMBER & 0xFF0000) >> 16;
constexpr uint32_t VERSION_MINOR = (VERSION_NUMBER & 0x00FF00) >> 8;
constexpr uint32_t VERSION_PATCH = (VERSION_NUMBER & 0x0000FF) >> 0;
} // namespace meta
} // namespace main

View File

@ -8,6 +8,9 @@
#include "stb/stb_image.h"
#include "../logging.h"
using namespace progressia::main::logging;
namespace progressia {
namespace main {
@ -22,8 +25,7 @@ Image loadImage(const std::filesystem::path &path) {
std::ifstream file(path, std::ios::ate | std::ios::binary);
if (!file.is_open()) {
std::cout << "Could not read a PNG image from file " << path
<< std::endl;
fatal() << "Could not access a PNG image in file " << path;
// REPORT_ERROR
exit(1);
}
@ -45,8 +47,7 @@ Image loadImage(const std::filesystem::path &path) {
&channelsInFile, STBI_rgb_alpha);
if (stbAllocatedData == NULL) {
std::cout << "Could not load a PNG image from file " << path
<< std::endl;
fatal() << "Could not decode a PNG image from file " << path;
// REPORT_ERROR
exit(1);
}

View File

@ -1,12 +1,15 @@
#!/bin/bash
usage=\
"Usage: build.sh [OPTIONS...] [TOOL-ARGUMENT...]
"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
@ -25,6 +28,10 @@ Environment variables:
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"
@ -38,6 +45,7 @@ source "$rsrc/bashlib.sh"
build_type=Debug
do_generate=true
cmake_gen_args=()
do_build=true
run_type=Normal
@ -47,10 +55,6 @@ debug_vulkan=''
memcheck_args=()
for arg in "$@"; do
if [ $is_cmake_arg ]; then
cmake_args+=("$arg")
else
case "$arg" in
-h | --help )
echo "$usage"
@ -62,6 +66,21 @@ for arg in "$@"; do
--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
;;
@ -90,10 +109,9 @@ for arg in "$@"; do
fail "Unknown option '$arg'"
;;
esac
fi
done
if [ -z "$do_build" -a -z "$do_generate" -a ${#cmake_args[@]} != 0 ]; then
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
@ -107,12 +125,23 @@ fi
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" \
-DCMAKE_BUILD_TYPE=$build_type \
-DVULKAN_ERROR_CHECKING=`[ $debug_vulkan ] && echo ON || echo OFF` \
"${cmake_args[@]}" \
"${cmake_gen_managed_args[@]}" \
"${private_cmake_gen_args[@]}" \
"${cmake_gen_args[@]}" \
|| fail "Could not generate build files"
fi

View File

@ -55,9 +55,7 @@ $unstaged_changes"
fi
git diff -U0 --no-color --relative HEAD \
'*.cpp' \
'*.h' \
'*.inl' \
{desktop,main}/{'*.cpp','*.h','*.inl'} \
| command "$CLANG_FORMAT_DIFF" -p1 -style="$style" -i --verbose
exit_code="$?"
git add "$root_dir"

View File

@ -13,3 +13,6 @@ missingInclude:*
# Shut up. Just shut up.
unmatchedSuppression:*
# Do not check third-party libraries
*:*lib*

View File

@ -74,7 +74,6 @@ function check_cmd() {
unset found
}
check_cmd pkg-config
check_cmd cmake
check_cmd python3
check_cmd glslc
@ -91,8 +90,8 @@ fi
# Try generating build files
if FAIL_SILENTLY=true find_cmd found_cmake cmake; then
if CMAKE="$found_cmake" "$tools_dir/build.sh" --dont-build; then
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'
@ -113,8 +112,8 @@ fail "Could not find the following required commands or libraries:
You can resolve these errors in the following ways:
1. Install required software packages. See README for specific instructions.
2. Edit PATH, PKG_CONFIG_PATH or CMAKE_MODULE_PATH environment variables in
tools/private.sh to include your installation directories.
2. Edit PATH or CMAKE_MODULE_PATH environment variables in tools/private.sh
to include your installation directories.
"