16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-17 22:39:17 +02:00

loading time fix

This commit is contained in:
maj00r
2026-05-19 19:04:46 +02:00
parent df5a8a897f
commit 2dfee2a322
11 changed files with 1722 additions and 157 deletions

View File

@@ -1084,12 +1084,8 @@ void eu07_application::init_files()
{
#ifdef _WIN32
DeleteFile("log.txt");
DeleteFile("errors.txt");
CreateDirectory("logs", NULL);
#elif __unix__
unlink("log.txt");
unlink("errors.txt");
mkdir("logs", 0755);
#endif
}
@@ -1127,6 +1123,22 @@ int eu07_application::init_settings(int Argc, char *Argv[])
{
std::string token{Argv[i]};
auto parse_bool_argument =
[](std::string Value, bool &Output)
{
Value = ToLower(Value);
if ((Value == "on") || (Value == "yes") || (Value == "true") || (Value == "1"))
{
Output = true;
return true;
}
if ((Value == "off") || (Value == "no") || (Value == "false") || (Value == "0"))
{
Output = false;
return true;
}
return false;
};
if (token == "-s")
{
@@ -1142,10 +1154,114 @@ int eu07_application::init_settings(int Argc, char *Argv[])
Global.local_start_vehicle = ToLower(Argv[++i]);
}
}
else if (token == "--no-scenario-fileexists-cache")
{
Global.ScenarioFileExistsCache = false;
}
else if (token == "--no-scenario-parser-file-cache")
{
Global.ScenarioParserFileCache = false;
}
else if (token == "--no-scenario-parser-fast-geometry")
{
Global.ScenarioParserFastGeometry = false;
}
else if (token == "--no-scenario-parser-fast-skip")
{
Global.ScenarioParserFastSkip = false;
}
else if (token == "--scenario-fileexists-cache")
{
if ((i + 1 < Argc) && parse_bool_argument(Argv[++i], Global.ScenarioFileExistsCache))
{
continue;
}
std::cout << "invalid value for --scenario-fileexists-cache" << std::endl;
return -1;
}
else if (token == "--scenario-parser-file-cache")
{
if ((i + 1 < Argc) && parse_bool_argument(Argv[++i], Global.ScenarioParserFileCache))
{
continue;
}
std::cout << "invalid value for --scenario-parser-file-cache" << std::endl;
return -1;
}
else if (token == "--scenario-parser-fast-geometry")
{
if ((i + 1 < Argc) && parse_bool_argument(Argv[++i], Global.ScenarioParserFastGeometry))
{
continue;
}
std::cout << "invalid value for --scenario-parser-fast-geometry" << std::endl;
return -1;
}
else if (token == "--scenario-parser-fast-skip")
{
if ((i + 1 < Argc) && parse_bool_argument(Argv[++i], Global.ScenarioParserFastSkip))
{
continue;
}
std::cout << "invalid value for --scenario-parser-fast-skip" << std::endl;
return -1;
}
else if (token.starts_with("--scenario-fileexists-cache="))
{
if (parse_bool_argument(token.substr(std::string("--scenario-fileexists-cache=").size()), Global.ScenarioFileExistsCache))
{
continue;
}
std::cout << "invalid value for --scenario-fileexists-cache" << std::endl;
return -1;
}
else if (token.starts_with("--scenario-parser-file-cache="))
{
if (parse_bool_argument(token.substr(std::string("--scenario-parser-file-cache=").size()), Global.ScenarioParserFileCache))
{
continue;
}
std::cout << "invalid value for --scenario-parser-file-cache" << std::endl;
return -1;
}
else if (token.starts_with("--scenario-parser-fast-geometry="))
{
if (parse_bool_argument(token.substr(std::string("--scenario-parser-fast-geometry=").size()), Global.ScenarioParserFastGeometry))
{
continue;
}
std::cout << "invalid value for --scenario-parser-fast-geometry" << std::endl;
return -1;
}
else if (token.starts_with("--scenario-parser-fast-skip="))
{
if (parse_bool_argument(token.substr(std::string("--scenario-parser-fast-skip=").size()), Global.ScenarioParserFastSkip))
{
continue;
}
std::cout << "invalid value for --scenario-parser-fast-skip" << std::endl;
return -1;
}
else
{
std::cout << "usage: " << std::string(Argv[0]) << " [-s sceneryfilepath]"
<< " [-v vehiclename]" << std::endl;
<< " [-v vehiclename]"
<< " [--scenario-fileexists-cache on|off]"
<< " [--no-scenario-fileexists-cache]"
<< " [--scenario-parser-file-cache on|off]"
<< " [--no-scenario-parser-file-cache]"
<< " [--scenario-parser-fast-geometry on|off]"
<< " [--no-scenario-parser-fast-geometry]"
<< " [--scenario-parser-fast-skip on|off]"
<< " [--no-scenario-parser-fast-skip]" << std::endl;
return -1;
}
}

