Files
MapLibreParticleLab/src/mbgl/gfx/rendering_stats.cpp
Adrian Cojocaru 2a7249a3b0 Rendering statistics view (#3322)
Co-authored-by: Bart Louwers <bart@emeel.net>
2025-06-03 17:59:51 +02:00

293 lines
11 KiB
C++

#include <mbgl/gfx/rendering_stats.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/sources/custom_geometry_source.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/util/monotonic_timer.hpp>
#include <algorithm>
#include <initializer_list>
#include <sstream>
#include <iomanip>
namespace mbgl {
namespace gfx {
bool RenderingStats::isZero() const {
const auto expectedZeros = {numActiveTextures,
numTextureBindings,
numBuffers,
numVertexBuffers,
numIndexBuffers,
numUniformBuffers,
numFrameBuffers,
memTextures,
memBuffers,
memIndexBuffers,
memVertexBuffers,
memUniformBuffers};
return std::all_of(expectedZeros.begin(), expectedZeros.end(), [](auto x) { return x == 0; });
}
RenderingStats& RenderingStats::operator+=(const RenderingStats& r) {
encodingTime += r.encodingTime;
renderingTime += r.renderingTime;
numFrames += r.numFrames;
numDrawCalls += r.numDrawCalls;
totalDrawCalls += r.totalDrawCalls;
numCreatedTextures += r.numCreatedTextures;
numActiveTextures += r.numActiveTextures;
numTextureBindings += r.numTextureBindings;
numTextureUpdates += r.numTextureUpdates;
textureUpdateBytes += r.textureUpdateBytes;
totalBuffers += r.totalBuffers;
totalBufferObjs += r.totalBufferObjs;
bufferUpdates += r.bufferUpdates;
bufferObjUpdates += r.bufferObjUpdates;
bufferUpdateBytes += r.bufferUpdateBytes;
numBuffers += r.numBuffers;
numFrameBuffers += r.numFrameBuffers;
numIndexBuffers += r.numIndexBuffers;
indexUpdateBytes += r.indexUpdateBytes;
numVertexBuffers += r.numVertexBuffers;
vertexUpdateBytes += r.vertexUpdateBytes;
numUniformBuffers += r.numUniformBuffers;
numUniformUpdates += r.numUniformUpdates;
uniformUpdateBytes += r.uniformUpdateBytes;
memTextures += r.memTextures;
memBuffers += r.memBuffers;
memIndexBuffers += r.memIndexBuffers;
memVertexBuffers += r.memVertexBuffers;
memUniformBuffers += r.memUniformBuffers;
stencilClears += r.stencilClears;
stencilUpdates += r.stencilUpdates;
return *this;
}
#if !defined(NDEBUG)
template <typename T>
std::ostream& optionalStatLine(std::ostream& stream, T value, std::string_view label, std::string_view sep) {
if (value) {
stream << label << " = " << value << sep;
}
return stream;
}
std::string RenderingStats::toString(std::string_view sep) const {
std::stringstream ss;
ss.precision(2);
optionalStatLine(ss, encodingTime, "encodingTime", sep);
optionalStatLine(ss, renderingTime, "renderingTime", sep);
optionalStatLine(ss, numFrames, "numFrames", sep);
optionalStatLine(ss, numDrawCalls, "numDrawCalls", sep);
optionalStatLine(ss, totalDrawCalls, "totalDrawCalls", sep);
optionalStatLine(ss, numCreatedTextures, "numCreatedTextures", sep);
optionalStatLine(ss, numActiveTextures, "numActiveTextures", sep);
optionalStatLine(ss, numTextureBindings, "numTextureBindings", sep);
optionalStatLine(ss, numTextureUpdates, "numTextureUpdates", sep);
optionalStatLine(ss, textureUpdateBytes, "textureUpdateBytes", sep);
optionalStatLine(ss, totalBuffers, "totalBuffers", sep);
optionalStatLine(ss, totalBufferObjs, "totalBufferObjs", sep);
optionalStatLine(ss, bufferUpdates, "bufferUpdates", sep);
optionalStatLine(ss, bufferObjUpdates, "bufferObjUpdates", sep);
optionalStatLine(ss, bufferUpdateBytes, "bufferUpdateBytes", sep);
optionalStatLine(ss, numBuffers, "numBuffers", sep);
optionalStatLine(ss, numFrameBuffers, "numFrameBuffers", sep);
optionalStatLine(ss, numIndexBuffers, "numIndexBuffers", sep);
optionalStatLine(ss, indexUpdateBytes, "indexUpdateBytes", sep);
optionalStatLine(ss, numVertexBuffers, "numVertexBuffers", sep);
optionalStatLine(ss, vertexUpdateBytes, "vertexUpdateBytes", sep);
optionalStatLine(ss, numUniformBuffers, "numUniformBuffers", sep);
optionalStatLine(ss, numUniformUpdates, "numUniformUpdates", sep);
optionalStatLine(ss, uniformUpdateBytes, "uniformUpdateBytes", sep);
optionalStatLine(ss, memTextures, "memTextures", sep);
optionalStatLine(ss, memBuffers, "memBuffers", sep);
optionalStatLine(ss, memIndexBuffers, "memIndexBuffers", sep);
optionalStatLine(ss, memVertexBuffers, "memVertexBuffers", sep);
optionalStatLine(ss, memUniformBuffers, "memUniformBuffers", sep);
optionalStatLine(ss, stencilClears, "stencilClears", sep);
optionalStatLine(ss, stencilUpdates, "stencilUpdates", sep);
return ss.str();
}
#endif
void RenderingStatsView::create(style::Style& style) {
if (!style.getSource(sourceID)) {
style::CustomGeometrySource::Options sourceOptions;
sourceOptions.zoomRange = {0, 0};
sourceOptions.fetchTileFunction = [&](const CanonicalTileID& tileID) {
auto source = static_cast<style::CustomGeometrySource*>(style.getSource(sourceID));
if (!source) {
return;
}
mbgl::FeatureCollection features;
mapbox::geojson::feature feature;
feature.geometry = mapbox::geometry::geometry<double>(Point<double>{0, 0});
features.emplace_back(feature);
source->setTileData(tileID, features);
};
style.addSource(std::make_unique<style::CustomGeometrySource>(sourceID, sourceOptions));
}
if (!style.getLayer(layerID)) {
auto infoLayer = std::make_unique<style::SymbolLayer>(layerID, sourceID);
// required
infoLayer->setSymbolScreenSpace(true);
infoLayer->setTextAllowOverlap(true);
infoLayer->setIconAllowOverlap(true);
infoLayer->setTextIgnorePlacement(true);
// customizable
infoLayer->setTextColor(options.textColor);
infoLayer->setTextSize(options.textSize);
infoLayer->setTextMaxWidth(300.0f);
infoLayer->setTextJustify(mbgl::style::TextJustifyType::Left);
infoLayer->setTextAnchor(mbgl::style::SymbolAnchorType::TopRight);
const float translation = mbgl::util::EXTENT / 2.0f - 100.0f;
infoLayer->setTextTranslateAnchor(mbgl::style::TranslateAnchorType::Viewport);
infoLayer->setTextTranslate(std::array<float, 2>{translation, translation});
style.addLayer(std::move(infoLayer));
}
}
void RenderingStatsView::destroy(style::Style& style) {
style.removeLayer(layerID);
style.removeSource(sourceID);
}
mbgl::style::SymbolLayer* RenderingStatsView::getLayer(style::Style& style) {
return static_cast<mbgl::style::SymbolLayer*>(style.getLayer(layerID));
}
namespace {
template <typename T>
void printNumber(std::stringstream& ss, const std::string_view label, const T& value, bool print) {
if (!print) {
return;
}
ss << label << ": ";
if (value >= 1e9) {
ss << value / 1e9 << "B";
} else if (value >= 1e6) {
ss << value / 1e6 << "M";
} else if (value >= 1e3) {
ss << value / 1e3 << "K";
} else {
ss << value;
}
ss << "\n";
};
template <typename T>
void printMemory(std::stringstream& ss, const std::string_view label, const T& value, bool print) {
if (!print) {
return;
}
constexpr auto base = 1024;
constexpr auto kb = base;
constexpr auto mb = kb * base;
constexpr auto gb = mb * base;
ss << label << ": ";
if (value >= gb) {
ss << value / gb << "GB";
} else if (value >= mb) {
ss << value / mb << "MB";
} else if (value >= kb) {
ss << value / kb << "KB";
} else {
ss << value << "B";
}
ss << "\n";
};
} // namespace
void RenderingStatsView::update(style::Style& style, const gfx::RenderingStats& stats) {
auto layer = getLayer(style);
// style reloaded? layer got removed?
if (!layer) {
create(style);
layer = getLayer(style);
}
if (!layer) {
return;
}
++frameCount;
encodingTime += stats.encodingTime;
renderingTime += stats.renderingTime;
const auto currentTime = util::MonotonicTimer::now().count();
if (currentTime - lastUpdate < options.updateInterval) {
return;
}
std::stringstream ss;
ss << std::setprecision(3) << std::fixed;
ss << "Encoding time (ms): " << std::setw(7) << encodingTime / frameCount * 1000 << "\n";
ss << "Rendering time (ms): " << std::setw(7) << renderingTime / frameCount * 1000 << "\n";
printNumber(ss, "Frame count", stats.numFrames, true);
printNumber(ss, "Draw calls", stats.numDrawCalls, true);
printNumber(ss, "Total draw calls", stats.totalDrawCalls, options.verbose);
printNumber(ss, "Textures", stats.numActiveTextures, true);
printNumber(ss, "Total textures", stats.numCreatedTextures, options.verbose);
printNumber(ss, "Texture updates", stats.numTextureUpdates, options.verbose);
printMemory(ss, "Texture updates", stats.textureUpdateBytes, options.verbose);
printNumber(ss, "Buffers", stats.numBuffers, true);
printNumber(ss, "Total buffers", stats.totalBuffers, options.verbose);
printNumber(ss, "Total buffer updates", stats.bufferUpdates, options.verbose);
printMemory(ss, "Total buffer updates", stats.bufferUpdateBytes, options.verbose);
printNumber(ss, "Index buffers", stats.numIndexBuffers, true);
printMemory(ss, "Index buffers updates", stats.indexUpdateBytes, options.verbose);
printNumber(ss, "Vertex buffers", stats.numVertexBuffers, true);
printMemory(ss, "Vertex buffers updates", stats.vertexUpdateBytes, options.verbose);
printNumber(ss, "Uniform buffers", stats.numUniformBuffers, true);
printNumber(ss, "Uniform buffer updates", stats.numUniformUpdates, options.verbose);
printMemory(ss, "Uniform buffer updates", stats.uniformUpdateBytes, options.verbose);
printMemory(ss, "Texture memory", stats.memTextures, true);
printMemory(ss, "Buffer memory", stats.memBuffers, true);
printMemory(ss, "Index buffer memory", stats.memIndexBuffers, true);
printMemory(ss, "Vertex buffer memory", stats.memVertexBuffers, true);
printMemory(ss, "Uniform buffer memory", stats.memUniformBuffers, true);
printNumber(ss, "Stencil buffer clears", stats.stencilClears, true);
printNumber(ss, "Stencil buffer updates", stats.stencilUpdates, options.verbose);
layer->setTextField(mbgl::style::expression::Formatted(ss.str().c_str()));
frameCount = 0;
encodingTime = 0.0;
renderingTime = 0.0;
lastUpdate = currentTime;
}
} // namespace gfx
} // namespace mbgl