forked from Xeeynamo/sonic-hybrid-rsdk
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
298 additions
and
0 deletions.
There are no files selected for viewing
107 changes: 107 additions & 0 deletions
107
sonic3air-main/Oxygen/oxygenengine/source/oxygen/helper/Logging.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/* | ||
* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. | ||
* Copyright (C) 2017-2024 by Eukaryot | ||
* | ||
* Published under the GNU GPLv3 open source software license, see license.txt | ||
* or https://www.gnu.org/licenses/gpl-3.0.en.html | ||
*/ | ||
|
||
#include "oxygen/pch.h" | ||
#include "oxygen/helper/Logging.h" | ||
#include "oxygen/platform/PlatformFunctions.h" | ||
#include "oxygen/simulation/LemonScriptRuntime.h" | ||
|
||
|
||
namespace | ||
{ | ||
|
||
class ErrorLogger : public rmx::ErrorHandling::LoggerInterface, public rmx::ErrorHandling::MessageBoxInterface | ||
{ | ||
public: | ||
std::string mCaption; | ||
|
||
protected: | ||
void logMessage(rmx::ErrorSeverity errorSeverity, const std::string& message) override | ||
{ | ||
switch (errorSeverity) | ||
{ | ||
default: | ||
case rmx::ErrorSeverity::INFO: rmx::Logging::log(rmx::LogLevel::INFO, message); break; | ||
case rmx::ErrorSeverity::WARNING: rmx::Logging::log(rmx::LogLevel::WARNING, message); break; | ||
case rmx::ErrorSeverity::ERROR: rmx::Logging::log(rmx::LogLevel::ERROR, message); break; | ||
} | ||
} | ||
|
||
Result showMessageBox(rmx::ErrorHandling::MessageBoxInterface::DialogType dialogType, rmx::ErrorSeverity errorSeverity, const std::string& message, const char* filename, int line) override | ||
{ | ||
#if defined(DEBUG) | ||
std::string name; | ||
std::string ext; | ||
rmx::FileIO::splitPath(filename, nullptr, &name, &ext); | ||
std::string text = message + "\n[" + name + "." + ext + ", line " + std::to_string(line) + "]"; | ||
#else | ||
std::string text = message; | ||
#endif | ||
|
||
// Check if it was caused inside a script function | ||
{ | ||
std::string_view functionName; | ||
std::wstring fileName; | ||
uint32 lineNumber = 0; | ||
std::string moduleName; | ||
if (LemonScriptRuntime::getCurrentScriptFunction(&functionName, &fileName, &lineNumber, &moduleName)) | ||
{ | ||
text += "\n\nCaused during script execution in function '" + std::string(functionName) + "' at line " + std::to_string(lineNumber) + " of file '" + WString(fileName).toStdString() + "' in module '" + moduleName + "'."; | ||
} | ||
} | ||
|
||
std::string caption = (errorSeverity == rmx::ErrorSeverity::ERROR) ? "Error" : "Warning"; | ||
if (!mCaption.empty()) | ||
{ | ||
caption = mCaption + " - " + caption; | ||
} | ||
|
||
if (rmx::ErrorHandling::isDebuggerAttached()) | ||
{ | ||
text += "\n\nBreak here?"; | ||
} | ||
|
||
PlatformFunctions::DialogButtons dialogButtons = PlatformFunctions::DialogButtons::OK_CANCEL; | ||
switch (dialogType) | ||
{ | ||
case rmx::ErrorHandling::MessageBoxInterface::DialogType::ACCEPT_ONLY: dialogButtons = PlatformFunctions::DialogButtons::OK; break; | ||
case rmx::ErrorHandling::MessageBoxInterface::DialogType::ACCEPT_OR_CANCEL: dialogButtons = PlatformFunctions::DialogButtons::OK_CANCEL; break; | ||
case rmx::ErrorHandling::MessageBoxInterface::DialogType::ALL_OPTIONS: dialogButtons = PlatformFunctions::DialogButtons::YES_NO_CANCEL; break; | ||
} | ||
const PlatformFunctions::DialogResult result = PlatformFunctions::showDialogBox(errorSeverity, dialogButtons, caption, text); | ||
return (result == PlatformFunctions::DialogResult::CANCEL) ? Result::IGNORE : (result == PlatformFunctions::DialogResult::OK) ? Result::ACCEPT : Result::ABORT; | ||
} | ||
}; | ||
|
||
static ErrorLogger mErrorLogger; | ||
} | ||
|
||
|
||
|
||
namespace oxygen | ||
{ | ||
void Logging::startup(const std::wstring& filename) | ||
{ | ||
rmx::Logging::addLogger(*new rmx::StdCoutLogger()); | ||
rmx::Logging::addLogger(*new rmx::FileLogger(filename, true)); | ||
|
||
// Register as logger and message box callback for rmx error handling | ||
rmx::ErrorHandling::mLogger = &mErrorLogger; | ||
rmx::ErrorHandling::mMessageBoxImplementation = &mErrorLogger; | ||
} | ||
|
||
void Logging::shutdown() | ||
{ | ||
rmx::Logging::clear(); | ||
} | ||
|
||
void Logging::setAssertBreakCaption(const std::string& caption) | ||
{ | ||
::mErrorLogger.mCaption = caption; | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
sonic3air-main/Oxygen/oxygenengine/source/oxygen/helper/Logging.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. | ||
* Copyright (C) 2017-2024 by Eukaryot | ||
* | ||
* Published under the GNU GPLv3 open source software license, see license.txt | ||
* or https://www.gnu.org/licenses/gpl-3.0.en.html | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <rmxbase.h> | ||
|
||
|
||
namespace oxygen | ||
{ | ||
class Logging | ||
{ | ||
public: | ||
static void startup(const std::wstring& filename); | ||
static void shutdown(); | ||
static void setAssertBreakCaption(const std::string& caption); | ||
}; | ||
} |
168 changes: 168 additions & 0 deletions
168
sonic3air-main/Oxygen/oxygenengine/source/oxygen/helper/Profiling.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
/* | ||
* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. | ||
* Copyright (C) 2017-2024 by Eukaryot | ||
* | ||
* Published under the GNU GPLv3 open source software license, see license.txt | ||
* or https://www.gnu.org/licenses/gpl-3.0.en.html | ||
*/ | ||
|
||
#include "oxygen/pch.h" | ||
#include "oxygen/helper/Profiling.h" | ||
|
||
|
||
namespace profiling | ||
{ | ||
Profiling::Region mRootRegion; | ||
std::map<uint16, Profiling::Region> mRegionsByID; // Does not contain root region | ||
std::vector<Profiling::Region*> mAllRegions; | ||
std::vector<Profiling::Region*> mRegionStack; | ||
Profiling::AdditionalData mAdditionalData; | ||
int mAccumulatedFrames = 0; | ||
|
||
Profiling::Region* getRegionByID(uint16 id) | ||
{ | ||
const auto it = mRegionsByID.find(id); | ||
return (it == mRegionsByID.end()) ? nullptr : &it->second; | ||
} | ||
} | ||
|
||
using namespace profiling; | ||
|
||
|
||
void Profiling::startup() | ||
{ | ||
// No need to do things twice | ||
if (!mAllRegions.empty()) | ||
return; | ||
|
||
mRootRegion.mName = "root"; | ||
mAllRegions.push_back(&mRootRegion); | ||
mRegionStack.push_back(&mRootRegion); | ||
} | ||
|
||
void Profiling::registerRegion(uint16 id, const char* name, const Color& color) | ||
{ | ||
RMX_ASSERT(mRegionsByID.count(id) == 0, "Profiling region with id " << id << " registered twice, second time with name '" << name << "'"); | ||
|
||
Profiling::Region& region = mRegionsByID[id]; | ||
region.mName = name; | ||
region.mColor = color; | ||
mAllRegions.push_back(®ion); | ||
} | ||
|
||
void Profiling::pushRegion(uint16 id) | ||
{ | ||
Region* region = getRegionByID(id); | ||
RMX_ASSERT(nullptr != region, "Profiling region with id " << id << " not found"); | ||
RMX_ASSERT(!region->mOnStack, "Profiling region with name '" << region->mName << "' is already on the stack"); | ||
RMX_ASSERT(mRegionStack.size() >= 1, "Profiling region stack got emptied before"); | ||
|
||
mRegionStack.push_back(region); | ||
region->mOnStack = true; | ||
region->mTimer.resumeTiming(); | ||
|
||
if (nullptr == region->mParent) | ||
{ | ||
region->mParent = mRegionStack[mRegionStack.size() - 2]; | ||
region->mParent->mChildren.push_back(region); | ||
} | ||
else | ||
{ | ||
RMX_ASSERT(region->mParent == mRegionStack[mRegionStack.size() - 2], "Profiling region '" << region->mName << "' has different parents on the stack: '" << region->mParent->mName << "' and '" << mRegionStack[mRegionStack.size() - 2] << "'"); | ||
} | ||
} | ||
|
||
void Profiling::popRegion(uint16 id) | ||
{ | ||
Region* region = getRegionByID(id); | ||
RMX_ASSERT(nullptr != region, "Profiling region with id " << id << " not found"); | ||
RMX_ASSERT(mRegionStack.size() >= 2, "Can't pop another profiling region from stack, that would remove the root region"); | ||
RMX_ASSERT(mRegionStack.back() == region, "Profiling region to be popped must be top of stack"); | ||
|
||
mRegionStack.pop_back(); | ||
region->mOnStack = false; | ||
region->mTimer.pauseTiming(); | ||
} | ||
|
||
void Profiling::nextFrame(int simulationFrameNumber) | ||
{ | ||
RMX_ASSERT(mRegionStack.size() == 1, "Profiling region stack must only contain the root on frame end"); | ||
|
||
for (Region* region : mAllRegions) | ||
{ | ||
const double lastTime = region->mTimer.getAccumulatedSecondsAndRestart(); // For root timer (which is usually still running), this will perform a stop and immediate restart | ||
while (region->mFrameTimes.size() >= MAX_FRAMES) | ||
region->mFrameTimes.pop_front(); | ||
region->mFrameTimes.emplace_back(); | ||
Region::Frame& frame = region->mFrameTimes.back(); | ||
frame.mInclusiveTime = lastTime; | ||
frame.mExclusiveTime = lastTime; | ||
region->mAccumulatedTime += lastTime; | ||
region->mTimer.resetTiming(); | ||
} | ||
for (Region* region : mAllRegions) | ||
{ | ||
if (nullptr != region->mParent) | ||
{ | ||
region->mParent->mFrameTimes.back().mExclusiveTime -= region->mFrameTimes.back().mExclusiveTime; | ||
} | ||
} | ||
mRootRegion.mTimer.resumeTiming(); // Needed for first frame to start timing | ||
|
||
static const PerFrameData dummy; | ||
const PerFrameData& oldData = mAdditionalData.mFrames.empty() ? dummy : mAdditionalData.mFrames.back(); | ||
while (mAdditionalData.mFrames.size() >= MAX_FRAMES) | ||
mAdditionalData.mFrames.pop_front(); | ||
mAdditionalData.mFrames.emplace_back(); | ||
PerFrameData& data = mAdditionalData.mFrames.back(); | ||
data.mSimulationFrameNumber = simulationFrameNumber; | ||
data.mNumSimulationFrames = simulationFrameNumber - oldData.mSimulationFrameNumber; | ||
mAdditionalData.mAccumulatedSimulationFrames += data.mNumSimulationFrames; | ||
|
||
++mAccumulatedFrames; | ||
if (mAccumulatedFrames >= 30 || mRootRegion.mAccumulatedTime >= 1.0) | ||
{ | ||
mAdditionalData.mAverageSimulationsPerSecond = (float)mAdditionalData.mAccumulatedSimulationFrames / (float)mRootRegion.mAccumulatedTime; | ||
mAdditionalData.mAccumulatedSimulationFrames = 0; | ||
|
||
while (mAdditionalData.mSimulationsPerSecondDeque.size() >= 10) | ||
mAdditionalData.mSimulationsPerSecondDeque.pop_front(); | ||
mAdditionalData.mSimulationsPerSecondDeque.push_back(mAdditionalData.mAverageSimulationsPerSecond); | ||
mAdditionalData.mSmoothedSimulationsPerSecond = 0.0f; | ||
for (float value : mAdditionalData.mSimulationsPerSecondDeque) | ||
mAdditionalData.mSmoothedSimulationsPerSecond += value; | ||
mAdditionalData.mSmoothedSimulationsPerSecond /= (float)std::max<size_t>(1, mAdditionalData.mSimulationsPerSecondDeque.size()); | ||
|
||
for (Region* region : mAllRegions) | ||
{ | ||
region->mAverageTime = region->mAccumulatedTime / (float)mAccumulatedFrames; | ||
region->mAccumulatedTime = 0.0; | ||
} | ||
mAccumulatedFrames = 0; | ||
} | ||
} | ||
|
||
Profiling::Region& Profiling::getRootRegion() | ||
{ | ||
return mRootRegion; | ||
} | ||
|
||
void Profiling::listRegionsRecursive(std::vector<std::pair<Region*, int>>& outRegions) | ||
{ | ||
outRegions.clear(); | ||
listRegionsRecursiveInternal(outRegions, mRootRegion, 1); | ||
} | ||
|
||
Profiling::AdditionalData& Profiling::getAdditionalData() | ||
{ | ||
return mAdditionalData; | ||
} | ||
|
||
void Profiling::listRegionsRecursiveInternal(std::vector<std::pair<Region*, int>>& outRegions, Region& parent, int level) | ||
{ | ||
for (Region* child : parent.mChildren) | ||
{ | ||
outRegions.emplace_back(child, level); | ||
listRegionsRecursiveInternal(outRegions, *child, level + 1); | ||
} | ||
} |