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

View File

@ -4,8 +4,12 @@
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <iostream> #include <iostream>
#include <sstream>
#include "../../main/logging.h"
#include "../../main/meta.h"
#include "vulkan_mgmt.h" #include "vulkan_mgmt.h"
using namespace progressia::main::logging;
namespace progressia { namespace progressia {
namespace desktop { namespace desktop {
@ -16,12 +20,12 @@ 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() { void initializeGlfw() {
std::cout << "Beginning GLFW init" << std::endl; debug("Beginning GLFW init");
glfwSetErrorCallback(onGlfwError); glfwSetErrorCallback(onGlfwError);
if (!glfwInit()) { if (!glfwInit()) {
std::cout << "glfwInit() failed" << std::endl; fatal("glfwInit() failed");
// REPORT_ERROR // REPORT_ERROR
exit(1); exit(1);
} }
@ -29,16 +33,26 @@ void initializeGlfw() {
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); 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); glfwSetWindowSizeCallback(window, onWindowGeometryChange);
std::cout << "GLFW init complete" << std::endl; debug("GLFW init complete");
} }
void showWindow() { void showWindow() {
glfwShowWindow(window); glfwShowWindow(window);
std::cout << "Window now visible" << std::endl; debug("Window now visible");
} }
bool shouldRun() { return !glfwWindowShouldClose(window); } bool shouldRun() { return !glfwWindowShouldClose(window); }
@ -48,8 +62,7 @@ void doGlfwRoutine() { glfwPollEvents(); }
void shutdownGlfw() { glfwTerminate(); } void shutdownGlfw() { glfwTerminate(); }
void onGlfwError(int errorCode, const char *description) { void onGlfwError(int errorCode, const char *description) {
std::cout << "[GLFW] " << description << " (" << errorCode << ")" fatal() << "[GLFW] " << description << " (" << errorCode << ")";
<< std::endl;
// REPORT_ERROR // REPORT_ERROR
exit(1); exit(1);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,19 +1,39 @@
#include <iostream> #include <iostream>
#include "../main/game.h" #include "../main/game.h"
#include "../main/logging.h"
#include "../main/meta.h"
#include "graphics/glfw_mgmt.h" #include "graphics/glfw_mgmt.h"
#include "graphics/vulkan_mgmt.h" #include "graphics/vulkan_mgmt.h"
int main() { using namespace progressia::main::logging;
int main(int argc, char *argv[]) {
using namespace progressia; 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::initializeGlfw();
desktop::initializeVulkan(); desktop::initializeVulkan();
desktop::showWindow(); desktop::showWindow();
main::initialize(desktop::getVulkan()->getGint()); main::initialize(desktop::getVulkan()->getGint());
info("Loading complete");
while (desktop::shouldRun()) { while (desktop::shouldRun()) {
bool abortFrame = !desktop::startRender(); bool abortFrame = !desktop::startRender();
if (abortFrame) { if (abortFrame) {
@ -25,6 +45,7 @@ int main() {
desktop::endRender(); desktop::endRender();
desktop::doGlfwRoutine(); desktop::doGlfwRoutine();
} }
info("Shutting down");
desktop::getVulkan()->waitIdle(); desktop::getVulkan()->waitIdle();
main::shutdown(); main::shutdown();

View File

@ -1,15 +1,15 @@
# Building guide # Building guide
At this time, building is only supported in GNU/Linux targeting GNU/Linux X11. At this time, building is only supported in GNU/Linux targeting GNU/Linux with
See also [Development Setup Guide](DevelopmentSetupGuide.md) if you want to make X11/Wayland and Windows (cross-compilation). See also
git commits. [Development Setup Guide](DevelopmentSetupGuide.md)
if you want to make git commits.
## Prerequisites ## Prerequisites
Install the following software: Install the following software:
- a C++ compiler (GCC or clang preferably), - a C++ compiler (GCC or clang preferably),
- CMake, - CMake,
- pkg-config,
- Python 3, - Python 3,
- glslc. - glslc.
@ -17,7 +17,6 @@ Install the following libraries with headers:
- Vulkan (loader library and headers), - Vulkan (loader library and headers),
- GLFW3, - GLFW3,
- GLM, - GLM,
- STB,
- Boost (only core library required). - Boost (only core library required).
### Debian ### Debian
@ -28,13 +27,11 @@ required software:
apt-get install \ apt-get install \
g++ \ g++ \
cmake \ cmake \
pkg-config \
python3 && python3 &&
apt-get install --no-install-recommends \ apt-get install --no-install-recommends \
libvulkan-dev \ libvulkan-dev \
libglfw3-dev \ libglfw3-dev \
libglm-dev \ libglm-dev \
libstb-dev \
libboost-dev libboost-dev
``` ```
@ -65,9 +62,9 @@ tools/setup.sh
`tools/setup.sh` will check the availability of all required commands and `tools/setup.sh` will check the availability of all required commands and
libraries. libraries.
Build tools use enviroment variables `PATH`, `PKG_CONFIG_PATH` and Build tools use enviroment variables `PATH`, `VULKAN_SDK`, `CMAKE_MODULE_PATH`
`CMAKE_MODULE_PATH`; you can edit these variables system-wide or use and `CMAKE_PREFIX_PATH` to locate the various components; you can edit these
`tools/private.sh` to make project-specific changes. variables system-wide or use `tools/private.sh` to amend them for build tools.
(Your changes to `tools/private.sh` are ignored by git.) (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 For example, of you ran the script to download glslc on Debian, you will need to
add the following line to `tools/private.sh`: 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 "rendering.h"
#include "logging.h"
using namespace progressia::main::logging;
namespace progressia { namespace progressia {
namespace main { namespace main {
@ -56,13 +59,13 @@ void addBox(glm::vec3 origin, glm::vec3 length, glm::vec3 height,
void initialize(GraphicsInterface &gintp) { void initialize(GraphicsInterface &gintp) {
std::cout << "game init begin" << std::endl; debug("game init begin");
gint = &gintp; gint = &gintp;
texture1.reset( texture1.reset(gint->newTexture(
gint->newTexture(progressia::main::loadImage(u"../assets/texture.png"))); progressia::main::loadImage(u"../assets/texture.png")));
texture2.reset( texture2.reset(gint->newTexture(
gint->newTexture(progressia::main::loadImage(u"../assets/texture2.png"))); progressia::main::loadImage(u"../assets/texture2.png")));
// Cube 1 // Cube 1
{ {
@ -121,7 +124,7 @@ void initialize(GraphicsInterface &gintp) {
perspective.reset(gint->newView()); perspective.reset(gint->newView());
light.reset(gint->newLight()); light.reset(gint->newLight());
std::cout << "game init complete" << std::endl; debug("game init complete");
} }
void renderTick() { void renderTick() {
@ -161,7 +164,7 @@ void renderTick() {
} }
void shutdown() { void shutdown() {
std::cout << "game shutdown begin" << std::endl; debug("game shutdown begin");
cube1.reset(); cube1.reset();
cube2.reset(); cube2.reset();
@ -171,7 +174,7 @@ void shutdown() {
light.reset(); light.reset();
perspective.reset(); perspective.reset();
std::cout << "game shutdown complete" << std::endl; debug("game shutdown complete");
} }
} // namespace main } // 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 #pragma once
#include "../config.h"
#include <cstdlib> #include <cstdlib>
namespace progressia { namespace progressia {
namespace main { namespace main {
namespace meta { 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 *NAME = "Progressia";
constexpr const char *VERSION = _VERSION;
constexpr const char *BUILD_ID = _BUILD_ID;
#ifndef _MAJOR constexpr uint32_t VERSION_NUMBER = detail::getVersionNumber(_VERSION);
#warning Version number (_MAJOR _MINOR _PATCH _BUILD) not set, using 0.0.0+1 constexpr uint32_t VERSION_MAJOR = (VERSION_NUMBER & 0xFF0000) >> 16;
#define _MAJOR 0 constexpr uint32_t VERSION_MINOR = (VERSION_NUMBER & 0x00FF00) >> 8;
#define _MINOR 0 constexpr uint32_t VERSION_PATCH = (VERSION_NUMBER & 0x0000FF) >> 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};
} // namespace meta } // namespace meta
} // namespace main } // namespace main

View File

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

View File

@ -1,12 +1,15 @@
#!/bin/bash #!/bin/bash
usage=\ usage=\
"Usage: build.sh [OPTIONS...] [TOOL-ARGUMENT...] "Usage: build.sh [OPTIONS...]
Build and run the game. Build and run the game.
Options: Options:
--debug make a debug build (default) --debug make a debug build (default)
--release make a release build --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 --dont-generate don't generate build instructions; use existing
configuration if building configuration if building
--dont-build don't build; run existing binaries or generate build --dont-build don't build; run existing binaries or generate build
@ -25,6 +28,10 @@ Environment variables:
CMAKE cmake executable CMAKE cmake executable
VALGRIND valgrind 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 See also: tools/cppcheck/use-cppcheck.sh --help
tools/clang-format/use-clang-format.sh --help tools/clang-format/use-clang-format.sh --help
tools/setup.sh --help" tools/setup.sh --help"
@ -38,6 +45,7 @@ source "$rsrc/bashlib.sh"
build_type=Debug build_type=Debug
do_generate=true do_generate=true
cmake_gen_args=()
do_build=true do_build=true
run_type=Normal run_type=Normal
@ -47,10 +55,6 @@ debug_vulkan=''
memcheck_args=() memcheck_args=()
for arg in "$@"; do for arg in "$@"; do
if [ $is_cmake_arg ]; then
cmake_args+=("$arg")
else
case "$arg" in case "$arg" in
-h | --help ) -h | --help )
echo "$usage" echo "$usage"
@ -62,6 +66,21 @@ for arg in "$@"; do
--release ) --release )
build_type=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 )
debug_vulkan=true debug_vulkan=true
;; ;;
@ -90,10 +109,9 @@ for arg in "$@"; do
fail "Unknown option '$arg'" fail "Unknown option '$arg'"
;; ;;
esac esac
fi
done 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" fail "CMake arguments are set, but no build is requested. Aborting"
fi fi
@ -107,12 +125,23 @@ fi
find_cmd CMAKE cmake find_cmd CMAKE cmake
if [ $do_generate ]; then 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" \ echo_and_run "$CMAKE" \
-B "$build_dir" \ -B "$build_dir" \
-S "$source_dir" \ -S "$source_dir" \
-DCMAKE_BUILD_TYPE=$build_type \ "${cmake_gen_managed_args[@]}" \
-DVULKAN_ERROR_CHECKING=`[ $debug_vulkan ] && echo ON || echo OFF` \ "${private_cmake_gen_args[@]}" \
"${cmake_args[@]}" \ "${cmake_gen_args[@]}" \
|| fail "Could not generate build files" || fail "Could not generate build files"
fi fi

View File

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

View File

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

View File

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