mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
Add profiling for VRAM, FPS ratios, and instancing analysis
Per-second frame log reports fps/ms, visible/total object & triangle ratios, VRAM breakdown (VBO+EBO), model count, and pending uploads. Upload-complete log includes per-model VBO/EBO MB and scene total VRAM. Streamer runs an instancing analysis keyed on geom.id(): total shapes, unique representations, dedup ratio, theoretical VBO/EBO/SSBO sizes if instanced, potential savings, and top-5 most-duplicated representations. Used to validate whether GPU instancing is worth the architectural rewrite for a given dataset. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,9 @@
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
GeometryStreamer::GeometryStreamer(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
@@ -126,6 +129,20 @@ void GeometryStreamer::run(const std::string& path, int num_threads) {
|
||||
|
||||
int last_progress = 0;
|
||||
|
||||
// Instancing analysis: count shapes grouped by representation id.
|
||||
struct GeomStat {
|
||||
uint32_t count = 0;
|
||||
size_t vertex_count = 0;
|
||||
size_t index_count = 0;
|
||||
std::string example_type;
|
||||
};
|
||||
std::unordered_map<std::string, GeomStat> geom_stats;
|
||||
uint32_t total_shapes = 0;
|
||||
size_t total_vertices = 0;
|
||||
size_t total_indices = 0;
|
||||
QElapsedTimer stream_timer;
|
||||
stream_timer.start();
|
||||
|
||||
do {
|
||||
if (cancel_requested_.load()) break;
|
||||
|
||||
@@ -147,6 +164,24 @@ void GeometryStreamer::run(const std::string& path, int num_threads) {
|
||||
info.type = tri_elem->type();
|
||||
info.parent_id = tri_elem->parent_id();
|
||||
|
||||
// Instancing stats: key by representation id, count unique vs repeated.
|
||||
const auto& geom = tri_elem->geometry();
|
||||
const std::string& geom_id = geom.id();
|
||||
size_t nv = geom.verts().size() / 3;
|
||||
size_t ni = geom.faces().size();
|
||||
if (!geom_id.empty()) {
|
||||
auto& gs = geom_stats[geom_id];
|
||||
gs.count++;
|
||||
if (gs.count == 1) {
|
||||
gs.vertex_count = nv;
|
||||
gs.index_count = ni;
|
||||
gs.example_type = info.type;
|
||||
}
|
||||
}
|
||||
total_shapes++;
|
||||
total_vertices += nv;
|
||||
total_indices += ni;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(elements_mutex_);
|
||||
pending_elements_.push_back(std::move(info));
|
||||
@@ -168,6 +203,74 @@ void GeometryStreamer::run(const std::string& path, int num_threads) {
|
||||
|
||||
progress_ = 100;
|
||||
emit progressChanged(100);
|
||||
|
||||
// === Instancing report ===
|
||||
{
|
||||
size_t unique_geoms = geom_stats.size();
|
||||
size_t unique_vertices = 0;
|
||||
size_t unique_indices = 0;
|
||||
size_t repeated_shapes = 0; // total shapes that share a repr with another
|
||||
for (const auto& [gid, gs] : geom_stats) {
|
||||
unique_vertices += gs.vertex_count;
|
||||
unique_indices += gs.index_count;
|
||||
if (gs.count > 1) repeated_shapes += gs.count;
|
||||
}
|
||||
|
||||
// Bytes assuming current layout (32 B/vertex, 4 B/index).
|
||||
size_t baked_vbo_bytes = total_vertices * 32;
|
||||
size_t baked_ebo_bytes = total_indices * 4;
|
||||
size_t instanced_vbo_bytes = unique_vertices * 32;
|
||||
size_t instanced_ebo_bytes = unique_indices * 4;
|
||||
// Per-instance data: 64 B transform + 8 B (object_id + color).
|
||||
size_t per_instance_bytes = 72;
|
||||
size_t instance_ssbo_bytes = total_shapes * per_instance_bytes;
|
||||
|
||||
double dedup_ratio = unique_geoms > 0
|
||||
? static_cast<double>(total_shapes) / static_cast<double>(unique_geoms)
|
||||
: 1.0;
|
||||
|
||||
qDebug("=== Instancing analysis: %s ===", path.c_str());
|
||||
qDebug(" Stream time: %.2f s", stream_timer.elapsed() / 1000.0);
|
||||
qDebug(" Total shapes: %u", total_shapes);
|
||||
qDebug(" Unique geometries: %zu (dedup ratio %.2fx)",
|
||||
unique_geoms, dedup_ratio);
|
||||
qDebug(" Repeated shapes: %zu (%.1f%% of total)",
|
||||
repeated_shapes,
|
||||
total_shapes > 0 ? 100.0 * repeated_shapes / total_shapes : 0.0);
|
||||
qDebug(" Baked geometry: VBO %.1f MB + EBO %.1f MB = %.1f MB",
|
||||
baked_vbo_bytes / (1024.0*1024.0),
|
||||
baked_ebo_bytes / (1024.0*1024.0),
|
||||
(baked_vbo_bytes + baked_ebo_bytes) / (1024.0*1024.0));
|
||||
qDebug(" If instanced: VBO %.1f MB + EBO %.1f MB + SSBO %.1f MB = %.1f MB",
|
||||
instanced_vbo_bytes / (1024.0*1024.0),
|
||||
instanced_ebo_bytes / (1024.0*1024.0),
|
||||
instance_ssbo_bytes / (1024.0*1024.0),
|
||||
(instanced_vbo_bytes + instanced_ebo_bytes + instance_ssbo_bytes)
|
||||
/ (1024.0*1024.0));
|
||||
size_t baked_total = baked_vbo_bytes + baked_ebo_bytes;
|
||||
size_t inst_total = instanced_vbo_bytes + instanced_ebo_bytes + instance_ssbo_bytes;
|
||||
if (inst_total > 0 && baked_total > inst_total) {
|
||||
qDebug(" Potential savings: %.1f MB (%.1f%%)",
|
||||
(baked_total - inst_total) / (1024.0*1024.0),
|
||||
100.0 * (baked_total - inst_total) / baked_total);
|
||||
} else {
|
||||
qDebug(" Potential savings: none (instance overhead exceeds dedup win)");
|
||||
}
|
||||
|
||||
// Top-5 most duplicated representations.
|
||||
std::vector<std::pair<std::string, GeomStat>> sorted(geom_stats.begin(), geom_stats.end());
|
||||
std::partial_sort(sorted.begin(),
|
||||
sorted.begin() + std::min<size_t>(5, sorted.size()),
|
||||
sorted.end(),
|
||||
[](const auto& a, const auto& b) { return a.second.count > b.second.count; });
|
||||
qDebug(" Top duplicated representations:");
|
||||
for (size_t i = 0; i < std::min<size_t>(5, sorted.size()); ++i) {
|
||||
const auto& [gid, gs] = sorted[i];
|
||||
qDebug(" [%zu] count=%u verts=%zu type=%s repr_id=%s",
|
||||
i + 1, gs.count, gs.vertex_count,
|
||||
gs.example_type.c_str(), gid.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static MaterialInfo materialFromStyle(const ifcopenshell::geometry::taxonomy::style::ptr& style) {
|
||||
|
||||
@@ -592,7 +592,19 @@ void ViewportWindow::processPendingUploads() {
|
||||
model_bvhs_[pu.model_id] = std::move(pu.bvh_set);
|
||||
}
|
||||
|
||||
qDebug("Progressive upload complete: model %u", pu.model_id);
|
||||
size_t total_vbo = 0, total_ebo = 0;
|
||||
for (const auto& [mid, mg] : models_gpu_) {
|
||||
total_vbo += mg.vbo_capacity;
|
||||
total_ebo += mg.ebo_capacity;
|
||||
}
|
||||
qDebug("Progressive upload complete: model %u (this: vbo %.1f MB + ebo %.1f MB, "
|
||||
"%u objects, %u triangles) scene total vram %.1f MB",
|
||||
pu.model_id,
|
||||
mgpu.vbo_capacity / (1024.0 * 1024.0),
|
||||
mgpu.ebo_capacity / (1024.0 * 1024.0),
|
||||
static_cast<uint32_t>(mgpu.draw_info.size()),
|
||||
mgpu.total_triangles,
|
||||
(total_vbo + total_ebo) / (1024.0 * 1024.0));
|
||||
pending_uploads_.pop_front();
|
||||
}
|
||||
|
||||
@@ -953,12 +965,17 @@ void ViewportWindow::render() {
|
||||
accumulated_time_ = 0.0f;
|
||||
|
||||
uint32_t total_obj = 0, total_tri = 0, vis_obj = 0;
|
||||
size_t total_vram = 0, total_vbo = 0, total_ebo = 0;
|
||||
size_t num_models = 0, num_hidden = 0;
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
if (!m.hidden) {
|
||||
total_obj += static_cast<uint32_t>(m.draw_info.size());
|
||||
total_tri += m.total_triangles;
|
||||
}
|
||||
num_models++;
|
||||
if (m.hidden) { num_hidden++; continue; }
|
||||
total_obj += static_cast<uint32_t>(m.draw_info.size());
|
||||
total_tri += m.total_triangles;
|
||||
total_vbo += m.vbo_capacity;
|
||||
total_ebo += m.ebo_capacity;
|
||||
}
|
||||
total_vram = total_vbo + total_ebo;
|
||||
for (const auto& cmd : frame_draw_cmds_) {
|
||||
vis_obj += static_cast<uint32_t>(cmd.counts.size());
|
||||
}
|
||||
@@ -971,6 +988,20 @@ void ViewportWindow::render() {
|
||||
stats.total_triangles = total_tri;
|
||||
stats.visible_triangles = visible_triangles_;
|
||||
emit frameStatsUpdated(stats);
|
||||
|
||||
double vis_obj_pct = total_obj > 0 ? 100.0 * vis_obj / total_obj : 0.0;
|
||||
double vis_tri_pct = total_tri > 0 ? 100.0 * visible_triangles_ / total_tri : 0.0;
|
||||
qDebug("[frame] %.1f fps %.2f ms obj %u/%u (%.1f%%) tri %u/%u (%.1f%%) "
|
||||
"vram %.1f MB (vbo %.1f + ebo %.1f) models %zu (%zu hidden) draws %zu pending_uploads %zu",
|
||||
last_fps_, 1000.0f / last_fps_,
|
||||
vis_obj, total_obj, vis_obj_pct,
|
||||
visible_triangles_, total_tri, vis_tri_pct,
|
||||
total_vram / (1024.0 * 1024.0),
|
||||
total_vbo / (1024.0 * 1024.0),
|
||||
total_ebo / (1024.0 * 1024.0),
|
||||
num_models, num_hidden,
|
||||
frame_draw_cmds_.size(),
|
||||
pending_uploads_.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user