Rendering statistics view (#3322)
Co-authored-by: Bart Louwers <bart@emeel.net>
This commit is contained in:
@@ -1239,6 +1239,7 @@ if(MLN_WITH_VULKAN)
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/vulkan/uniform_buffer.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/vulkan/upload_pass.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/vulkan/vertex_attribute.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/vulkan/vertex_buffer_resource.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/vulkan/shader_group.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/vulkan/shader_program.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/vulkan/background.hpp
|
||||
@@ -1282,6 +1283,7 @@ if(MLN_WITH_VULKAN)
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/vulkan/uniform_buffer.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/vulkan/upload_pass.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/vulkan/vertex_attribute.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/vulkan/vertex_buffer_resource.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/vulkan/shader_program.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/vulkan/background.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/vulkan/circle.cpp
|
||||
|
||||
@@ -2,14 +2,27 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mbgl/util/color.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
namespace style {
|
||||
class Style;
|
||||
class SymbolLayer;
|
||||
} // namespace style
|
||||
|
||||
namespace gfx {
|
||||
|
||||
struct RenderingStats {
|
||||
RenderingStats() = default;
|
||||
bool isZero() const;
|
||||
|
||||
/// Frame CPU encoding time (milliseconds)
|
||||
double encodingTime = 0.0;
|
||||
/// Frame CPU rendering time (milliseconds)
|
||||
double renderingTime = 0.0;
|
||||
|
||||
/// Number of frames rendered
|
||||
int numFrames = 0;
|
||||
/// Number of draw calls (`glDrawElements`, `drawIndexedPrimitives`, etc.) executed during the most recent frame
|
||||
@@ -41,25 +54,40 @@ struct RenderingStats {
|
||||
|
||||
/// Number of active buffers
|
||||
int numBuffers = 0;
|
||||
/// Number of active offscreen frame buffers
|
||||
int numFrameBuffers = 0;
|
||||
|
||||
/// Number of active index buffers
|
||||
int numIndexBuffers = 0;
|
||||
/// Sum of index buffers update sizes
|
||||
std::size_t indexUpdateBytes = 0;
|
||||
|
||||
/// Number of active vertex buffers
|
||||
int numVertexBuffers = 0;
|
||||
/// Sum of vertex buffers update sizes
|
||||
std::size_t vertexUpdateBytes = 0;
|
||||
|
||||
/// Number of active uniform buffers
|
||||
int numUniformBuffers = 0;
|
||||
/// Number of times a uniform buffer is updated
|
||||
int numUniformUpdates = 0;
|
||||
/// Sum of uniform buffers update sizes
|
||||
std::size_t uniformUpdateBytes = 0;
|
||||
|
||||
/// Total texture memory
|
||||
int memTextures = 0;
|
||||
/// Total buffer memory
|
||||
int memBuffers = 0;
|
||||
/// Total index buffer memory
|
||||
int memIndexBuffers = 0;
|
||||
/// Total vertex buffer memory
|
||||
int memVertexBuffers = 0;
|
||||
/// Total uniform buffer memory
|
||||
int memUniformBuffers = 0;
|
||||
|
||||
/// Number of stencil buffer clears
|
||||
int stencilClears = 0;
|
||||
/// Number of stencil buffer updates
|
||||
int stencilUpdates = 0;
|
||||
|
||||
RenderingStats& operator+=(const RenderingStats&);
|
||||
@@ -69,5 +97,38 @@ struct RenderingStats {
|
||||
#endif
|
||||
};
|
||||
|
||||
class RenderingStatsView final {
|
||||
public:
|
||||
struct Options {
|
||||
float updateInterval = 0.25f;
|
||||
bool verbose = false;
|
||||
Color textColor = Color::red();
|
||||
float textSize = 4.0f;
|
||||
};
|
||||
|
||||
RenderingStatsView() = default;
|
||||
RenderingStatsView(const Options& options_)
|
||||
: options(options_) {}
|
||||
~RenderingStatsView() = default;
|
||||
|
||||
void create(style::Style& style);
|
||||
void destroy(style::Style& style);
|
||||
|
||||
mbgl::style::SymbolLayer* getLayer(style::Style& style);
|
||||
|
||||
void update(style::Style& style, const gfx::RenderingStats& stats);
|
||||
|
||||
protected:
|
||||
const std::string layerID = "rendering-stats";
|
||||
const std::string sourceID = layerID + "-source";
|
||||
|
||||
Options options;
|
||||
|
||||
double lastUpdate = 0.0;
|
||||
uint32_t frameCount = 0;
|
||||
double encodingTime = 0.0;
|
||||
double renderingTime = 0.0;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -11,7 +11,7 @@ class UniformBufferGL final : public gfx::UniformBuffer {
|
||||
UniformBufferGL(const UniformBufferGL&);
|
||||
|
||||
public:
|
||||
UniformBufferGL(const void* data, std::size_t size_, IBufferAllocator& allocator);
|
||||
UniformBufferGL(Context& context, const void* data, std::size_t size_, IBufferAllocator& allocator);
|
||||
~UniformBufferGL() override;
|
||||
|
||||
UniformBufferGL(UniformBufferGL&& rhs) noexcept;
|
||||
@@ -27,6 +27,8 @@ public:
|
||||
void update(const void* data, std::size_t dataSize) override;
|
||||
|
||||
private:
|
||||
Context& context;
|
||||
|
||||
// unique id used for debugging and profiling purposes
|
||||
// localID should not be used as unique id because a const buffer pool is managed using IBufferAllocator
|
||||
// Currently unique IDs for constant buffers are only used when Tracy profiling is enabled
|
||||
|
||||
@@ -145,6 +145,9 @@ public:
|
||||
void setDebug(MapDebugOptions);
|
||||
MapDebugOptions getDebug() const;
|
||||
|
||||
bool isRenderingStatsViewEnabled() const;
|
||||
void enableRenderingStatsView(bool value);
|
||||
|
||||
bool isFullyLoaded() const;
|
||||
void dumpDebugLogs() const;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <mbgl/style/sprite.hpp>
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/shaders/shader_source.hpp>
|
||||
#include <mbgl/gfx/rendering_stats.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -47,8 +48,7 @@ public:
|
||||
RenderMode mode;
|
||||
bool needsRepaint; // In continous mode, shows that there are ongoig transitions.
|
||||
bool placementChanged;
|
||||
double frameEncodingTime;
|
||||
double frameRenderingTime;
|
||||
gfx::RenderingStats renderingStats;
|
||||
};
|
||||
|
||||
virtual void onCameraWillChange(CameraChangeMode) {}
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
virtual void onDidFinishLoadingMap() {}
|
||||
virtual void onDidFailLoadingMap(MapLoadError, const std::string&) {}
|
||||
virtual void onWillStartRenderingFrame() {}
|
||||
virtual void onDidFinishRenderingFrame(RenderFrameStatus) {}
|
||||
virtual void onDidFinishRenderingFrame(const RenderFrameStatus&) {}
|
||||
virtual void onWillStartRenderingMap() {}
|
||||
virtual void onDidFinishRenderingMap(RenderMode) {}
|
||||
virtual void onDidFinishLoadingStyle() {}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <mbgl/tile/tile_id.hpp>
|
||||
#include <mbgl/util/font_stack.hpp>
|
||||
#include <mbgl/gfx/rendering_stats.hpp>
|
||||
#include <mbgl/text/glyph_range.hpp>
|
||||
#include <mbgl/tile/tile_operation.hpp>
|
||||
#include <mbgl/gfx/backend.hpp>
|
||||
@@ -51,6 +52,13 @@ public:
|
||||
onDidFinishRenderingFrame(mode, repaint, placementChanged);
|
||||
}
|
||||
|
||||
virtual void onDidFinishRenderingFrame(RenderMode mode,
|
||||
bool repaint,
|
||||
bool placementChanged,
|
||||
const gfx::RenderingStats& stats) {
|
||||
onDidFinishRenderingFrame(mode, repaint, placementChanged, stats.encodingTime, stats.renderingTime);
|
||||
}
|
||||
|
||||
/// Final frame
|
||||
virtual void onDidFinishRenderingMap() {}
|
||||
|
||||
|
||||
@@ -89,6 +89,10 @@ public:
|
||||
const PropertyValue<SymbolPlacementType>& getSymbolPlacement() const;
|
||||
void setSymbolPlacement(const PropertyValue<SymbolPlacementType>&);
|
||||
|
||||
static PropertyValue<bool> getDefaultSymbolScreenSpace();
|
||||
const PropertyValue<bool>& getSymbolScreenSpace() const;
|
||||
void setSymbolScreenSpace(const PropertyValue<bool>&);
|
||||
|
||||
static PropertyValue<float> getDefaultSymbolSortKey();
|
||||
const PropertyValue<float>& getSymbolSortKey() const;
|
||||
void setSymbolSortKey(const PropertyValue<float>&);
|
||||
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
const mbgl::unordered_map<std::string, std::string>& additionalDefines);
|
||||
|
||||
/// Called at the end of a frame.
|
||||
void performCleanup() override {}
|
||||
void performCleanup() override;
|
||||
void reduceMemoryUsage() override {}
|
||||
|
||||
gfx::UniqueDrawableBuilder createDrawableBuilder(std::string name) override;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/gfx/vertex_buffer.hpp>
|
||||
#include <mbgl/vulkan/buffer_resource.hpp>
|
||||
|
||||
#include <memory>
|
||||
@@ -9,12 +10,10 @@ namespace vulkan {
|
||||
|
||||
class VertexBufferResource : public gfx::VertexBufferResource {
|
||||
public:
|
||||
VertexBufferResource() noexcept = delete;
|
||||
VertexBufferResource(BufferResource&& buffer_) noexcept
|
||||
: buffer(std::move(buffer_)) {}
|
||||
VertexBufferResource(BufferResource&& buffer_) noexcept;
|
||||
VertexBufferResource(VertexBufferResource&& other) noexcept
|
||||
: buffer(std::move(other.buffer)) {}
|
||||
~VertexBufferResource() noexcept override = default;
|
||||
~VertexBufferResource() noexcept override;
|
||||
|
||||
std::size_t getSizeInBytes() const noexcept { return buffer.getSizeInBytes(); }
|
||||
const void* contents() const noexcept { return buffer.contents(); }
|
||||
|
||||
@@ -122,6 +122,8 @@ add_library(maplibre SHARED
|
||||
${PROJECT_SOURCE_DIR}/offline/offline_region_error.hpp
|
||||
${PROJECT_SOURCE_DIR}/offline/offline_region_status.cpp
|
||||
${PROJECT_SOURCE_DIR}/offline/offline_region_status.hpp
|
||||
${PROJECT_SOURCE_DIR}/rendering_stats.cpp
|
||||
${PROJECT_SOURCE_DIR}/rendering_stats.hpp
|
||||
${PROJECT_SOURCE_DIR}/snapshotter/map_snapshot.cpp
|
||||
${PROJECT_SOURCE_DIR}/snapshotter/map_snapshot.hpp
|
||||
${PROJECT_SOURCE_DIR}/snapshotter/map_snapshotter.cpp
|
||||
|
||||
@@ -36,11 +36,10 @@ public:
|
||||
void onDidFinishRenderingFrame(RenderMode mode,
|
||||
bool repaintNeeded,
|
||||
bool placementChanged,
|
||||
double frameEncodingTime,
|
||||
double frameRenderingTime) override {
|
||||
const gfx::RenderingStats& stats) override {
|
||||
void (RendererObserver::*f)(
|
||||
RenderMode, bool, bool, double, double) = &RendererObserver::onDidFinishRenderingFrame;
|
||||
delegate.invoke(f, mode, repaintNeeded, placementChanged, frameEncodingTime, frameRenderingTime);
|
||||
RenderMode, bool, bool, const gfx::RenderingStats&) = &RendererObserver::onDidFinishRenderingFrame;
|
||||
delegate.invoke(f, mode, repaintNeeded, placementChanged, stats);
|
||||
}
|
||||
|
||||
void onDidFinishRenderingMap() override { delegate.invoke(&RendererObserver::onDidFinishRenderingMap); }
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "maplibre.hpp"
|
||||
#include "native_map_view.hpp"
|
||||
#include "native_map_options.hpp"
|
||||
#include "rendering_stats.hpp"
|
||||
#include "util/tile_server_options.hpp"
|
||||
#ifndef MBGL_MODULE_OFFLINE_DISABLE
|
||||
#include "offline/offline_manager.hpp"
|
||||
@@ -111,6 +112,7 @@ void registerNatives(JavaVM* vm) {
|
||||
MapRendererRunnable::registerNative(env);
|
||||
NativeMapView::registerNative(env);
|
||||
NativeMapOptions::registerNative(env);
|
||||
RenderingStats::registerNative(env);
|
||||
|
||||
// Http
|
||||
RegisterNativeHTTPRequest(env);
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "conversion/collection.hpp"
|
||||
#include "style/conversion/filter.hpp"
|
||||
#include "geojson/feature.hpp"
|
||||
#include "rendering_stats.hpp"
|
||||
|
||||
#include "android_renderer_frontend.hpp"
|
||||
#include "attach_env.hpp"
|
||||
@@ -187,20 +188,25 @@ void NativeMapView::onWillStartRenderingFrame() {
|
||||
}
|
||||
}
|
||||
|
||||
void NativeMapView::onDidFinishRenderingFrame(MapObserver::RenderFrameStatus status) {
|
||||
void NativeMapView::onDidFinishRenderingFrame(const MapObserver::RenderFrameStatus& status) {
|
||||
assert(vm != nullptr);
|
||||
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
||||
static auto onDidFinishRenderingFrame = javaClass.GetMethod<void(jboolean, jdouble, jdouble)>(
|
||||
static auto onDidFinishRenderingFrame = javaClass.GetMethod<void(jboolean, jni::Object<RenderingStats>)>(
|
||||
*_env, "onDidFinishRenderingFrame");
|
||||
auto weakReference = javaPeer.get(*_env);
|
||||
if (weakReference) {
|
||||
if (!renderingStats) {
|
||||
renderingStats = jni::NewGlobal(*_env, RenderingStats::Create(*_env));
|
||||
}
|
||||
|
||||
RenderingStats::Update(*_env, renderingStats, status.renderingStats);
|
||||
|
||||
weakReference.Call(*_env,
|
||||
onDidFinishRenderingFrame,
|
||||
(jboolean)(status.mode != MapObserver::RenderMode::Partial),
|
||||
(jdouble)status.frameEncodingTime,
|
||||
(jdouble)status.frameRenderingTime);
|
||||
renderingStats);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1272,6 +1278,16 @@ void NativeMapView::triggerRepaint(JNIEnv&) {
|
||||
map->triggerRepaint();
|
||||
}
|
||||
|
||||
jni::jboolean NativeMapView::isRenderingStatsViewEnabled(JNIEnv&) {
|
||||
assert(map);
|
||||
return jni::jboolean(map->isRenderingStatsViewEnabled());
|
||||
}
|
||||
|
||||
void NativeMapView::enableRenderingStatsView(JNIEnv&, jni::jboolean value) {
|
||||
assert(map);
|
||||
map->enableRenderingStatsView(value);
|
||||
}
|
||||
|
||||
// Static methods //
|
||||
|
||||
void NativeMapView::registerNative(jni::JNIEnv& env) {
|
||||
@@ -1384,7 +1400,9 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
|
||||
METHOD(&NativeMapView::getPrefetchZoomDelta, "nativeGetPrefetchZoomDelta"),
|
||||
METHOD(&NativeMapView::setTileCacheEnabled, "nativeSetTileCacheEnabled"),
|
||||
METHOD(&NativeMapView::getTileCacheEnabled, "nativeGetTileCacheEnabled"),
|
||||
METHOD(&NativeMapView::triggerRepaint, "nativeTriggerRepaint"));
|
||||
METHOD(&NativeMapView::triggerRepaint, "nativeTriggerRepaint"),
|
||||
METHOD(&NativeMapView::isRenderingStatsViewEnabled, "nativeIsRenderingStatsViewEnabled"),
|
||||
METHOD(&NativeMapView::enableRenderingStatsView, "nativeEnableRenderingStatsView"));
|
||||
}
|
||||
|
||||
void NativeMapView::onRegisterShaders(gfx::ShaderRegistry&) {};
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace android {
|
||||
class AndroidRendererFrontend;
|
||||
class FileSource;
|
||||
class MapRenderer;
|
||||
class RenderingStats;
|
||||
|
||||
class NativeMapView : public MapObserver {
|
||||
public:
|
||||
@@ -62,7 +63,7 @@ public:
|
||||
void onDidFinishLoadingMap() override;
|
||||
void onDidFailLoadingMap(MapLoadError, const std::string&) override;
|
||||
void onWillStartRenderingFrame() override;
|
||||
void onDidFinishRenderingFrame(MapObserver::RenderFrameStatus) override;
|
||||
void onDidFinishRenderingFrame(const MapObserver::RenderFrameStatus&) override;
|
||||
void onWillStartRenderingMap() override;
|
||||
void onDidFinishRenderingMap(MapObserver::RenderMode) override;
|
||||
void onDidBecomeIdle() override;
|
||||
@@ -305,6 +306,9 @@ public:
|
||||
|
||||
void triggerRepaint(JNIEnv&);
|
||||
|
||||
jni::jboolean isRenderingStatsViewEnabled(JNIEnv&);
|
||||
void enableRenderingStatsView(JNIEnv&, jni::jboolean);
|
||||
|
||||
// Shader compilation
|
||||
void onRegisterShaders(mbgl::gfx::ShaderRegistry&) override;
|
||||
void onPreCompileShader(mbgl::shaders::BuiltIn, mbgl::gfx::Backend::Type, const std::string&) override;
|
||||
@@ -336,6 +340,8 @@ private:
|
||||
|
||||
float pixelRatio;
|
||||
|
||||
jni::Global<jni::Object<RenderingStats>> renderingStats;
|
||||
|
||||
// Minimum texture size according to OpenGL ES 2.0 specification.
|
||||
int width = 64;
|
||||
int height = 64;
|
||||
|
||||
61
platform/android/MapLibreAndroid/src/cpp/rendering_stats.cpp
Normal file
61
platform/android/MapLibreAndroid/src/cpp/rendering_stats.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "rendering_stats.hpp"
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
void RenderingStats::registerNative(jni::JNIEnv& env) {
|
||||
jni::Class<RenderingStats>::Singleton(env);
|
||||
}
|
||||
|
||||
jni::Local<jni::Object<RenderingStats>> RenderingStats::Create(jni::JNIEnv& env) {
|
||||
auto& javaClass = jni::Class<RenderingStats>::Singleton(env);
|
||||
auto constructor = javaClass.GetConstructor(env);
|
||||
return javaClass.New(env, constructor);
|
||||
}
|
||||
|
||||
void RenderingStats::Update(jni::JNIEnv& env,
|
||||
jni::Object<RenderingStats>& javaObject,
|
||||
const gfx::RenderingStats& stats) {
|
||||
static auto& javaClass = jni::Class<RenderingStats>::Singleton(env);
|
||||
|
||||
#define SetField(name, type) \
|
||||
static auto name##Field = javaClass.GetField<type>(env, #name); \
|
||||
javaObject.Set(env, name##Field, static_cast<type>(stats.name));
|
||||
|
||||
SetField(encodingTime, jni::jdouble);
|
||||
SetField(renderingTime, jni::jdouble);
|
||||
SetField(numFrames, jni::jint);
|
||||
SetField(numDrawCalls, jni::jint);
|
||||
SetField(totalDrawCalls, jni::jint);
|
||||
SetField(numCreatedTextures, jni::jint);
|
||||
SetField(numActiveTextures, jni::jint);
|
||||
SetField(numTextureBindings, jni::jint);
|
||||
SetField(numTextureUpdates, jni::jint);
|
||||
SetField(textureUpdateBytes, jni::jlong);
|
||||
SetField(totalBuffers, jni::jlong);
|
||||
SetField(totalBufferObjs, jni::jlong);
|
||||
SetField(bufferUpdates, jni::jlong);
|
||||
SetField(bufferObjUpdates, jni::jlong);
|
||||
SetField(bufferUpdateBytes, jni::jlong);
|
||||
SetField(numBuffers, jni::jint);
|
||||
SetField(numFrameBuffers, jni::jint);
|
||||
SetField(numIndexBuffers, jni::jint);
|
||||
SetField(indexUpdateBytes, jni::jlong);
|
||||
SetField(numVertexBuffers, jni::jint);
|
||||
SetField(vertexUpdateBytes, jni::jlong);
|
||||
SetField(numUniformBuffers, jni::jint);
|
||||
SetField(numUniformUpdates, jni::jint);
|
||||
SetField(uniformUpdateBytes, jni::jlong);
|
||||
SetField(memTextures, jni::jint);
|
||||
SetField(memBuffers, jni::jint);
|
||||
SetField(memIndexBuffers, jni::jint);
|
||||
SetField(memVertexBuffers, jni::jint);
|
||||
SetField(memUniformBuffers, jni::jint);
|
||||
SetField(stencilClears, jni::jint);
|
||||
SetField(stencilUpdates, jni::jint);
|
||||
|
||||
#undef SetField
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
22
platform/android/MapLibreAndroid/src/cpp/rendering_stats.hpp
Normal file
22
platform/android/MapLibreAndroid/src/cpp/rendering_stats.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/gfx/rendering_stats.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <jni.h>
|
||||
#include <jni/jni.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
class RenderingStats {
|
||||
public:
|
||||
static constexpr auto Name() { return "org/maplibre/android/maps/RenderingStats"; };
|
||||
static void registerNative(jni::JNIEnv& env);
|
||||
|
||||
static jni::Local<jni::Object<RenderingStats>> Create(jni::JNIEnv&);
|
||||
static void Update(jni::JNIEnv&, jni::Object<RenderingStats>&, const gfx::RenderingStats&);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -249,6 +249,11 @@ jni::Local<jni::Object<>> SymbolLayer::getTextOptional(jni::JNIEnv& env) {
|
||||
return std::move(*convert<jni::Local<jni::Object<>>>(env, toSymbolLayer(layer).getTextOptional()));
|
||||
}
|
||||
|
||||
jni::Local<jni::Object<>> SymbolLayer::getSymbolScreenSpace(jni::JNIEnv& env) {
|
||||
using namespace mbgl::android::conversion;
|
||||
return std::move(*convert<jni::Local<jni::Object<>>>(env, toSymbolLayer(layer).getSymbolScreenSpace()));
|
||||
}
|
||||
|
||||
jni::Local<jni::Object<>> SymbolLayer::getIconOpacity(jni::JNIEnv& env) {
|
||||
using namespace mbgl::android::conversion;
|
||||
return std::move(*convert<jni::Local<jni::Object<>>>(env, toSymbolLayer(layer).getIconOpacity()));
|
||||
@@ -557,6 +562,7 @@ void SymbolJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) {
|
||||
METHOD(&SymbolLayer::getTextAllowOverlap, "nativeGetTextAllowOverlap"),
|
||||
METHOD(&SymbolLayer::getTextIgnorePlacement, "nativeGetTextIgnorePlacement"),
|
||||
METHOD(&SymbolLayer::getTextOptional, "nativeGetTextOptional"),
|
||||
METHOD(&SymbolLayer::getSymbolScreenSpace, "nativeGetSymbolScreenSpace"),
|
||||
METHOD(&SymbolLayer::getIconOpacityTransition, "nativeGetIconOpacityTransition"),
|
||||
METHOD(&SymbolLayer::setIconOpacityTransition, "nativeSetIconOpacityTransition"),
|
||||
METHOD(&SymbolLayer::getIconOpacity, "nativeGetIconOpacity"),
|
||||
|
||||
@@ -110,6 +110,8 @@ public:
|
||||
|
||||
jni::Local<jni::Object<jni::ObjectTag>> getTextOptional(jni::JNIEnv&);
|
||||
|
||||
jni::Local<jni::Object<jni::ObjectTag>> getSymbolScreenSpace(jni::JNIEnv&);
|
||||
|
||||
jni::Local<jni::Object<jni::ObjectTag>> getIconOpacity(jni::JNIEnv&);
|
||||
void setIconOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay);
|
||||
jni::Local<jni::Object<TransitionOptions>> getIconOpacityTransition(jni::JNIEnv&);
|
||||
|
||||
@@ -23,6 +23,8 @@ class MapChangeReceiver implements NativeMapView.StateCallback {
|
||||
= new CopyOnWriteArrayList<>();
|
||||
private final List<MapView.OnDidFinishRenderingFrameListener> onDidFinishRenderingFrameList
|
||||
= new CopyOnWriteArrayList<>();
|
||||
private final List<MapView.OnDidFinishRenderingFrameWithStatsListener> onDidFinishRenderingFrameWithStatsList
|
||||
= new CopyOnWriteArrayList<>();
|
||||
private final List<MapView.OnWillStartRenderingMapListener> onWillStartRenderingMapListenerList
|
||||
= new CopyOnWriteArrayList<>();
|
||||
private final List<MapView.OnDidFinishRenderingMapListener> onDidFinishRenderingMapListenerList
|
||||
@@ -156,11 +158,17 @@ class MapChangeReceiver implements NativeMapView.StateCallback {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime) {
|
||||
public void onDidFinishRenderingFrame(boolean fully, RenderingStats stats) {
|
||||
try {
|
||||
if (!onDidFinishRenderingFrameList.isEmpty()) {
|
||||
for (MapView.OnDidFinishRenderingFrameListener listener : onDidFinishRenderingFrameList) {
|
||||
listener.onDidFinishRenderingFrame(fully, frameEncodingTime, frameRenderingTime);
|
||||
listener.onDidFinishRenderingFrame(fully, stats.encodingTime, stats.renderingTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (!onDidFinishRenderingFrameWithStatsList.isEmpty()) {
|
||||
for (MapView.OnDidFinishRenderingFrameWithStatsListener listener : onDidFinishRenderingFrameWithStatsList) {
|
||||
listener.onDidFinishRenderingFrame(fully, stats);
|
||||
}
|
||||
}
|
||||
} catch (Throwable err) {
|
||||
@@ -480,6 +488,14 @@ class MapChangeReceiver implements NativeMapView.StateCallback {
|
||||
onDidFinishRenderingFrameList.remove(listener);
|
||||
}
|
||||
|
||||
void addOnDidFinishRenderingFrameListener(MapView.OnDidFinishRenderingFrameWithStatsListener listener) {
|
||||
onDidFinishRenderingFrameWithStatsList.add(listener);
|
||||
}
|
||||
|
||||
void removeOnDidFinishRenderingFrameListener(MapView.OnDidFinishRenderingFrameWithStatsListener listener) {
|
||||
onDidFinishRenderingFrameWithStatsList.remove(listener);
|
||||
}
|
||||
|
||||
void addOnWillStartRenderingMapListener(MapView.OnWillStartRenderingMapListener listener) {
|
||||
onWillStartRenderingMapListenerList.add(listener);
|
||||
}
|
||||
|
||||
@@ -103,6 +103,20 @@ public final class MapLibreMap {
|
||||
nativeMapView.triggerRepaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query rendering statistics overlay status.
|
||||
*/
|
||||
public boolean isRenderingStatsViewEnabled() {
|
||||
return nativeMapView.isRenderingStatsViewEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable rendering statistics overlay with {@link RenderingStats} values.
|
||||
*/
|
||||
public void enableRenderingStatsView(boolean value) {
|
||||
nativeMapView.enableRenderingStatsView(value);
|
||||
}
|
||||
|
||||
public void setSwapBehaviorFlush(boolean flush) {
|
||||
nativeMapView.setSwapBehaviorFlush(flush);
|
||||
}
|
||||
|
||||
@@ -763,6 +763,24 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
mapChangeReceiver.removeOnDidFinishRenderingFrameListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that's invoked when the map has finished rendering a frame.
|
||||
*
|
||||
* @param listener The callback that's invoked when the map has finished rendering a frame
|
||||
*/
|
||||
public void addOnDidFinishRenderingFrameListener(@NonNull OnDidFinishRenderingFrameWithStatsListener listener) {
|
||||
mapChangeReceiver.addOnDidFinishRenderingFrameListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that's invoked when the map has finished rendering a frame.
|
||||
*
|
||||
* @param listener The callback that's invoked when the map has finished rendering a frame
|
||||
*/
|
||||
public void removeOnDidFinishRenderingFrameListener(@NonNull OnDidFinishRenderingFrameWithStatsListener listener) {
|
||||
mapChangeReceiver.removeOnDidFinishRenderingFrameListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that's invoked when the map will start rendering.
|
||||
*
|
||||
@@ -1182,14 +1200,34 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
* </p>
|
||||
*/
|
||||
public interface OnDidFinishRenderingFrameListener {
|
||||
|
||||
/**
|
||||
* Called when the map has finished rendering a frame
|
||||
*
|
||||
* @param fully true if all frames have been rendered, false if partially rendered
|
||||
* @param frameEncodingTime CPU encoding time
|
||||
* @param frameRenderingTime CPU rendering time
|
||||
*/
|
||||
void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when the map finished rendering a frame.
|
||||
* <p>
|
||||
* {@link MapView#addOnDidFinishRenderingFrameListener(OnDidFinishRenderingFrameListener)}
|
||||
* </p>
|
||||
*/
|
||||
public interface OnDidFinishRenderingFrameWithStatsListener {
|
||||
|
||||
/**
|
||||
* Called when the map has finished rendering a frame
|
||||
*
|
||||
* @param fully true if all frames have been rendered, false if partially rendered
|
||||
* @param stats rendering statistics
|
||||
*/
|
||||
void onDidFinishRenderingFrame(boolean fully, RenderingStats stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when the map will start rendering the map.
|
||||
* <p>
|
||||
@@ -1528,7 +1566,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
* The initial render callback waits for rendering to happen before making the map visible for end-users.
|
||||
* We wait for the second DID_FINISH_RENDERING_FRAME map change event as the first will still show a black surface.
|
||||
*/
|
||||
private class InitialRenderCallback implements OnDidFinishRenderingFrameListener {
|
||||
private class InitialRenderCallback implements OnDidFinishRenderingFrameWithStatsListener {
|
||||
|
||||
private int renderCount;
|
||||
|
||||
@@ -1537,7 +1575,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime) {
|
||||
public void onDidFinishRenderingFrame(boolean fully, RenderingStats stats) {
|
||||
if (maplibreMap != null && maplibreMap.getStyle() != null && maplibreMap.getStyle().isFullyLoaded()) {
|
||||
renderCount++;
|
||||
if (renderCount == 3) {
|
||||
@@ -1643,7 +1681,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
}
|
||||
|
||||
private class MapCallback implements OnDidFinishLoadingStyleListener,
|
||||
OnDidFinishRenderingFrameListener, OnDidFinishLoadingMapListener,
|
||||
OnDidFinishRenderingFrameWithStatsListener, OnDidFinishLoadingMapListener,
|
||||
OnCameraIsChangingListener, OnCameraDidChangeListener, OnDidFailLoadingMapListener {
|
||||
|
||||
private final List<OnMapReadyCallback> onMapReadyCallbackList = new ArrayList<>();
|
||||
@@ -1709,7 +1747,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime) {
|
||||
public void onDidFinishRenderingFrame(boolean fully, RenderingStats stats) {
|
||||
if (maplibreMap != null) {
|
||||
maplibreMap.onUpdateFullyRendered();
|
||||
}
|
||||
|
||||
@@ -250,6 +250,10 @@ interface NativeMap {
|
||||
|
||||
void triggerRepaint();
|
||||
|
||||
boolean isRenderingStatsViewEnabled();
|
||||
|
||||
void enableRenderingStatsView(boolean value);
|
||||
|
||||
void setSwapBehaviorFlush(boolean flush);
|
||||
|
||||
//
|
||||
|
||||
@@ -1075,6 +1075,16 @@ final class NativeMapView implements NativeMap {
|
||||
nativeTriggerRepaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRenderingStatsViewEnabled() {
|
||||
return nativeIsRenderingStatsViewEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableRenderingStatsView(boolean value) {
|
||||
nativeEnableRenderingStatsView(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSwapBehaviorFlush(boolean flush) {
|
||||
mapRenderer.setSwapBehaviorFlush(flush);
|
||||
@@ -1145,9 +1155,9 @@ final class NativeMapView implements NativeMap {
|
||||
}
|
||||
|
||||
@Keep
|
||||
private void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime) {
|
||||
private void onDidFinishRenderingFrame(boolean fully, RenderingStats stats) {
|
||||
if (stateCallback != null) {
|
||||
stateCallback.onDidFinishRenderingFrame(fully, frameEncodingTime, frameRenderingTime);
|
||||
stateCallback.onDidFinishRenderingFrame(fully, stats);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1642,6 +1652,12 @@ final class NativeMapView implements NativeMap {
|
||||
@Keep
|
||||
private native void nativeTriggerRepaint();
|
||||
|
||||
@Keep
|
||||
private native boolean nativeIsRenderingStatsViewEnabled();
|
||||
|
||||
@Keep
|
||||
private native void nativeEnableRenderingStatsView(boolean enabled);
|
||||
|
||||
//
|
||||
// Snapshot
|
||||
//
|
||||
@@ -1719,7 +1735,7 @@ final class NativeMapView implements NativeMap {
|
||||
|
||||
void onWillStartRenderingFrame();
|
||||
|
||||
void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime);
|
||||
void onDidFinishRenderingFrame(boolean fully, RenderingStats stats);
|
||||
|
||||
void onWillStartRenderingMap();
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.maplibre.android.maps;
|
||||
|
||||
public class RenderingStats {
|
||||
/// Frame CPU encoding time (milliseconds)
|
||||
public double encodingTime = 0.0;
|
||||
/// Frame CPU rendering time (milliseconds)
|
||||
public double renderingTime = 0.0;
|
||||
|
||||
/// Number of frames rendered
|
||||
public int numFrames = 0;
|
||||
/// Number of draw calls (`glDrawElements`, `drawIndexedPrimitives`, etc.) executed during the most recent frame
|
||||
public int numDrawCalls = 0;
|
||||
/// Total number of draw calls executed during all the frames
|
||||
public int totalDrawCalls = 0;
|
||||
|
||||
/// Total number of textures created
|
||||
public int numCreatedTextures = 0;
|
||||
/// Net textures
|
||||
public int numActiveTextures = 0;
|
||||
/// Net texture bindings
|
||||
public int numTextureBindings = 0;
|
||||
/// Number of times a texture was updated
|
||||
public int numTextureUpdates = 0;
|
||||
/// Number of bytes used in texture updates
|
||||
public long textureUpdateBytes = 0;
|
||||
|
||||
/// Number of buffers created
|
||||
public long totalBuffers = 0;
|
||||
/// Number of SDK-specific buffers created
|
||||
public long totalBufferObjs = 0;
|
||||
/// Number of times a buffer is updated
|
||||
public long bufferUpdates = 0;
|
||||
/// Number of times an SDK-specific buffer is updated
|
||||
public long bufferObjUpdates = 0;
|
||||
/// Sum of update sizes
|
||||
public long bufferUpdateBytes = 0;
|
||||
|
||||
/// Number of active buffers
|
||||
public int numBuffers = 0;
|
||||
/// Number of active offscreen frame buffers
|
||||
public int numFrameBuffers = 0;
|
||||
|
||||
/// Number of active index buffers
|
||||
public int numIndexBuffers = 0;
|
||||
/// Sum of index buffers update sizes
|
||||
public long indexUpdateBytes = 0;
|
||||
|
||||
/// Number of active vertex buffers
|
||||
public int numVertexBuffers = 0;
|
||||
/// Sum of vertex buffers update sizes
|
||||
public long vertexUpdateBytes = 0;
|
||||
|
||||
/// Number of active uniform buffers
|
||||
public int numUniformBuffers = 0;
|
||||
/// Number of times a uniform buffer is updated
|
||||
public int numUniformUpdates = 0;
|
||||
/// Sum of uniform buffers update sizes
|
||||
public long uniformUpdateBytes = 0;
|
||||
|
||||
/// Total texture memory
|
||||
public int memTextures = 0;
|
||||
/// Total buffer memory
|
||||
public int memBuffers = 0;
|
||||
/// Total index buffer memory
|
||||
public int memIndexBuffers = 0;
|
||||
/// Total vertex buffer memory
|
||||
public int memVertexBuffers = 0;
|
||||
/// Total uniform buffer memory
|
||||
public int memUniformBuffers = 0;
|
||||
|
||||
/// Number of stencil buffer clears
|
||||
public int stencilClears = 0;
|
||||
/// Number of stencil buffer updates
|
||||
public int stencilUpdates = 0;
|
||||
}
|
||||
@@ -2651,6 +2651,26 @@ The unit is in density-independent pixels only for SDF sprites that were created
|
||||
return new LayoutPropertyValue<>("text-optional", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only
|
||||
*
|
||||
* @param value a Boolean value
|
||||
* @return property wrapper around Boolean
|
||||
*/
|
||||
public static PropertyValue<Boolean> symbolScreenSpace(Boolean value) {
|
||||
return new LayoutPropertyValue<>("symbol-screen-space", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only
|
||||
*
|
||||
* @param value a Boolean value
|
||||
* @return property wrapper around Boolean
|
||||
*/
|
||||
public static PropertyValue<Expression> symbolScreenSpace(Expression value) {
|
||||
return new LayoutPropertyValue<>("symbol-screen-space", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts features in ascending order based on this value. Features with a higher sort key will appear above features with a lower sort key.
|
||||
*
|
||||
|
||||
@@ -649,6 +649,18 @@ public class SymbolLayer extends Layer {
|
||||
return (PropertyValue<Boolean>) new PropertyValue("text-optional", nativeGetTextOptional());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SymbolScreenSpace property
|
||||
*
|
||||
* @return property wrapper value around Boolean
|
||||
*/
|
||||
@NonNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public PropertyValue<Boolean> getSymbolScreenSpace() {
|
||||
checkThread();
|
||||
return (PropertyValue<Boolean>) new PropertyValue("symbol-screen-space", nativeGetSymbolScreenSpace());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IconOpacity property
|
||||
*
|
||||
@@ -1305,6 +1317,10 @@ public class SymbolLayer extends Layer {
|
||||
@Keep
|
||||
private native Object nativeGetTextOptional();
|
||||
|
||||
@NonNull
|
||||
@Keep
|
||||
private native Object nativeGetSymbolScreenSpace();
|
||||
|
||||
@NonNull
|
||||
@Keep
|
||||
private native Object nativeGetIconOpacity();
|
||||
|
||||
@@ -42,6 +42,9 @@ class MapChangeReceiverTest {
|
||||
@Mock
|
||||
private val onDidFinishRenderingFrameListener: OnDidFinishRenderingFrameListener? = null
|
||||
|
||||
@Mock
|
||||
private val onDidFinishRenderingFrameWithStatsListener: OnDidFinishRenderingFrameWithStatsListener? = null
|
||||
|
||||
@Mock
|
||||
private val onWillStartRenderingMapListener: OnWillStartRenderingMapListener? = null
|
||||
|
||||
@@ -401,13 +404,13 @@ class MapChangeReceiverTest {
|
||||
mapChangeEventManager!!.addOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameListener
|
||||
)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, TEST_RENDERING_STATS)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
mapChangeEventManager!!.removeOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameListener
|
||||
)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(true,.0, .0)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, TEST_RENDERING_STATS)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
mapChangeEventManager!!.addOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameListener
|
||||
)
|
||||
@@ -416,7 +419,7 @@ class MapChangeReceiverTest {
|
||||
Mockito.doThrow(exc).`when`(onDidFinishRenderingFrameListener)
|
||||
?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
try {
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, TEST_RENDERING_STATS)
|
||||
Assert.fail("The exception should've been re-thrown.")
|
||||
} catch (throwable: RuntimeException) {
|
||||
Mockito.verify(loggerDefinition)?.e(
|
||||
@@ -429,7 +432,51 @@ class MapChangeReceiverTest {
|
||||
Mockito.doThrow(err).`when`(onDidFinishRenderingFrameListener)
|
||||
?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
try {
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, TEST_RENDERING_STATS)
|
||||
Assert.fail("The exception should've been re-thrown.")
|
||||
} catch (throwable: ExecutionError) {
|
||||
Mockito.verify(loggerDefinition)?.e(
|
||||
ArgumentMatchers.anyString(),
|
||||
ArgumentMatchers.anyString(),
|
||||
ArgumentMatchers.eq(err)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnDidFinishRenderingFrameWithStatsListener() {
|
||||
mapChangeEventManager!!.addOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameWithStatsListener
|
||||
)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, TEST_RENDERING_STATS)
|
||||
Mockito.verify(onDidFinishRenderingFrameWithStatsListener)?.onDidFinishRenderingFrame(true, TEST_RENDERING_STATS)
|
||||
mapChangeEventManager!!.removeOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameWithStatsListener
|
||||
)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, TEST_RENDERING_STATS)
|
||||
Mockito.verify(onDidFinishRenderingFrameWithStatsListener)?.onDidFinishRenderingFrame(true, TEST_RENDERING_STATS)
|
||||
mapChangeEventManager!!.addOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameWithStatsListener
|
||||
)
|
||||
Logger.setLoggerDefinition(loggerDefinition)
|
||||
val exc: Exception = RuntimeException()
|
||||
Mockito.doThrow(exc).`when`(onDidFinishRenderingFrameWithStatsListener)
|
||||
?.onDidFinishRenderingFrame(true, TEST_RENDERING_STATS)
|
||||
try {
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, TEST_RENDERING_STATS)
|
||||
Assert.fail("The exception should've been re-thrown.")
|
||||
} catch (throwable: RuntimeException) {
|
||||
Mockito.verify(loggerDefinition)?.e(
|
||||
ArgumentMatchers.anyString(),
|
||||
ArgumentMatchers.anyString(),
|
||||
ArgumentMatchers.eq(exc)
|
||||
)
|
||||
}
|
||||
val err: Error = ExecutionError("", Error())
|
||||
Mockito.doThrow(err).`when`(onDidFinishRenderingFrameWithStatsListener)
|
||||
?.onDidFinishRenderingFrame(true, TEST_RENDERING_STATS)
|
||||
try {
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, TEST_RENDERING_STATS)
|
||||
Assert.fail("The exception should've been re-thrown.")
|
||||
} catch (throwable: ExecutionError) {
|
||||
Mockito.verify(loggerDefinition)?.e(
|
||||
@@ -445,12 +492,12 @@ class MapChangeReceiverTest {
|
||||
mapChangeEventManager!!.addOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameListener
|
||||
)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false, TEST_RENDERING_STATS)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
mapChangeEventManager!!.removeOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameListener
|
||||
)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false, TEST_RENDERING_STATS)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
mapChangeEventManager!!.addOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameListener
|
||||
@@ -460,7 +507,7 @@ class MapChangeReceiverTest {
|
||||
Mockito.doThrow(exc).`when`(onDidFinishRenderingFrameListener)
|
||||
?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
try {
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false, TEST_RENDERING_STATS)
|
||||
Assert.fail("The exception should've been re-thrown.")
|
||||
} catch (throwable: RuntimeException) {
|
||||
Mockito.verify(loggerDefinition)?.e(
|
||||
@@ -473,7 +520,7 @@ class MapChangeReceiverTest {
|
||||
Mockito.doThrow(err).`when`(onDidFinishRenderingFrameListener)
|
||||
?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
try {
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false, TEST_RENDERING_STATS)
|
||||
Assert.fail("The exception should've been re-thrown.")
|
||||
} catch (throwable: ExecutionError) {
|
||||
Mockito.verify(loggerDefinition)?.e(
|
||||
@@ -710,5 +757,6 @@ class MapChangeReceiverTest {
|
||||
|
||||
companion object {
|
||||
private const val TEST_STRING = "mapChangeRandom"
|
||||
private val TEST_RENDERING_STATS = RenderingStats();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class RemoveUnusedImagesTest : AppCenter() {
|
||||
mapView.addOnCanRemoveUnusedStyleImageListener {
|
||||
callbackLatch.countDown()
|
||||
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 120.0), 8.0))
|
||||
mapView.addOnDidFinishRenderingFrameListener{ _, _, _ ->
|
||||
mapView.addOnDidFinishRenderingFrameListener{ _, _ ->
|
||||
assertNotNull(maplibreMap.style!!.getImage("small"))
|
||||
assertNotNull(maplibreMap.style!!.getImage("large"))
|
||||
latch.countDown()
|
||||
@@ -89,7 +89,7 @@ class RemoveUnusedImagesTest : AppCenter() {
|
||||
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 120.0), 8.0))
|
||||
|
||||
// Wait for the next frame and check that images were removed from the style.
|
||||
mapView.addOnDidFinishRenderingFrameListener { _, _, _ ->
|
||||
mapView.addOnDidFinishRenderingFrameListener { _, _ ->
|
||||
if (maplibreMap.style!!.getImage("small") == null && maplibreMap.style!!.getImage("large") == null) {
|
||||
latch.countDown()
|
||||
}
|
||||
|
||||
@@ -917,6 +917,19 @@ public class SymbolLayerTest extends BaseLayerTest {
|
||||
assertEquals(layer.getTextOptional().getValue(), propertyValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testSymbolScreenSpaceAsConstant() {
|
||||
Timber.i("symbol-screen-space");
|
||||
assertNotNull(layer);
|
||||
assertNull(layer.getSymbolScreenSpace().getValue());
|
||||
|
||||
// Set and Get
|
||||
Boolean propertyValue = true;
|
||||
layer.setProperties(symbolScreenSpace(propertyValue));
|
||||
assertEquals(layer.getSymbolScreenSpace().getValue(), propertyValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testIconOpacityTransition() {
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.maplibre.android.log.Logger.INFO
|
||||
import org.maplibre.android.maps.MapLibreMap
|
||||
import org.maplibre.android.maps.MapLibreMap.CancelableCallback
|
||||
import org.maplibre.android.maps.MapView
|
||||
import org.maplibre.android.maps.RenderingStats
|
||||
import org.maplibre.android.testapp.R
|
||||
import org.maplibre.android.testapp.styles.TestStyles
|
||||
import org.maplibre.android.testapp.utils.BenchmarkInputData
|
||||
@@ -250,9 +251,9 @@ class BenchmarkActivity : AppCompatActivity() {
|
||||
|
||||
maplibreMap.setSwapBehaviorFlush(benchmarkRun.syncRendering)
|
||||
|
||||
val listener = MapView.OnDidFinishRenderingFrameListener { _: Boolean, frameEncodingTime: Double, frameRenderingTime: Double ->
|
||||
encodingTimeStore.add(frameEncodingTime * 1e3)
|
||||
renderingTimeStore.add(frameRenderingTime * 1e3)
|
||||
val listener = MapView.OnDidFinishRenderingFrameWithStatsListener { _: Boolean, stats: RenderingStats ->
|
||||
encodingTimeStore.add(stats.encodingTime * 1e3)
|
||||
renderingTimeStore.add(stats.renderingTime * 1e3)
|
||||
numFrames++;
|
||||
}
|
||||
mapView.addOnDidFinishRenderingFrameListener(listener)
|
||||
|
||||
@@ -19,10 +19,10 @@ import kotlin.time.TimeSource
|
||||
import kotlin.time.TimeSource.Monotonic
|
||||
import org.maplibre.android.log.Logger
|
||||
import org.maplibre.android.log.Logger.INFO
|
||||
import org.maplibre.android.maps.RenderingStats
|
||||
import org.maplibre.android.maps.MapLibreMap
|
||||
|
||||
|
||||
|
||||
// # --8<-- [start:ObserverActivity]
|
||||
/**
|
||||
* Test activity showcasing logging observer actions from the core
|
||||
@@ -34,7 +34,8 @@ class ObserverActivity : AppCompatActivity(),
|
||||
MapView.OnGlyphsLoadedListener,
|
||||
MapView.OnGlyphsRequestedListener,
|
||||
MapView.OnSpriteLoadedListener,
|
||||
MapView.OnSpriteRequestedListener {
|
||||
MapView.OnSpriteRequestedListener,
|
||||
MapView.OnDidFinishRenderingFrameWithStatsListener {
|
||||
// # --8<-- [end:ObserverActivity]
|
||||
|
||||
private lateinit var mapView: MapView
|
||||
@@ -57,12 +58,14 @@ class ObserverActivity : AppCompatActivity(),
|
||||
mapView.addOnGlyphsRequestedListener(this)
|
||||
mapView.addOnSpriteLoadedListener(this)
|
||||
mapView.addOnSpriteRequestedListener(this)
|
||||
mapView.addOnDidFinishRenderingFrameListener(this)
|
||||
// # --8<-- [end:addListeners]
|
||||
|
||||
mapView.getMapAsync {
|
||||
it.setStyle(
|
||||
Style.Builder().fromUri(TestStyles.DEMOTILES)
|
||||
)
|
||||
it.enableRenderingStatsView(true)
|
||||
}
|
||||
|
||||
// print and clear action journal
|
||||
@@ -131,6 +134,10 @@ class ObserverActivity : AppCompatActivity(),
|
||||
TileOperation.NullOp -> Logger.e(TAG, "An unknown tile operation was emitted for tile ${tile}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDidFinishRenderingFrame(fully: Boolean, stats: RenderingStats) {
|
||||
|
||||
}
|
||||
// # --8<-- [end:mapEvents]
|
||||
|
||||
override fun onStart() {
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.maplibre.android.log.Logger
|
||||
import org.maplibre.android.maps.MapView
|
||||
import org.maplibre.android.maps.MapLibreMap
|
||||
import org.maplibre.android.maps.OnMapReadyCallback
|
||||
import org.maplibre.android.maps.RenderingStats
|
||||
import org.maplibre.android.maps.Style
|
||||
import org.maplibre.android.testapp.databinding.ActivitySnapshotBinding
|
||||
import org.maplibre.android.testapp.styles.TestStyles
|
||||
|
||||
@@ -59,7 +59,7 @@ class MapChangeActivity : AppCompatActivity() {
|
||||
mapView.addOnDidFinishLoadingMapListener(OnDidFinishLoadingMapListener { Timber.v("OnDidFinishLoadingMap") })
|
||||
mapView.addOnDidFinishLoadingStyleListener(OnDidFinishLoadingStyleListener { Timber.v("OnDidFinishLoadingStyle") })
|
||||
mapView.addOnDidFinishRenderingFrameListener(
|
||||
OnDidFinishRenderingFrameListener { fully: Boolean, frameEncodingTime: Double, frameRenderingTime: Double ->
|
||||
OnDidFinishRenderingFrameListener { fully: Boolean, _: Double, _: Double ->
|
||||
Timber.v(
|
||||
"OnDidFinishRenderingFrame: fully: %s",
|
||||
fully
|
||||
|
||||
@@ -86,6 +86,7 @@ MLN_DARWIN_OBJC_HEADERS = [
|
||||
"src/MLNPolyline.h",
|
||||
"src/MLNRasterDEMSource.h",
|
||||
"src/MLNRasterTileSource.h",
|
||||
"src/MLNRenderingStats.h",
|
||||
"src/MLNSettings.h",
|
||||
"src/MLNShape.h",
|
||||
"src/MLNShapeCollection.h",
|
||||
@@ -148,6 +149,7 @@ MLN_DARWIN_PRIVATE_HEADERS = [
|
||||
"src/MLNPolygon_Private.h",
|
||||
"src/MLNPolyline_Private.h",
|
||||
"src/MLNRasterTileSource_Private.h",
|
||||
"src/MLNRenderingStats_Private.h",
|
||||
"src/MLNSettings_Private.h",
|
||||
"src/MLNShapeOfflineRegion_Private.h",
|
||||
"src/MLNShapeSource_Private.h",
|
||||
@@ -189,6 +191,7 @@ MLN_DARWIN_PUBLIC_OBJCPP_SOURCE = [
|
||||
"src/MLNPolyline.mm",
|
||||
"src/MLNRasterDEMSource.mm",
|
||||
"src/MLNRasterTileSource.mm",
|
||||
"src/MLNRenderingStats.mm",
|
||||
"src/MLNSettings.mm",
|
||||
"src/MLNShape.mm",
|
||||
"src/MLNShapeCollection.mm",
|
||||
|
||||
83
platform/darwin/src/MLNRenderingStats.h
Normal file
83
platform/darwin/src/MLNRenderingStats.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MLNFoundation.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
MLN_EXPORT
|
||||
@interface MLNRenderingStats : NSObject
|
||||
|
||||
/// Frame CPU encoding time (milliseconds)
|
||||
@property (readonly) double encodingTime;
|
||||
/// Frame CPU rendering time (milliseconds)
|
||||
@property (readonly) double renderingTime;
|
||||
|
||||
/// Number of frames rendered
|
||||
@property (readonly) int numFrames;
|
||||
/// Number of draw calls (`glDrawElements`, `drawIndexedPrimitives`, etc.) executed during the most
|
||||
/// recent frame
|
||||
@property (readonly) int numDrawCalls;
|
||||
/// Total number of draw calls executed during all the frames
|
||||
@property (readonly) int totalDrawCalls;
|
||||
|
||||
/// Total number of textures created
|
||||
@property (readonly) int numCreatedTextures;
|
||||
/// Net textures
|
||||
@property (readonly) int numActiveTextures;
|
||||
/// Net texture bindings
|
||||
@property (readonly) int numTextureBindings;
|
||||
/// Number of times a texture was updated
|
||||
@property (readonly) int numTextureUpdates;
|
||||
/// Number of bytes used in texture updates
|
||||
@property (readonly) unsigned long textureUpdateBytes;
|
||||
|
||||
/// Number of buffers created
|
||||
@property (readonly) unsigned long totalBuffers;
|
||||
/// Number of SDK-specific buffers created
|
||||
@property (readonly) unsigned long totalBufferObjs;
|
||||
/// Number of times a buffer is updated
|
||||
@property (readonly) unsigned long bufferUpdates;
|
||||
/// Number of times an SDK-specific buffer is updated
|
||||
@property (readonly) unsigned long bufferObjUpdates;
|
||||
/// Sum of update sizes
|
||||
@property (readonly) unsigned long bufferUpdateBytes;
|
||||
|
||||
/// Number of active buffers
|
||||
@property (readonly) int numBuffers;
|
||||
/// Number of active offscreen frame buffers
|
||||
@property (readonly) int numFrameBuffers;
|
||||
|
||||
/// Number of active index buffers
|
||||
@property (readonly) int numIndexBuffers;
|
||||
/// Sum of index buffers update sizes
|
||||
@property (readonly) unsigned long indexUpdateBytes;
|
||||
|
||||
/// Number of active vertex buffers
|
||||
@property (readonly) int numVertexBuffers;
|
||||
/// Sum of vertex buffers update sizes
|
||||
@property (readonly) unsigned long vertexUpdateBytes;
|
||||
|
||||
/// Number of active uniform buffers
|
||||
@property (readonly) int numUniformBuffers;
|
||||
/// Number of times a uniform buffer is updated
|
||||
@property (readonly) int numUniformUpdates;
|
||||
/// Sum of uniform buffers update sizes
|
||||
@property (readonly) unsigned long uniformUpdateBytes;
|
||||
|
||||
/// Total texture memory
|
||||
@property (readonly) int memTextures;
|
||||
/// Total buffer memory
|
||||
@property (readonly) int memBuffers;
|
||||
/// Total index buffer memory
|
||||
@property (readonly) int memIndexBuffers;
|
||||
/// Total vertex buffer memory
|
||||
@property (readonly) int memVertexBuffers;
|
||||
/// Total uniform buffer memory
|
||||
@property (readonly) int memUniformBuffers;
|
||||
|
||||
/// Number of stencil buffer clears
|
||||
@property (readonly) int stencilClears;
|
||||
/// Number of stencil buffer updates
|
||||
@property (readonly) int stencilUpdates;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
38
platform/darwin/src/MLNRenderingStats.mm
Normal file
38
platform/darwin/src/MLNRenderingStats.mm
Normal file
@@ -0,0 +1,38 @@
|
||||
#import "MLNRenderingStats_Private.h"
|
||||
|
||||
@implementation MLNRenderingStats
|
||||
|
||||
- (void)setCoreData:(const mbgl::gfx::RenderingStats&)stats {
|
||||
_encodingTime = stats.encodingTime;
|
||||
_renderingTime = stats.renderingTime;
|
||||
_numFrames = stats.numFrames;
|
||||
_numDrawCalls = stats.numDrawCalls;
|
||||
_totalDrawCalls = stats.totalDrawCalls;
|
||||
_numCreatedTextures = stats.numCreatedTextures;
|
||||
_numActiveTextures = stats.numActiveTextures;
|
||||
_numTextureBindings = stats.numTextureBindings;
|
||||
_numTextureUpdates = stats.numTextureUpdates;
|
||||
_textureUpdateBytes = stats.textureUpdateBytes;
|
||||
_totalBuffers = stats.totalBuffers;
|
||||
_totalBufferObjs = stats.totalBufferObjs;
|
||||
_bufferUpdates = stats.bufferUpdates;
|
||||
_bufferObjUpdates = stats.bufferObjUpdates;
|
||||
_bufferUpdateBytes = stats.bufferUpdateBytes;
|
||||
_numIndexBuffers = stats.numIndexBuffers;
|
||||
_indexUpdateBytes = stats.indexUpdateBytes;
|
||||
_numVertexBuffers = stats.numVertexBuffers;
|
||||
_vertexUpdateBytes = stats.vertexUpdateBytes;
|
||||
_numUniformBuffers = stats.numUniformBuffers;
|
||||
_numUniformUpdates = stats.numUniformUpdates;
|
||||
_uniformUpdateBytes = stats.uniformUpdateBytes;
|
||||
_memTextures = stats.memTextures;
|
||||
_memBuffers = stats.memBuffers;
|
||||
_memIndexBuffers = stats.memIndexBuffers;
|
||||
_memVertexBuffers = stats.memVertexBuffers;
|
||||
_memUniformBuffers = stats.memUniformBuffers;
|
||||
_stencilClears = stats.stencilClears;
|
||||
_stencilUpdates = stats.stencilUpdates;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
12
platform/darwin/src/MLNRenderingStats_Private.h
Normal file
12
platform/darwin/src/MLNRenderingStats_Private.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#import <mbgl/gfx/rendering_stats.hpp>
|
||||
#import "MLNRenderingStats.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MLNRenderingStats (Private)
|
||||
|
||||
- (void)setCoreData:(const mbgl::gfx::RenderingStats&)stats;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -39,11 +39,10 @@ public:
|
||||
void onDidFinishRenderingFrame(RenderMode mode,
|
||||
bool repaintNeeded,
|
||||
bool placementChanged,
|
||||
double frameEncodingTime,
|
||||
double frameRenderingTime) override {
|
||||
const gfx::RenderingStats& stats) override {
|
||||
void (RendererObserver::*f)(
|
||||
RenderMode, bool, bool, double, double) = &RendererObserver::onDidFinishRenderingFrame;
|
||||
delegate.invoke(f, mode, repaintNeeded, placementChanged, frameEncodingTime, frameRenderingTime);
|
||||
RenderMode, bool, bool, const gfx::RenderingStats&) = &RendererObserver::onDidFinishRenderingFrame;
|
||||
delegate.invoke(f, mode, repaintNeeded, placementChanged, stats);
|
||||
}
|
||||
|
||||
void onStyleImageMissing(const std::string& image, const StyleImageMissingCallback& cb) override {
|
||||
@@ -95,6 +94,16 @@ public:
|
||||
mode, repaintNeeded, placementChanged, frameEncodingTime, frameRenderingTime);
|
||||
}
|
||||
|
||||
void onDidFinishRenderingFrame(RenderMode mode,
|
||||
bool repaint,
|
||||
bool placementChanged,
|
||||
const gfx::RenderingStats& stats) override {
|
||||
if (mode == RenderMode::Full && hasPendingStillImageRequest) {
|
||||
stillImage = frontend.readStillImage();
|
||||
}
|
||||
rendererObserver->onDidFinishRenderingFrame(mode, repaint, placementChanged, stats);
|
||||
}
|
||||
|
||||
void onStyleImageMissing(const std::string& id, const StyleImageMissingCallback& done) override {
|
||||
rendererObserver->onStyleImageMissing(id, done);
|
||||
}
|
||||
|
||||
@@ -278,6 +278,8 @@ GLFWView::GLFWView(bool fullscreen_,
|
||||
printf("- Press `K` to add a random custom runtime imagery annotation\n");
|
||||
printf("- Press `L` to add a random line annotation\n");
|
||||
printf("- Press `W` to pop the last-added annotation off\n");
|
||||
printf("- Press `V` to toggle custom drawable layer\n");
|
||||
printf("- Press `B` to toggle rendering stats\n");
|
||||
printf("- Press `P` to pause tile requests\n");
|
||||
printf("\n");
|
||||
printf("- Hold `Control` + mouse drag to rotate\n");
|
||||
@@ -373,6 +375,9 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
|
||||
case GLFW_KEY_V:
|
||||
view->toggleCustomDrawableStyle();
|
||||
break;
|
||||
case GLFW_KEY_B:
|
||||
view->map->enableRenderingStatsView(!view->map->isRenderingStatsViewEnabled());
|
||||
break;
|
||||
case GLFW_KEY_I:
|
||||
view->resetDatabaseCallback();
|
||||
break;
|
||||
|
||||
@@ -63,7 +63,10 @@ Enabling the action journal.
|
||||
<!-- include-example(actionJournalOptions) -->
|
||||
|
||||
```swift
|
||||
MLNSettings.actionJournalOptions.enabled = true
|
||||
let options = MLNMapOptions()
|
||||
options.actionJournalOptions.enabled = true
|
||||
options.styleURL = AMERICANA_STYLE
|
||||
mapView = MLNMapView(frame: view.bounds, options: options)
|
||||
```
|
||||
|
||||
<!-- include-example(ObserverExampleActionJournal) -->
|
||||
|
||||
@@ -57,6 +57,7 @@ Powerful, free and open-source mapping toolkit with full control over data sourc
|
||||
### Advanced
|
||||
|
||||
- <doc:CustomStyleLayerExample>
|
||||
- <doc:RenderingStatisticsHud>
|
||||
|
||||
### Other Articles
|
||||
|
||||
|
||||
@@ -6,6 +6,20 @@ Learn about the ``MLNMapViewDelegate`` methods for observing map events.
|
||||
|
||||
You can observe certain low-level events as they happen. Use these methods to collect metrics or investigate issues during map rendering. This feature is intended primarily for power users. We are always interested in improving observability, so if you have a special use case, feel free to [open an issue or pull request](https://github.com/maplibre/maplibre-native) to extend the types of observability methods.
|
||||
|
||||
## Frame Events
|
||||
|
||||
Observe frame rendering statistics with ``MLNMapViewDelegate/mapViewDidFinishRenderingFrame:fullyRendered:renderingStats:``.
|
||||
|
||||
<!-- include-example(ObserverExampleRenderingStats) -->
|
||||
|
||||
```swift
|
||||
func mapViewDidFinishRenderingFrame(_: MLNMapView, fullyRendered: Bool, renderingStats: MLNRenderingStats) {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
See also: ``MLNMapViewDelegate/mapViewDidFinishRenderingFrame:fullyRendered:`` and ``MLNMapViewDelegate/mapViewDidFinishRenderingFrame:fullyRendered:frameEncodingTime:frameRenderingTime:``
|
||||
|
||||
## Shader Events
|
||||
|
||||
Observe shader compilation with ``MLNMapViewDelegate/mapView:shaderWillCompile:backend:defines:`` and ``MLNMapViewDelegate/mapView:shaderDidCompile:backend:defines:``.
|
||||
|
||||
13
platform/ios/MapLibre.docc/RenderingStatisticsHud.md
Normal file
13
platform/ios/MapLibre.docc/RenderingStatisticsHud.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Rendering Statistics HUD
|
||||
|
||||
Show rendering statistics on the map
|
||||
|
||||
Enable the rendering HUD with:
|
||||
|
||||
<!-- include-example(enableRenderingStatsView) -->
|
||||
|
||||
```swift
|
||||
mapView.enableRenderingStatsView(true)
|
||||
```
|
||||
|
||||

|
||||
@@ -35,7 +35,7 @@ struct MapLibreNavigationView: View {
|
||||
AddMarkerSymbolExampleUIViewControllerRepresentable()
|
||||
}
|
||||
NavigationLink("ObserverExample") {
|
||||
ObserverExampleViewExampleUIViewControllerRepresentable()
|
||||
ObserverExampleViewUIViewControllerRepresentable()
|
||||
}
|
||||
Group {
|
||||
NavigationLink("AnimatedLineExample") {
|
||||
|
||||
@@ -18,11 +18,6 @@ class ObserverExampleView: UIViewController, MLNMapViewDelegate {
|
||||
|
||||
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
|
||||
mapView.setCenter(
|
||||
CLLocationCoordinate2D(latitude: 45.5076, longitude: -122.6736),
|
||||
zoomLevel: 11,
|
||||
animated: false
|
||||
)
|
||||
view.addSubview(mapView)
|
||||
|
||||
mapView.delegate = self
|
||||
@@ -45,6 +40,17 @@ class ObserverExampleView: UIViewController, MLNMapViewDelegate {
|
||||
|
||||
// #-end-example-code
|
||||
|
||||
func mapViewDidFinishLoadingMap(_: MLNMapView) {
|
||||
// #-example-code(enableRenderingStatsView)
|
||||
mapView.enableRenderingStatsView(true)
|
||||
// #-end-example-code
|
||||
}
|
||||
|
||||
// #-example-code(ObserverExampleRenderingStats)
|
||||
func mapViewDidFinishRenderingFrame(_: MLNMapView, fullyRendered _: Bool, renderingStats _: MLNRenderingStats) {}
|
||||
|
||||
// #-end-example-code
|
||||
|
||||
// #-example-code(ObserverExampleShaders)
|
||||
func mapView(_: MLNMapView, shaderWillCompile id: Int, backend: Int, defines: String) {
|
||||
print("A new shader is being compiled - shaderID:\(id), backend type:\(backend), program configuration:\(defines)")
|
||||
@@ -125,7 +131,7 @@ class ObserverExampleView: UIViewController, MLNMapViewDelegate {
|
||||
// #-end-example-code
|
||||
}
|
||||
|
||||
struct ObserverExampleViewExampleUIViewControllerRepresentable: UIViewControllerRepresentable {
|
||||
struct ObserverExampleViewUIViewControllerRepresentable: UIViewControllerRepresentable {
|
||||
typealias UIViewControllerType = ObserverExampleView
|
||||
|
||||
func makeUIViewController(context _: Context) -> ObserverExampleView {
|
||||
|
||||
@@ -54,7 +54,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsDebugToolsRows) {
|
||||
MBXSettingsDebugToolsOverdrawVisualization,
|
||||
MBXSettingsDebugToolsShowZoomLevel,
|
||||
MBXSettingsDebugToolsShowFrameTimeGraph,
|
||||
MBXSettingsDebugToolsShowReuseQueueStats
|
||||
MBXSettingsDebugToolsShowReuseQueueStats,
|
||||
MBXSettingsDebugToolsShowRenderingStats
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, MBXSettingsAnnotationsRows) {
|
||||
@@ -220,6 +221,7 @@ CLLocationCoordinate2D randomWorldCoordinate(void) {
|
||||
@property (nonatomic, getter=isLocalizingLabels) BOOL localizingLabels;
|
||||
@property (nonatomic) BOOL reuseQueueStatsEnabled;
|
||||
@property (nonatomic) BOOL frameTimeGraphEnabled;
|
||||
@property (nonatomic) BOOL renderingStatsEnabled;
|
||||
@property (nonatomic) BOOL shouldLimitCameraChanges;
|
||||
@property (nonatomic) BOOL randomWalk;
|
||||
@property (nonatomic) BOOL zoomLevelOrnamentEnabled;
|
||||
@@ -399,7 +401,8 @@ CLLocationCoordinate2D randomWorldCoordinate(void) {
|
||||
(debugMask & MLNMapDebugOverdrawVisualizationMask ? @"Hide" :@"Show")],
|
||||
[NSString stringWithFormat:@"%@ zoom level ornament", (self.zoomLevelOrnamentEnabled ? @"Hide" :@"Show")],
|
||||
[NSString stringWithFormat:@"%@ frame time graph", (self.frameTimeGraphEnabled ? @"Hide" :@"Show")],
|
||||
[NSString stringWithFormat:@"%@ reuse queue stats", (self.reuseQueueStatsEnabled ? @"Hide" :@"Show")]
|
||||
[NSString stringWithFormat:@"%@ reuse queue stats", (self.reuseQueueStatsEnabled ? @"Hide" :@"Show")],
|
||||
[NSString stringWithFormat:@"%@ rendering stats", (self.renderingStatsEnabled ? @"Hide" :@"Show")]
|
||||
]];
|
||||
break;
|
||||
case MBXSettingsAnnotations:
|
||||
@@ -533,6 +536,12 @@ CLLocationCoordinate2D randomWorldCoordinate(void) {
|
||||
[self updateHUD];
|
||||
break;
|
||||
}
|
||||
case MBXSettingsDebugToolsShowRenderingStats:
|
||||
{
|
||||
self.renderingStatsEnabled = !self.renderingStatsEnabled;
|
||||
[self.mapView enableRenderingStatsView:self.renderingStatsEnabled];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NSAssert(NO, @"All debug tools setting rows should be implemented");
|
||||
break;
|
||||
@@ -2691,9 +2700,11 @@ CLLocationCoordinate2D randomWorldCoordinate(void) {
|
||||
return features;
|
||||
}
|
||||
|
||||
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView fullyRendered:(BOOL)fullyRendered frameEncodingTime:(double)frameEncodingTime frameRenderingTime:(double)frameRenderingTime {
|
||||
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView
|
||||
fullyRendered:(BOOL)fullyRendered
|
||||
renderingStats:(nonnull MLNRenderingStats *)renderingStats {
|
||||
if (self.frameTimeGraphEnabled) {
|
||||
[self.frameTimeGraphView updatePathWithFrameDuration:frameEncodingTime];
|
||||
[self.frameTimeGraphView updatePathWithFrameDuration:renderingStats.encodingTime];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class MLNMapViewImpl : public mbgl::MapObserver {
|
||||
void onDidFinishLoadingMap() override;
|
||||
void onDidFailLoadingMap(mbgl::MapLoadError mapError, const std::string& what) override;
|
||||
void onWillStartRenderingFrame() override;
|
||||
void onDidFinishRenderingFrame(mbgl::MapObserver::RenderFrameStatus) override;
|
||||
void onDidFinishRenderingFrame(const mbgl::MapObserver::RenderFrameStatus&) override;
|
||||
void onWillStartRenderingMap() override;
|
||||
void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) override;
|
||||
void onDidFinishLoadingStyle() override;
|
||||
|
||||
@@ -79,9 +79,9 @@ void MLNMapViewImpl::onWillStartRenderingFrame() {
|
||||
[mapView mapViewWillStartRenderingFrame];
|
||||
}
|
||||
|
||||
void MLNMapViewImpl::onDidFinishRenderingFrame(mbgl::MapObserver::RenderFrameStatus status) {
|
||||
bool fullyRendered = status.mode == mbgl::MapObserver::RenderMode::Full;
|
||||
[mapView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered frameEncodingTime:status.frameEncodingTime frameRenderingTime:status.frameRenderingTime];
|
||||
void MLNMapViewImpl::onDidFinishRenderingFrame(const mbgl::MapObserver::RenderFrameStatus& status) {
|
||||
const bool fullyRendered = status.mode == mbgl::MapObserver::RenderMode::Full;
|
||||
[mapView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered renderingStats:status.renderingStats];
|
||||
}
|
||||
|
||||
void MLNMapViewImpl::onWillStartRenderingMap() {
|
||||
|
||||
@@ -2157,6 +2157,16 @@ vertically on the map.
|
||||
*/
|
||||
@property (nonatomic) MLNMapDebugMaskOptions debugMask;
|
||||
|
||||
/**
|
||||
Returns the status of the rendering statistics overlay.
|
||||
*/
|
||||
- (BOOL)isRenderingStatsViewEnabled;
|
||||
|
||||
/**
|
||||
Enable a rendering statistics overlay with ``MLNRenderingStats`` values.
|
||||
*/
|
||||
- (void)enableRenderingStatsView:(BOOL)value;
|
||||
|
||||
/**
|
||||
Get the list of action journal log files from oldest to newest.
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
#import "MLNLoggingConfiguration_Private.h"
|
||||
#import "MLNNetworkConfiguration_Private.h"
|
||||
#import "MLNReachability.h"
|
||||
#import "MLNRenderingStats_Private.h"
|
||||
#import "MLNSettings_Private.h"
|
||||
#import "MLNActionJournalOptions_Private.h"
|
||||
#import "MLNMapProjection.h"
|
||||
@@ -495,6 +496,8 @@ public:
|
||||
CFTimeInterval _frameCounterStartTime;
|
||||
NSInteger _frameCount;
|
||||
CFTimeInterval _frameDurations;
|
||||
|
||||
MLNRenderingStats* _renderingStats;
|
||||
}
|
||||
|
||||
// MARK: - Setup & Teardown -
|
||||
@@ -6913,8 +6916,7 @@ static void *windowScreenContext = &windowScreenContext;
|
||||
}
|
||||
|
||||
- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered
|
||||
frameEncodingTime:(double)frameEncodingTime
|
||||
frameRenderingTime:(double)frameRenderingTime {
|
||||
renderingStats:(const mbgl::gfx::RenderingStats &)stats {
|
||||
if (!_mbglMap)
|
||||
{
|
||||
return;
|
||||
@@ -6926,9 +6928,21 @@ static void *windowScreenContext = &windowScreenContext;
|
||||
[self.style didChangeValueForKey:@"layers"];
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:frameEncodingTime:frameRenderingTime:)])
|
||||
if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:renderingStats:)])
|
||||
{
|
||||
[self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:fullyRendered frameEncodingTime:frameEncodingTime frameRenderingTime:frameRenderingTime];
|
||||
if (!_renderingStats) {
|
||||
_renderingStats = [[MLNRenderingStats alloc] init];
|
||||
}
|
||||
|
||||
[_renderingStats setCoreData:stats];
|
||||
[self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:fullyRendered renderingStats:_renderingStats];
|
||||
}
|
||||
else if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:frameEncodingTime:frameRenderingTime:)])
|
||||
{
|
||||
[self.delegate mapViewDidFinishRenderingFrame:self
|
||||
fullyRendered:fullyRendered
|
||||
frameEncodingTime:stats.encodingTime
|
||||
frameRenderingTime:stats.renderingTime];
|
||||
}
|
||||
else if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)])
|
||||
{
|
||||
@@ -7557,6 +7571,14 @@ static void *windowScreenContext = &windowScreenContext;
|
||||
return _annotationViewReuseQueueByIdentifier[identifier];
|
||||
}
|
||||
|
||||
- (BOOL)isRenderingStatsViewEnabled {
|
||||
return _mbglMap->isRenderingStatsViewEnabled();
|
||||
}
|
||||
|
||||
- (void)enableRenderingStatsView:(BOOL)value {
|
||||
_mbglMap->enableRenderingStatsView(value);
|
||||
}
|
||||
|
||||
- (void)triggerRepaint
|
||||
{
|
||||
_mbglMap->triggerRepaint();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "MLNCameraChangeReason.h"
|
||||
#import "MLNRenderingStats.h"
|
||||
#import "MLNTileOperation.h"
|
||||
#import "Mapbox.h"
|
||||
|
||||
@@ -267,6 +268,23 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
fullyRendered:(BOOL)fullyRendered
|
||||
frameEncodingTime:(double)frameEncodingTime
|
||||
frameRenderingTime:(double)frameRenderingTime;
|
||||
/**
|
||||
Tells the delegate that the map view has just redrawn.
|
||||
|
||||
This method is called any time the map view needs to redraw due to a change in
|
||||
the viewpoint or style property transition. This method may be called very
|
||||
frequently, even moreso than `-mapViewRegionIsChanging:`. Therefore, your
|
||||
implementation of this method should be as lightweight as possible to avoid
|
||||
affecting performance.
|
||||
|
||||
@param mapView The map view that has just redrawn.
|
||||
@param fullyRendered A Boolean value indicating whether the map is fully rendered or not.
|
||||
@param renderingStats A collection of rendering statistics
|
||||
*/
|
||||
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView
|
||||
fullyRendered:(BOOL)fullyRendered
|
||||
renderingStats:(MLNRenderingStats *)renderingStats;
|
||||
|
||||
/**
|
||||
Tells the delegate that the map view is entering an idle state, and no more
|
||||
drawing will be necessary until new data is loaded or there is some interaction
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
#import "MLNTileOperation.h"
|
||||
#import "MLNUserLocationAnnotationView.h"
|
||||
|
||||
#include <mbgl/util/size.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
class Map;
|
||||
class Renderer;
|
||||
|
||||
namespace gfx {
|
||||
struct RenderingStats;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
class MLNMapViewImpl;
|
||||
@@ -39,8 +42,7 @@ FOUNDATION_EXTERN MLN_EXPORT MLNExceptionName const _Nonnull MLNUnderlyingMapUna
|
||||
- (void)mapViewDidFailLoadingMapWithError:(nonnull NSError *)error;
|
||||
- (void)mapViewWillStartRenderingFrame;
|
||||
- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered
|
||||
frameEncodingTime:(double)frameEncodingTime
|
||||
frameRenderingTime:(double)frameRenderingTime;
|
||||
renderingStats:(const mbgl::gfx::RenderingStats &)stats;
|
||||
- (void)mapViewWillStartRenderingMap;
|
||||
- (void)mapViewDidFinishRenderingMapFullyRendered:(BOOL)fullyRendered;
|
||||
- (void)mapViewDidBecomeIdle;
|
||||
|
||||
@@ -62,6 +62,8 @@ extension MLNMapViewDelegateIntegrationTests: MLNMapViewDelegate {
|
||||
|
||||
func mapViewDidFinishRenderingFrame(_: MLNMapView, fullyRendered _: Bool, frameEncodingTime _: Double, frameRenderingTime _: Double) {}
|
||||
|
||||
func mapViewDidFinishRenderingFrame(_: MLNMapView, fullyRendered _: Bool, renderingStats _: MLNRenderingStats) {}
|
||||
|
||||
func mapView(_: MLNMapView, shapeAnnotationIsEnabled _: MLNShape) -> Bool { false }
|
||||
|
||||
func mapView(_: MLNMapView, didAdd _: [MLNAnnotationView]) {}
|
||||
|
||||
@@ -31,7 +31,7 @@ class MLNMapViewImpl : public mbgl::MapObserver {
|
||||
void onDidFinishLoadingMap() override;
|
||||
void onDidFailLoadingMap(mbgl::MapLoadError mapError, const std::string& what) override;
|
||||
void onWillStartRenderingFrame() override;
|
||||
void onDidFinishRenderingFrame(mbgl::MapObserver::RenderFrameStatus) override;
|
||||
void onDidFinishRenderingFrame(const mbgl::MapObserver::RenderFrameStatus&) override;
|
||||
void onWillStartRenderingMap() override;
|
||||
void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) override;
|
||||
void onDidFinishLoadingStyle() override;
|
||||
|
||||
@@ -80,9 +80,9 @@ void MLNMapViewImpl::onWillStartRenderingFrame() {
|
||||
[mapView mapViewWillStartRenderingFrame];
|
||||
}
|
||||
|
||||
void MLNMapViewImpl::onDidFinishRenderingFrame(mbgl::MapObserver::RenderFrameStatus status) {
|
||||
void MLNMapViewImpl::onDidFinishRenderingFrame(const mbgl::MapObserver::RenderFrameStatus& status) {
|
||||
bool fullyRendered = status.mode == mbgl::MapObserver::RenderMode::Full;
|
||||
[mapView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered];
|
||||
[mapView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered renderingStats:status.renderingStats];
|
||||
}
|
||||
|
||||
void MLNMapViewImpl::onWillStartRenderingMap() {
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
#import "MLNLoggingConfiguration_Private.h"
|
||||
#import "MLNReachability.h"
|
||||
#import "MLNActionJournalOptions_Private.h"
|
||||
#import "MLNRenderingStats_Private.h"
|
||||
#import "MLNSettings_Private.h"
|
||||
|
||||
#import <CoreImage/CIFilter.h>
|
||||
@@ -207,6 +208,8 @@ public:
|
||||
|
||||
/// reachability instance
|
||||
MLNReachability *_reachability;
|
||||
|
||||
MLNRenderingStats* _renderingStats;
|
||||
}
|
||||
|
||||
// MARK: Lifecycle
|
||||
@@ -954,7 +957,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered {
|
||||
- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered
|
||||
renderingStats:(const mbgl::gfx::RenderingStats &)stats {
|
||||
if (!_mbglMap) {
|
||||
return;
|
||||
}
|
||||
@@ -963,7 +967,25 @@ public:
|
||||
_isChangingAnnotationLayers = NO;
|
||||
[self.style didChangeValueForKey:@"layers"];
|
||||
}
|
||||
if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) {
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:renderingStats:)])
|
||||
{
|
||||
if (!_renderingStats) {
|
||||
_renderingStats = [[MLNRenderingStats alloc] init];
|
||||
}
|
||||
|
||||
[_renderingStats setCoreData:stats];
|
||||
[self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:fullyRendered renderingStats:_renderingStats];
|
||||
}
|
||||
else if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:frameEncodingTime:frameRenderingTime:)])
|
||||
{
|
||||
[self.delegate mapViewDidFinishRenderingFrame:self
|
||||
fullyRendered:fullyRendered
|
||||
frameEncodingTime:stats.encodingTime
|
||||
frameRenderingTime:stats.renderingTime];
|
||||
}
|
||||
else if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)])
|
||||
{
|
||||
[self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:fullyRendered];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MLNRenderingStats.h"
|
||||
#import "MLNTileOperation.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@@ -169,11 +170,31 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
affecting performance.
|
||||
|
||||
@param mapView The map view that has just redrawn.
|
||||
@param frameTimeNanos The time taken to render the frame, in nanoseconds
|
||||
@param fullyRendered A Boolean value indicating whether the map is fully rendered or not.
|
||||
@param frameEncodingTime The time taken to encode the frame, in milliseconds.
|
||||
@param frameRenderingTime The time taken to render the frame, in milliseconds.
|
||||
*/
|
||||
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView
|
||||
fullyRendered:(BOOL)fullyRendered
|
||||
frameTime:(double)frameTime;
|
||||
frameEncodingTime:(double)frameEncodingTime
|
||||
frameRenderingTime:(double)frameRenderingTime;
|
||||
|
||||
/**
|
||||
Tells the delegate that the map view has just redrawn.
|
||||
|
||||
This method is called any time the map view needs to redraw due to a change in
|
||||
the viewpoint or style property transition. This method may be called very
|
||||
frequently, even moreso than `-mapViewRegionIsChanging:`. Therefore, your
|
||||
implementation of this method should be as lightweight as possible to avoid
|
||||
affecting performance.
|
||||
|
||||
@param mapView The map view that has just redrawn.
|
||||
@param fullyRendered A Boolean value indicating whether the map is fully rendered or not.
|
||||
@param renderingStats A collection of rendering statistics
|
||||
*/
|
||||
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView
|
||||
fullyRendered:(BOOL)fullyRendered
|
||||
renderingStats:(MLNRenderingStats *)renderingStats;
|
||||
|
||||
/**
|
||||
Tells the delegate that the map view is entering an idle state, and no more
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
namespace mbgl {
|
||||
class Map;
|
||||
class Renderer;
|
||||
|
||||
namespace gfx {
|
||||
struct RenderingStats;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@class MLNSource;
|
||||
@@ -40,7 +45,8 @@ class Renderer;
|
||||
- (void)mapViewDidFinishLoadingMap;
|
||||
- (void)mapViewDidFailLoadingMapWithError:(nonnull NSError *)error;
|
||||
- (void)mapViewWillStartRenderingFrame;
|
||||
- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered;
|
||||
- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered
|
||||
renderingStats:(const mbgl::gfx::RenderingStats &)stats;
|
||||
- (void)mapViewWillStartRenderingMap;
|
||||
- (void)mapViewDidFinishRenderingMapFullyRendered:(BOOL)fullyRendered;
|
||||
- (void)mapViewDidBecomeIdle;
|
||||
|
||||
@@ -190,4 +190,12 @@ modifiedReferenceSpec["paint_location-indicator"] = {
|
||||
}
|
||||
};
|
||||
|
||||
// internal use
|
||||
modifiedReferenceSpec["layout_symbol"]["symbol-screen-space"] = {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"property-type": "data-constant",
|
||||
"doc": "Internal use only"
|
||||
};
|
||||
|
||||
export default modifiedReferenceSpec;
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
#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 {
|
||||
@@ -24,6 +30,8 @@ bool RenderingStats::isZero() const {
|
||||
}
|
||||
|
||||
RenderingStats& RenderingStats::operator+=(const RenderingStats& r) {
|
||||
encodingTime += r.encodingTime;
|
||||
renderingTime += r.renderingTime;
|
||||
numFrames += r.numFrames;
|
||||
numDrawCalls += r.numDrawCalls;
|
||||
totalDrawCalls += r.totalDrawCalls;
|
||||
@@ -66,6 +74,10 @@ std::ostream& optionalStatLine(std::ostream& stream, T value, std::string_view l
|
||||
}
|
||||
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);
|
||||
@@ -99,5 +111,182 @@ std::string RenderingStats::toString(std::string_view sep) const {
|
||||
}
|
||||
#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
|
||||
|
||||
@@ -100,6 +100,10 @@ Context::~Context() noexcept {
|
||||
if (cleanupOnDestruction) {
|
||||
backend.getThreadPool().runRenderJobs(true /* closeQueue */);
|
||||
|
||||
for (size_t i = 0; i < globalUniformBuffers.allocatedSize(); i++) {
|
||||
globalUniformBuffers.set(i, nullptr);
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
// Delete all pooled resources while the context is still valid
|
||||
@@ -536,7 +540,7 @@ gfx::UniformBufferPtr Context::createUniformBuffer(const void* data,
|
||||
bool /*ssbo*/) {
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
return std::make_shared<gl::UniformBufferGL>(data, size, *uboAllocator);
|
||||
return std::make_shared<gl::UniformBufferGL>(*this, data, size, *uboAllocator);
|
||||
}
|
||||
|
||||
gfx::UniqueUniformBufferArray Context::createLayerUniformBufferArray() {
|
||||
@@ -622,6 +626,7 @@ void Context::clear(std::optional<mbgl::Color> color, std::optional<float> depth
|
||||
MBGL_CHECK_ERROR(glClear(mask));
|
||||
|
||||
stats.numDrawCalls = 0;
|
||||
stats.numFrames++;
|
||||
}
|
||||
|
||||
void Context::setCullFaceMode(const gfx::CullFaceMode& mode) {
|
||||
@@ -728,6 +733,7 @@ void Context::draw(const gfx::DrawMode& drawMode, std::size_t indexOffset, std::
|
||||
reinterpret_cast<GLvoid*>(sizeof(uint16_t) * indexOffset)));
|
||||
|
||||
stats.numDrawCalls++;
|
||||
stats.totalDrawCalls++;
|
||||
}
|
||||
|
||||
void Context::performCleanup() {
|
||||
|
||||
@@ -9,13 +9,29 @@ IndexBufferResource::IndexBufferResource(UniqueBuffer&& buffer_, int byteSize_)
|
||||
: buffer(std::move(buffer_)),
|
||||
byteSize(byteSize_) {
|
||||
MLN_TRACE_ALLOC_INDEX_BUFFER(buffer.get(), byteSize);
|
||||
|
||||
if (buffer) {
|
||||
auto& stats = buffer.get_deleter().context.renderingStats();
|
||||
stats.numIndexBuffers++;
|
||||
stats.memIndexBuffers += byteSize;
|
||||
|
||||
stats.numBuffers++;
|
||||
stats.totalBuffers++;
|
||||
stats.memBuffers += byteSize;
|
||||
}
|
||||
}
|
||||
|
||||
IndexBufferResource::~IndexBufferResource() noexcept {
|
||||
MLN_TRACE_FREE_INDEX_BUFFER(buffer.get());
|
||||
auto& stats = buffer.get_deleter().context.renderingStats();
|
||||
stats.memIndexBuffers -= byteSize;
|
||||
assert(stats.memIndexBuffers >= 0);
|
||||
|
||||
if (buffer) {
|
||||
auto& stats = buffer.get_deleter().context.renderingStats();
|
||||
stats.numIndexBuffers--;
|
||||
stats.memIndexBuffers -= byteSize;
|
||||
stats.memBuffers -= byteSize;
|
||||
|
||||
assert(stats.memIndexBuffers >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
|
||||
@@ -187,6 +187,9 @@ void Texture2D::uploadSubRegion(const void* pixelData, const Size& size_, uint16
|
||||
Enum<gfx::TexturePixelType>::to(pixelFormat),
|
||||
Enum<gfx::TextureChannelDataType>::to(channelType),
|
||||
pixelData));
|
||||
|
||||
context.renderingStats().numTextureUpdates++;
|
||||
context.renderingStats().textureUpdateBytes += static_cast<size_t>(getPixelStride() * size_.width * size_.height);
|
||||
}
|
||||
|
||||
void Texture2D::upload() noexcept {
|
||||
|
||||
@@ -26,12 +26,21 @@ int64_t generateDebugId() noexcept {
|
||||
|
||||
} // namespace
|
||||
|
||||
UniformBufferGL::UniformBufferGL(const void* data_, std::size_t size_, IBufferAllocator& allocator_)
|
||||
UniformBufferGL::UniformBufferGL(Context& context_, const void* data_, std::size_t size_, IBufferAllocator& allocator_)
|
||||
: UniformBuffer(size_),
|
||||
context(context_),
|
||||
#ifdef MLN_TRACY_ENABLE
|
||||
uniqueDebugId(generateDebugId()),
|
||||
#endif
|
||||
managedBuffer(allocator_, this) {
|
||||
|
||||
context.renderingStats().numUniformBuffers++;
|
||||
context.renderingStats().memUniformBuffers += size;
|
||||
|
||||
context.renderingStats().totalBuffers++;
|
||||
context.renderingStats().numBuffers++;
|
||||
context.renderingStats().memBuffers += size;
|
||||
|
||||
MLN_TRACE_ALLOC_CONST_BUFFER(uniqueDebugId, size_);
|
||||
if (size_ > managedBuffer.allocator.pageSize()) {
|
||||
// Buffer is very large, won't fit in the provided allocator
|
||||
@@ -48,6 +57,7 @@ UniformBufferGL::UniformBufferGL(const void* data_, std::size_t size_, IBufferAl
|
||||
|
||||
UniformBufferGL::UniformBufferGL(UniformBufferGL&& rhs) noexcept
|
||||
: UniformBuffer(rhs.size),
|
||||
context(rhs.context),
|
||||
#ifdef MLN_TRACY_ENABLE
|
||||
uniqueDebugId(rhs.uniqueDebugId),
|
||||
#endif
|
||||
@@ -62,6 +72,7 @@ UniformBufferGL::UniformBufferGL(UniformBufferGL&& rhs) noexcept
|
||||
|
||||
UniformBufferGL::UniformBufferGL(const UniformBufferGL& other)
|
||||
: UniformBuffer(other),
|
||||
context(other.context),
|
||||
#ifdef MLN_TRACY_ENABLE
|
||||
uniqueDebugId(generateDebugId()),
|
||||
#endif
|
||||
@@ -83,6 +94,13 @@ UniformBufferGL::~UniformBufferGL() {
|
||||
#ifdef MLN_TRACY_ENABLE
|
||||
assert(uniqueDebugId > 0);
|
||||
#endif
|
||||
|
||||
context.renderingStats().numUniformBuffers--;
|
||||
context.renderingStats().memUniformBuffers -= size;
|
||||
|
||||
context.renderingStats().numBuffers--;
|
||||
context.renderingStats().memBuffers -= size;
|
||||
|
||||
MLN_TRACE_FREE_CONST_BUFFER(uniqueDebugId);
|
||||
if (isManagedAllocation) {
|
||||
return;
|
||||
@@ -123,6 +141,12 @@ void UniformBufferGL::update(const void* data, std::size_t dataSize) {
|
||||
MBGL_CHECK_ERROR(glBufferSubData(GL_UNIFORM_BUFFER, 0, dataSize, data));
|
||||
MBGL_CHECK_ERROR(glBindBuffer(GL_UNIFORM_BUFFER, 0));
|
||||
}
|
||||
|
||||
context.renderingStats().numUniformUpdates++;
|
||||
context.renderingStats().bufferUpdates++;
|
||||
context.renderingStats().bufferObjUpdates++;
|
||||
context.renderingStats().uniformUpdateBytes += dataSize;
|
||||
context.renderingStats().bufferUpdateBytes += dataSize;
|
||||
}
|
||||
|
||||
void UniformBufferArrayGL::bind() const {
|
||||
|
||||
@@ -31,8 +31,7 @@ std::unique_ptr<gfx::VertexBufferResource> UploadPass::createVertexBufferResourc
|
||||
bool /*persistent*/) {
|
||||
BufferID id = 0;
|
||||
MBGL_CHECK_ERROR(glGenBuffers(1, &id));
|
||||
commandEncoder.context.renderingStats().numBuffers++;
|
||||
commandEncoder.context.renderingStats().memVertexBuffers += static_cast<int>(size);
|
||||
|
||||
// NOLINTNEXTLINE(performance-move-const-arg)
|
||||
UniqueBuffer result{std::move(id), {commandEncoder.context}};
|
||||
commandEncoder.context.vertexBuffer = result;
|
||||
@@ -43,6 +42,11 @@ std::unique_ptr<gfx::VertexBufferResource> UploadPass::createVertexBufferResourc
|
||||
void UploadPass::updateVertexBufferResource(gfx::VertexBufferResource& resource, const void* data, std::size_t size) {
|
||||
commandEncoder.context.vertexBuffer = static_cast<gl::VertexBufferResource&>(resource).getBuffer();
|
||||
MBGL_CHECK_ERROR(glBufferSubData(GL_ARRAY_BUFFER, 0, size, data));
|
||||
|
||||
commandEncoder.context.renderingStats().vertexUpdateBytes += size;
|
||||
commandEncoder.context.renderingStats().bufferUpdateBytes += size;
|
||||
commandEncoder.context.renderingStats().bufferUpdates++;
|
||||
commandEncoder.context.renderingStats().bufferObjUpdates++;
|
||||
}
|
||||
|
||||
std::unique_ptr<gfx::IndexBufferResource> UploadPass::createIndexBufferResource(const void* data,
|
||||
@@ -51,8 +55,7 @@ std::unique_ptr<gfx::IndexBufferResource> UploadPass::createIndexBufferResource(
|
||||
bool /*persistent*/) {
|
||||
BufferID id = 0;
|
||||
MBGL_CHECK_ERROR(glGenBuffers(1, &id));
|
||||
commandEncoder.context.renderingStats().numBuffers++;
|
||||
commandEncoder.context.renderingStats().memIndexBuffers += static_cast<int>(size);
|
||||
|
||||
// NOLINTNEXTLINE(performance-move-const-arg)
|
||||
UniqueBuffer result{std::move(id), {commandEncoder.context}};
|
||||
commandEncoder.context.bindVertexArray = 0;
|
||||
@@ -67,6 +70,11 @@ void UploadPass::updateIndexBufferResource(gfx::IndexBufferResource& resource, c
|
||||
commandEncoder.context.bindVertexArray = 0;
|
||||
commandEncoder.context.globalVertexArrayState.indexBuffer = static_cast<gl::IndexBufferResource&>(resource).buffer;
|
||||
MBGL_CHECK_ERROR(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, size, data));
|
||||
|
||||
commandEncoder.context.renderingStats().indexUpdateBytes += size;
|
||||
commandEncoder.context.renderingStats().bufferUpdateBytes += size;
|
||||
commandEncoder.context.renderingStats().bufferUpdates++;
|
||||
commandEncoder.context.renderingStats().bufferObjUpdates++;
|
||||
}
|
||||
|
||||
struct VertexBufferGL : public gfx::VertexBufferBase {
|
||||
|
||||
@@ -9,13 +9,29 @@ VertexBufferResource::VertexBufferResource(UniqueBuffer&& buffer_, int byteSize_
|
||||
: buffer(std::move(buffer_)),
|
||||
byteSize(byteSize_) {
|
||||
MLN_TRACE_ALLOC_VERTEX_BUFFER(buffer.get(), byteSize);
|
||||
|
||||
if (buffer) {
|
||||
auto& stats = buffer.get_deleter().context.renderingStats();
|
||||
stats.numVertexBuffers++;
|
||||
stats.memVertexBuffers += byteSize;
|
||||
|
||||
stats.numBuffers++;
|
||||
stats.totalBuffers++;
|
||||
stats.memBuffers += byteSize;
|
||||
}
|
||||
}
|
||||
|
||||
VertexBufferResource::~VertexBufferResource() noexcept {
|
||||
MLN_TRACE_FREE_VERTEX_BUFFER(buffer.get());
|
||||
auto& stats = buffer.get_deleter().context.renderingStats();
|
||||
stats.memVertexBuffers -= byteSize;
|
||||
assert(stats.memVertexBuffers >= 0);
|
||||
|
||||
if (buffer) {
|
||||
auto& stats = buffer.get_deleter().context.renderingStats();
|
||||
stats.numVertexBuffers--;
|
||||
stats.memVertexBuffers -= byteSize;
|
||||
stats.memBuffers -= byteSize;
|
||||
|
||||
assert(stats.memVertexBuffers >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
|
||||
@@ -507,6 +507,14 @@ MapDebugOptions Map::getDebug() const {
|
||||
return impl->debugOptions;
|
||||
}
|
||||
|
||||
bool Map::isRenderingStatsViewEnabled() const {
|
||||
return impl->isRenderingStatsViewEnabled();
|
||||
}
|
||||
|
||||
void Map::enableRenderingStatsView(bool value) {
|
||||
impl->enableRenderingStatsView(value);
|
||||
}
|
||||
|
||||
void Map::setPrefetchZoomDelta(uint8_t delta) {
|
||||
impl->prefetchZoomDelta = delta;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <mbgl/util/traits.hpp>
|
||||
#include <mbgl/util/action_journal.hpp>
|
||||
#include <mbgl/util/action_journal_impl.hpp>
|
||||
#include <mbgl/gfx/rendering_stats.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -240,14 +241,16 @@ void Map::Impl::onWillStartRenderingFrame() {
|
||||
void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode,
|
||||
bool needsRepaint,
|
||||
bool placemenChanged,
|
||||
double frameEncodingTime,
|
||||
double frameRenderingTime) {
|
||||
const gfx::RenderingStats& stats) {
|
||||
rendererFullyLoaded = renderMode == RenderMode::Full;
|
||||
|
||||
if (renderingStatsView && style) {
|
||||
renderingStatsView->update(*style, stats);
|
||||
}
|
||||
|
||||
if (mode == MapMode::Continuous) {
|
||||
const MapObserver::RenderFrameStatus frameStatus{
|
||||
MapObserver::RenderMode(renderMode), needsRepaint, placemenChanged, frameEncodingTime, frameRenderingTime};
|
||||
|
||||
MapObserver::RenderMode(renderMode), needsRepaint, placemenChanged, stats};
|
||||
observer.onDidFinishRenderingFrame(frameStatus);
|
||||
|
||||
if (actionJournal) {
|
||||
@@ -304,6 +307,28 @@ void Map::Impl::jumpTo(const CameraOptions& camera) {
|
||||
onUpdate();
|
||||
}
|
||||
|
||||
bool Map::Impl::isRenderingStatsViewEnabled() const {
|
||||
return !!renderingStatsView;
|
||||
}
|
||||
|
||||
void Map::Impl::enableRenderingStatsView(bool value) {
|
||||
if (value) {
|
||||
if (!renderingStatsView) {
|
||||
renderingStatsView = std::make_unique<gfx::RenderingStatsView>();
|
||||
if (style) {
|
||||
renderingStatsView->create(*style);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (renderingStatsView) {
|
||||
if (style) {
|
||||
renderingStatsView->destroy(*style);
|
||||
}
|
||||
renderingStatsView = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::Impl::onStyleImageMissing(const std::string& id, const std::function<void()>& done) {
|
||||
if (!style->getImage(id)) {
|
||||
observer.onStyleImageMissing(id);
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
void onInvalidate() final;
|
||||
void onResourceError(std::exception_ptr) final;
|
||||
void onWillStartRenderingFrame() final;
|
||||
void onDidFinishRenderingFrame(RenderMode, bool, bool, double, double) final;
|
||||
void onDidFinishRenderingFrame(RenderMode, bool, bool, const gfx::RenderingStats&) final;
|
||||
void onWillStartRenderingMap() final;
|
||||
void onDidFinishRenderingMap() final;
|
||||
void onStyleImageMissing(const std::string&, const std::function<void()>&) final;
|
||||
@@ -76,6 +76,9 @@ public:
|
||||
// Map
|
||||
void jumpTo(const CameraOptions&);
|
||||
|
||||
bool isRenderingStatsViewEnabled() const;
|
||||
void enableRenderingStatsView(bool value);
|
||||
|
||||
MapObserver& observer;
|
||||
RendererFrontend& rendererFrontend;
|
||||
std::unique_ptr<util::ActionJournal> actionJournal;
|
||||
@@ -87,6 +90,7 @@ public:
|
||||
const bool crossSourceCollisions;
|
||||
|
||||
MapDebugOptions debugOptions{MapDebugOptions::NoDebug};
|
||||
std::unique_ptr<gfx::RenderingStatsView> renderingStatsView;
|
||||
|
||||
std::shared_ptr<FileSource> fileSource;
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ BufferResource& BufferResource::operator=(BufferResource&& other) noexcept {
|
||||
context.renderingStats().numBuffers--;
|
||||
context.renderingStats().memBuffers -= size;
|
||||
}
|
||||
|
||||
buffer = std::move(other.buffer);
|
||||
raw = std::move(other.raw);
|
||||
size = other.size;
|
||||
|
||||
@@ -15,9 +15,7 @@ namespace mtl {
|
||||
CommandEncoder::CommandEncoder(Context& context_)
|
||||
: context(context_) {}
|
||||
|
||||
CommandEncoder::~CommandEncoder() {
|
||||
context.performCleanup();
|
||||
}
|
||||
CommandEncoder::~CommandEncoder() {}
|
||||
|
||||
std::unique_ptr<gfx::UploadPass> CommandEncoder::createUploadPass(const char* name, gfx::Renderable& renderable) {
|
||||
return std::make_unique<UploadPass>(renderable, *this, name);
|
||||
|
||||
@@ -58,7 +58,8 @@ void SymbolLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamete
|
||||
|
||||
auto& context = parameters.context;
|
||||
const auto& state = parameters.state;
|
||||
const auto& evaluated = static_cast<const SymbolLayerProperties&>(*evaluatedProperties).evaluated;
|
||||
const auto& symbolLayerProperties = static_cast<const SymbolLayerProperties&>(*evaluatedProperties);
|
||||
const auto& evaluated = symbolLayerProperties.evaluated;
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
const auto label = layerGroup.getName() + "-update-uniforms";
|
||||
@@ -94,6 +95,10 @@ void SymbolLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamete
|
||||
#endif
|
||||
|
||||
const auto camDist = state.getCameraToCenterDistance();
|
||||
const auto screenSpaceProp = symbolLayerProperties.layerImpl().layout.get<SymbolScreenSpace>();
|
||||
const auto isScreenSpace = screenSpaceProp.isConstant() ? screenSpaceProp.asConstant()
|
||||
: SymbolScreenSpace::defaultValue();
|
||||
|
||||
visitLayerGroupDrawables(layerGroup, [&](gfx::Drawable& drawable) {
|
||||
if (!drawable.getTileID() || !drawable.getData()) {
|
||||
return;
|
||||
@@ -117,12 +122,20 @@ void SymbolLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamete
|
||||
|
||||
// from RenderTile::translatedMatrix
|
||||
const auto translate = isText ? evaluated.get<style::TextTranslate>() : evaluated.get<style::IconTranslate>();
|
||||
const auto anchor = isText ? evaluated.get<style::TextTranslateAnchor>()
|
||||
: evaluated.get<style::IconTranslateAnchor>();
|
||||
constexpr bool nearClipped = false;
|
||||
constexpr bool inViewportPixelUnits = false;
|
||||
const auto matrix = getTileMatrix(
|
||||
tileID, parameters, translate, anchor, nearClipped, inViewportPixelUnits, drawable);
|
||||
|
||||
mat4 matrix;
|
||||
|
||||
if (isScreenSpace) {
|
||||
matrix::ortho(matrix, 0, util::EXTENT, -util::EXTENT, 0, 0, 1);
|
||||
matrix::translate(matrix, matrix, 0, -util::EXTENT, 0);
|
||||
matrix::translate(matrix, matrix, translate[0], translate[1], 0);
|
||||
} else {
|
||||
constexpr bool nearClipped = false;
|
||||
constexpr bool inViewportPixelUnits = false;
|
||||
const auto anchor = isText ? evaluated.get<style::TextTranslateAnchor>()
|
||||
: evaluated.get<style::IconTranslateAnchor>();
|
||||
matrix = getTileMatrix(tileID, parameters, translate, anchor, nearClipped, inViewportPixelUnits, drawable);
|
||||
}
|
||||
|
||||
// from symbol_program, makeValues
|
||||
const auto currentZoom = static_cast<float>(parameters.state.getZoom());
|
||||
|
||||
@@ -417,7 +417,7 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
const auto startRendering = util::MonotonicTimer::now().count();
|
||||
// present submits render commands
|
||||
parameters.encoder->present(parameters.backend.getDefaultRenderable());
|
||||
const auto renderingTime = util::MonotonicTimer::now().count() - startRendering;
|
||||
context.renderingStats().renderingTime = util::MonotonicTimer::now().count() - startRendering;
|
||||
|
||||
parameters.encoder.reset();
|
||||
context.endFrame();
|
||||
@@ -435,14 +435,13 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
}
|
||||
#endif // MLN_RENDER_BACKEND_METAL
|
||||
|
||||
const auto encodingTime = renderTree.getElapsedTime() - renderingTime;
|
||||
context.renderingStats().encodingTime = renderTree.getElapsedTime() - context.renderingStats().renderingTime;
|
||||
|
||||
observer->onDidFinishRenderingFrame(
|
||||
renderTreeParameters.loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial,
|
||||
renderTreeParameters.needsRepaint,
|
||||
renderTreeParameters.placementChanged,
|
||||
encodingTime,
|
||||
renderingTime);
|
||||
context.renderingStats());
|
||||
|
||||
if (!renderTreeParameters.loaded) {
|
||||
renderState = RenderState::Partial;
|
||||
|
||||
@@ -302,6 +302,21 @@ void SymbolLayer::setSymbolPlacement(const PropertyValue<SymbolPlacementType>& v
|
||||
baseImpl = std::move(impl_);
|
||||
observer->onLayerChanged(*this);
|
||||
}
|
||||
PropertyValue<bool> SymbolLayer::getDefaultSymbolScreenSpace() {
|
||||
return SymbolScreenSpace::defaultValue();
|
||||
}
|
||||
|
||||
const PropertyValue<bool>& SymbolLayer::getSymbolScreenSpace() const {
|
||||
return impl().layout.get<SymbolScreenSpace>();
|
||||
}
|
||||
|
||||
void SymbolLayer::setSymbolScreenSpace(const PropertyValue<bool>& value) {
|
||||
if (value == getSymbolScreenSpace()) return;
|
||||
auto impl_ = mutableImpl();
|
||||
impl_->layout.get<SymbolScreenSpace>() = value;
|
||||
baseImpl = std::move(impl_);
|
||||
observer->onLayerChanged(*this);
|
||||
}
|
||||
PropertyValue<float> SymbolLayer::getDefaultSymbolSortKey() {
|
||||
return SymbolSortKey::defaultValue();
|
||||
}
|
||||
@@ -1124,6 +1139,7 @@ enum class Property : uint8_t {
|
||||
IconTextFitPadding,
|
||||
SymbolAvoidEdges,
|
||||
SymbolPlacement,
|
||||
SymbolScreenSpace,
|
||||
SymbolSortKey,
|
||||
SymbolSpacing,
|
||||
SymbolZOrder,
|
||||
@@ -1202,6 +1218,7 @@ constexpr const auto layerProperties = mapbox::eternal::hash_map<mapbox::eternal
|
||||
{"icon-text-fit-padding", toUint8(Property::IconTextFitPadding)},
|
||||
{"symbol-avoid-edges", toUint8(Property::SymbolAvoidEdges)},
|
||||
{"symbol-placement", toUint8(Property::SymbolPlacement)},
|
||||
{"symbol-screen-space", toUint8(Property::SymbolScreenSpace)},
|
||||
{"symbol-sort-key", toUint8(Property::SymbolSortKey)},
|
||||
{"symbol-spacing", toUint8(Property::SymbolSpacing)},
|
||||
{"symbol-z-order", toUint8(Property::SymbolZOrder)},
|
||||
@@ -1319,6 +1336,8 @@ StyleProperty getLayerProperty(const SymbolLayer& layer, Property property) {
|
||||
return makeStyleProperty(layer.getSymbolAvoidEdges());
|
||||
case Property::SymbolPlacement:
|
||||
return makeStyleProperty(layer.getSymbolPlacement());
|
||||
case Property::SymbolScreenSpace:
|
||||
return makeStyleProperty(layer.getSymbolScreenSpace());
|
||||
case Property::SymbolSortKey:
|
||||
return makeStyleProperty(layer.getSymbolSortKey());
|
||||
case Property::SymbolSpacing:
|
||||
@@ -1548,9 +1567,9 @@ std::optional<Error> SymbolLayer::setPropertyInternal(const std::string& name, c
|
||||
}
|
||||
if (property == Property::IconAllowOverlap || property == Property::IconIgnorePlacement ||
|
||||
property == Property::IconKeepUpright || property == Property::IconOptional ||
|
||||
property == Property::SymbolAvoidEdges || property == Property::TextAllowOverlap ||
|
||||
property == Property::TextIgnorePlacement || property == Property::TextKeepUpright ||
|
||||
property == Property::TextOptional) {
|
||||
property == Property::SymbolAvoidEdges || property == Property::SymbolScreenSpace ||
|
||||
property == Property::TextAllowOverlap || property == Property::TextIgnorePlacement ||
|
||||
property == Property::TextKeepUpright || property == Property::TextOptional) {
|
||||
Error error;
|
||||
const auto& typedValue = convert<PropertyValue<bool>>(value, error, false, false);
|
||||
if (!typedValue) {
|
||||
@@ -1582,6 +1601,11 @@ std::optional<Error> SymbolLayer::setPropertyInternal(const std::string& name, c
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (property == Property::SymbolScreenSpace) {
|
||||
setSymbolScreenSpace(*typedValue);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (property == Property::TextAllowOverlap) {
|
||||
setTextAllowOverlap(*typedValue);
|
||||
return std::nullopt;
|
||||
|
||||
@@ -96,6 +96,11 @@ struct SymbolPlacement : LayoutProperty<SymbolPlacementType> {
|
||||
static SymbolPlacementType defaultValue() { return SymbolPlacementType::Point; }
|
||||
};
|
||||
|
||||
struct SymbolScreenSpace : LayoutProperty<bool> {
|
||||
static constexpr const char *name() { return "symbol-screen-space"; }
|
||||
static bool defaultValue() { return false; }
|
||||
};
|
||||
|
||||
struct SymbolSortKey : DataDrivenLayoutProperty<float> {
|
||||
static constexpr const char *name() { return "symbol-sort-key"; }
|
||||
static float defaultValue() { return 0.f; }
|
||||
@@ -302,6 +307,7 @@ class SymbolLayoutProperties : public Properties<
|
||||
IconTextFitPadding,
|
||||
SymbolAvoidEdges,
|
||||
SymbolPlacement,
|
||||
SymbolScreenSpace,
|
||||
SymbolSortKey,
|
||||
SymbolSpacing,
|
||||
SymbolZOrder,
|
||||
|
||||
@@ -974,6 +974,7 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket,
|
||||
const bool rotateWithMap = bucket.layout->get<style::TextRotationAlignment>() == style::AlignmentType::Map;
|
||||
const bool pitchWithMap = bucket.layout->get<style::TextPitchAlignment>() == style::AlignmentType::Map;
|
||||
const bool hasIconTextFit = bucket.layout->get<style::IconTextFit>() != style::IconTextFitType::None;
|
||||
const bool screenSpace = bucket.layout->get<style::SymbolScreenSpace>();
|
||||
|
||||
// If allow-overlap is true, we can show symbols before placement runs on them
|
||||
// But we have to wait for placement if we potentially depend on a paired icon/text
|
||||
@@ -981,10 +982,10 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket,
|
||||
// See https://github.com/mapbox/mapbox-gl-native/issues/12483
|
||||
// Prevent a flickering issue when showing a symbol allowing overlap.
|
||||
const JointOpacityState defaultOpacityState(
|
||||
bucket.justReloaded && textAllowOverlap &&
|
||||
(screenSpace || bucket.justReloaded) && textAllowOverlap &&
|
||||
(iconAllowOverlap || !(bucket.hasIconData() || bucket.hasSdfIconData()) ||
|
||||
bucket.layout->get<style::IconOptional>()),
|
||||
bucket.justReloaded && iconAllowOverlap &&
|
||||
(screenSpace || bucket.justReloaded) && iconAllowOverlap &&
|
||||
(textAllowOverlap || !bucket.hasTextData() || bucket.layout->get<style::TextOptional>()),
|
||||
true);
|
||||
|
||||
|
||||
@@ -129,7 +129,12 @@ BufferResource::BufferResource(BufferResource&& other) noexcept
|
||||
BufferResource::~BufferResource() noexcept {
|
||||
if (isValid()) {
|
||||
context.renderingStats().numBuffers--;
|
||||
context.renderingStats().memBuffers -= size;
|
||||
|
||||
if (bufferWindowSize > 0) {
|
||||
context.renderingStats().memBuffers -= bufferWindowSize * context.getBackend().getMaxFrames();
|
||||
} else {
|
||||
context.renderingStats().memBuffers -= size;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bufferAllocation) return;
|
||||
@@ -146,7 +151,7 @@ BufferResource& BufferResource::operator=(BufferResource&& other) noexcept {
|
||||
if (isValid()) {
|
||||
context.renderingStats().numBuffers--;
|
||||
context.renderingStats().memBuffers -= size;
|
||||
};
|
||||
}
|
||||
|
||||
size = other.size;
|
||||
usage = other.usage;
|
||||
@@ -171,6 +176,7 @@ void BufferResource::update(const void* newData, std::size_t updateSize, std::si
|
||||
auto& stats = context.renderingStats();
|
||||
stats.bufferUpdateBytes += updateSize;
|
||||
stats.bufferUpdates++;
|
||||
stats.bufferObjUpdates++;
|
||||
version++;
|
||||
|
||||
if (bufferWindowSize) {
|
||||
|
||||
@@ -67,6 +67,12 @@ Context::~Context() noexcept {
|
||||
if (--glslangRefCount == 0) {
|
||||
glslang::FinalizeProcess();
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
Log::Debug(Event::General, "Rendering Stats:\n" + stats.toString("\n"));
|
||||
#endif
|
||||
|
||||
assert(stats.isZero());
|
||||
}
|
||||
|
||||
void Context::initFrameResources() {
|
||||
@@ -134,10 +140,20 @@ void Context::destroyResources() {
|
||||
frame.runDeletionQueue(*this);
|
||||
}
|
||||
|
||||
globalUniformBuffers.freeDescriptorSets();
|
||||
|
||||
// all resources have unique handles
|
||||
frameResources.clear();
|
||||
|
||||
globalUniformBuffers.freeDescriptorSets();
|
||||
|
||||
for (size_t i = 0; i < globalUniformBuffers.allocatedSize(); i++) {
|
||||
globalUniformBuffers.set(i, nullptr);
|
||||
}
|
||||
|
||||
dummyBuffer.reset();
|
||||
dummyTexture2D.reset();
|
||||
|
||||
clipping.indexBuffer.reset();
|
||||
clipping.vertexBuffer.reset();
|
||||
}
|
||||
|
||||
void Context::enqueueDeletion(std::function<void(Context&)>&& function) {
|
||||
@@ -355,6 +371,11 @@ UniqueShaderProgram Context::createProgram(shaders::BuiltIn shaderID,
|
||||
return program;
|
||||
}
|
||||
|
||||
void Context::performCleanup() {
|
||||
stats.numDrawCalls = 0;
|
||||
++stats.numFrames;
|
||||
}
|
||||
|
||||
gfx::UniqueDrawableBuilder Context::createDrawableBuilder(std::string name) {
|
||||
return std::make_unique<DrawableBuilder>(std::move(name));
|
||||
}
|
||||
|
||||
@@ -44,9 +44,6 @@ Texture2D::Texture2D(Context& context_)
|
||||
Texture2D::~Texture2D() {
|
||||
destroyTexture();
|
||||
destroySampler();
|
||||
|
||||
context.renderingStats().numActiveTextures--;
|
||||
context.renderingStats().memTextures -= Texture2D::getDataSize();
|
||||
}
|
||||
|
||||
gfx::Texture2D& Texture2D::setSamplerConfiguration(const SamplerState& samplerState_) noexcept {
|
||||
@@ -209,6 +206,7 @@ void Texture2D::uploadSubRegion(const void* pixelData,
|
||||
context.enqueueDeletion([buffAlloc = std::move(bufferAllocation)](auto&) mutable { buffAlloc.reset(); });
|
||||
|
||||
context.renderingStats().numTextureUpdates++;
|
||||
context.renderingStats().textureUpdateBytes += bufferInfo.size;
|
||||
}
|
||||
|
||||
vk::Format Texture2D::vulkanFormat(const gfx::TexturePixelType pixel, gfx::TextureChannelDataType channel) {
|
||||
@@ -402,6 +400,9 @@ void Texture2D::destroyTexture() {
|
||||
context.enqueueDeletion([allocation = std::move(imageAllocation)](auto&) mutable { allocation.reset(); });
|
||||
|
||||
imageLayout = vk::ImageLayout::eUndefined;
|
||||
|
||||
context.renderingStats().numActiveTextures--;
|
||||
context.renderingStats().memTextures -= Texture2D::getDataSize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void onDidFinishRenderingFrame(RenderFrameStatus status) final {
|
||||
void onDidFinishRenderingFrame(const RenderFrameStatus& status) final {
|
||||
if (didFinishRenderingFrameCallback) {
|
||||
didFinishRenderingFrameCallback(status);
|
||||
}
|
||||
|
||||
@@ -416,7 +416,7 @@ TEST(ActionJournal, ValidateEvents) {
|
||||
"");
|
||||
|
||||
const auto onDidFinishRenderingFrame =
|
||||
static_cast<void (RendererObserver::*)(RendererObserver::RenderMode, bool, bool, double, double)>(
|
||||
static_cast<void (RendererObserver::*)(RendererObserver::RenderMode, bool, bool, const gfx::RenderingStats&)>(
|
||||
&RendererObserver::onDidFinishRenderingFrame);
|
||||
|
||||
validateEvent(test,
|
||||
@@ -426,6 +426,5 @@ TEST(ActionJournal, ValidateEvents) {
|
||||
RendererObserver::RenderMode::Full,
|
||||
false,
|
||||
false,
|
||||
0.0,
|
||||
0.0);
|
||||
gfx::RenderingStats());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user