mirror of
https://github.com/slendidev/lunar.git
synced 2025-12-13 11:49:51 +02:00
116
src/Application.cpp
Normal file
116
src/Application.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "Application.h"
|
||||
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <iostream>
|
||||
#include <print>
|
||||
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
#include <VkBootstrap.h>
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
namespace Lunar {
|
||||
|
||||
Application::Application()
|
||||
{
|
||||
vkb::InstanceBuilder instance_builder {};
|
||||
instance_builder.request_validation_layers()
|
||||
.set_app_name("Lunar")
|
||||
.set_engine_name("Lunar")
|
||||
.require_api_version(1, 0, 0)
|
||||
.set_debug_callback_user_data_pointer(this)
|
||||
.set_debug_callback(
|
||||
[](VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT message_type,
|
||||
VkDebugUtilsMessengerCallbackDataEXT const *callback_data,
|
||||
void *user_data) {
|
||||
auto app { reinterpret_cast<Application *>(user_data) };
|
||||
|
||||
auto level = Logger::Level::Debug;
|
||||
if (message_severity
|
||||
& VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
||||
level = Logger::Level::Error;
|
||||
} else if (message_severity
|
||||
& VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
|
||||
level = Logger::Level::Warning;
|
||||
} else if (message_severity
|
||||
& VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
|
||||
level = Logger::Level::Info;
|
||||
}
|
||||
|
||||
app->m_logger.log(level,
|
||||
std::format("[{}] {}",
|
||||
vkb::to_string_message_type(message_type),
|
||||
callback_data->pMessage));
|
||||
|
||||
return VK_FALSE;
|
||||
});
|
||||
auto const instance_builder_ret { instance_builder.build() };
|
||||
if (!instance_builder_ret) {
|
||||
std::println(std::cerr, "Failed to create Vulkan instance. Error: {}",
|
||||
instance_builder_ret.error().message());
|
||||
throw std::runtime_error("App init fail");
|
||||
}
|
||||
|
||||
m_vkb_instance = instance_builder_ret.value();
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||
std::println(std::cerr, "Failed to initialize SDL.");
|
||||
throw std::runtime_error("App init fail");
|
||||
}
|
||||
|
||||
m_window = SDL_CreateWindow(
|
||||
"Lunar", 1280, 720, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE);
|
||||
if (!m_window) {
|
||||
m_logger.err("Failed to create SDL window");
|
||||
throw std::runtime_error("App init fail");
|
||||
}
|
||||
|
||||
if (!SDL_Vulkan_CreateSurface(
|
||||
m_window, m_vkb_instance, nullptr, &m_vk_surface)) {
|
||||
m_logger.err("Failed to create vulkan surface");
|
||||
throw std::runtime_error("App init fail");
|
||||
}
|
||||
|
||||
vkb::PhysicalDeviceSelector phys_device_selector { m_vkb_instance };
|
||||
phys_device_selector.set_surface(m_vk_surface)
|
||||
.add_required_extensions({ VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
|
||||
VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,
|
||||
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
|
||||
VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
|
||||
VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME });
|
||||
auto physical_device_selector_return { phys_device_selector.select() };
|
||||
if (!physical_device_selector_return) {
|
||||
std::println(std::cerr, "Failed to find Vulkan device. Error: {}",
|
||||
physical_device_selector_return.error().message());
|
||||
throw std::runtime_error("App init fail");
|
||||
}
|
||||
auto phys_device = physical_device_selector_return.value();
|
||||
|
||||
m_logger.info("App init done!");
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
SDL_Vulkan_DestroySurface(m_vkb_instance, m_vk_surface, nullptr);
|
||||
SDL_DestroyWindow(m_window);
|
||||
SDL_Quit();
|
||||
|
||||
vkb::destroy_instance(m_vkb_instance);
|
||||
|
||||
m_logger.info("App destroy done!");
|
||||
}
|
||||
|
||||
auto Application::run() -> void
|
||||
{
|
||||
SDL_Event e;
|
||||
|
||||
while (m_running) {
|
||||
while (SDL_PollEvent(&e)) {
|
||||
if (e.type == SDL_EVENT_QUIT)
|
||||
m_running = false;
|
||||
}
|
||||
// nothing else
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Lunar
|
||||
26
src/Application.h
Normal file
26
src/Application.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_video.h>
|
||||
#include <VkBootstrap.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "src/Logger.h"
|
||||
|
||||
namespace Lunar {
|
||||
|
||||
struct Application {
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
auto run() -> void;
|
||||
|
||||
private:
|
||||
vkb::Instance m_vkb_instance;
|
||||
VkSurfaceKHR m_vk_surface { nullptr };
|
||||
SDL_Window *m_window { nullptr };
|
||||
Logger m_logger { "Lunar" };
|
||||
|
||||
bool m_running { true };
|
||||
};
|
||||
|
||||
} // namespace Lunar
|
||||
205
src/Logger.cpp
Normal file
205
src/Logger.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
#include "Logger.h"
|
||||
|
||||
#include "Util.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <shlobj.h> // SHGetKnownFolderPath
|
||||
# include <windows.h>
|
||||
#elif defined(__APPLE__)
|
||||
# include <pwd.h>
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# include <pwd.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
# include <zlib.h>
|
||||
#endif
|
||||
|
||||
#define FG_BLUE "\033[34m"
|
||||
#define FG_RED "\033[31m"
|
||||
#define FG_YELLOW "\033[33m"
|
||||
#define FG_GRAY "\033[90m"
|
||||
#define ANSI_RESET "\033[0m"
|
||||
|
||||
std::filesystem::path get_log_path(std::string_view app_name)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
PWSTR path = nullptr;
|
||||
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path);
|
||||
std::wstring wpath(path);
|
||||
CoTaskMemFree(path);
|
||||
return std::filesystem::path(wpath) / app_name / "logs";
|
||||
#elif defined(__APPLE__)
|
||||
const char *home = getenv("HOME");
|
||||
if (!home)
|
||||
home = getpwuid(getuid())->pw_dir;
|
||||
return std::filesystem::path(home) / "Library" / "Logs" / app_name;
|
||||
#else
|
||||
auto const *home { getenv("HOME") };
|
||||
if (!home)
|
||||
home = getpwuid(getuid())->pw_dir;
|
||||
return std::filesystem::path(home) / ".local" / "share" / app_name / "logs";
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
int compress_file(std::filesystem::path const &input_path,
|
||||
std::filesystem::path const &output_path)
|
||||
{
|
||||
size_t const chunk_size = 4096;
|
||||
|
||||
std::ifstream in { input_path, std::ios::binary };
|
||||
if (!in)
|
||||
return 1;
|
||||
|
||||
gzFile out { gzopen(output_path.string().c_str(), "wb") };
|
||||
if (!out)
|
||||
return 1;
|
||||
defer(gzclose(out));
|
||||
|
||||
std::vector<char> buffer(chunk_size);
|
||||
while (in) {
|
||||
in.read(buffer.data(), buffer.size());
|
||||
std::streamsize bytes = in.gcount();
|
||||
if (bytes > 0)
|
||||
gzwrite(out, buffer.data(), static_cast<unsigned int>(bytes));
|
||||
}
|
||||
|
||||
std::filesystem::remove(input_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
Logger::Logger(std::string_view app_name)
|
||||
{
|
||||
#ifndef __EMSCRIPTEN__
|
||||
auto path { get_log_path(app_name) };
|
||||
auto const exists { std::filesystem::exists(path) };
|
||||
if (exists && !std::filesystem::is_directory(path)) {
|
||||
std::filesystem::remove_all(path);
|
||||
}
|
||||
if (!exists) {
|
||||
std::filesystem::create_directories(path);
|
||||
}
|
||||
|
||||
int max { -1 };
|
||||
std::filesystem::directory_iterator iter(path);
|
||||
for (auto const &file : iter) {
|
||||
if (!file.is_regular_file())
|
||||
continue;
|
||||
|
||||
int v;
|
||||
if (std::sscanf(
|
||||
file.path().filename().stem().string().c_str(), "log_%d", &v)
|
||||
!= 1) {
|
||||
continue;
|
||||
}
|
||||
if (v > max)
|
||||
max = v;
|
||||
|
||||
auto ext = file.path().filename().extension().string();
|
||||
if (ext == ".txt") {
|
||||
auto np = file.path();
|
||||
np.replace_extension(ext + ".gz");
|
||||
compress_file(file.path(), np);
|
||||
}
|
||||
}
|
||||
max++;
|
||||
|
||||
path /= std::format("log_{}.txt", max);
|
||||
m_fout = std::ofstream(path, std::ios::app | std::ios::out);
|
||||
#endif // EMSCRIPTEN
|
||||
}
|
||||
|
||||
auto Logger::debug(std::string_view msg) -> void
|
||||
{
|
||||
log(Logger::Level::Debug, msg);
|
||||
}
|
||||
auto Logger::info(std::string_view msg) -> void
|
||||
{
|
||||
log(Logger::Level::Info, msg);
|
||||
}
|
||||
auto Logger::warn(std::string_view msg) -> void
|
||||
{
|
||||
log(Logger::Level::Warning, msg);
|
||||
}
|
||||
auto Logger::err(std::string_view msg) -> void
|
||||
{
|
||||
log(Logger::Level::Error, msg);
|
||||
}
|
||||
|
||||
std::string get_current_time_string()
|
||||
{
|
||||
auto now { std::chrono::system_clock::now() };
|
||||
auto now_c { std::chrono::system_clock::to_time_t(now) };
|
||||
std::tm tm { *std::gmtime(&now_c) };
|
||||
std::ostringstream oss;
|
||||
oss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%SZ");
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void Logger::log(Level level, std::string_view msg)
|
||||
{
|
||||
auto time_str = get_current_time_string();
|
||||
std::string level_str;
|
||||
switch (level) {
|
||||
case Logger::Level::Debug:
|
||||
level_str = "DEBUG";
|
||||
break;
|
||||
case Logger::Level::Info:
|
||||
level_str = " INFO";
|
||||
break;
|
||||
case Logger::Level::Warning:
|
||||
level_str = " WARN";
|
||||
break;
|
||||
case Logger::Level::Error:
|
||||
level_str = "ERROR";
|
||||
break;
|
||||
default:
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
auto const msg_file { std::format("{} [{}] {}", time_str, level_str, msg) };
|
||||
#ifdef _WIN32
|
||||
auto const msg_stdout = msg_file;
|
||||
#elif __EMSCRIPTEN__
|
||||
auto const msg_stdout = msg_file;
|
||||
#else
|
||||
char const *color;
|
||||
switch (level) {
|
||||
case Logger::Level::Debug:
|
||||
color = FG_GRAY;
|
||||
break;
|
||||
case Logger::Level::Info:
|
||||
color = FG_BLUE;
|
||||
break;
|
||||
case Logger::Level::Warning:
|
||||
color = FG_YELLOW;
|
||||
break;
|
||||
case Logger::Level::Error:
|
||||
color = FG_RED;
|
||||
break;
|
||||
default:
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
auto const msg_stdout { std::format(
|
||||
"{}{} [{}] {}" ANSI_RESET, color, time_str, level_str, msg) };
|
||||
#endif
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
m_fout << msg_file << std::endl;
|
||||
#endif // EMSCRIPTEN
|
||||
std::println(std::cerr, "{}", msg_stdout);
|
||||
}
|
||||
52
src/Logger.h
Normal file
52
src/Logger.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <string_view>
|
||||
|
||||
struct Logger {
|
||||
enum class Level {
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
};
|
||||
|
||||
Logger(std::string_view app_name);
|
||||
|
||||
auto debug(std::string_view msg) -> void;
|
||||
auto info(std::string_view msg) -> void;
|
||||
auto warn(std::string_view msg) -> void;
|
||||
auto err(std::string_view msg) -> void;
|
||||
|
||||
template<typename... Args>
|
||||
auto debug(std::format_string<Args...> fmt, Args &&...args) -> void
|
||||
{
|
||||
log(Level::Debug, std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
auto info(std::format_string<Args...> fmt, Args &&...args) -> void
|
||||
{
|
||||
log(Level::Info, std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
auto warn(std::format_string<Args...> fmt, Args &&...args) -> void
|
||||
{
|
||||
log(Level::Warning, std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
auto err(std::format_string<Args...> fmt, Args &&...args) -> void
|
||||
{
|
||||
log(Level::Error, std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
auto log(Level level, std::string_view msg) -> void;
|
||||
|
||||
private:
|
||||
#ifndef __EMSCRIPTEN__
|
||||
std::ofstream m_fout;
|
||||
#endif
|
||||
};
|
||||
26
src/Util.h
Normal file
26
src/Util.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
template<typename F> struct privDefer {
|
||||
F f;
|
||||
privDefer(F f)
|
||||
: f(f)
|
||||
{
|
||||
}
|
||||
~privDefer() { f(); }
|
||||
};
|
||||
|
||||
template<typename F> privDefer<F> defer_func(F f) { return privDefer<F>(f); }
|
||||
|
||||
#define DEFER_1(x, y) x##y
|
||||
#define DEFER_2(x, y) DEFER_1(x, y)
|
||||
#define DEFER_3(x) DEFER_2(x, __COUNTER__)
|
||||
#define defer(code) auto DEFER_3(_defer_) = defer_func([&]() { code; })
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define ALIGN(a) __declspec(align(a))
|
||||
#else
|
||||
# define ALIGN(a) __attribute__((aligned(a)))
|
||||
#endif
|
||||
7
src/main.cpp
Normal file
7
src/main.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "src/Application.h"
|
||||
|
||||
auto main() -> int
|
||||
{
|
||||
Lunar::Application app {};
|
||||
app.run();
|
||||
}
|
||||
Reference in New Issue
Block a user