16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-20 12:39:17 +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:
maj00r
2026-06-14 19:49:24 +02:00
parent 8c069ec5f2
commit 56d72fb25e
9 changed files with 336 additions and 23 deletions

View File

@@ -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> 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 kPackSectionFormatV10 = 3;
inline constexpr std::size_t kPackSectionChunkModels = 512;
struct PackWriteStats {
std::size_t strings_total = 0;
@@ -437,7 +439,22 @@ struct PackPayloadBuild {
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,
const std::vector<runtime::RuntimeModelInstance>& models,
const std::uint32_t inst_count = 0,
@@ -445,18 +462,51 @@ struct PackPayloadBuild {
const auto mesh_ids = collectUniqueMeshIds(table, models, prototype_mesh_paths);
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);
io::writeU8(packOut, kPackSectionFormatV9);
io::writeU8(packOut, kPackSectionFormatV10);
io::writeU32(packOut, solo_count);
io::writeU32(packOut, inst_count);
io::writeU32(packOut, static_cast<std::uint32_t>(mesh_ids.size()));
for (const std::uint32_t id : mesh_ids) {
io::writeU32(packOut, id);
}
for (const runtime::RuntimeModelInstance& model : models) {
runtime::RuntimeModelInstance worldModel = model;
worldModel.node.transform = {};
writeRuntimeModel(packOut, table, worldModel);
io::writeU32(packOut, chunk_count);
for (std::uint32_t chunk_index = 0; chunk_index < chunk_count; ++chunk_index) {
io::writeU32(packOut, chunk_model_counts[chunk_index]);
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();
return {blob.begin(), blob.end()};
@@ -490,7 +540,7 @@ struct PackPayloadBuild {
entry.row = static_cast<std::uint16_t>(batch.section.z);
entry.column = static_cast<std::uint16_t>(batch.section.x);
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;
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.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);
}
});
}