mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-22 05:49:19 +02:00
Tune EU7 PACK streaming with v10 CHNK sub-chunks and worker read cache
Chunk PACK sections at bake time and decode them incrementally at runtime to cut apply hitches during fast camera flight; cache parsed section headers per worker thread to avoid reopening and reparsing on every sub-chunk read.
This commit is contained in:
@@ -831,6 +831,28 @@ Sekcje v7 bez nagłówka (flat) pozostają czytelne — brak UMES, runtime skanu
|
|||||||
|
|
||||||
Runtime używa UMES do cold preload meshów zamiast liniowego skanowania wszystkich instancji w sekcji.
|
Runtime używa UMES do cold preload meshów zamiast liniowego skanowania wszystkich instancji w sekcji.
|
||||||
|
|
||||||
|
### Format sekcji — v10 CHNK (`kPackSectionFormatV10 = 3`)
|
||||||
|
|
||||||
|
Rozszerzenie v9: po UMES tabela sub-chunków (domyślnie 512 modeli/chunk przy bake).
|
||||||
|
|
||||||
|
```
|
||||||
|
uint8 section_format — musi być = 3
|
||||||
|
uint32 solo_count
|
||||||
|
uint32 inst_count
|
||||||
|
uint32 unique_mesh_count
|
||||||
|
uint32[unique_mesh_count] string_id
|
||||||
|
|
||||||
|
uint32 chunk_count
|
||||||
|
Powtórzone chunk_count razy:
|
||||||
|
uint32 chunk_model_count
|
||||||
|
uint32 chunk_byte_offset — offset od początku sekcji (względem PIDX pack_offset)
|
||||||
|
|
||||||
|
[payload chunków — solo modele, chunk_model_count rekordów każdy]
|
||||||
|
```
|
||||||
|
|
||||||
|
Runtime: worker czyta `read_pack_section_chunk_load(row, col, chunk_index)` z O(1) seek.
|
||||||
|
Sekcje v9 (format=2) i v7 flat pozostają kompatybilne (`chunk_count` implicit = 1).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 30. PROT — prototypy modeli (v8+)
|
## 30. PROT — prototypy modeli (v8+)
|
||||||
|
|||||||
@@ -49,8 +49,10 @@ inline constexpr std::array<char, 4> kChunkPlac{'P', 'L', 'A', 'C'};
|
|||||||
inline constexpr std::array<char, 4> kChunkPidx{'P', 'I', 'D', 'X'};
|
inline constexpr std::array<char, 4> kChunkPidx{'P', 'I', 'D', 'X'};
|
||||||
inline constexpr std::array<char, 4> kChunkPack{'P', 'A', 'C', 'K'};
|
inline constexpr std::array<char, 4> kChunkPack{'P', 'A', 'C', 'K'};
|
||||||
|
|
||||||
// PACK section payload: v9 = UMES (unique mesh list per 1 km section).
|
// PACK section payload: v9 = UMES, v10 = UMES + CHNK (sub-chunk offset table).
|
||||||
inline constexpr std::uint8_t kPackSectionFormatV9 = 2;
|
inline constexpr std::uint8_t kPackSectionFormatV9 = 2;
|
||||||
|
inline constexpr std::uint8_t kPackSectionFormatV10 = 3;
|
||||||
|
inline constexpr std::size_t kPackSectionChunkModels = 512;
|
||||||
|
|
||||||
struct PackWriteStats {
|
struct PackWriteStats {
|
||||||
std::size_t strings_total = 0;
|
std::size_t strings_total = 0;
|
||||||
@@ -437,7 +439,22 @@ struct PackPayloadBuild {
|
|||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline std::vector<char> buildPackSectionPayloadV9(
|
[[nodiscard]] inline std::vector<char> buildPackModelBlob(
|
||||||
|
StringTable& table,
|
||||||
|
const std::vector<runtime::RuntimeModelInstance>& models,
|
||||||
|
const std::size_t begin,
|
||||||
|
const std::size_t end) {
|
||||||
|
std::ostringstream out(std::ios::binary);
|
||||||
|
for (std::size_t index = begin; index < end; ++index) {
|
||||||
|
runtime::RuntimeModelInstance worldModel = models[index];
|
||||||
|
worldModel.node.transform = {};
|
||||||
|
writeRuntimeModel(out, table, worldModel);
|
||||||
|
}
|
||||||
|
const std::string blob = out.str();
|
||||||
|
return {blob.begin(), blob.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::vector<char> buildPackSectionPayloadV10(
|
||||||
StringTable& table,
|
StringTable& table,
|
||||||
const std::vector<runtime::RuntimeModelInstance>& models,
|
const std::vector<runtime::RuntimeModelInstance>& models,
|
||||||
const std::uint32_t inst_count = 0,
|
const std::uint32_t inst_count = 0,
|
||||||
@@ -445,18 +462,51 @@ struct PackPayloadBuild {
|
|||||||
const auto mesh_ids = collectUniqueMeshIds(table, models, prototype_mesh_paths);
|
const auto mesh_ids = collectUniqueMeshIds(table, models, prototype_mesh_paths);
|
||||||
const std::uint32_t solo_count = static_cast<std::uint32_t>(models.size());
|
const std::uint32_t solo_count = static_cast<std::uint32_t>(models.size());
|
||||||
|
|
||||||
|
std::vector<std::vector<char>> chunk_payloads;
|
||||||
|
chunk_payloads.reserve((models.size() + kPackSectionChunkModels - 1) / kPackSectionChunkModels);
|
||||||
|
for (std::size_t offset = 0; offset < models.size(); offset += kPackSectionChunkModels) {
|
||||||
|
const std::size_t end =
|
||||||
|
std::min(offset + kPackSectionChunkModels, models.size());
|
||||||
|
chunk_payloads.push_back(buildPackModelBlob(table, models, offset, end));
|
||||||
|
}
|
||||||
|
if (chunk_payloads.empty()) {
|
||||||
|
chunk_payloads.emplace_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint32_t chunk_count = static_cast<std::uint32_t>(chunk_payloads.size());
|
||||||
|
const std::uint32_t header_size =
|
||||||
|
1u + 4u + 4u + 4u + static_cast<std::uint32_t>(mesh_ids.size()) * 4u + 4u +
|
||||||
|
chunk_count * 8u;
|
||||||
|
|
||||||
|
std::vector<std::uint32_t> chunk_offsets(chunk_count);
|
||||||
|
std::vector<std::uint32_t> chunk_model_counts(chunk_count);
|
||||||
|
std::uint32_t payload_offset = header_size;
|
||||||
|
for (std::uint32_t chunk_index = 0; chunk_index < chunk_count; ++chunk_index) {
|
||||||
|
chunk_offsets[chunk_index] = payload_offset;
|
||||||
|
const std::size_t model_begin =
|
||||||
|
static_cast<std::size_t>(chunk_index) * kPackSectionChunkModels;
|
||||||
|
const std::size_t model_end =
|
||||||
|
std::min(model_begin + kPackSectionChunkModels, models.size());
|
||||||
|
chunk_model_counts[chunk_index] =
|
||||||
|
static_cast<std::uint32_t>(model_end > model_begin ? model_end - model_begin : 0);
|
||||||
|
payload_offset += static_cast<std::uint32_t>(chunk_payloads[chunk_index].size());
|
||||||
|
}
|
||||||
|
|
||||||
std::ostringstream packOut(std::ios::binary);
|
std::ostringstream packOut(std::ios::binary);
|
||||||
io::writeU8(packOut, kPackSectionFormatV9);
|
io::writeU8(packOut, kPackSectionFormatV10);
|
||||||
io::writeU32(packOut, solo_count);
|
io::writeU32(packOut, solo_count);
|
||||||
io::writeU32(packOut, inst_count);
|
io::writeU32(packOut, inst_count);
|
||||||
io::writeU32(packOut, static_cast<std::uint32_t>(mesh_ids.size()));
|
io::writeU32(packOut, static_cast<std::uint32_t>(mesh_ids.size()));
|
||||||
for (const std::uint32_t id : mesh_ids) {
|
for (const std::uint32_t id : mesh_ids) {
|
||||||
io::writeU32(packOut, id);
|
io::writeU32(packOut, id);
|
||||||
}
|
}
|
||||||
for (const runtime::RuntimeModelInstance& model : models) {
|
io::writeU32(packOut, chunk_count);
|
||||||
runtime::RuntimeModelInstance worldModel = model;
|
for (std::uint32_t chunk_index = 0; chunk_index < chunk_count; ++chunk_index) {
|
||||||
worldModel.node.transform = {};
|
io::writeU32(packOut, chunk_model_counts[chunk_index]);
|
||||||
writeRuntimeModel(packOut, table, worldModel);
|
io::writeU32(packOut, chunk_offsets[chunk_index]);
|
||||||
|
}
|
||||||
|
for (const std::vector<char>& payload : chunk_payloads) {
|
||||||
|
packOut.write(payload.data(), static_cast<std::streamsize>(payload.size()));
|
||||||
}
|
}
|
||||||
const std::string blob = packOut.str();
|
const std::string blob = packOut.str();
|
||||||
return {blob.begin(), blob.end()};
|
return {blob.begin(), blob.end()};
|
||||||
@@ -490,7 +540,7 @@ struct PackPayloadBuild {
|
|||||||
entry.row = static_cast<std::uint16_t>(batch.section.z);
|
entry.row = static_cast<std::uint16_t>(batch.section.z);
|
||||||
entry.column = static_cast<std::uint16_t>(batch.section.x);
|
entry.column = static_cast<std::uint16_t>(batch.section.x);
|
||||||
entry.modelCount = static_cast<std::uint32_t>(batch.models.size());
|
entry.modelCount = static_cast<std::uint32_t>(batch.models.size());
|
||||||
section_payloads[batch_index] = buildPackSectionPayloadV9(table, batch.models);
|
section_payloads[batch_index] = buildPackSectionPayloadV10(table, batch.models);
|
||||||
}
|
}
|
||||||
std::size_t total_size = 0;
|
std::size_t total_size = 0;
|
||||||
for (const std::vector<char>& payload : section_payloads) {
|
for (const std::vector<char>& payload : section_payloads) {
|
||||||
@@ -524,7 +574,7 @@ struct PackPayloadBuild {
|
|||||||
entry.column = static_cast<std::uint16_t>(batch.section.x);
|
entry.column = static_cast<std::uint16_t>(batch.section.x);
|
||||||
entry.modelCount = static_cast<std::uint32_t>(batch.models.size());
|
entry.modelCount = static_cast<std::uint32_t>(batch.models.size());
|
||||||
|
|
||||||
section_payloads[batch_index] = buildPackSectionPayloadV9(table, batch.models);
|
section_payloads[batch_index] = buildPackSectionPayloadV10(table, batch.models);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,5 +59,7 @@ constexpr std::uint32_t kChunkProt { detail::make_id4( 'P', 'R', 'O', 'T' ) };
|
|||||||
|
|
||||||
constexpr std::uint8_t kPackSectionFormatV8 { 1 };
|
constexpr std::uint8_t kPackSectionFormatV8 { 1 };
|
||||||
constexpr std::uint8_t kPackSectionFormatV9 { 2 };
|
constexpr std::uint8_t kPackSectionFormatV9 { 2 };
|
||||||
|
constexpr std::uint8_t kPackSectionFormatV10 { 3 };
|
||||||
|
constexpr std::size_t kPackSectionChunkModels { 512 };
|
||||||
|
|
||||||
} // namespace scene::eu7
|
} // namespace scene::eu7
|
||||||
|
|||||||
@@ -87,7 +87,8 @@ log_pack_bench_impl( Eu7PackBench const &bench, char const *const title ) {
|
|||||||
|
|
||||||
std::vector<BenchLine> lines {
|
std::vector<BenchLine> lines {
|
||||||
{ "worker read_pack", bench.worker_read_pack_ms,
|
{ "worker read_pack", bench.worker_read_pack_ms,
|
||||||
"sections=" + std::to_string( bench.worker_sections_done ) +
|
"chunks=" + std::to_string( bench.worker_chunks_decoded ) +
|
||||||
|
" sections=" + std::to_string( bench.worker_sections_done ) +
|
||||||
" finalized=" + std::to_string( bench.sections_finalized ) +
|
" finalized=" + std::to_string( bench.sections_finalized ) +
|
||||||
" models=" + std::to_string( bench.worker_models_decoded ) +
|
" models=" + std::to_string( bench.worker_models_decoded ) +
|
||||||
" fail=" + std::to_string( bench.worker_failures ) },
|
" fail=" + std::to_string( bench.worker_failures ) },
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace scene::eu7 {
|
|||||||
struct Eu7PackBench {
|
struct Eu7PackBench {
|
||||||
// --- worker (async read PACK) ---
|
// --- worker (async read PACK) ---
|
||||||
double worker_read_pack_ms { 0.0 };
|
double worker_read_pack_ms { 0.0 };
|
||||||
|
std::uint64_t worker_chunks_decoded { 0 };
|
||||||
std::uint64_t worker_sections_done { 0 };
|
std::uint64_t worker_sections_done { 0 };
|
||||||
std::uint64_t sections_finalized { 0 };
|
std::uint64_t sections_finalized { 0 };
|
||||||
std::uint64_t worker_models_decoded { 0 };
|
std::uint64_t worker_models_decoded { 0 };
|
||||||
|
|||||||
@@ -505,6 +505,41 @@ parse_pack_section_header(
|
|||||||
const std::uint8_t first_byte {
|
const std::uint8_t first_byte {
|
||||||
peek == EOF ? std::uint8_t { 0 } : static_cast<std::uint8_t>( peek ) };
|
peek == EOF ? std::uint8_t { 0 } : static_cast<std::uint8_t>( peek ) };
|
||||||
|
|
||||||
|
bool use_v10_header { false };
|
||||||
|
if( first_byte == kPackSectionFormatV10 ) {
|
||||||
|
cursor.section_format = sn_utils::d_uint8( input );
|
||||||
|
const std::uint32_t solo_total { sn_utils::ld_uint32( input ) };
|
||||||
|
const std::uint32_t inst_total { sn_utils::ld_uint32( input ) };
|
||||||
|
const std::uint32_t unique_mesh_count { sn_utils::ld_uint32( input ) };
|
||||||
|
if( solo_total + inst_total == entry.model_count ) {
|
||||||
|
use_v10_header = true;
|
||||||
|
cursor.model_total = solo_total + inst_total;
|
||||||
|
cursor.solo_remaining = 0;
|
||||||
|
cursor.inst_remaining = 0;
|
||||||
|
|
||||||
|
StringTable const strings { module.strings };
|
||||||
|
cursor.unique_meshes.reserve( unique_mesh_count );
|
||||||
|
for( std::uint32_t mesh_idx { 0 }; mesh_idx < unique_mesh_count; ++mesh_idx ) {
|
||||||
|
cursor.unique_meshes.push_back(
|
||||||
|
strings.resolve( sn_utils::ld_uint32( input ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.chunk_count = sn_utils::ld_uint32( input );
|
||||||
|
cursor.chunk_model_counts.resize( cursor.chunk_count );
|
||||||
|
cursor.chunk_byte_offsets.resize( cursor.chunk_count );
|
||||||
|
for( std::uint32_t chunk_idx { 0 }; chunk_idx < cursor.chunk_count; ++chunk_idx ) {
|
||||||
|
cursor.chunk_model_counts[ chunk_idx ] = sn_utils::ld_uint32( input );
|
||||||
|
cursor.chunk_byte_offsets[ chunk_idx ] = sn_utils::ld_uint32( input );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( use_v10_header ) {
|
||||||
|
cursor.models_read = 0;
|
||||||
|
cursor.header_parsed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool use_v9_header { false };
|
bool use_v9_header { false };
|
||||||
if( first_byte == kPackSectionFormatV9 ) {
|
if( first_byte == kPackSectionFormatV9 ) {
|
||||||
cursor.section_format = sn_utils::d_uint8( input );
|
cursor.section_format = sn_utils::d_uint8( input );
|
||||||
@@ -1037,6 +1072,129 @@ find_pack_entry_impl( Eu7Module const &module, int const row, int const column )
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PackSectionReadSession {
|
||||||
|
std::string source_path;
|
||||||
|
int row { -1 };
|
||||||
|
int column { -1 };
|
||||||
|
Eu7PackIndexEntry entry {};
|
||||||
|
Eu7PackSectionCursor header {};
|
||||||
|
std::ifstream input;
|
||||||
|
bool header_ready { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
thread_local PackSectionReadSession g_pack_section_read_session;
|
||||||
|
|
||||||
|
void
|
||||||
|
reset_pack_section_read_session( PackSectionReadSession &session ) {
|
||||||
|
if( session.input.is_open() ) {
|
||||||
|
session.input.close();
|
||||||
|
}
|
||||||
|
session = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ensure_pack_file( Eu7Module const &module, PackSectionReadSession &session ) {
|
||||||
|
if( module.source_path == session.source_path && session.input.is_open() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( session.input.is_open() ) {
|
||||||
|
session.input.close();
|
||||||
|
}
|
||||||
|
session.source_path = module.source_path;
|
||||||
|
session.header_ready = false;
|
||||||
|
session.row = -1;
|
||||||
|
session.column = -1;
|
||||||
|
if( false == module.source_path.empty() ) {
|
||||||
|
session.input.open( module.source_path, std::ios::binary );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ensure_section_header(
|
||||||
|
Eu7Module const &module,
|
||||||
|
int const row,
|
||||||
|
int const column,
|
||||||
|
Eu7PackIndexEntry const &entry,
|
||||||
|
PackSectionReadSession &session ) {
|
||||||
|
if(
|
||||||
|
session.header_ready &&
|
||||||
|
session.row == row &&
|
||||||
|
session.column == column &&
|
||||||
|
session.source_path == module.source_path ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
session.row = row;
|
||||||
|
session.column = column;
|
||||||
|
session.entry = entry;
|
||||||
|
parse_pack_section_header( module, session.input, entry, session.header );
|
||||||
|
session.header_ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Eu7PackSectionChunkLoad
|
||||||
|
read_pack_section_chunk_load_impl(
|
||||||
|
Eu7Module const &module,
|
||||||
|
int const row,
|
||||||
|
int const column,
|
||||||
|
std::uint32_t const chunk_index ) {
|
||||||
|
Eu7PackSectionChunkLoad result;
|
||||||
|
if( false == module.has_pack_chunk || module.source_path.empty() ) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const entry { find_pack_entry_impl( module, row, column ) };
|
||||||
|
if( false == entry.has_value() || entry->model_count == 0 ) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &session { g_pack_section_read_session };
|
||||||
|
ensure_pack_file( module, session );
|
||||||
|
if( !session.input ) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"EU7 PACK: nie mozna otworzyc \"" + module.source_path + "\"" );
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_section_header( module, row, column, *entry, session );
|
||||||
|
|
||||||
|
StringTable const strings { module.strings };
|
||||||
|
auto const &header { session.header };
|
||||||
|
result.chunk_count = header.chunk_count > 0 ? header.chunk_count : 1u;
|
||||||
|
|
||||||
|
if( chunk_index >= result.chunk_count ) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.chunk_index = chunk_index;
|
||||||
|
if( chunk_index == 0 ) {
|
||||||
|
result.unique_meshes = header.unique_meshes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Eu7PackSectionCursor chunk_cursor { header };
|
||||||
|
if(
|
||||||
|
header.section_format == kPackSectionFormatV10 &&
|
||||||
|
false == header.chunk_byte_offsets.empty() ) {
|
||||||
|
auto const section_start {
|
||||||
|
static_cast<std::streamoff>( module.pack_payload_offset + entry->pack_offset ) };
|
||||||
|
session.input.seekg(
|
||||||
|
section_start +
|
||||||
|
static_cast<std::streamoff>( header.chunk_byte_offsets[ chunk_index ] ) );
|
||||||
|
chunk_cursor.solo_remaining = header.chunk_model_counts[ chunk_index ];
|
||||||
|
chunk_cursor.inst_remaining = 0;
|
||||||
|
}
|
||||||
|
else if( chunk_index > 0 ) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.models = read_pack_models_chunk_impl(
|
||||||
|
module,
|
||||||
|
session.input,
|
||||||
|
chunk_cursor,
|
||||||
|
std::numeric_limits<std::size_t>::max(),
|
||||||
|
strings );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] Eu7PackSectionLoad
|
[[nodiscard]] Eu7PackSectionLoad
|
||||||
read_pack_section_load_impl( Eu7Module const &module, int const row, int const column ) {
|
read_pack_section_load_impl( Eu7Module const &module, int const row, int const column ) {
|
||||||
Eu7PackSectionLoad result;
|
Eu7PackSectionLoad result;
|
||||||
@@ -1059,7 +1217,23 @@ read_pack_section_load_impl( Eu7Module const &module, int const row, int const c
|
|||||||
Eu7PackSectionCursor header;
|
Eu7PackSectionCursor header;
|
||||||
parse_pack_section_header( module, input, *entry, header );
|
parse_pack_section_header( module, input, *entry, header );
|
||||||
|
|
||||||
result.unique_meshes = std::move( header.unique_meshes );
|
result.unique_meshes = header.unique_meshes;
|
||||||
|
|
||||||
|
if(
|
||||||
|
header.section_format == kPackSectionFormatV10 &&
|
||||||
|
header.chunk_count > 0 ) {
|
||||||
|
result.models.reserve( entry->model_count );
|
||||||
|
for( std::uint32_t chunk_idx { 0 }; chunk_idx < header.chunk_count; ++chunk_idx ) {
|
||||||
|
auto const chunk {
|
||||||
|
read_pack_section_chunk_load_impl( module, row, column, chunk_idx ) };
|
||||||
|
result.models.insert(
|
||||||
|
result.models.end(),
|
||||||
|
std::make_move_iterator( chunk.models.begin() ),
|
||||||
|
std::make_move_iterator( chunk.models.end() ) );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
result.models = read_pack_models_chunk_impl(
|
result.models = read_pack_models_chunk_impl(
|
||||||
module, input, header, std::numeric_limits<std::size_t>::max(), strings );
|
module, input, header, std::numeric_limits<std::size_t>::max(), strings );
|
||||||
return result;
|
return result;
|
||||||
@@ -1156,4 +1330,18 @@ read_pack_section_load( Eu7Module const &module, int const row, int const column
|
|||||||
return read_pack_section_load_impl( module, row, column );
|
return read_pack_section_load_impl( module, row, column );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Eu7PackSectionChunkLoad
|
||||||
|
read_pack_section_chunk_load(
|
||||||
|
Eu7Module const &module,
|
||||||
|
int const row,
|
||||||
|
int const column,
|
||||||
|
std::uint32_t const chunk_index ) {
|
||||||
|
return read_pack_section_chunk_load_impl( module, row, column, chunk_index );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
reset_pack_section_read_cache() {
|
||||||
|
reset_pack_section_read_session( g_pack_section_read_session );
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace scene::eu7
|
} // namespace scene::eu7
|
||||||
|
|||||||
@@ -31,8 +31,20 @@ find_pack_entry( Eu7Module const &Module, int Row, int Column );
|
|||||||
[[nodiscard]] std::vector<Eu7Model>
|
[[nodiscard]] std::vector<Eu7Model>
|
||||||
read_pack_section( Eu7Module const &Module, int Row, int Column );
|
read_pack_section( Eu7Module const &Module, int Row, int Column );
|
||||||
|
|
||||||
// Odczyt sekcji PACK: modele + precomputed UMES (v9).
|
// Odczyt sekcji PACK: modele + precomputed UMES (v9/v10).
|
||||||
[[nodiscard]] Eu7PackSectionLoad
|
[[nodiscard]] Eu7PackSectionLoad
|
||||||
read_pack_section_load( Eu7Module const &Module, int Row, int Column );
|
read_pack_section_load( Eu7Module const &Module, int Row, int Column );
|
||||||
|
|
||||||
|
// Odczyt jednego sub-chunka sekcji PACK (v10 CHNK; v9/v7 = cala sekcja przy ChunkIndex 0).
|
||||||
|
[[nodiscard]] Eu7PackSectionChunkLoad
|
||||||
|
read_pack_section_chunk_load(
|
||||||
|
Eu7Module const &Module,
|
||||||
|
int Row,
|
||||||
|
int Column,
|
||||||
|
std::uint32_t ChunkIndex );
|
||||||
|
|
||||||
|
// Czysci thread-local cache odczytu PACK (otwarty plik + sparsowany naglowek sekcji).
|
||||||
|
void
|
||||||
|
reset_pack_section_read_cache();
|
||||||
|
|
||||||
} // namespace scene::eu7
|
} // namespace scene::eu7
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ struct PackSectionJob {
|
|||||||
int column { 0 };
|
int column { 0 };
|
||||||
std::size_t section_idx { 0 };
|
std::size_t section_idx { 0 };
|
||||||
int priority { 0 };
|
int priority { 0 };
|
||||||
|
std::uint32_t next_chunk { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PackSectionReady {
|
struct PackSectionReady {
|
||||||
@@ -110,6 +111,7 @@ struct PackSectionReady {
|
|||||||
std::unique_ptr<std::vector<Eu7Model>> models;
|
std::unique_ptr<std::vector<Eu7Model>> models;
|
||||||
std::vector<std::string> unique_meshes;
|
std::vector<std::string> unique_meshes;
|
||||||
bool failed { false };
|
bool failed { false };
|
||||||
|
bool section_final { true };
|
||||||
std::size_t apply_offset { 0 };
|
std::size_t apply_offset { 0 };
|
||||||
std::size_t texture_warm_offset { 0 };
|
std::size_t texture_warm_offset { 0 };
|
||||||
std::size_t umes_cold_offset { 0 };
|
std::size_t umes_cold_offset { 0 };
|
||||||
@@ -820,7 +822,7 @@ apply_pending_chunk(
|
|||||||
if( batch.failed ) {
|
if( batch.failed ) {
|
||||||
fail_section( batch.section_idx, batch.row, batch.column );
|
fail_section( batch.section_idx, batch.row, batch.column );
|
||||||
}
|
}
|
||||||
else {
|
else if( batch.section_final ) {
|
||||||
finalize_section( batch );
|
finalize_section( batch );
|
||||||
}
|
}
|
||||||
release_pending_buffer();
|
release_pending_buffer();
|
||||||
@@ -832,7 +834,9 @@ apply_pending_chunk(
|
|||||||
auto const total { batch.models->size() };
|
auto const total { batch.models->size() };
|
||||||
auto const offset { g_stream.pending_apply_offset };
|
auto const offset { g_stream.pending_apply_offset };
|
||||||
if( offset >= total ) {
|
if( offset >= total ) {
|
||||||
finalize_section( batch );
|
if( batch.section_final ) {
|
||||||
|
finalize_section( batch );
|
||||||
|
}
|
||||||
release_pending_buffer();
|
release_pending_buffer();
|
||||||
applied_work = true;
|
applied_work = true;
|
||||||
continue;
|
continue;
|
||||||
@@ -983,7 +987,9 @@ apply_pending_chunk(
|
|||||||
++chunks_done;
|
++chunks_done;
|
||||||
|
|
||||||
if( g_stream.pending_apply_offset >= total ) {
|
if( g_stream.pending_apply_offset >= total ) {
|
||||||
finalize_section( batch );
|
if( batch.section_final ) {
|
||||||
|
finalize_section( batch );
|
||||||
|
}
|
||||||
release_pending_buffer();
|
release_pending_buffer();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1115,12 +1121,17 @@ worker_loop( std::stop_token const stop_token ) {
|
|||||||
|
|
||||||
std::unique_ptr<std::vector<Eu7Model>> models;
|
std::unique_ptr<std::vector<Eu7Model>> models;
|
||||||
std::vector<std::string> unique_meshes;
|
std::vector<std::string> unique_meshes;
|
||||||
|
std::uint32_t chunk_count { 1 };
|
||||||
{
|
{
|
||||||
PackBenchTimer const read_timer { &Eu7PackBench::worker_read_pack_ms };
|
PackBenchTimer const read_timer { &Eu7PackBench::worker_read_pack_ms };
|
||||||
auto section { read_pack_section_load( module, job.row, job.column ) };
|
auto const chunk {
|
||||||
unique_meshes = std::move( section.unique_meshes );
|
read_pack_section_chunk_load(
|
||||||
if( false == section.models.empty() ) {
|
module, job.row, job.column, job.next_chunk ) };
|
||||||
models = std::make_unique<std::vector<Eu7Model>>( std::move( section.models ) );
|
chunk_count = chunk.chunk_count;
|
||||||
|
unique_meshes = std::move( chunk.unique_meshes );
|
||||||
|
if( false == chunk.models.empty() ) {
|
||||||
|
models = std::make_unique<std::vector<Eu7Model>>(
|
||||||
|
std::move( chunk.models ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1131,11 +1142,13 @@ worker_loop( std::stop_token const stop_token ) {
|
|||||||
result.models = std::move( models );
|
result.models = std::move( models );
|
||||||
result.unique_meshes = std::move( unique_meshes );
|
result.unique_meshes = std::move( unique_meshes );
|
||||||
result.failed = result.models == nullptr;
|
result.failed = result.models == nullptr;
|
||||||
|
result.section_final = job.next_chunk + 1 >= chunk_count;
|
||||||
|
|
||||||
if( result.failed ) {
|
if( result.failed ) {
|
||||||
ErrorLog(
|
ErrorLog(
|
||||||
"EU7 PACK: pusty odczyt sekcji " + std::to_string( job.row ) + "," +
|
"EU7 PACK: pusty odczyt sekcji " + std::to_string( job.row ) + "," +
|
||||||
std::to_string( job.column ) + " (PIDX model_count=" +
|
std::to_string( job.column ) + " chunk=" +
|
||||||
|
std::to_string( job.next_chunk ) + " (PIDX model_count=" +
|
||||||
std::to_string( entry->model_count ) + ")" );
|
std::to_string( entry->model_count ) + ")" );
|
||||||
enqueue_failed_section( job );
|
enqueue_failed_section( job );
|
||||||
continue;
|
continue;
|
||||||
@@ -1151,7 +1164,20 @@ worker_loop( std::stop_token const stop_token ) {
|
|||||||
g_stream.ready.data.push_back( std::move( result ) );
|
g_stream.ready.data.push_back( std::move( result ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
pack_bench_inc( &Eu7PackBench::worker_sections_done );
|
pack_bench_inc( &Eu7PackBench::worker_chunks_decoded );
|
||||||
|
if( result.section_final ) {
|
||||||
|
pack_bench_inc( &Eu7PackBench::worker_sections_done );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( job.next_chunk + 1 < chunk_count ) {
|
||||||
|
PackSectionJob continue_job { job };
|
||||||
|
continue_job.next_chunk = job.next_chunk + 1;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock { g_stream.jobs.mutex };
|
||||||
|
g_stream.jobs.data.push_front( continue_job );
|
||||||
|
}
|
||||||
|
g_stream.work_cv.notify_one();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch( std::exception const &ex ) {
|
catch( std::exception const &ex ) {
|
||||||
ErrorLog(
|
ErrorLog(
|
||||||
@@ -1168,6 +1194,7 @@ stop_workers() {
|
|||||||
g_stream.work_cv.notify_all();
|
g_stream.work_cv.notify_all();
|
||||||
g_stream.workers.clear();
|
g_stream.workers.clear();
|
||||||
g_stream.worker_exit = false;
|
g_stream.worker_exit = false;
|
||||||
|
reset_pack_section_read_cache();
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock { g_stream.jobs.mutex };
|
std::lock_guard<std::mutex> lock { g_stream.jobs.mutex };
|
||||||
@@ -1581,7 +1608,7 @@ try_dequeue_ready_batch() {
|
|||||||
if( it->failed ) {
|
if( it->failed ) {
|
||||||
fail_section( it->section_idx, it->row, it->column );
|
fail_section( it->section_idx, it->row, it->column );
|
||||||
}
|
}
|
||||||
else {
|
else if( it->section_final ) {
|
||||||
finalize_section( *it );
|
finalize_section( *it );
|
||||||
}
|
}
|
||||||
it = g_stream.ready.data.erase( it );
|
it = g_stream.ready.data.erase( it );
|
||||||
@@ -1672,7 +1699,7 @@ try_dequeue_ready_batch() {
|
|||||||
if( batch.failed ) {
|
if( batch.failed ) {
|
||||||
fail_section( batch.section_idx, batch.row, batch.column );
|
fail_section( batch.section_idx, batch.row, batch.column );
|
||||||
}
|
}
|
||||||
else {
|
else if( batch.section_final ) {
|
||||||
finalize_section( batch );
|
finalize_section( batch );
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -369,6 +369,9 @@ struct Eu7PackSectionCursor {
|
|||||||
std::uint8_t section_format { 0 };
|
std::uint8_t section_format { 0 };
|
||||||
bool header_parsed { false };
|
bool header_parsed { false };
|
||||||
std::vector<std::string> unique_meshes;
|
std::vector<std::string> unique_meshes;
|
||||||
|
std::uint32_t chunk_count { 0 };
|
||||||
|
std::vector<std::uint32_t> chunk_model_counts;
|
||||||
|
std::vector<std::uint32_t> chunk_byte_offsets;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Eu7PackSectionLoad {
|
struct Eu7PackSectionLoad {
|
||||||
@@ -376,6 +379,13 @@ struct Eu7PackSectionLoad {
|
|||||||
std::vector<std::string> unique_meshes;
|
std::vector<std::string> unique_meshes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Eu7PackSectionChunkLoad {
|
||||||
|
std::vector<Eu7Model> models;
|
||||||
|
std::vector<std::string> unique_meshes;
|
||||||
|
std::uint32_t chunk_count { 1 };
|
||||||
|
std::uint32_t chunk_index { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
struct Eu7PackCatalog {
|
struct Eu7PackCatalog {
|
||||||
std::vector<Eu7PackIndexEntry> entries;
|
std::vector<Eu7PackIndexEntry> entries;
|
||||||
std::unordered_map<std::uint32_t, std::size_t> index_by_section;
|
std::unordered_map<std::uint32_t, std::size_t> index_by_section;
|
||||||
|
|||||||
Reference in New Issue
Block a user