|
@@ -32,23 +32,27 @@ bool resource_id(const topology::Snapshot& topology,
|
|
|
static_cast<unsigned>(slot.slotType));
|
|
static_cast<unsigned>(slot.slotType));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** Formats one scene-to-squad edge ID from its exact descriptor tuple. */
|
|
|
|
|
|
|
+/** Formats one scene-to-squad edge ID from its descriptor tuple and the squad slot it names. */
|
|
|
bool edge_id(const topology::Snapshot& topology,
|
|
bool edge_id(const topology::Snapshot& topology,
|
|
|
const squad::DescriptorFact& descriptor,
|
|
const squad::DescriptorFact& descriptor,
|
|
|
|
|
+ std::uint32_t squadSlotRow,
|
|
|
Text& output) noexcept {
|
|
Text& output) noexcept {
|
|
|
if (descriptor.objectIndex >= topology.objects.size()
|
|
if (descriptor.objectIndex >= topology.objects.size()
|
|
|
- || descriptor.slotIndex >= topology.slots.size()) {
|
|
|
|
|
|
|
+ || descriptor.slotIndex >= topology.slots.size() || squadSlotRow >= topology.slots.size()) {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
const topology::Object& object = topology.objects[descriptor.objectIndex];
|
|
const topology::Object& object = topology.objects[descriptor.objectIndex];
|
|
|
const topology::Slot& slot = topology.slots[descriptor.slotIndex];
|
|
const topology::Slot& slot = topology.slots[descriptor.slotIndex];
|
|
|
|
|
+ const topology::Slot& squad = topology.slots[squadSlotRow];
|
|
|
return format_text(output,
|
|
return format_text(output,
|
|
|
- "authored-scene-squad-edge/%08x/%08x/%08x/%04x/%04x",
|
|
|
|
|
|
|
+ "authored-scene-squad-edge/%08x/%08x/%08x/%04x/%04x/%04x/%04x",
|
|
|
static_cast<unsigned>(descriptor.configTag),
|
|
static_cast<unsigned>(descriptor.configTag),
|
|
|
static_cast<unsigned>(object.objectTag),
|
|
static_cast<unsigned>(object.objectTag),
|
|
|
static_cast<unsigned>(descriptor.descriptorOffset),
|
|
static_cast<unsigned>(descriptor.descriptorOffset),
|
|
|
static_cast<unsigned>(slot.slotIndex),
|
|
static_cast<unsigned>(slot.slotIndex),
|
|
|
- static_cast<unsigned>(slot.slotType));
|
|
|
|
|
|
|
+ static_cast<unsigned>(slot.slotType),
|
|
|
|
|
+ static_cast<unsigned>(squad.slotIndex),
|
|
|
|
|
+ static_cast<unsigned>(squad.slotType));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/** Formats one task-to-objective target ID from its exact descriptor tuple. */
|
|
/** Formats one task-to-objective target ID from its exact descriptor tuple. */
|
|
@@ -191,6 +195,30 @@ void log_performance_edge(const squad::DescriptorFact& descriptor, const char* r
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/** Logs a scene descriptor that produced no resource row, and why. */
|
|
|
|
|
+void log_scene_resource(const squad::DescriptorFact& descriptor,
|
|
|
|
|
+ std::uint32_t resourceTag,
|
|
|
|
|
+ const char* result,
|
|
|
|
|
+ core::log::Level level) noexcept {
|
|
|
|
|
+ std::array<char, 176> line{};
|
|
|
|
|
+ const int written =
|
|
|
|
|
+ std::snprintf(line.data(),
|
|
|
|
|
+ line.size(),
|
|
|
|
|
+ "ev=activity_sdk_scene_resource result=%s config=0x%08X offset=0x%X "
|
|
|
|
|
+ "slot_row=%u resource=0x%08X",
|
|
|
|
|
+ result,
|
|
|
|
|
+ static_cast<unsigned>(descriptor.configTag),
|
|
|
|
|
+ static_cast<unsigned>(descriptor.descriptorOffset),
|
|
|
|
|
+ static_cast<unsigned>(descriptor.slotIndex),
|
|
|
|
|
+ static_cast<unsigned>(resourceTag));
|
|
|
|
|
+ if (written > 0) {
|
|
|
|
|
+ core::log::write(
|
|
|
|
|
+ core::log::Channel::client,
|
|
|
|
|
+ level,
|
|
|
|
|
+ {line.data(), (std::min)(static_cast<std::size_t>(written), line.size() - 1U)});
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/** Reads one tag once and retains the physical class beside its bytes. */
|
|
/** Reads one tag once and retains the physical class beside its bytes. */
|
|
|
[[nodiscard]] bool package_row(squad::TagReader reader,
|
|
[[nodiscard]] bool package_row(squad::TagReader reader,
|
|
|
void* readerContext,
|
|
void* readerContext,
|
|
@@ -435,7 +463,7 @@ bool build(const topology::Snapshot& topology,
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
SquadEdge row{};
|
|
SquadEdge row{};
|
|
|
- if (!edge_id(topology, descriptor, row.id)) {
|
|
|
|
|
|
|
+ if (!edge_id(topology, descriptor, linkedSlot, row.id)) {
|
|
|
log_performance_edge(descriptor, "identity");
|
|
log_performance_edge(descriptor, "identity");
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
@@ -474,11 +502,18 @@ bool build(const topology::Snapshot& topology,
|
|
|
if (!read_value(blob, resourceField, resourceTag)) {
|
|
if (!read_value(blob, resourceField, resourceTag)) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
- if (resourceTag != 0 && resourceTag != format::kAbsentIndex) {
|
|
|
|
|
|
|
+ if (resourceTag == 0 || resourceTag == format::kAbsentIndex) {
|
|
|
|
|
+ log_scene_resource(descriptor, resourceTag, "unresourced", core::log::Level::debug);
|
|
|
|
|
+ pending.unresourcedSlots.push_back(descriptor.slotIndex);
|
|
|
|
|
+ } else {
|
|
|
const PackageRow* resourcePackage = nullptr;
|
|
const PackageRow* resourcePackage = nullptr;
|
|
|
- if (package_row(reader, readerContext, resourceTag, cache, resourcePackage)
|
|
|
|
|
- && resourcePackage != nullptr
|
|
|
|
|
- && resourcePackage->classId == format::kAuthoredSceneResourceClass) {
|
|
|
|
|
|
|
+ if (!package_row(reader, readerContext, resourceTag, cache, resourcePackage)
|
|
|
|
|
+ || resourcePackage == nullptr) {
|
|
|
|
|
+ log_scene_resource(
|
|
|
|
|
+ descriptor, resourceTag, "unreadable", core::log::Level::warn);
|
|
|
|
|
+ } else if (resourcePackage->classId != format::kAuthoredSceneResourceClass) {
|
|
|
|
|
+ log_scene_resource(descriptor, resourceTag, "class", core::log::Level::warn);
|
|
|
|
|
+ } else {
|
|
|
Resource row{};
|
|
Resource row{};
|
|
|
if (resource_id(topology, descriptor, row.id)) {
|
|
if (resource_id(topology, descriptor, row.id)) {
|
|
|
row.slotIndex = descriptor.slotIndex;
|
|
row.slotIndex = descriptor.slotIndex;
|
|
@@ -493,61 +528,87 @@ bool build(const topology::Snapshot& topology,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const std::size_t blockClassField =
|
|
|
|
|
- static_cast<std::size_t>(descriptor.descriptorOffset)
|
|
|
|
|
- + format::kAuthoredSceneSquadBlockClassRelativeOffset;
|
|
|
|
|
- std::uint32_t blockClass = 0;
|
|
|
|
|
- if (!read_value(blob, blockClassField, blockClass)) {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
- if (blockClass != format::kAuthoredSceneSquadBlockClass) {
|
|
|
|
|
|
|
+ // Every squad in the participant table is an edge; the other participant kinds
|
|
|
|
|
+ // (point sets, objects) are not.
|
|
|
|
|
+ const std::size_t descriptorOffset = descriptor.descriptorOffset;
|
|
|
|
|
+ std::uint64_t participantCount = 0;
|
|
|
|
|
+ std::uint32_t tableClass = 0;
|
|
|
|
|
+ if (!read_value(blob,
|
|
|
|
|
+ descriptorOffset + format::kAuthoredSceneParticipantCountRelativeOffset,
|
|
|
|
|
+ participantCount)
|
|
|
|
|
+ || !read_value(blob,
|
|
|
|
|
+ descriptorOffset
|
|
|
|
|
+ + format::kAuthoredSceneParticipantTableClassRelativeOffset,
|
|
|
|
|
+ tableClass)
|
|
|
|
|
+ || tableClass != format::kAuthoredSceneParticipantTableClass
|
|
|
|
|
+ || participantCount > format::kAuthoredSceneParticipantCapacity) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
- const std::size_t referenceField = static_cast<std::size_t>(descriptor.descriptorOffset)
|
|
|
|
|
- + format::kAuthoredSceneSquadReferenceRelativeOffset;
|
|
|
|
|
- std::uint32_t targetObjectKey = 0;
|
|
|
|
|
- std::uint16_t targetSlotType = 0;
|
|
|
|
|
- std::uint16_t targetSlotIndex = 0;
|
|
|
|
|
- if (!read_value(blob, referenceField, targetObjectKey)
|
|
|
|
|
- || !read_value(blob, referenceField + kTargetSlotTypeRelativeOffset, targetSlotType)
|
|
|
|
|
- || !read_value(
|
|
|
|
|
- blob, referenceField + kTargetSlotIndexRelativeOffset, targetSlotIndex)) {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
- if (targetSlotType != format::kSquadSlotType) {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
- if (descriptor.objectIndex >= topology.objects.size()
|
|
|
|
|
- || targetObjectKey != topology.objects[descriptor.objectIndex].objectKey) {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
- std::uint32_t linkedSlot = format::kAbsentIndex;
|
|
|
|
|
- if (!same_object_slot(
|
|
|
|
|
- topology, descriptor, targetSlotType, targetSlotIndex, linkedSlot)) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- if (linkedSlot == format::kAbsentIndex
|
|
|
|
|
- || !slot_shape(topology,
|
|
|
|
|
- schemas,
|
|
|
|
|
- linkedSlot,
|
|
|
|
|
- format::kSquadSlotType,
|
|
|
|
|
- format::kSquadComponentClass,
|
|
|
|
|
- format::kSquadSenseSchema,
|
|
|
|
|
- format::kSquadAuthSchema)) {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
- SquadEdge row{};
|
|
|
|
|
- if (!edge_id(topology, descriptor, row.id)) {
|
|
|
|
|
- continue;
|
|
|
|
|
|
|
+ for (std::uint64_t entry = 0; entry < participantCount; ++entry) {
|
|
|
|
|
+ const std::size_t pointerField =
|
|
|
|
|
+ descriptorOffset + format::kAuthoredSceneParticipantTableRelativeOffset
|
|
|
|
|
+ + static_cast<std::size_t>(entry)
|
|
|
|
|
+ * format::kAuthoredSceneParticipantPointerSize;
|
|
|
|
|
+ std::uint64_t pointer = 0;
|
|
|
|
|
+ if (!read_value(blob, pointerField, pointer)
|
|
|
|
|
+ || pointer > blob.size() - pointerField) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ const std::size_t payload = pointerField + static_cast<std::size_t>(pointer);
|
|
|
|
|
+ std::uint32_t blockClass = 0;
|
|
|
|
|
+ if (payload < format::kAuthoredSceneParticipantClassSize
|
|
|
|
|
+ || !read_value(
|
|
|
|
|
+ blob, payload - format::kAuthoredSceneParticipantClassSize, blockClass)) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (blockClass != format::kAuthoredSceneSquadBlockClass) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ const std::size_t referenceField =
|
|
|
|
|
+ payload + format::kAuthoredSceneSquadPayloadReferenceOffset;
|
|
|
|
|
+ std::uint32_t targetObjectKey = 0;
|
|
|
|
|
+ std::uint16_t targetSlotType = 0;
|
|
|
|
|
+ std::uint16_t targetSlotIndex = 0;
|
|
|
|
|
+ if (!read_value(blob, referenceField, targetObjectKey)
|
|
|
|
|
+ || !read_value(
|
|
|
|
|
+ blob, referenceField + kTargetSlotTypeRelativeOffset, targetSlotType)
|
|
|
|
|
+ || !read_value(
|
|
|
|
|
+ blob, referenceField + kTargetSlotIndexRelativeOffset, targetSlotIndex)) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (targetSlotType != format::kSquadSlotType
|
|
|
|
|
+ || descriptor.objectIndex >= topology.objects.size()
|
|
|
|
|
+ || targetObjectKey != topology.objects[descriptor.objectIndex].objectKey) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ std::uint32_t linkedSlot = format::kAbsentIndex;
|
|
|
|
|
+ if (!same_object_slot(
|
|
|
|
|
+ topology, descriptor, targetSlotType, targetSlotIndex, linkedSlot)) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (linkedSlot == format::kAbsentIndex
|
|
|
|
|
+ || !slot_shape(topology,
|
|
|
|
|
+ schemas,
|
|
|
|
|
+ linkedSlot,
|
|
|
|
|
+ format::kSquadSlotType,
|
|
|
|
|
+ format::kSquadComponentClass,
|
|
|
|
|
+ format::kSquadSenseSchema,
|
|
|
|
|
+ format::kSquadAuthSchema)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ SquadEdge row{};
|
|
|
|
|
+ if (!edge_id(topology, descriptor, linkedSlot, row.id)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ row.sceneSlotIndex = descriptor.slotIndex;
|
|
|
|
|
+ row.squadSlotIndex = linkedSlot;
|
|
|
|
|
+ row.configTag = descriptor.configTag;
|
|
|
|
|
+ row.descriptorOffset = descriptor.descriptorOffset;
|
|
|
|
|
+ row.referenceFieldOffset = static_cast<std::uint32_t>(referenceField);
|
|
|
|
|
+ row.targetObjectKey = targetObjectKey;
|
|
|
|
|
+ row.flags = format::kAuthoredSceneSquadSameObjectExact;
|
|
|
|
|
+ pending.squadEdges.push_back(row);
|
|
|
}
|
|
}
|
|
|
- row.sceneSlotIndex = descriptor.slotIndex;
|
|
|
|
|
- row.squadSlotIndex = linkedSlot;
|
|
|
|
|
- row.configTag = descriptor.configTag;
|
|
|
|
|
- row.descriptorOffset = descriptor.descriptorOffset;
|
|
|
|
|
- row.referenceFieldOffset = static_cast<std::uint32_t>(referenceField);
|
|
|
|
|
- row.targetObjectKey = targetObjectKey;
|
|
|
|
|
- row.flags = format::kAuthoredSceneSquadSameObjectExact;
|
|
|
|
|
- pending.squadEdges.push_back(row);
|
|
|
|
|
}
|
|
}
|
|
|
// A sensor with no descriptor fact logs nothing above, so count both sides here.
|
|
// A sensor with no descriptor fact logs nothing above, so count both sides here.
|
|
|
{
|
|
{
|
|
@@ -595,6 +656,10 @@ bool build(const topology::Snapshot& topology,
|
|
|
return task_natural(left) == task_natural(right);
|
|
return task_natural(left) == task_natural(right);
|
|
|
}),
|
|
}),
|
|
|
pending.taskTargets.end());
|
|
pending.taskTargets.end());
|
|
|
|
|
+ std::sort(pending.unresourcedSlots.begin(), pending.unresourcedSlots.end());
|
|
|
|
|
+ pending.unresourcedSlots.erase(
|
|
|
|
|
+ std::unique(pending.unresourcedSlots.begin(), pending.unresourcedSlots.end()),
|
|
|
|
|
+ pending.unresourcedSlots.end());
|
|
|
pending.complete = true;
|
|
pending.complete = true;
|
|
|
output = std::move(pending);
|
|
output = std::move(pending);
|
|
|
return true;
|
|
return true;
|