mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-21 06:52:22 -04:00
Merge branch 'main' of https://github.com/TakaRikka/dusk
This commit is contained in:
+72
-13
@@ -48,12 +48,17 @@ else ()
|
||||
message(STATUS "Unable to find git, commit information will not be available")
|
||||
endif ()
|
||||
|
||||
if (DUSK_WC_DESCRIBE)
|
||||
string(REGEX REPLACE "v([0-9]+)\.([0-9]+)\.([0-9]+)\-([0-9]+).*" "\\1.\\2.\\3.\\4" DUSK_VERSION_STRING "${DUSK_WC_DESCRIBE}")
|
||||
string(REGEX REPLACE "v([0-9]+)\.([0-9]+)\.([0-9]+).*" "\\1.\\2.\\3" DUSK_SHORT_VERSION_STRING "${DUSK_WC_DESCRIBE}")
|
||||
if (DUSK_WC_DESCRIBE MATCHES "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)(-([0-9]+).*)?$")
|
||||
set(DUSK_SHORT_VERSION_STRING "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
|
||||
if (CMAKE_MATCH_5)
|
||||
set(DUSK_VERSION_STRING "${DUSK_SHORT_VERSION_STRING}.${CMAKE_MATCH_5}")
|
||||
else ()
|
||||
set(DUSK_VERSION_STRING "${DUSK_SHORT_VERSION_STRING}.0")
|
||||
endif ()
|
||||
else ()
|
||||
set(DUSK_WC_DESCRIBE "UNKNOWN-VERSION")
|
||||
set(DUSK_VERSION_STRING "0.0.0")
|
||||
set(DUSK_VERSION_STRING "0.0.0.0")
|
||||
set(DUSK_SHORT_VERSION_STRING "0.0.0")
|
||||
endif ()
|
||||
|
||||
# Add version information to CI environment variables
|
||||
@@ -95,6 +100,10 @@ option(DUSK_BUILD_WARNINGS "Enable compiler warnings (off by default)")
|
||||
option(DUSK_SELECTED_OPT "If on, selected parts of the project will be compiled with optimizations on Debug, intending to make the game run at 30 FPS. Note for MSVC: you will need to remove '/RTC1' from your debug flags in CMake.")
|
||||
option(DUSK_MOVIE_SUPPORT "If on, compile against libjpeg-turbo to enable THP file decoding" ON)
|
||||
|
||||
option(DUSK_ENABLE_SENTRY_NATIVE "Enable sentry-native crash reporting support" OFF)
|
||||
set(DUSK_SENTRY_DSN "" CACHE STRING "Sentry DSN")
|
||||
set(DUSK_SENTRY_ENVIRONMENT "development" CACHE STRING "Sentry environment")
|
||||
|
||||
if (DUSK_MOVIE_SUPPORT)
|
||||
find_package(libjpeg-turbo QUIET)
|
||||
if (libjpeg-turbo_FOUND)
|
||||
@@ -148,21 +157,23 @@ elseif (APPLE)
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN")
|
||||
set(CMAKE_BUILD_RPATH "$ORIGIN")
|
||||
elseif (MSVC)
|
||||
add_compile_options(/bigobj)
|
||||
add_compile_options(/Zc:strictStrings-)
|
||||
add_compile_options(/MP)
|
||||
add_compile_options(/FS)
|
||||
add_compile_options(
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:/bigobj>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:/Zc:strictStrings->
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:/MP>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:/FS>
|
||||
)
|
||||
|
||||
if (NOT DUSK_BUILD_WARNINGS)
|
||||
add_compile_options(/W0)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/W0>)
|
||||
else ()
|
||||
# Disable warnings
|
||||
add_compile_options(/wd4068) # unknown pragma
|
||||
add_compile_options(/wd4291) # no matching delete operator, leaks if exception thrown
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wd4068>) # unknown pragma
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wd4291>) # no matching delete operator, leaks if exception thrown
|
||||
# Only show warnings once
|
||||
add_compile_options(/wo4244) # narrowing conversion, possible data loss
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wo4244>) # narrowing conversion, possible data loss
|
||||
endif ()
|
||||
add_compile_options(/utf-8)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/utf-8>)
|
||||
endif ()
|
||||
|
||||
|
||||
@@ -183,6 +194,46 @@ FetchContent_Declare(json
|
||||
)
|
||||
FetchContent_MakeAvailable(cxxopts json)
|
||||
|
||||
if (DUSK_ENABLE_SENTRY_NATIVE)
|
||||
message(STATUS "dusk: Fetching sentry-native")
|
||||
set(SENTRY_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||
set(SENTRY_BACKEND crashpad CACHE STRING "" FORCE)
|
||||
if (WIN32)
|
||||
set(SENTRY_TRANSPORT winhttp CACHE STRING "" FORCE)
|
||||
endif ()
|
||||
set(SENTRY_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(SENTRY_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
set(SENTRY_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
|
||||
FetchContent_Declare(sentry_native
|
||||
GIT_REPOSITORY https://github.com/getsentry/sentry-native.git
|
||||
GIT_TAG 0.13.6
|
||||
GIT_SHALLOW TRUE
|
||||
GIT_PROGRESS TRUE
|
||||
GIT_SUBMODULES_RECURSE TRUE
|
||||
)
|
||||
if (NOT sentry_native_POPULATED)
|
||||
FetchContent_Populate(sentry_native)
|
||||
set(_dusk_skip_install_rules ${CMAKE_SKIP_INSTALL_RULES})
|
||||
set(CMAKE_SKIP_INSTALL_RULES ON)
|
||||
add_subdirectory(${sentry_native_SOURCE_DIR} ${sentry_native_BINARY_DIR} EXCLUDE_FROM_ALL)
|
||||
set(CMAKE_SKIP_INSTALL_RULES ${_dusk_skip_install_rules})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL Windows)
|
||||
set(PLATFORM_NAME win32)
|
||||
elseif (CMAKE_SYSTEM_NAME STREQUAL Darwin)
|
||||
if (IOS)
|
||||
set(PLATFORM_NAME ios)
|
||||
elseif (TVOS)
|
||||
set(PLATFORM_NAME tvos)
|
||||
else ()
|
||||
set(PLATFORM_NAME macos)
|
||||
endif ()
|
||||
else ()
|
||||
string(TOLOWER CMAKE_SYSTEM_NAME PLATFORM_NAME)
|
||||
endif ()
|
||||
|
||||
configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_BINARY_DIR}/version.h)
|
||||
|
||||
include(files.cmake)
|
||||
@@ -222,6 +273,11 @@ set(GAME_LIBS aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::p
|
||||
|
||||
list(APPEND GAME_LIBS libzstd_static)
|
||||
|
||||
if (DUSK_ENABLE_SENTRY_NATIVE)
|
||||
list(APPEND GAME_LIBS sentry)
|
||||
list(APPEND GAME_COMPILE_DEFS DUSK_ENABLE_SENTRY_NATIVE=1 SENTRY_BUILD_STATIC=1)
|
||||
endif ()
|
||||
|
||||
if (DUSK_MOVIE_SUPPORT)
|
||||
if (TARGET libjpeg-turbo::turbojpeg-static)
|
||||
list(APPEND GAME_LIBS libjpeg-turbo::turbojpeg-static)
|
||||
@@ -274,6 +330,9 @@ add_executable(dusk src/dusk/main.cpp)
|
||||
target_compile_definitions(dusk PRIVATE TARGET_PC AVOID_UB=1 VERSION=0)
|
||||
target_include_directories(dusk PRIVATE include)
|
||||
target_link_libraries(dusk PRIVATE game aurora::main)
|
||||
if (TARGET crashpad_handler)
|
||||
add_dependencies(dusk crashpad_handler)
|
||||
endif ()
|
||||
|
||||
add_custom_command(TARGET dusk POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
|
||||
@@ -1338,6 +1338,7 @@ set(DUSK_FILES
|
||||
src/d/actor/d_a_alink_dusk.cpp
|
||||
src/dusk/asserts.cpp
|
||||
src/dusk/config.cpp
|
||||
src/dusk/crash_reporting.cpp
|
||||
src/dusk/endian.cpp
|
||||
src/dusk/extras.c
|
||||
src/dusk/extras.cpp
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace dusk {
|
||||
|
||||
void InitializeCrashReporting();
|
||||
void ShutdownCrashReporting();
|
||||
|
||||
} // namespace dusk
|
||||
@@ -20,6 +20,11 @@ void begin_record();
|
||||
void end_record();
|
||||
void interpolate(float step);
|
||||
float get_interpolation_step();
|
||||
|
||||
void notify_presentation_frame();
|
||||
void request_presentation_sync();
|
||||
bool presentation_sync_active();
|
||||
|
||||
void notify_sim_tick_complete();
|
||||
uint32_t begin_presentation_ui_pass();
|
||||
uint32_t get_presentation_ui_advance_ticks();
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
void aurora_log_callback(AuroraLogLevel level, const char* module, const char* message, unsigned int len);
|
||||
|
||||
namespace dusk {
|
||||
void InitializeFileLogging(const char* configDir, AuroraLogLevel logLevel);
|
||||
void ShutdownFileLogging();
|
||||
const char* GetLogFilePath();
|
||||
void SendToStubLog(AuroraLogLevel level, const char* module, const char* message);
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ struct UserSettings {
|
||||
ConfigVar<bool> skipPreLaunchUI;
|
||||
ConfigVar<bool> showPipelineCompilation;
|
||||
ConfigVar<bool> wasPresetChosen;
|
||||
ConfigVar<bool> enableCrashReporting;
|
||||
} backend;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
#include "JSystem/JStudio/JStudio/jstudio-object.h"
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/audio.h"
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#include "dusk/settings.h"
|
||||
#endif
|
||||
|
||||
namespace JStudio {
|
||||
namespace {
|
||||
@@ -650,10 +654,25 @@ value_or_fun:
|
||||
return;
|
||||
|
||||
value:
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation && u <= 5 &&
|
||||
(operation == data::UNK_0x2 || operation == data::UNK_0x3 || operation == data::UNK_0x12))
|
||||
{
|
||||
dusk::frame_interp::request_presentation_sync();
|
||||
}
|
||||
#endif
|
||||
adaptor->adaptor_setVariableValue(control, u, operation, param_2, param_3);
|
||||
return;
|
||||
|
||||
value_n:
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation &&
|
||||
(pN == TAdaptor_camera::sauVariableValue_3_POSITION_XYZ || pN == TAdaptor_camera::sauVariableValue_3_TARGET_POSITION_XYZ) &&
|
||||
(operation == data::UNK_0x2 || operation == data::UNK_0x3 || operation == data::UNK_0x12))
|
||||
{
|
||||
dusk::frame_interp::request_presentation_sync();
|
||||
}
|
||||
#endif
|
||||
adaptor->adaptor_setVariableValue_n(control, pN, u, operation, param_2, param_3);
|
||||
return;
|
||||
|
||||
|
||||
+37
-1
@@ -20,7 +20,6 @@
|
||||
#include "m_Do/m_Do_controller_pad.h"
|
||||
#include "m_Do/m_Do_graphic.h"
|
||||
#include "m_Do/m_Do_lib.h"
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
@@ -29,6 +28,11 @@
|
||||
#include "d/d_debug_camera.h"
|
||||
#endif
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#include "dusk/logging.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
static f32 limitf(f32 value, f32 min, f32 max) {
|
||||
@@ -2048,6 +2052,18 @@ s32 dCamera_c::nextType(s32 i_curType) {
|
||||
bool dCamera_c::onTypeChange(s32 i_curType, s32 i_nextType) {
|
||||
daAlink_c* unusedPlayer = daAlink_getAlinkActorClass();
|
||||
|
||||
#if TARGET_PC
|
||||
const s32 event_type_id = specialType[CAM_TYPE_EVENT];
|
||||
DuskLog.debug(
|
||||
"frameInterp: onTypeChange {} -> {} (event_type_id={}, leaving_event={}, entering_event={})",
|
||||
static_cast<int>(i_curType),
|
||||
static_cast<int>(i_nextType),
|
||||
static_cast<int>(event_type_id),
|
||||
i_curType == event_type_id,
|
||||
i_nextType == event_type_id
|
||||
);
|
||||
#endif
|
||||
|
||||
if (i_curType == specialType[CAM_TYPE_EVENT]) {
|
||||
if (mCamSetup.CheckFlag(0x4000)) {
|
||||
mGear = 0;
|
||||
@@ -10161,6 +10177,26 @@ bool dCamera_c::eventCamera(s32 param_0) {
|
||||
ActionNames[var_r29]);
|
||||
#endif
|
||||
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
switch (var_r29) {
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 12:
|
||||
dusk::frame_interp::request_presentation_sync();
|
||||
break;
|
||||
default:
|
||||
DuskLog.debug(
|
||||
"frameInterp: presentation sync not requested for ZEV event [{}] (staff idx {})",
|
||||
static_cast<const char*>(ActionNames[var_r29]),
|
||||
static_cast<int>(mEventData.mStaffIdx)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (getEvFloatData(&sp28, "KeepDist") != 0 && mViewCache.mDirection.R() < sp28)
|
||||
{
|
||||
mViewCache.mDirection.R(sp28);
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
#include "dusk/crash_reporting.h"
|
||||
|
||||
#include "dusk/app_info.hpp"
|
||||
#include "dusk/dusk.h"
|
||||
#include "dusk/logging.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
|
||||
#include "SDL3/SDL_filesystem.h"
|
||||
|
||||
#if DUSK_ENABLE_SENTRY_NATIVE
|
||||
#include <sentry.h>
|
||||
#endif
|
||||
|
||||
namespace dusk {
|
||||
|
||||
namespace {
|
||||
|
||||
#if DUSK_ENABLE_SENTRY_NATIVE
|
||||
bool g_sentryInitialized = false;
|
||||
|
||||
bool IsTruthy(std::string_view value) {
|
||||
return value == "1" || value == "true" || value == "TRUE" || value == "yes"
|
||||
|| value == "YES" || value == "on" || value == "ON";
|
||||
}
|
||||
|
||||
std::string GetEnvOrEmpty(const char* name) {
|
||||
if (const char* value = std::getenv(name)) {
|
||||
return value;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool GetEffectiveEnabled() {
|
||||
const std::string env = GetEnvOrEmpty("DUSK_SENTRY_ENABLED");
|
||||
if (!env.empty()) {
|
||||
return IsTruthy(env);
|
||||
}
|
||||
return getSettings().backend.enableCrashReporting;
|
||||
}
|
||||
|
||||
std::string GetEffectiveDsn() {
|
||||
const std::string env = GetEnvOrEmpty("DUSK_SENTRY_DSN");
|
||||
if (!env.empty()) {
|
||||
return env;
|
||||
}
|
||||
return DUSK_SENTRY_DSN;
|
||||
}
|
||||
|
||||
bool GetEffectiveDebug() {
|
||||
const std::string env = GetEnvOrEmpty("DUSK_SENTRY_DEBUG");
|
||||
if (!env.empty()) {
|
||||
return IsTruthy(env);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GetReleaseName() {
|
||||
return std::string(AppName) + "@" DUSK_WC_DESCRIBE;
|
||||
}
|
||||
|
||||
std::filesystem::path GetSentryDatabasePath() {
|
||||
return std::filesystem::path(configPath) / "sentry";
|
||||
}
|
||||
|
||||
std::filesystem::path GetLogAttachmentPath() {
|
||||
if (const char* logPath = GetLogFilePath()) {
|
||||
return logPath;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::filesystem::path GetCrashpadHandlerPath() {
|
||||
const char* basePath = SDL_GetBasePath();
|
||||
if (!basePath) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::filesystem::path handlerDir(basePath);
|
||||
#if _WIN32
|
||||
return handlerDir / "crashpad_handler.exe";
|
||||
#else
|
||||
return handlerDir / "crashpad_handler";
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConfigurePathOptions(sentry_options_t* options) {
|
||||
const auto databasePath = GetSentryDatabasePath();
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(databasePath, ec);
|
||||
if (ec) {
|
||||
DuskLog.warn("Unable to create Sentry database path '{}': {}",
|
||||
databasePath.string(), ec.message());
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
const std::wstring databasePathWide = databasePath.wstring();
|
||||
sentry_options_set_database_pathw(options, databasePathWide.c_str());
|
||||
|
||||
const auto handlerPath = GetCrashpadHandlerPath();
|
||||
if (!handlerPath.empty()) {
|
||||
const std::wstring handlerPathWide = handlerPath.wstring();
|
||||
sentry_options_set_handler_pathw(options, handlerPathWide.c_str());
|
||||
}
|
||||
#else
|
||||
const std::string databasePathUtf8 = databasePath.string();
|
||||
sentry_options_set_database_path(options, databasePathUtf8.c_str());
|
||||
|
||||
const auto handlerPath = GetCrashpadHandlerPath();
|
||||
if (!handlerPath.empty()) {
|
||||
const std::string handlerPathUtf8 = handlerPath.string();
|
||||
sentry_options_set_handler_path(options, handlerPathUtf8.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
const auto logPath = GetLogAttachmentPath();
|
||||
if (!logPath.empty()) {
|
||||
#if _WIN32
|
||||
sentry_options_add_attachmentw(options, logPath.wstring().c_str());
|
||||
#else
|
||||
sentry_options_add_attachment(options, logPath.string().c_str());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
void InitializeCrashReporting() {
|
||||
#if DUSK_ENABLE_SENTRY_NATIVE
|
||||
if (g_sentryInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetEffectiveEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string dsn = GetEffectiveDsn();
|
||||
if (dsn.empty()) {
|
||||
DuskLog.warn("Crash reporting is enabled but no Sentry DSN is configured");
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string release = GetReleaseName();
|
||||
|
||||
sentry_options_t* options = sentry_options_new();
|
||||
sentry_options_set_dsn(options, dsn.c_str());
|
||||
sentry_options_set_release(options, release.c_str());
|
||||
sentry_options_set_environment(options, DUSK_SENTRY_ENVIRONMENT);
|
||||
sentry_options_set_debug(options, GetEffectiveDebug() ? 1 : 0);
|
||||
sentry_options_set_cache_keep(options, 1);
|
||||
sentry_options_set_max_breadcrumbs(options, 100);
|
||||
ConfigurePathOptions(options);
|
||||
|
||||
if (sentry_init(options) != 0) {
|
||||
DuskLog.warn("Failed to initialize Sentry crash reporting");
|
||||
return;
|
||||
}
|
||||
|
||||
sentry_set_tag("git_branch", DUSK_WC_BRANCH);
|
||||
sentry_set_tag("build_type", DUSK_BUILD_TYPE);
|
||||
sentry_set_tag("tp_version", DUSK_TP_VERSION);
|
||||
g_sentryInitialized = true;
|
||||
|
||||
DuskLog.info("Initialized Sentry crash reporting");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ShutdownCrashReporting() {
|
||||
#if DUSK_ENABLE_SENTRY_NATIVE
|
||||
if (!g_sentryInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
sentry_close();
|
||||
g_sentryInitialized = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace dusk
|
||||
@@ -63,6 +63,10 @@ bool s_initialized = false;
|
||||
bool g_enabled = false;
|
||||
bool g_recording = false;
|
||||
bool g_interpolating = false;
|
||||
bool g_sync_presentation = false;
|
||||
uint32_t g_presentation_counter = 0;
|
||||
uint32_t g_presentation_sync_end = 0;
|
||||
|
||||
float g_step = 0.0f;
|
||||
uint32_t g_pending_presentation_ui_ticks = 0;
|
||||
uint32_t g_current_presentation_ui_ticks = 0;
|
||||
@@ -235,7 +239,7 @@ void interpolate_branch(const Path& old_path, const Path& new_path, float step)
|
||||
}
|
||||
|
||||
const Mtx* resolve_replacement(const Mtx* source, Mtx* scratch) {
|
||||
if (!g_interpolating || source == nullptr) {
|
||||
if (!g_interpolating || source == nullptr || dusk::frame_interp::presentation_sync_active()) {
|
||||
return source;
|
||||
}
|
||||
|
||||
@@ -268,6 +272,7 @@ void begin_record() {
|
||||
ensure_initialized();
|
||||
if (!g_enabled) {
|
||||
g_interpolating = false;
|
||||
g_sync_presentation = false;
|
||||
g_previous_recording = {};
|
||||
g_current_recording = {};
|
||||
g_current_path.clear();
|
||||
@@ -275,6 +280,10 @@ void begin_record() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_sync_presentation && g_presentation_counter > g_presentation_sync_end) {
|
||||
g_sync_presentation = false;
|
||||
}
|
||||
|
||||
g_previous_recording = std::move(g_current_recording);
|
||||
g_current_recording = {};
|
||||
g_current_path.clear();
|
||||
@@ -292,21 +301,38 @@ void interpolate(float step) {
|
||||
ensure_initialized();
|
||||
clear_replacements();
|
||||
g_step = std::clamp(step, 0.0f, 1.0f);
|
||||
g_interpolating = g_enabled && !g_recording && has_recording_data(g_current_recording);
|
||||
g_interpolating = g_enabled && !g_recording && !g_sync_presentation && has_recording_data(g_current_recording);
|
||||
if (!g_interpolating) {
|
||||
return;
|
||||
}
|
||||
const Path& old_root = has_recording_data(g_previous_recording) ? g_previous_recording.root : g_current_recording.root;
|
||||
interpolate_branch(old_root, g_current_recording.root, g_step);
|
||||
}
|
||||
|
||||
if (!has_recording_data(g_previous_recording)) {
|
||||
interpolate_branch(g_current_recording.root, g_current_recording.root, g_step);
|
||||
void notify_presentation_frame() {
|
||||
ensure_initialized();
|
||||
++g_presentation_counter;
|
||||
}
|
||||
|
||||
void request_presentation_sync() {
|
||||
ensure_initialized();
|
||||
if (!g_enabled) {
|
||||
return;
|
||||
}
|
||||
g_sync_presentation = true;
|
||||
g_presentation_sync_end = g_presentation_counter + 1;
|
||||
}
|
||||
|
||||
interpolate_branch(g_previous_recording.root, g_current_recording.root, g_step);
|
||||
bool presentation_sync_active() {
|
||||
if (!s_initialized || !g_enabled) {
|
||||
return false;
|
||||
}
|
||||
return g_sync_presentation;
|
||||
}
|
||||
|
||||
float get_interpolation_step() {
|
||||
return g_step;
|
||||
ensure_initialized();
|
||||
return presentation_sync_active() ? 1.0f : g_step;
|
||||
}
|
||||
|
||||
void notify_sim_tick_complete() {
|
||||
@@ -371,7 +397,7 @@ void record_final_mtx_raw(const Mtx* dest, const Mtx src) {
|
||||
}
|
||||
|
||||
bool lookup_replacement(const void* source, Mtx out) {
|
||||
if (!s_initialized || !g_interpolating || source == nullptr) {
|
||||
if (presentation_sync_active() || !g_interpolating || source == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -385,7 +411,7 @@ bool lookup_replacement(const void* source, Mtx out) {
|
||||
}
|
||||
|
||||
bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out) {
|
||||
if (!s_initialized || !g_interpolating || lhs == nullptr || rhs == nullptr) {
|
||||
if (presentation_sync_active() || !g_interpolating || lhs == nullptr || rhs == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -393,9 +419,7 @@ bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out) {
|
||||
Mtx rhs_scratch;
|
||||
const Mtx* resolved_lhs = resolve_replacement(reinterpret_cast<const Mtx*>(lhs), &lhs_scratch);
|
||||
const Mtx* resolved_rhs = resolve_replacement(reinterpret_cast<const Mtx*>(rhs), &rhs_scratch);
|
||||
if (resolved_lhs == reinterpret_cast<const Mtx*>(lhs) &&
|
||||
resolved_rhs == reinterpret_cast<const Mtx*>(rhs))
|
||||
{
|
||||
if (resolved_lhs == reinterpret_cast<const Mtx*>(lhs) && resolved_rhs == reinterpret_cast<const Mtx*>(rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ void ImGuiEngine_Initialize(float scale) {
|
||||
|
||||
Image GetImage(const std::string& path) {
|
||||
if (!AssetExists(path)) {
|
||||
DuskLog.warn("Image '{}' does not exist", path);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +133,9 @@ namespace dusk {
|
||||
if (ImGui::BeginMenu("Interface")) {
|
||||
config::ImGuiCheckbox("Skip Pre-Launch UI", getSettings().backend.skipPreLaunchUI);
|
||||
config::ImGuiCheckbox("Show Pipeline Compilation", getSettings().backend.showPipelineCompilation);
|
||||
#if DUSK_ENABLE_SENTRY_NATIVE
|
||||
config::ImGuiCheckbox("Enable Crash Reporting", getSettings().backend.enableCrashReporting);
|
||||
#endif
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
+118
-22
@@ -1,6 +1,11 @@
|
||||
#include "dusk/logging.h"
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "tracy/Tracy.hpp"
|
||||
|
||||
@@ -20,6 +25,60 @@ static constexpr std::string_view StubFragments[] = {
|
||||
"but selective updates are not implemented"sv,
|
||||
};
|
||||
|
||||
namespace {
|
||||
std::mutex g_logMutex;
|
||||
FILE* g_logFile = nullptr;
|
||||
std::string g_logFilePath;
|
||||
|
||||
const char* LogLevelString(AuroraLogLevel level) {
|
||||
switch (level) {
|
||||
case LOG_DEBUG:
|
||||
return "DEBUG";
|
||||
case LOG_INFO:
|
||||
return "INFO";
|
||||
case LOG_WARNING:
|
||||
return "WARNING";
|
||||
case LOG_ERROR:
|
||||
return "ERROR";
|
||||
case LOG_FATAL:
|
||||
return "FATAL";
|
||||
}
|
||||
|
||||
return "??";
|
||||
}
|
||||
|
||||
FILE* LogStreamForLevel(AuroraLogLevel level) {
|
||||
return level >= LOG_ERROR ? stderr : stdout;
|
||||
}
|
||||
|
||||
std::string MakeTimestampedLogName() {
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const std::time_t nowTime = std::chrono::system_clock::to_time_t(now);
|
||||
|
||||
std::tm localTime{};
|
||||
#if _WIN32
|
||||
localtime_s(&localTime, &nowTime);
|
||||
#else
|
||||
localtime_r(&nowTime, &localTime);
|
||||
#endif
|
||||
|
||||
std::array<char, 32> buffer{};
|
||||
std::strftime(buffer.data(), buffer.size(), "dusk-%Y%m%d-%H%M%S.log", &localTime);
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
void WriteLogLine(FILE* out, const char* levelStr, const char* module, const char* message, unsigned int len) {
|
||||
if (out == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::fprintf(out, "[%s | %s] ", levelStr, module);
|
||||
std::fwrite(message, 1, len, out);
|
||||
std::fputc('\n', out);
|
||||
std::fflush(out);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static bool IsForStubLog(const char* message) {
|
||||
std::string_view msg_view(message);
|
||||
|
||||
@@ -40,32 +99,69 @@ void aurora_log_callback(AuroraLogLevel level, const char* module, const char* m
|
||||
return;
|
||||
}
|
||||
|
||||
const char* levelStr = "??";
|
||||
FILE* out = stdout;
|
||||
switch (level) {
|
||||
case LOG_DEBUG:
|
||||
levelStr = "DEBUG";
|
||||
break;
|
||||
case LOG_INFO:
|
||||
levelStr = "INFO";
|
||||
break;
|
||||
case LOG_WARNING:
|
||||
levelStr = "WARNING";
|
||||
break;
|
||||
case LOG_ERROR:
|
||||
levelStr = "ERROR";
|
||||
out = stderr;
|
||||
break;
|
||||
case LOG_FATAL:
|
||||
levelStr = "FATAL";
|
||||
out = stderr;
|
||||
break;
|
||||
if (module == nullptr) {
|
||||
module = "";
|
||||
}
|
||||
fprintf(out, "[%s | %s] %s\n", levelStr, module, message);
|
||||
|
||||
const char* levelStr = LogLevelString(level);
|
||||
FILE* out = LogStreamForLevel(level);
|
||||
WriteLogLine(out, levelStr, module, message, len);
|
||||
|
||||
{
|
||||
std::lock_guard lock(g_logMutex);
|
||||
if (g_logFile != nullptr) {
|
||||
WriteLogLine(g_logFile, levelStr, module, message, len);
|
||||
}
|
||||
}
|
||||
|
||||
if (level == LOG_FATAL) {
|
||||
fflush(out);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
aurora::Module DuskLog("dusk");
|
||||
|
||||
void dusk::InitializeFileLogging(const char* configDir, AuroraLogLevel logLevel) {
|
||||
std::lock_guard lock(g_logMutex);
|
||||
if (g_logFile != nullptr || configDir == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
const std::filesystem::path logsDir = std::filesystem::path(configDir) / "logs";
|
||||
std::filesystem::create_directories(logsDir, ec);
|
||||
if (ec) {
|
||||
std::fprintf(stderr, "[WARNING | dusk] Failed to create log directory '%s': %s\n",
|
||||
logsDir.string().c_str(), ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
const std::filesystem::path logPath = logsDir / MakeTimestampedLogName();
|
||||
g_logFile = std::fopen(logPath.string().c_str(), "wb");
|
||||
if (g_logFile == nullptr) {
|
||||
std::fprintf(stderr, "[WARNING | dusk] Failed to open log file '%s'\n",
|
||||
logPath.string().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
g_logFilePath = logPath.string();
|
||||
aurora::g_config.logCallback = &aurora_log_callback;
|
||||
aurora::g_config.logLevel = logLevel;
|
||||
WriteLogLine(g_logFile, "INFO", "dusk", "File logging initialized", 24);
|
||||
}
|
||||
|
||||
void dusk::ShutdownFileLogging() {
|
||||
std::lock_guard lock(g_logMutex);
|
||||
if (g_logFile == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::fflush(g_logFile);
|
||||
std::fclose(g_logFile);
|
||||
g_logFile = nullptr;
|
||||
}
|
||||
|
||||
const char* dusk::GetLogFilePath() {
|
||||
std::lock_guard lock(g_logMutex);
|
||||
return g_logFilePath.empty() ? nullptr : g_logFilePath.c_str();
|
||||
}
|
||||
|
||||
@@ -76,7 +76,8 @@ UserSettings g_userSettings = {
|
||||
.graphicsBackend {"backend.graphicsBackend", "auto"},
|
||||
.skipPreLaunchUI {"backend.skipPreLaunchUI", false},
|
||||
.showPipelineCompilation {"backend.showPipelineCompilation", false},
|
||||
.wasPresetChosen {"backend.wasPresetChosen", false}
|
||||
.wasPresetChosen {"backend.wasPresetChosen", false},
|
||||
.enableCrashReporting {"backend.enableCrashReporting", true}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -139,6 +140,7 @@ void registerSettings() {
|
||||
Register(g_userSettings.backend.skipPreLaunchUI);
|
||||
Register(g_userSettings.backend.showPipelineCompilation);
|
||||
Register(g_userSettings.backend.wasPresetChosen);
|
||||
Register(g_userSettings.backend.enableCrashReporting);
|
||||
}
|
||||
|
||||
// Transient settings
|
||||
|
||||
+55
-1
@@ -43,9 +43,12 @@
|
||||
#include <cstring>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
#include "SSystem/SComponent/c_API.h"
|
||||
#include "dusk/app_info.hpp"
|
||||
#include "dusk/crash_reporting.h"
|
||||
#include "dusk/dusk.h"
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#include "dusk/gyro_aim.h"
|
||||
@@ -240,6 +243,8 @@ void main01(void) {
|
||||
}
|
||||
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
dusk::frame_interp::notify_presentation_frame();
|
||||
|
||||
while (accumulator >= kSimStepSeconds) {
|
||||
mDoCPd_c::read();
|
||||
if (dusk::getSettings().game.enableGyroAim) {
|
||||
@@ -363,6 +368,48 @@ static const char* CalculateConfigPath() {
|
||||
return result;
|
||||
}
|
||||
|
||||
static void EnsureInitialPipelineCache(const char* configDir) {
|
||||
if (configDir == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::filesystem::path configPathFs(configDir);
|
||||
const std::filesystem::path pipelineCachePath = configPathFs / "pipeline_cache.db";
|
||||
if (std::filesystem::exists(pipelineCachePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char* basePath = SDL_GetBasePath();
|
||||
if (basePath == nullptr) {
|
||||
DuskLog.warn("Unable to resolve base path while seeding pipeline cache: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
const std::filesystem::path initialPipelineCachePath =
|
||||
std::filesystem::path(basePath) / "initial_pipeline_cache.db";
|
||||
if (!std::filesystem::exists(initialPipelineCachePath)) {
|
||||
DuskLog.info("No bundled initial pipeline cache found at '{}'", initialPipelineCachePath.string());
|
||||
return;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(configPathFs, ec);
|
||||
if (ec) {
|
||||
DuskLog.warn("Failed to create config directory '{}' for pipeline cache: {}",
|
||||
configPathFs.string(), ec.message());
|
||||
return;
|
||||
}
|
||||
|
||||
std::filesystem::copy_file(initialPipelineCachePath, pipelineCachePath, std::filesystem::copy_options::none, ec);
|
||||
if (ec) {
|
||||
DuskLog.warn("Failed to seed pipeline cache from '{}' to '{}': {}",
|
||||
initialPipelineCachePath.string(), pipelineCachePath.string(), ec.message());
|
||||
return;
|
||||
}
|
||||
|
||||
DuskLog.info("Seeded pipeline cache from '{}'", initialPipelineCachePath.string());
|
||||
}
|
||||
|
||||
static constexpr PADDefaultMapping defaultPadMapping = {
|
||||
.buttons = {
|
||||
{SDL_GAMEPAD_BUTTON_SOUTH, PAD_BUTTON_A},
|
||||
@@ -441,9 +488,13 @@ int game_main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
configPath = CalculateConfigPath();
|
||||
const auto startupLogLevel = static_cast<AuroraLogLevel>(parsed_arg_options["log-level"].as<uint8_t>());
|
||||
dusk::InitializeFileLogging(configPath, startupLogLevel);
|
||||
|
||||
dusk::config::LoadFromUserPreferences();
|
||||
ApplyCVarOverrides(parsed_arg_options["cvar"]);
|
||||
dusk::InitializeCrashReporting();
|
||||
EnsureInitialPipelineCache(configPath);
|
||||
|
||||
AuroraConfig config{};
|
||||
config.appName = dusk::AppName;
|
||||
@@ -456,7 +507,7 @@ int game_main(int argc, char* argv[]) {
|
||||
config.windowHeight = defaultWindowHeight * 2;
|
||||
config.desiredBackend = ResolveDesiredBackend(parsed_arg_options);
|
||||
config.logCallback = &aurora_log_callback;
|
||||
config.logLevel = (AuroraLogLevel)parsed_arg_options["log-level"].as<uint8_t>();
|
||||
config.logLevel = startupLogLevel;
|
||||
config.mem1Size = 256 * 1024 * 1024;
|
||||
config.mem2Size = 24 * 1024 * 1024;
|
||||
config.allowJoystickBackgroundEvents = true;
|
||||
@@ -499,6 +550,7 @@ int game_main(int argc, char* argv[]) {
|
||||
if (!dvd_opened) {
|
||||
// pre game launch ui main loop
|
||||
if (!launchUILoop()) {
|
||||
dusk::ShutdownCrashReporting();
|
||||
aurora_shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -536,6 +588,8 @@ int game_main(int argc, char* argv[]) {
|
||||
|
||||
main01();
|
||||
|
||||
dusk::ShutdownCrashReporting();
|
||||
dusk::ShutdownFileLogging();
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
|
||||
+9
-3
@@ -10,11 +10,17 @@
|
||||
#define DUSK_BUILD_TYPE "@CMAKE_BUILD_TYPE@"
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_AMD64)
|
||||
#define DUSK_DLPACKAGE "dusk-@DUSK_WC_DESCRIBE@-@PLATFORM_NAME@-x86_64"
|
||||
#define DUSK_ARCH "x86_64"
|
||||
#elif defined(__i386__) || defined(_M_IX86)
|
||||
#define DUSK_DLPACKAGE "dusk-@DUSK_WC_DESCRIBE@-@PLATFORM_NAME@-x86"
|
||||
#define DUSK_ARCH "x86"
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define DUSK_DLPACKAGE "dusk-@DUSK_WC_DESCRIBE@-@PLATFORM_NAME@-arm64"
|
||||
#define DUSK_ARCH "arm64"
|
||||
#endif
|
||||
|
||||
#define DUSK_PLATFORM_NAME "@PLATFORM_NAME@"
|
||||
#define DUSK_DLPACKAGE "dusk-@DUSK_WC_DESCRIBE@-" DUSK_PLATFORM_NAME "-" DUSK_ARCH
|
||||
|
||||
#define DUSK_SENTRY_DSN "@DUSK_SENTRY_DSN@"
|
||||
#define DUSK_SENTRY_ENVIRONMENT "@DUSK_SENTRY_ENVIRONMENT@"
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user