View File

@@ -13,9 +13,161 @@ http://mozilla.org/MPL/2.0/.
#include "model/Model3d.h"
#include "rendering/renderer.h"
#include "utilities/parser.h"
#include "utilities/Globals.h"
#include "utilities/Logs.h"
#include "scene/sn_utils.h"
namespace
{
constexpr char const *geometry_token_break = "\n\r\t ;";
enum class shape_import_subtype
{
triangles,
triangle_strip,
triangle_fan
};
bool read_shape_vertex_fast(cParser &Input, world_vertex &vertex)
{
return Input.readTokenDouble(vertex.position.x, false, geometry_token_break)
&& Input.readTokenDouble(vertex.position.y, false, geometry_token_break)
&& Input.readTokenDouble(vertex.position.z, false, geometry_token_break)
&& Input.readTokenFloat(vertex.normal.x, false, geometry_token_break)
&& Input.readTokenFloat(vertex.normal.y, false, geometry_token_break)
&& Input.readTokenFloat(vertex.normal.z, false, geometry_token_break)
&& Input.readTokenFloat(vertex.texture.s, false, geometry_token_break)
&& Input.readTokenFloat(vertex.texture.t, false, geometry_token_break);
}
void process_shape_imported_vertex(scene::shape_node::shapenode_data &data,
shape_import_subtype const nodetype,
world_vertex &vertex,
world_vertex &vertex1,
world_vertex &vertex2,
std::size_t &vertexcount,
bool const clamps,
bool const clampt,
std::string const &node_name)
{
if (true == clamps)
{
vertex.texture.s = std::clamp(vertex.texture.s, 0.001f, 0.999f);
}
if (true == clampt)
{
vertex.texture.t = std::clamp(vertex.texture.t, 0.001f, 0.999f);
}
switch (nodetype)
{
case shape_import_subtype::triangles:
{
if (vertexcount == 0)
{
vertex1 = vertex;
}
else if (vertexcount == 1)
{
vertex2 = vertex;
}
else if (vertexcount >= 2)
{
if (false == degenerate(vertex1.position, vertex2.position, vertex.position))
{
data.vertices.emplace_back(vertex1);
data.vertices.emplace_back(vertex2);
data.vertices.emplace_back(vertex);
}
else
{
ErrorLog(
"Bad geometry: degenerate triangle encountered"
+ (node_name != "" ? " in node \"" + node_name + "\"" : "")
+ " (vertices: " + to_string(vertex1.position) + " + " + to_string(vertex2.position) + " + " + to_string(vertex.position) + ")");
}
}
++vertexcount;
if (vertexcount > 2)
{
vertexcount = 0;
}
break;
}
case shape_import_subtype::triangle_fan:
{
if (vertexcount == 0)
{
vertex1 = vertex;
}
else if (vertexcount == 1)
{
vertex2 = vertex;
}
else if (vertexcount >= 2)
{
if (false == degenerate(vertex1.position, vertex2.position, vertex.position))
{
data.vertices.emplace_back(vertex1);
data.vertices.emplace_back(vertex2);
data.vertices.emplace_back(vertex);
vertex2 = vertex;
}
else
{
ErrorLog(
"Bad geometry: degenerate triangle encountered"
+ (node_name != "" ? " in node \"" + node_name + "\"" : "")
+ " (vertices: " + to_string(vertex1.position) + " + " + to_string(vertex2.position) + " + " + to_string(vertex.position) + ")");
}
}
++vertexcount;
break;
}
case shape_import_subtype::triangle_strip:
{
if (vertexcount == 0)
{
vertex1 = vertex;
}
else if (vertexcount == 1)
{
vertex2 = vertex;
}
else if (vertexcount >= 2)
{
if (false == degenerate(vertex1.position, vertex2.position, vertex.position))
{
if (vertexcount % 2 == 0)
{
data.vertices.emplace_back(vertex1);
data.vertices.emplace_back(vertex2);
}
else
{
data.vertices.emplace_back(vertex2);
data.vertices.emplace_back(vertex1);
}
data.vertices.emplace_back(vertex);
vertex1 = vertex2;
vertex2 = vertex;
}
else
{
ErrorLog(
"Bad geometry: degenerate triangle encountered"
+ (node_name != "" ? " in node \"" + node_name + "\"" : "")
+ " (vertices: " + to_string(vertex1.position) + " + " + to_string(vertex2.position) + " + " + to_string(vertex.position) + ")");
}
}
++vertexcount;
break;
}
}
}
} // namespace
// stores content of the struct in provided output stream
void
lighting_data::serialize( std::ostream &Output ) const {
@@ -231,111 +383,35 @@ shape_node::import( cParser &Input, scene::node_data const &Nodedata ) {
}
// geometry
enum subtype {
triangles,
triangle_strip,
triangle_fan
};
subtype const nodetype = (
Nodedata.type == "triangles" ? triangles :
Nodedata.type == "triangle_strip" ? triangle_strip :
triangle_fan );
shape_import_subtype const nodetype = (
Nodedata.type == "triangles" ? shape_import_subtype::triangles :
Nodedata.type == "triangle_strip" ? shape_import_subtype::triangle_strip :
shape_import_subtype::triangle_fan );
std::size_t vertexcount{ 0 };
world_vertex vertex, vertex1, vertex2;
do {
Input.getTokens( 8, false );
Input
>> vertex.position.x
>> vertex.position.y
>> vertex.position.z
>> vertex.normal.x
>> vertex.normal.y
>> vertex.normal.z
>> vertex.texture.s
>> vertex.texture.t;
// clamp texture coordinates if texture wrapping is off
if( true == clamps ) { vertex.texture.s = std::clamp( vertex.texture.s, 0.001f, 0.999f ); }
if( true == clampt ) { vertex.texture.t = std::clamp( vertex.texture.t, 0.001f, 0.999f ); }
// convert all data to gl_triangles to allow data merge for matching nodes
switch( nodetype ) {
case triangles: {
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex2 );
m_data.vertices.emplace_back( vertex );
}
else {
ErrorLog(
"Bad geometry: degenerate triangle encountered"
+ ( m_name != "" ? " in node \"" + m_name + "\"" : "" )
+ " (vertices: " + to_string( vertex1.position ) + " + " + to_string( vertex2.position ) + " + " + to_string( vertex.position ) + ")" );
}
}
++vertexcount;
if( vertexcount > 2 ) { vertexcount = 0; } // start new triangle if needed
if (Global.ScenarioParserFastGeometry)
{
do
{
if (false == read_shape_vertex_fast(Input, vertex))
{
break;
}
case triangle_fan: {
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex2 );
m_data.vertices.emplace_back( vertex );
vertex2 = vertex;
}
else {
ErrorLog(
"Bad geometry: degenerate triangle encountered"
+ ( m_name != "" ? " in node \"" + m_name + "\"" : "" )
+ " (vertices: " + to_string( vertex1.position ) + " + " + to_string( vertex2.position ) + " + " + to_string( vertex.position ) + ")" );
}
}
++vertexcount;
break;
}
case triangle_strip: {
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
// swap order every other triangle, to maintain consistent winding
if( vertexcount % 2 == 0 ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex2 );
}
else {
m_data.vertices.emplace_back( vertex2 );
m_data.vertices.emplace_back( vertex1 );
}
m_data.vertices.emplace_back( vertex );
vertex1 = vertex2;
vertex2 = vertex;
}
else {
ErrorLog(
"Bad geometry: degenerate triangle encountered"
+ ( m_name != "" ? " in node \"" + m_name + "\"" : "" )
+ " (vertices: " + to_string( vertex1.position ) + " + " + to_string( vertex2.position ) + " + " + to_string( vertex.position ) + ")" );
}
}
++vertexcount;
break;
}
default: { break; }
}
token = Input.getToken<std::string>();
} while( token != "endtri" );
process_shape_imported_vertex(m_data, nodetype, vertex, vertex1, vertex2, vertexcount, clamps, clampt, m_name);
Input.readNextToken(token, false, geometry_token_break);
} while (token != "endtri");
}
else
{
do
{
Input.getTokens(8, false);
Input >> vertex.position.x >> vertex.position.y >> vertex.position.z >> vertex.normal.x >> vertex.normal.y >> vertex.normal.z >> vertex.texture.s >> vertex.texture.t;
process_shape_imported_vertex(m_data, nodetype, vertex, vertex1, vertex2, vertexcount, clamps, clampt, m_name);
token = Input.getToken<std::string>();
} while (token != "endtri");
}
return *this;
}
@@ -595,12 +671,9 @@ lines_node::import( cParser &Input, scene::node_data const &Nodedata ) {
std::size_t vertexcount { 0 };
world_vertex vertex, vertex0, vertex1;
std::string token = Input.getToken<std::string>();
do {
vertex.position.x = std::atof( token.c_str() );
Input.getTokens( 2, false );
Input
>> vertex.position.y
>> vertex.position.z;
auto process_line_vertex = [&]()
{
// convert all data to gl_lines to allow data merge for matching nodes
switch( nodetype ) {
case lines: {
@@ -631,9 +704,34 @@ lines_node::import( cParser &Input, scene::node_data const &Nodedata ) {
}
default: { break; }
}
token = Input.getToken<std::string>();
};
} while( token != "endline" );
if (Global.ScenarioParserFastGeometry)
{
do
{
char *end = nullptr;
vertex.position.x = std::strtod(token.c_str(), &end);
if (false == Input.readTokenDouble(vertex.position.y, false, geometry_token_break)
|| false == Input.readTokenDouble(vertex.position.z, false, geometry_token_break))
{
break;
}
process_line_vertex();
Input.readNextToken(token, false, geometry_token_break);
} while (token != "endline");
}
else
{
do
{
vertex.position.x = std::atof(token.c_str());
Input.getTokens(2, false);
Input >> vertex.position.y >> vertex.position.z;
process_line_vertex();
token = Input.getToken<std::string>();
} while (token != "endline");
}
// add closing line for the loop
if( ( nodetype == line_loop )
&& ( vertexcount > 2 ) ) {

View File

@@ -65,6 +65,11 @@ state_serializer::deserialize_begin( std::string const &Scenariofile ) {
Global.file_binary_terrain_state = false;
WriteLog("Default SBT absent");
}
WriteLog(
std::string("Scenario parser: fast_geometry=") + (Global.ScenarioParserFastGeometry ? "on" : "off")
+ ", fast_skip=" + (Global.ScenarioParserFastSkip ? "on" : "off")
+ ", text_triangles=" + (state->scratchpad.binary.terrain ? "skipped_via_sbt" : "parsed"));
scene::Groups.create();
if( false == state->input.ok() )
@@ -160,6 +165,9 @@ state_serializer::deserialize_continue(std::shared_ptr<deserializer_state> state
Region->serialize( state->scenariofile );
}
state->parser_metrics_scope.end();
state->parser_file_cache_scope.end();
state->fileexists_cache_scope.end();
return false;
}
@@ -1128,6 +1136,12 @@ state_serializer::deserialize_sound( cParser &Input, scene::scratch_data &Scratc
void
state_serializer::skip_until( cParser &Input, std::string const &Token ) {
if (Global.ScenarioParserFastSkip)
{
Input.skipUntilKeyword(Token);
return;
}
std::string token { Input.getToken<std::string>() };
while( ( false == token.empty() )
&& ( token != Token ) ) {

View File

@@ -10,12 +10,16 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include "utilities/parser.h"
#include "utilities/utilities.h"
#include "scene/scene.h"
namespace simulation {
struct deserializer_state {
std::string scenariofile;
FileExistsCacheScope fileexists_cache_scope;
ParserFileCacheScope parser_file_cache_scope;
ParserMetricsScope parser_metrics_scope;
cParser input;
scene::scratch_data scratchpad;
using deserializefunctionbind = std::function<void()>;

View File

@@ -625,7 +625,31 @@ bool global_settings::ConfigParseSimulation(cParser& Parser, const std::string&
if (token == "file.binary.terrain")
{
ParseOne(Parser, file_binary_terrain, 1, false);
// ParseOne(Parser, file_binary_terrain, 1, false);
return true;
}
if (token == "scenario.fileexists.cache")
{
ParseOne(Parser, ScenarioFileExistsCache, 1, false);
return true;
}
if (token == "scenario.parser.filecache")
{
ParseOne(Parser, ScenarioParserFileCache, 1, false);
return true;
}
if (token == "scenario.parser.fastgeometry")
{
ParseOne(Parser, ScenarioParserFastGeometry, 1, false);
return true;
}
if (token == "scenario.parser.fastskip")
{
ParseOne(Parser, ScenarioParserFastSkip, 1, false);
return true;
}
@@ -1578,6 +1602,10 @@ global_settings::export_as_text( std::ostream &Output ) const {
export_as_text( Output, "latitude", fLatitudeDeg );
export_as_text( Output, "convertmodels", iConvertModels );
export_as_text( Output, "file.binary.terrain", file_binary_terrain );
export_as_text( Output, "scenario.fileexists.cache", ScenarioFileExistsCache );
export_as_text( Output, "scenario.parser.filecache", ScenarioParserFileCache );
export_as_text( Output, "scenario.parser.fastgeometry", ScenarioParserFastGeometry );
export_as_text( Output, "scenario.parser.fastskip", ScenarioParserFastSkip );
export_as_text( Output, "inactivepause", bInactivePause );
export_as_text( Output, "slowmotion", iSlowMotionMask );
export_as_text( Output, "hideconsole", bHideConsole );

View File

@@ -80,8 +80,12 @@ struct global_settings {
std::string local_start_vehicle{ "EU07-424" };
int iConvertModels{ 0 }; // tworzenie plików binarnych
int iConvertIndexRange{ 1000 }; // range of duplicate vertex scan
bool file_binary_terrain{ true }; // enable binary terrain (de)serialization
bool file_binary_terrain{ false }; // enable binary terrain (de)serialization
bool file_binary_terrain_state{true};
bool ScenarioFileExistsCache{ true }; // cache FileExists calls during scenario loading
bool ScenarioParserFileCache{ false }; // cache parser input file bytes during scenario loading
bool ScenarioParserFastGeometry{ false }; // fast float reads for triangles/lines import
bool ScenarioParserFastSkip{ false }; // fast skip_until for endtri/endline blocks
// logs
bool priorityLoadText3D{false}; // ladowanie T3D priorytetowo
int iWriteLogEnabled{ 3 }; // maska bitowa: 1-zapis do pliku, 2-okienko, 4-nazwy torów

View File

@@ -25,50 +25,116 @@ char endstring[10] = "\n";
std::deque<std::string> log_scrollback;
std::string filename_date() {
::SYSTEMTIME st;
namespace
{
std::string sanitize_filename_component(std::string value)
{
for (auto &character : value)
{
switch (character)
{
case '\\':
case '/':
case ':':
case '*':
case '?':
case '\"':
case '<':
case '>':
case '|':
character = '_';
break;
default:
break;
}
}
return value;
}
std::string filename_session_timestamp()
{
::SYSTEMTIME st {};
#ifdef __unix__
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
tm *tms = localtime(&ts.tv_sec);
st.wYear = tms->tm_year;
st.wMonth = tms->tm_mon;
st.wDayOfWeek = tms->tm_wday;
st.wDay = tms->tm_mday;
st.wHour = tms->tm_hour;
st.wMinute = tms->tm_min;
st.wSecond = tms->tm_sec;
st.wMilliseconds = ts.tv_nsec / 1000000;
timespec ts {};
clock_gettime(CLOCK_REALTIME, &ts);
tm *tms = localtime(&ts.tv_sec);
if (tms != nullptr)
{
st.wYear = static_cast<unsigned short>(tms->tm_year + 1900);
st.wMonth = static_cast<unsigned short>(tms->tm_mon + 1);
st.wDay = static_cast<unsigned short>(tms->tm_mday);
st.wHour = static_cast<unsigned short>(tms->tm_hour);
st.wMinute = static_cast<unsigned short>(tms->tm_min);
st.wSecond = static_cast<unsigned short>(tms->tm_sec);
st.wMilliseconds = static_cast<unsigned short>(ts.tv_nsec / 1000000);
}
#elif _WIN32
::GetLocalTime( &st );
::GetLocalTime(&st);
#endif
std::snprintf(
logbuffer,
sizeof(logbuffer),
"%d%02d%02d_%02d%02d%03d",
st.wYear,
st.wMonth,
st.wDay,
st.wHour,
st.wMinute,
st.wMilliseconds);
std::snprintf(
logbuffer,
sizeof(logbuffer),
"%04u%02u%02u_%02u%02u%02u_%03u",
static_cast<unsigned>(st.wYear),
static_cast<unsigned>(st.wMonth),
static_cast<unsigned>(st.wDay),
static_cast<unsigned>(st.wHour),
static_cast<unsigned>(st.wMinute),
static_cast<unsigned>(st.wSecond),
static_cast<unsigned>(st.wMilliseconds));
return std::string( logbuffer );
return std::string(logbuffer);
}
std::string filename_scenery() {
auto extension = Global.SceneryFile.rfind( '.' );
if( extension != std::string::npos ) {
return Global.SceneryFile.substr( 0, extension );
}
else {
return Global.SceneryFile;
}
std::string const &log_session_id()
{
static std::string const session_id = []()
{
std::string session = filename_session_timestamp();
#ifdef _WIN32
char pid_suffix[32];
std::snprintf(pid_suffix, sizeof(pid_suffix), "_%lu", static_cast<unsigned long>(::GetCurrentProcessId()));
session += pid_suffix;
#endif
return session;
}();
return session_id;
}
std::string filename_scenery()
{
auto extension = Global.SceneryFile.rfind('.');
if (extension != std::string::npos)
{
return sanitize_filename_component(Global.SceneryFile.substr(0, extension));
}
return sanitize_filename_component(Global.SceneryFile);
}
std::string log_output_filename()
{
if (Global.MultipleLogs && false == Global.SceneryFile.empty())
{
return "logs/log (" + filename_scenery() + ") " + log_session_id() + ".txt";
}
return "logs/log_" + log_session_id() + ".txt";
}
std::string log_errors_filename()
{
if (Global.MultipleLogs && false == Global.SceneryFile.empty())
{
return "logs/errors (" + filename_scenery() + ") " + log_session_id() + ".txt";
}
return "logs/errors_" + log_session_id() + ".txt";
}
} // namespace
// log service stacks
std::deque < std::pair<std::string, bool>> InfoStack;
std::deque<std::string> ErrorStack;
@@ -108,8 +174,7 @@ void LogService()
{
if (!output.is_open())
{
std::string filename = (Global.MultipleLogs ? "logs/log (" + filename_scenery() + ") " + filename_date() + ".txt" : "log.txt");
output.open(filename, std::ios::trunc);
output.open(log_output_filename(), std::ios::trunc);
}
output << msg << "\n";
output.flush();
@@ -143,8 +208,7 @@ void LogService()
if (!errors.is_open())
{
std::string filename = (Global.MultipleLogs ? "logs/errors (" + filename_scenery() + ") " + filename_date() + ".txt" : "errors.txt");
errors.open(filename, std::ios::trunc);
errors.open(log_errors_filename(), std::ios::trunc);
errors << "EU07.EXE " + Global.asVersion << "\n";
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include <chrono>
#include <string>
#include <sstream>
#include <fstream>
@@ -69,6 +70,11 @@ class cParser //: public std::stringstream
return m_autoclear; }
bool
getTokens( unsigned int Count = 1, bool ToLower = true, char const *Break = "\n\r\t ;" );
// read one token and parse as number without using the tokens deque / stringstream
bool readTokenFloat( float &Value, bool ToLower = true, char const *Break = "\n\r\t ;" );
bool readTokenDouble( double &Value, bool ToLower = true, char const *Break = "\n\r\t ;" );
void readNextToken( std::string &Token, bool ToLower = true, char const *Break = "\n\r\t ;" );
void skipUntilKeyword( std::string const &Keyword, bool ToLower = true, char const *Break = "\n\r\t ;" );
std::string readTokenFromStream(bool ToLower, const char *Break);
void stripFirstTokenBOM(std::string &token, bool ToLower, const char *Break);
void substituteParameters(std::string &token, bool ToLower);
@@ -106,6 +112,8 @@ class cParser //: public std::stringstream
bool handleIncludeIfPresent(std::string &token, bool ToLower, const char *Break);
// methods:
void readToken(std::string& out, bool ToLower = true, const char *Break = "\n\r\t ;");
void readTokenForSkip(std::string &out, bool ToLower = true, const char *Break = "\n\r\t ;");
std::string readTokenFromStreamFast(bool ToLower, const char *Break);
static std::vector<std::string> readParameters( cParser &Input );
std::string readQuotes( char const Quote = '\"' );
void skipComment( std::string const &Endmark );
@@ -131,6 +139,50 @@ class cParser //: public std::stringstream
std::deque<std::string> tokens;
};
class ParserFileCacheScope
{
public:
ParserFileCacheScope();
~ParserFileCacheScope();
ParserFileCacheScope(ParserFileCacheScope const &) = delete;
ParserFileCacheScope &operator=(ParserFileCacheScope const &) = delete;
void end();
private:
bool m_active { true };
};
class ParserMetricsScope
{
public:
ParserMetricsScope();
~ParserMetricsScope();
ParserMetricsScope(ParserMetricsScope const &) = delete;
ParserMetricsScope &operator=(ParserMetricsScope const &) = delete;
void end();
private:
bool m_active { true };
};
namespace parser_metrics
{
bool active();
struct convert_timer
{
std::chrono::steady_clock::time_point start {};
bool enabled { false };
convert_timer();
~convert_timer();
};
} // namespace parser_metrics
template <>
glm::vec3
@@ -143,6 +195,7 @@ cParser::operator>>( Type_ &Right ) {
if( true == this->tokens.empty() ) { return *this; }
parser_metrics::convert_timer timer;
std::stringstream converter( this->tokens.front() );
converter >> Right;
this->tokens.pop_front();

View File

@@ -15,7 +15,12 @@ Copyright (C) 2007-2014 Maciej Cierniak
//
//#include <sys/types.h>
//#include <sys/stat.h>
#include <atomic>
#include <algorithm>
#include <cctype>
#include <ranges>
#include <mutex>
#include <unordered_map>
//#ifndef WIN32
//#include <unistd.h>
//#endif
@@ -26,10 +31,9 @@ Copyright (C) 2007-2014 Maciej Cierniak
#include "utilities/utilities.h"
#include "utilities/Globals.h"
#include "utilities/Logs.h"
#include "utilities/parser.h"
//#include "utilities/Logs.h"
// TODO: This shouldn't be in Globals?
bool DebugModeFlag = false;
bool FreeFlyModeFlag = false;
@@ -37,6 +41,136 @@ bool EditorModeFlag = false;
bool DebugCameraFlag = false;
bool DebugTractionFlag = false;
namespace
{
std::atomic<unsigned int> g_fileexists_cache_depth { 0 };
bool g_fileexists_cache_enabled { false };
std::mutex g_fileexists_cache_mutex;
std::unordered_map<std::string, bool> g_fileexists_cache;
struct fileexists_cache_metrics
{
std::uint64_t total_calls { 0 };
std::uint64_t cache_hits { 0 };
std::uint64_t cache_misses { 0 };
std::uint64_t filesystem_exists_calls { 0 };
std::uint64_t positive_results { 0 };
std::uint64_t negative_results { 0 };
std::uint64_t untracked_path_calls { 0 };
std::chrono::steady_clock::duration filesystem_exists_time {};
std::unordered_map<std::string, std::uint64_t> path_counts;
void reset()
{
*this = {};
}
};
fileexists_cache_metrics g_fileexists_cache_metrics;
std::string FileExistsCacheKey(std::string const &Filename)
{
if (Filename.empty())
{
return Filename;
}
try
{
auto path = std::filesystem::path(Filename);
if (path.is_relative())
{
path = std::filesystem::absolute(path);
}
auto key = path.lexically_normal().generic_string();
#ifdef _WIN32
for (auto &character : key)
{
auto const value = static_cast<unsigned char>(character);
if (value < 128)
{
character = static_cast<char>(std::tolower(value));
}
}
#endif
return key;
}
catch (std::filesystem::filesystem_error const &)
{
auto key = Filename;
std::replace(key.begin(), key.end(), '\\', '/');
return key;
}
}
double FileExistsDurationMilliseconds(std::chrono::steady_clock::duration const Duration)
{
return std::chrono::duration<double, std::milli>(Duration).count();
}
std::string FileExistsFormatPathCount(std::pair<std::string, std::uint64_t> const &Entry)
{
return " " + std::to_string(Entry.second) + "x " + Entry.first;
}
std::vector<std::string> FileExistsCacheReport()
{
std::vector<std::string> report;
report.emplace_back(
std::string("FileExists scope: mode=") + (g_fileexists_cache_enabled ? "cached" : "direct")
+ ", calls=" + std::to_string(g_fileexists_cache_metrics.total_calls)
+ ", hits=" + std::to_string(g_fileexists_cache_metrics.cache_hits)
+ ", misses=" + std::to_string(g_fileexists_cache_metrics.cache_misses)
+ ", fs_exists=" + std::to_string(g_fileexists_cache_metrics.filesystem_exists_calls)
+ ", found=" + std::to_string(g_fileexists_cache_metrics.positive_results)
+ ", missing=" + std::to_string(g_fileexists_cache_metrics.negative_results)
+ ", fs_time_ms=" + to_string(FileExistsDurationMilliseconds(g_fileexists_cache_metrics.filesystem_exists_time), 3)
+ ", untracked_paths=" + std::to_string(g_fileexists_cache_metrics.untracked_path_calls)
+ ", cached_paths=" + std::to_string(g_fileexists_cache.size()));
std::vector<std::pair<std::string, std::uint64_t>> top_paths;
top_paths.reserve(g_fileexists_cache_metrics.path_counts.size());
for (auto const &entry : g_fileexists_cache_metrics.path_counts)
{
if (entry.second > 1)
{
top_paths.emplace_back(entry);
}
}
constexpr std::size_t top_path_limit = 5;
auto const top_count = std::min(top_path_limit, top_paths.size());
std::partial_sort(
top_paths.begin(),
top_paths.begin() + top_count,
top_paths.end(),
[](auto const &Left, auto const &Right)
{
if (Left.second != Right.second)
{
return Left.second > Right.second;
}
return Left.first < Right.first;
});
for (std::size_t index = 0; index < top_count; ++index)
{
report.emplace_back(FileExistsFormatPathCount(top_paths[index]));
}
return report;
}
void FileExistsCacheLogReport(std::vector<std::string> const &Report)
{
for (auto const &line : Report)
{
WriteLog(line);
}
}
} // namespace
std::string Now()
{
auto now = std::chrono::system_clock::now();
@@ -328,7 +462,62 @@ template <> bool extract_value(bool &Variable, std::string const &Key, std::stri
bool FileExists(std::string const &Filename)
{
return std::filesystem::exists(Filename);
if (g_fileexists_cache_depth.load(std::memory_order_acquire) == 0)
{
return std::filesystem::exists(Filename);
}
std::lock_guard<std::mutex> lock(g_fileexists_cache_mutex);
if (g_fileexists_cache_depth.load(std::memory_order_relaxed) == 0)
{
return std::filesystem::exists(Filename);
}
auto const cache_enabled = g_fileexists_cache_enabled;
auto const key = cache_enabled ? FileExistsCacheKey(Filename) : Filename;
++g_fileexists_cache_metrics.total_calls;
if (auto lookup = g_fileexists_cache_metrics.path_counts.find(key); lookup != g_fileexists_cache_metrics.path_counts.end())
{
++lookup->second;
}
else if (g_fileexists_cache_metrics.path_counts.size() < 4096)
{
g_fileexists_cache_metrics.path_counts.emplace(key, 1);
}
else
{
++g_fileexists_cache_metrics.untracked_path_calls;
}
if (cache_enabled)
{
auto const lookup = g_fileexists_cache.find(key);
if (lookup != g_fileexists_cache.end())
{
++g_fileexists_cache_metrics.cache_hits;
return lookup->second;
}
++g_fileexists_cache_metrics.cache_misses;
}
++g_fileexists_cache_metrics.filesystem_exists_calls;
auto const timestart = std::chrono::steady_clock::now();
auto const exists = std::filesystem::exists(Filename);
g_fileexists_cache_metrics.filesystem_exists_time += std::chrono::steady_clock::now() - timestart;
if (exists)
{
++g_fileexists_cache_metrics.positive_results;
}
else
{
++g_fileexists_cache_metrics.negative_results;
}
if (cache_enabled)
{
g_fileexists_cache.emplace(key, exists);
}
return exists;
}
std::pair<std::string, std::string> FileExists(std::vector<std::string> const &Names, std::vector<std::string> const &Extensions)
@@ -348,6 +537,64 @@ std::pair<std::string, std::string> FileExists(std::vector<std::string> const &N
return {{}, {}};
}
FileExistsCacheScope::FileExistsCacheScope()
{
std::lock_guard<std::mutex> lock(g_fileexists_cache_mutex);
if (g_fileexists_cache_depth.load(std::memory_order_relaxed) == 0)
{
g_fileexists_cache.clear();
g_fileexists_cache_enabled = Global.ScenarioFileExistsCache;
g_fileexists_cache_metrics.reset();
}
g_fileexists_cache_depth.fetch_add(1, std::memory_order_release);
}
FileExistsCacheScope::~FileExistsCacheScope()
{
end();
}
void FileExistsCacheScope::clear()
{
std::vector<std::string> report;
{
std::lock_guard<std::mutex> lock(g_fileexists_cache_mutex);
report = FileExistsCacheReport();
g_fileexists_cache.clear();
g_fileexists_cache_metrics.reset();
}
FileExistsCacheLogReport(report);
}
void FileExistsCacheScope::end()
{
if (false == m_active)
{
return;
}
std::vector<std::string> report;
{
std::lock_guard<std::mutex> lock(g_fileexists_cache_mutex);
auto const depth = g_fileexists_cache_depth.load(std::memory_order_relaxed);
if (depth <= 1)
{
report = FileExistsCacheReport();
g_fileexists_cache_depth.store(0, std::memory_order_release);
g_fileexists_cache.clear();
g_fileexists_cache_enabled = false;
g_fileexists_cache_metrics.reset();
}
else
{
g_fileexists_cache_depth.store(depth - 1, std::memory_order_release);
}
m_active = false;
}
FileExistsCacheLogReport(report);
}
// returns time of last modification for specified file
std::time_t last_modified(std::string const &Filename)
{

View File

@@ -201,6 +201,22 @@ bool FileExists(std::string const &Filename);
std::pair<std::string, std::string> FileExists(std::vector<std::string> const &Names, std::vector<std::string> const &Extensions);
class FileExistsCacheScope
{
public:
FileExistsCacheScope();
~FileExistsCacheScope();
FileExistsCacheScope(FileExistsCacheScope const &) = delete;
FileExistsCacheScope &operator=(FileExistsCacheScope const &) = delete;
void clear();
void end();
private:
bool m_active { true };
};
// returns time of last modification for specified file
std::time_t last_modified(std::string const &Filename);