From bb34568af28e2f0ae3e8c09b9d8608525f40f7db Mon Sep 17 00:00:00 2001 From: mwilsnd <53413200+mwilsnd@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:48:03 -0400 Subject: [PATCH] Add extra observer hooks for interesting internal processes (#2694) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- CHANGELOG.md | 1 + CMakeLists.txt | 12 +- bazel/core.bzl | 13 +- include/mbgl/gfx/context.hpp | 11 +- include/mbgl/gfx/context_observer.hpp | 19 + include/mbgl/map/map_observer.hpp | 25 +- include/mbgl/mtl/context.hpp | 3 +- include/mbgl/renderer/renderer_observer.hpp | 18 + include/mbgl/shaders/gl/shader_group_gl.hpp | 2 +- include/mbgl/shaders/mtl/shader_group.hpp | 2 +- include/mbgl/shaders/vulkan/shader_group.hpp | 2 +- .../mbgl/shaders/vulkan/shader_program.hpp | 6 +- {src => include}/mbgl/style/sprite.hpp | 0 {src => include}/mbgl/text/glyph.hpp | 0 {src => include}/mbgl/text/glyph_range.hpp | 0 include/mbgl/tile/tile_operation.hpp | 16 + {src => include}/mbgl/util/hash.hpp | 0 {src => include}/mbgl/util/rect.hpp | 0 include/mbgl/vulkan/context.hpp | 3 +- platform/android/CHANGELOG.md | 2 + .../MapLibreAndroid/src/cpp/CMakeLists.txt | 3 +- .../src/cpp/android_renderer_frontend.cpp | 35 ++ .../MapLibreAndroid/src/cpp/jni_native.cpp | 4 + .../src/cpp/native_map_view.cpp | 195 ++++++++++ .../src/cpp/native_map_view.hpp | 20 + .../src/cpp/tile/tile_operation.cpp | 35 ++ .../src/cpp/tile/tile_operation.hpp | 20 + .../android/maps/MapChangeReceiver.java | 251 ++++++++++++ .../org/maplibre/android/maps/MapView.java | 358 ++++++++++++++++++ .../maplibre/android/maps/NativeMapView.java | 91 +++++ .../maplibre/android/tile/TileOperation.java | 13 + .../MapLibreAndroidTestApp/proguard-rules.pro | 2 +- .../src/main/AndroidManifest.xml | 12 + .../activity/events/ObserverActivity.kt | 137 +++++++ .../src/main/res/values/categories.xml | 1 + .../src/main/res/values/descriptions.xml | 1 + .../src/main/res/values/titles.xml | 1 + src/mbgl/annotation/annotation_tile.cpp | 6 +- src/mbgl/annotation/annotation_tile.hpp | 2 +- .../annotation/render_annotation_source.cpp | 25 +- src/mbgl/gl/context.hpp | 2 + src/mbgl/map/map_impl.cpp | 46 +++ src/mbgl/map/map_impl.hpp | 12 + src/mbgl/mtl/context.cpp | 17 +- src/mbgl/programs/program_parameters.cpp | 6 + src/mbgl/programs/program_parameters.hpp | 9 + src/mbgl/renderer/render_orchestrator.cpp | 15 + src/mbgl/renderer/render_orchestrator.hpp | 4 +- src/mbgl/renderer/render_source.cpp | 4 + src/mbgl/renderer/render_source.hpp | 1 + src/mbgl/renderer/render_source_observer.hpp | 3 + src/mbgl/renderer/renderer_impl.cpp | 20 + src/mbgl/renderer/renderer_impl.hpp | 10 +- .../sources/render_custom_geometry_source.cpp | 4 +- .../sources/render_geojson_source.cpp | 4 +- .../sources/render_raster_dem_source.cpp | 21 +- .../renderer/sources/render_raster_source.cpp | 21 +- .../renderer/sources/render_vector_source.cpp | 4 +- src/mbgl/renderer/tile_pyramid.cpp | 5 +- src/mbgl/renderer/tile_pyramid.hpp | 2 +- src/mbgl/shaders/gl/shader_program_gl.cpp | 118 +++--- src/mbgl/shaders/vulkan/shader_program.cpp | 21 +- src/mbgl/sprite/sprite_loader.cpp | 2 + src/mbgl/sprite/sprite_loader_observer.hpp | 2 + src/mbgl/style/observer.hpp | 6 + src/mbgl/style/style_impl.cpp | 6 + src/mbgl/style/style_impl.hpp | 1 + src/mbgl/text/glyph_manager.cpp | 2 + src/mbgl/text/glyph_manager_observer.hpp | 1 + src/mbgl/tile/custom_geometry_tile.cpp | 5 +- src/mbgl/tile/custom_geometry_tile.hpp | 6 +- src/mbgl/tile/geojson_tile.cpp | 5 +- src/mbgl/tile/geojson_tile.hpp | 3 +- src/mbgl/tile/geometry_tile.cpp | 23 +- src/mbgl/tile/geometry_tile.hpp | 7 +- src/mbgl/tile/raster_dem_tile.cpp | 19 +- src/mbgl/tile/raster_dem_tile.hpp | 3 +- src/mbgl/tile/raster_tile.cpp | 19 +- src/mbgl/tile/raster_tile.hpp | 3 +- src/mbgl/tile/tile.cpp | 10 +- src/mbgl/tile/tile.hpp | 11 +- src/mbgl/tile/tile_loader.hpp | 2 +- src/mbgl/tile/tile_loader_impl.hpp | 21 +- src/mbgl/tile/tile_loader_observer.hpp | 17 + src/mbgl/tile/tile_observer.hpp | 3 + src/mbgl/tile/vector_tile.cpp | 5 +- src/mbgl/tile/vector_tile.hpp | 6 +- src/mbgl/vulkan/context.cpp | 7 +- test/map/map.test.cpp | 162 ++++++++ test/src/mbgl/test/stub_map_observer.hpp | 51 +++ test/style/source.test.cpp | 8 +- test/tile/raster_dem_tile.test.cpp | 8 +- test/tile/raster_tile.test.cpp | 8 +- 93 files changed, 1951 insertions(+), 177 deletions(-) create mode 100644 include/mbgl/gfx/context_observer.hpp rename {src => include}/mbgl/style/sprite.hpp (100%) rename {src => include}/mbgl/text/glyph.hpp (100%) rename {src => include}/mbgl/text/glyph_range.hpp (100%) create mode 100644 include/mbgl/tile/tile_operation.hpp rename {src => include}/mbgl/util/hash.hpp (100%) rename {src => include}/mbgl/util/rect.hpp (100%) create mode 100644 platform/android/MapLibreAndroid/src/cpp/tile/tile_operation.cpp create mode 100644 platform/android/MapLibreAndroid/src/cpp/tile/tile_operation.hpp create mode 100644 platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/tile/TileOperation.java create mode 100644 platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/events/ObserverActivity.kt create mode 100644 src/mbgl/tile/tile_loader_observer.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 99b43b89e..683f6f911 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### ✨ New features - *...Add new stuff here...* +- [core] Added new map observer events: onPreCompileShader, onPostCompileShader, onShaderCompileFailed, onGlyphsLoaded, onGlyphsError, onGlyphsRequested, onTileAction, onSpriteLoaded, onSpriteError, onSpriteRequested ([#2694](https://github.com/maplibre/maplibre-native/pull/2694)). - [core] Add WebP image decoding support to default platform (Linux, Windows) - [core] All CMake properties are now prefixed `MLN_*` [1054](https://github.com/maplibre/maplibre-native/pull/1054). - [windows] Added windows build support for core applications and node [#707](https://github.com/maplibre/maplibre-native/pull/707) diff --git a/CMakeLists.txt b/CMakeLists.txt index 292e9254f..60a9791d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -369,12 +369,16 @@ list(APPEND INCLUDE_FILES ${PROJECT_SOURCE_DIR}/include/mbgl/style/sources/raster_dem_source.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/sources/raster_source.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/sources/vector_source.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/sprite.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/style.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/style_property.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/transition_options.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/types.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/undefined.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/text/glyph.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/text/glyph_range.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/tile/tile_id.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/tile/tile_operation.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/tile/tile_necessity.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/async_request.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/async_task.hpp @@ -396,6 +400,7 @@ list(APPEND INCLUDE_FILES ${PROJECT_SOURCE_DIR}/include/mbgl/util/geo.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/geojson.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/geometry.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/hash.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/identity.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/ignore.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/image.hpp @@ -410,6 +415,7 @@ list(APPEND INCLUDE_FILES ${PROJECT_SOURCE_DIR}/include/mbgl/util/premultiply.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/projection.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/range.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/rect.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/run_loop.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/scoped.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/util/size.hpp @@ -757,7 +763,6 @@ list(APPEND SRC_FILES ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/within.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/style/filter.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/style/image.cpp - ${PROJECT_SOURCE_DIR}/src/mbgl/style/sprite.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/style/sprite.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/style/image_impl.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/style/image_impl.hpp @@ -863,7 +868,6 @@ list(APPEND SRC_FILES ${PROJECT_SOURCE_DIR}/src/mbgl/text/get_anchors.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/text/get_anchors.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph.cpp - ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_atlas.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_atlas.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_manager.cpp @@ -871,7 +875,6 @@ list(APPEND SRC_FILES ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_manager_observer.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_pbf.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_pbf.hpp - ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_range.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/text/language_tag.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/text/language_tag.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/text/local_glyph_rasterizer.hpp @@ -910,6 +913,7 @@ list(APPEND SRC_FILES ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile_id_io.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile_loader.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile_loader_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile_loader_observer.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile_observer.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/tile/vector_tile.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/tile/vector_tile.hpp @@ -932,7 +936,6 @@ list(APPEND SRC_FILES ${PROJECT_SOURCE_DIR}/src/mbgl/util/geometry_util.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/util/grid_index.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/util/grid_index.hpp - ${PROJECT_SOURCE_DIR}/src/mbgl/util/hash.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/util/http_header.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/util/http_header.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/util/http_timeout.cpp @@ -961,7 +964,6 @@ list(APPEND SRC_FILES ${PROJECT_SOURCE_DIR}/src/mbgl/util/quaternion.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/util/rapidjson.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/util/rapidjson.hpp - ${PROJECT_SOURCE_DIR}/src/mbgl/util/rect.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/util/std.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/util/stopwatch.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/util/stopwatch.hpp diff --git a/bazel/core.bzl b/bazel/core.bzl index 8f85b956e..d168dd7e3 100644 --- a/bazel/core.bzl +++ b/bazel/core.bzl @@ -441,7 +441,6 @@ MLN_CORE_SOURCE = [ "src/mbgl/style/expression/value.cpp", "src/mbgl/style/expression/within.cpp", "src/mbgl/style/filter.cpp", - "src/mbgl/style/sprite.hpp", "src/mbgl/style/sprite.cpp", "src/mbgl/style/image.cpp", "src/mbgl/style/image_impl.cpp", @@ -521,7 +520,6 @@ MLN_CORE_SOURCE = [ "src/mbgl/text/get_anchors.cpp", "src/mbgl/text/get_anchors.hpp", "src/mbgl/text/glyph.cpp", - "src/mbgl/text/glyph.hpp", "src/mbgl/text/glyph_atlas.cpp", "src/mbgl/text/glyph_atlas.hpp", "src/mbgl/text/glyph_manager.cpp", @@ -529,7 +527,6 @@ MLN_CORE_SOURCE = [ "src/mbgl/text/glyph_manager_observer.hpp", "src/mbgl/text/glyph_pbf.cpp", "src/mbgl/text/glyph_pbf.hpp", - "src/mbgl/text/glyph_range.hpp", "src/mbgl/text/language_tag.cpp", "src/mbgl/text/language_tag.hpp", "src/mbgl/text/local_glyph_rasterizer.hpp", @@ -568,6 +565,7 @@ MLN_CORE_SOURCE = [ "src/mbgl/tile/tile_id_io.cpp", "src/mbgl/tile/tile_loader.hpp", "src/mbgl/tile/tile_loader_impl.hpp", + "src/mbgl/tile/tile_loader_observer.hpp", "src/mbgl/tile/tile_observer.hpp", "src/mbgl/tile/vector_tile.cpp", "src/mbgl/tile/vector_tile.hpp", @@ -591,7 +589,6 @@ MLN_CORE_SOURCE = [ "src/mbgl/util/geometry_util.hpp", "src/mbgl/util/grid_index.cpp", "src/mbgl/util/grid_index.hpp", - "src/mbgl/util/hash.hpp", "src/mbgl/util/http_header.cpp", "src/mbgl/util/http_header.hpp", "src/mbgl/util/http_timeout.cpp", @@ -621,7 +618,6 @@ MLN_CORE_SOURCE = [ "src/mbgl/util/quaternion.hpp", "src/mbgl/util/rapidjson.cpp", "src/mbgl/util/rapidjson.hpp", - "src/mbgl/util/rect.hpp", "src/mbgl/util/std.hpp", "src/mbgl/util/stopwatch.cpp", "src/mbgl/util/stopwatch.hpp", @@ -652,6 +648,7 @@ MLN_CORE_SOURCE = [ MLN_CORE_HEADERS = [ "include/mbgl/gfx/context.hpp", + "include/mbgl/gfx/context_observer.hpp", "include/mbgl/actor/actor.hpp", "include/mbgl/actor/actor_ref.hpp", "include/mbgl/actor/aspiring_actor.hpp", @@ -794,12 +791,16 @@ MLN_CORE_HEADERS = [ "include/mbgl/style/sources/raster_dem_source.hpp", "include/mbgl/style/sources/raster_source.hpp", "include/mbgl/style/sources/vector_source.hpp", + "include/mbgl/style/sprite.hpp", "include/mbgl/style/style.hpp", "include/mbgl/style/style_property.hpp", "include/mbgl/style/transition_options.hpp", "include/mbgl/style/types.hpp", "include/mbgl/style/undefined.hpp", + "include/mbgl/text/glyph.hpp", + "include/mbgl/text/glyph_range.hpp", "include/mbgl/tile/tile_id.hpp", + "include/mbgl/tile/tile_operation.hpp", "include/mbgl/tile/tile_necessity.hpp", "include/mbgl/util/async_request.hpp", "include/mbgl/util/async_task.hpp", @@ -822,6 +823,7 @@ MLN_CORE_HEADERS = [ "include/mbgl/util/geo.hpp", "include/mbgl/util/geojson.hpp", "include/mbgl/util/geometry.hpp", + "include/mbgl/util/hash.hpp", "include/mbgl/util/identity.hpp", "include/mbgl/util/ignore.hpp", "include/mbgl/util/image.hpp", @@ -837,6 +839,7 @@ MLN_CORE_HEADERS = [ "include/mbgl/util/premultiply.hpp", "include/mbgl/util/projection.hpp", "include/mbgl/util/range.hpp", + "include/mbgl/util/rect.hpp", "include/mbgl/util/run_loop.hpp", "include/mbgl/util/scoped.hpp", "include/mbgl/util/size.hpp", diff --git a/include/mbgl/gfx/context.hpp b/include/mbgl/gfx/context.hpp index a27c9fd88..8e2126509 100644 --- a/include/mbgl/gfx/context.hpp +++ b/include/mbgl/gfx/context.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -51,10 +52,15 @@ using UniqueDrawableBuilder = std::unique_ptr; using VertexAttributeArrayPtr = std::shared_ptr; #endif +namespace { +ContextObserver nullObserver; +} + class Context { protected: Context(uint32_t maximumVertexBindingCount_) - : maximumVertexBindingCount(maximumVertexBindingCount_) {} + : maximumVertexBindingCount(maximumVertexBindingCount_), + observer(&nullObserver) {} public: static constexpr const uint32_t minimumRequiredVertexBindingCount = 8; @@ -66,6 +72,8 @@ public: Context& operator=(const Context& other) = delete; virtual ~Context() = default; + virtual void setObserver(ContextObserver* observer_) { observer = observer_ ? observer_ : &nullObserver; } + virtual void beginFrame() = 0; virtual void endFrame() = 0; @@ -173,6 +181,7 @@ protected: virtual std::unique_ptr createDrawScopeResource() = 0; gfx::RenderingStats stats; + ContextObserver* observer; }; } // namespace gfx diff --git a/include/mbgl/gfx/context_observer.hpp b/include/mbgl/gfx/context_observer.hpp new file mode 100644 index 000000000..5e0ca2eb9 --- /dev/null +++ b/include/mbgl/gfx/context_observer.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { +namespace gfx { + +class ContextObserver { +public: + virtual void onPreCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {} + virtual void onPostCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {} + virtual void onShaderCompileFailed(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {} +}; + +} // namespace gfx +} // namespace mbgl diff --git a/include/mbgl/map/map_observer.hpp b/include/mbgl/map/map_observer.hpp index 2a34062af..4c9170fc1 100644 --- a/include/mbgl/map/map_observer.hpp +++ b/include/mbgl/map/map_observer.hpp @@ -1,7 +1,12 @@ #pragma once +#include +#include #include #include +#include +#include +#include #include #include @@ -65,7 +70,25 @@ public: virtual bool onCanRemoveUnusedStyleImage(const std::string&) { return true; } // Observe this event to easily mutate or observe shaders as soon // as the registry becomes available. - virtual void onRegisterShaders(gfx::ShaderRegistry&) {}; + virtual void onRegisterShaders(gfx::ShaderRegistry&) {} + + // Shaders compilation + virtual void onPreCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {} + virtual void onPostCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {} + virtual void onShaderCompileFailed(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {} + + // Glyph requests + virtual void onGlyphsLoaded(const FontStack&, const GlyphRange&) {} + virtual void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) {} + virtual void onGlyphsRequested(const FontStack&, const GlyphRange&) {} + + // Tile requests + virtual void onTileAction(TileOperation, const OverscaledTileID&, const std::string&) {} + + // Sprite requests + virtual void onSpriteLoaded(const std::optional&) {} + virtual void onSpriteError(const std::optional&, std::exception_ptr) {} + virtual void onSpriteRequested(const std::optional&) {} }; } // namespace mbgl diff --git a/include/mbgl/mtl/context.hpp b/include/mbgl/mtl/context.hpp index da71c3b6d..99e54e00a 100644 --- a/include/mbgl/mtl/context.hpp +++ b/include/mbgl/mtl/context.hpp @@ -66,7 +66,8 @@ public: BufferResource createBuffer( const void* data, std::size_t size, gfx::BufferUsageType usage, bool isIndexBuffer, bool persistent) const; - UniqueShaderProgram createProgram(std::string name, + UniqueShaderProgram createProgram(shaders::BuiltIn shaderID, + std::string name, std::string_view source, std::string_view vertexName, std::string_view fragmentName, diff --git a/include/mbgl/renderer/renderer_observer.hpp b/include/mbgl/renderer/renderer_observer.hpp index cc957d6b9..5df817afa 100644 --- a/include/mbgl/renderer/renderer_observer.hpp +++ b/include/mbgl/renderer/renderer_observer.hpp @@ -1,5 +1,12 @@ #pragma once +#include +#include +#include +#include +#include +#include + #include #include #include @@ -54,6 +61,17 @@ public: // Entry point for custom shader registration virtual void onRegisterShaders(gfx::ShaderRegistry&) {}; + virtual void onPreCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {} + virtual void onPostCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {} + virtual void onShaderCompileFailed(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {} + + // Glyph loading + virtual void onGlyphsLoaded(const FontStack&, const GlyphRange&) {} + virtual void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) {} + virtual void onGlyphsRequested(const FontStack&, const GlyphRange&) {} + + // Tile loading + virtual void onTileAction(TileOperation, const OverscaledTileID&, const std::string&) {} }; } // namespace mbgl diff --git a/include/mbgl/shaders/gl/shader_group_gl.hpp b/include/mbgl/shaders/gl/shader_group_gl.hpp index 15e402227..6eada1fc9 100644 --- a/include/mbgl/shaders/gl/shader_group_gl.hpp +++ b/include/mbgl/shaders/gl/shader_group_gl.hpp @@ -14,7 +14,7 @@ class ShaderGroupGL final : public gfx::ShaderGroup { public: ShaderGroupGL(const ProgramParameters& programParameters_) : gfx::ShaderGroup(), - programParameters(programParameters_) {} + programParameters(programParameters_.withProgramType(ShaderID)) {} ~ShaderGroupGL() noexcept override = default; gfx::ShaderPtr getOrCreateShader(gfx::Context& context, diff --git a/include/mbgl/shaders/mtl/shader_group.hpp b/include/mbgl/shaders/mtl/shader_group.hpp index 04bed97fb..d4f5b5dbe 100644 --- a/include/mbgl/shaders/mtl/shader_group.hpp +++ b/include/mbgl/shaders/mtl/shader_group.hpp @@ -65,7 +65,7 @@ public: auto& context = static_cast(gfxContext); const auto shaderSource = std::string(shaders::prelude) + source; shader = context.createProgram( - shaderName, shaderSource, vertMain, fragMain, programParameters, additionalDefines); + ShaderID, shaderName, shaderSource, vertMain, fragMain, programParameters, additionalDefines); assert(shader); if (!shader || !registerShader(shader, shaderName)) { assert(false); diff --git a/include/mbgl/shaders/vulkan/shader_group.hpp b/include/mbgl/shaders/vulkan/shader_group.hpp index 751f28432..1363ab35e 100644 --- a/include/mbgl/shaders/vulkan/shader_group.hpp +++ b/include/mbgl/shaders/vulkan/shader_group.hpp @@ -61,7 +61,7 @@ public: addAdditionalDefines(propertiesAsUniforms, additionalDefines); auto& context = static_cast(gfxContext); - shader = context.createProgram(shaderName, vert, frag, programParameters, additionalDefines); + shader = context.createProgram(ShaderID, shaderName, vert, frag, programParameters, additionalDefines); assert(shader); if (!shader || !registerShader(shader, shaderName)) { assert(false); diff --git a/include/mbgl/shaders/vulkan/shader_program.hpp b/include/mbgl/shaders/vulkan/shader_program.hpp index e4f911a27..4cd59471d 100644 --- a/include/mbgl/shaders/vulkan/shader_program.hpp +++ b/include/mbgl/shaders/vulkan/shader_program.hpp @@ -51,12 +51,14 @@ using UniqueShaderProgram = std::unique_ptr; class ShaderProgram final : public gfx::ShaderProgramBase { public: - ShaderProgram(const std::string& name, + ShaderProgram(shaders::BuiltIn shaderID, + const std::string& name, const std::string_view& vertex, const std::string_view& fragment, const ProgramParameters& programParameters, const mbgl::unordered_map& additionalDefines, - RendererBackend& backend); + RendererBackend& backend, + gfx::ContextObserver& observer); ~ShaderProgram() noexcept override; static constexpr std::string_view Name{"GenericVulkanShader"}; diff --git a/src/mbgl/style/sprite.hpp b/include/mbgl/style/sprite.hpp similarity index 100% rename from src/mbgl/style/sprite.hpp rename to include/mbgl/style/sprite.hpp diff --git a/src/mbgl/text/glyph.hpp b/include/mbgl/text/glyph.hpp similarity index 100% rename from src/mbgl/text/glyph.hpp rename to include/mbgl/text/glyph.hpp diff --git a/src/mbgl/text/glyph_range.hpp b/include/mbgl/text/glyph_range.hpp similarity index 100% rename from src/mbgl/text/glyph_range.hpp rename to include/mbgl/text/glyph_range.hpp diff --git a/include/mbgl/tile/tile_operation.hpp b/include/mbgl/tile/tile_operation.hpp new file mode 100644 index 000000000..b98a045cf --- /dev/null +++ b/include/mbgl/tile/tile_operation.hpp @@ -0,0 +1,16 @@ +#pragma once +#include + +namespace mbgl { +enum class TileOperation : uint8_t { + RequestedFromCache, ///< A read request from the cache + RequestedFromNetwork, ///< A read request from the online source + LoadFromNetwork, ///< Tile data from the network has been retrieved + LoadFromCache, ///< Tile data from the cache has been retrieved + StartParse, ///< Background processing of tile data has been initiated + EndParse, ///< Background processing of tile data has been completed + Error, ///< An error occurred while loading the tile + Cancelled, ///< Loading of a tile was cancelled + NullOp, ///< No operation has taken place +}; +} // namespace mbgl diff --git a/src/mbgl/util/hash.hpp b/include/mbgl/util/hash.hpp similarity index 100% rename from src/mbgl/util/hash.hpp rename to include/mbgl/util/hash.hpp diff --git a/src/mbgl/util/rect.hpp b/include/mbgl/util/rect.hpp similarity index 100% rename from src/mbgl/util/rect.hpp rename to include/mbgl/util/rect.hpp diff --git a/include/mbgl/vulkan/context.hpp b/include/mbgl/vulkan/context.hpp index 6001879e1..93669b6e1 100644 --- a/include/mbgl/vulkan/context.hpp +++ b/include/mbgl/vulkan/context.hpp @@ -60,7 +60,8 @@ public: BufferResource createBuffer(const void* data, std::size_t size, std::uint32_t usage, bool persistent) const; - UniqueShaderProgram createProgram(std::string name, + UniqueShaderProgram createProgram(shaders::BuiltIn shaderID, + std::string name, const std::string_view vertex, const std::string_view fragment, const ProgramParameters& programParameters, diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index d7bb0755e..26a3693bd 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -4,6 +4,8 @@ ### ✨ Features and improvements +- Added new map observer events: onPreCompileShader, onPostCompileShader, onShaderCompileFailed, onGlyphsLoaded, onGlyphsError, onGlyphsRequested, onTileAction, onSpriteLoaded, onSpriteError, onSpriteRequested ([#2694](https://github.com/maplibre/maplibre-native/pull/2694)). + ### 🐞 Bug fixes ## 11.2.0 diff --git a/platform/android/MapLibreAndroid/src/cpp/CMakeLists.txt b/platform/android/MapLibreAndroid/src/cpp/CMakeLists.txt index 8ca6e97b8..b5161bf2a 100644 --- a/platform/android/MapLibreAndroid/src/cpp/CMakeLists.txt +++ b/platform/android/MapLibreAndroid/src/cpp/CMakeLists.txt @@ -189,7 +189,8 @@ add_library(maplibre SHARED ${PROJECT_SOURCE_DIR}/style/transition_options.hpp ${PROJECT_SOURCE_DIR}/style/value.cpp ${PROJECT_SOURCE_DIR}/style/value.hpp - ${PROJECT_SOURCE_DIR}/util/default_style.cpp + ${PROJECT_SOURCE_DIR}/tile/tile_operation.cpp + ${PROJECT_SOURCE_DIR}/util/default_style.cpp ${PROJECT_SOURCE_DIR}/util/tile_server_options.cpp ${PROJECT_SOURCE_DIR}/util/default_style.hpp ${PROJECT_SOURCE_DIR}/util/tile_server_options.hpp) diff --git a/platform/android/MapLibreAndroid/src/cpp/android_renderer_frontend.cpp b/platform/android/MapLibreAndroid/src/cpp/android_renderer_frontend.cpp index 87977dd73..0f5ddb662 100644 --- a/platform/android/MapLibreAndroid/src/cpp/android_renderer_frontend.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/android_renderer_frontend.cpp @@ -1,5 +1,6 @@ #include "android_renderer_frontend.hpp" +#include #include #include #include @@ -52,6 +53,40 @@ public: delegate.invoke(&RendererObserver::onRemoveUnusedStyleImages, ids); } + void onPreCompileShader(mbgl::shaders::BuiltIn id, + mbgl::gfx::Backend::Type type, + const std::string& additionalDefines) override { + delegate.invoke(&RendererObserver::onPreCompileShader, id, type, additionalDefines); + } + + void onPostCompileShader(mbgl::shaders::BuiltIn id, + mbgl::gfx::Backend::Type type, + const std::string& additionalDefines) override { + delegate.invoke(&RendererObserver::onPostCompileShader, id, type, additionalDefines); + } + + void onShaderCompileFailed(mbgl::shaders::BuiltIn id, + mbgl::gfx::Backend::Type type, + const std::string& additionalDefines) override { + delegate.invoke(&RendererObserver::onShaderCompileFailed, id, type, additionalDefines); + } + + void onGlyphsLoaded(const mbgl::FontStack& stack, const mbgl::GlyphRange& range) override { + delegate.invoke(&RendererObserver::onGlyphsLoaded, stack, range); + } + + void onGlyphsError(const mbgl::FontStack& stack, const mbgl::GlyphRange& range, std::exception_ptr ex) override { + delegate.invoke(&RendererObserver::onGlyphsError, stack, range, ex); + } + + void onGlyphsRequested(const mbgl::FontStack& stack, const mbgl::GlyphRange& range) override { + delegate.invoke(&RendererObserver::onGlyphsRequested, stack, range); + } + + void onTileAction(TileOperation op, const OverscaledTileID& id, const std::string& sourceID) override { + delegate.invoke(&RendererObserver::onTileAction, op, id, sourceID); + } + private: std::shared_ptr mailbox; ActorRef delegate; diff --git a/platform/android/MapLibreAndroid/src/cpp/jni_native.cpp b/platform/android/MapLibreAndroid/src/cpp/jni_native.cpp index 638c722cf..e8d7041e2 100644 --- a/platform/android/MapLibreAndroid/src/cpp/jni_native.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/jni_native.cpp @@ -56,6 +56,7 @@ #include "i18n/number_format_jni.hpp" #include "logger.hpp" #include "text/local_glyph_rasterizer_jni.hpp" +#include "tile/tile_operation.hpp" namespace mbgl { namespace android { @@ -158,6 +159,9 @@ void registerNatives(JavaVM* vm) { StringUtils::registerNative(env); NumberFormat::registerNative(env); + // Tile + TileOperation::registerNative(env); + // Logger Logger::registerNative(env); diff --git a/platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp b/platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp index a86de91c3..082443b98 100644 --- a/platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp @@ -53,6 +53,7 @@ #include "map_renderer.hpp" #include "run_loop_impl.hpp" #include "style/light.hpp" +#include "tile/tile_operation.hpp" namespace mbgl { namespace android { @@ -1341,5 +1342,199 @@ void NativeMapView::registerNative(jni::JNIEnv& env) { METHOD(&NativeMapView::triggerRepaint, "nativeTriggerRepaint")); } +void NativeMapView::onRegisterShaders(gfx::ShaderRegistry&) {}; + +// Shader compilation +void NativeMapView::onPreCompileShader(shaders::BuiltIn id, + gfx::Backend::Type type, + const std::string& additionalDefines) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onPreCompileShader = javaClass.GetMethod(*_env, + "onPreCompileShader"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, + onPreCompileShader, + static_cast(id), + static_cast(type), + jni::Make(*_env, additionalDefines)); + } +} + +void NativeMapView::onPostCompileShader(shaders::BuiltIn id, + gfx::Backend::Type type, + const std::string& additionalDefines) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onPostCompileShader = javaClass.GetMethod( + *_env, "onPostCompileShader"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, + onPostCompileShader, + static_cast(id), + static_cast(type), + jni::Make(*_env, additionalDefines)); + } +} + +void NativeMapView::onShaderCompileFailed(shaders::BuiltIn id, + gfx::Backend::Type type, + const std::string& additionalDefines) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onShaderCompileFailed = javaClass.GetMethod( + *_env, "onShaderCompileFailed"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, + onShaderCompileFailed, + static_cast(id), + static_cast(type), + jni::Make(*_env, additionalDefines)); + } +} + +// Glyph requests +void NativeMapView::onGlyphsLoaded(const FontStack& stack, const GlyphRange& range) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onGlyphsLoaded = javaClass.GetMethod, jni::jint, jni::jint)>( + *_env, "onGlyphsLoaded"); + + auto fontStack = jni::Array::New(*_env, stack.size()); + for (std::size_t i = 0; i < stack.size(); i++) { + fontStack.Set(*_env, i, jni::Make(*_env, stack.at(i))); + } + + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onGlyphsLoaded, fontStack, range.first, range.second); + } +} + +void NativeMapView::onGlyphsError(const FontStack& stack, const GlyphRange& range, std::exception_ptr) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onGlyphsError = javaClass.GetMethod, jni::jint, jni::jint)>( + *_env, "onGlyphsError"); + + auto fontStack = jni::Array::New(*_env, stack.size()); + for (std::size_t i = 0; i < stack.size(); i++) { + fontStack.Set(*_env, i, jni::Make(*_env, stack.at(i))); + } + + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onGlyphsError, fontStack, range.first, range.second); + } +} + +void NativeMapView::onGlyphsRequested(const FontStack& stack, const GlyphRange& range) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onGlyphsRequested = javaClass.GetMethod, jni::jint, jni::jint)>( + *_env, "onGlyphsRequested"); + + auto fontStack = jni::Array::New(*_env, stack.size()); + for (std::size_t i = 0; i < stack.size(); i++) { + fontStack.Set(*_env, i, jni::Make(*_env, stack.at(i))); + } + + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onGlyphsRequested, fontStack, range.first, range.second); + } +} + +// Tile requests +void NativeMapView::onTileAction(mbgl::TileOperation op, const OverscaledTileID& id, const std::string& sourceID) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onTileAction = javaClass.GetMethod, jni::jint, jni::jint, jni::jint, jni::jint, jni::jint, jni::String)>( + *_env, "onTileAction"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, + onTileAction, + mbgl::android::TileOperation::Create(*_env, op), + static_cast(id.canonical.x), + static_cast(id.canonical.y), + static_cast(id.canonical.z), + static_cast(id.wrap), + static_cast(id.overscaledZ), + jni::Make(*_env, sourceID)); + } +} + +// Sprite requests +void NativeMapView::onSpriteLoaded(const std::optional& sprite) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onSpriteLoaded = javaClass.GetMethod(*_env, "onSpriteLoaded"); + auto weakReference = javaPeer.get(*_env); + if (weakReference && sprite) { + weakReference.Call(*_env, + onSpriteLoaded, + jni::Make(*_env, sprite->id), + jni::Make(*_env, sprite->spriteURL)); + } else { + weakReference.Call(*_env, onSpriteLoaded, jni::Make(*_env, ""), jni::Make(*_env, "")); + } +} + +void NativeMapView::onSpriteError(const std::optional& sprite, std::exception_ptr ex) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onSpriteError = javaClass.GetMethod(*_env, "onSpriteError"); + auto weakReference = javaPeer.get(*_env); + if (weakReference && sprite) { + weakReference.Call(*_env, + onSpriteError, + jni::Make(*_env, sprite->id), + jni::Make(*_env, sprite->spriteURL)); + } else { + weakReference.Call(*_env, onSpriteError, jni::Make(*_env, ""), jni::Make(*_env, "")); + } +} + +void NativeMapView::onSpriteRequested(const std::optional& sprite) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onSpriteRequested = javaClass.GetMethod(*_env, "onSpriteRequested"); + auto weakReference = javaPeer.get(*_env); + if (weakReference && sprite) { + weakReference.Call(*_env, + onSpriteRequested, + jni::Make(*_env, sprite->id), + jni::Make(*_env, sprite->spriteURL)); + } else { + weakReference.Call( + *_env, onSpriteRequested, jni::Make(*_env, ""), jni::Make(*_env, "")); + } +} + } // namespace android } // namespace mbgl diff --git a/platform/android/MapLibreAndroid/src/cpp/native_map_view.hpp b/platform/android/MapLibreAndroid/src/cpp/native_map_view.hpp index 56cce5160..7ec2a4dd3 100644 --- a/platform/android/MapLibreAndroid/src/cpp/native_map_view.hpp +++ b/platform/android/MapLibreAndroid/src/cpp/native_map_view.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "annotation/marker.hpp" @@ -298,6 +299,25 @@ public: void triggerRepaint(JNIEnv&); + // Shader compilation + void onRegisterShaders(mbgl::gfx::ShaderRegistry&) override; + void onPreCompileShader(mbgl::shaders::BuiltIn, mbgl::gfx::Backend::Type, const std::string&) override; + void onPostCompileShader(mbgl::shaders::BuiltIn, mbgl::gfx::Backend::Type, const std::string&) override; + void onShaderCompileFailed(mbgl::shaders::BuiltIn, mbgl::gfx::Backend::Type, const std::string&) override; + + // Glyph requests + void onGlyphsLoaded(const mbgl::FontStack&, const mbgl::GlyphRange&) override; + void onGlyphsError(const mbgl::FontStack&, const mbgl::GlyphRange&, std::exception_ptr) override; + void onGlyphsRequested(const mbgl::FontStack&, const mbgl::GlyphRange&) override; + + // Tile requests + void onTileAction(mbgl::TileOperation, const mbgl::OverscaledTileID&, const std::string&) override; + + // Sprite requests + void onSpriteLoaded(const std::optional&) override; + void onSpriteError(const std::optional&, std::exception_ptr) override; + void onSpriteRequested(const std::optional&) override; + private: std::unique_ptr rendererFrontend; diff --git a/platform/android/MapLibreAndroid/src/cpp/tile/tile_operation.cpp b/platform/android/MapLibreAndroid/src/cpp/tile/tile_operation.cpp new file mode 100644 index 000000000..728c6a371 --- /dev/null +++ b/platform/android/MapLibreAndroid/src/cpp/tile/tile_operation.cpp @@ -0,0 +1,35 @@ +#include "tile_operation.hpp" + +namespace mbgl { +namespace android { + +jni::Local> TileOperation::Create(jni::JNIEnv& env, mbgl::TileOperation op) { + static auto& _class = jni::Class::Singleton(env); + switch (op) { + case mbgl::TileOperation::RequestedFromNetwork: + return _class.Get(env, _class.GetStaticField>(env, "RequestedFromNetwork")); + case mbgl::TileOperation::RequestedFromCache: + return _class.Get(env, _class.GetStaticField>(env, "RequestedFromCache")); + case mbgl::TileOperation::LoadFromCache: + return _class.Get(env, _class.GetStaticField>(env, "LoadFromCache")); + case mbgl::TileOperation::LoadFromNetwork: + return _class.Get(env, _class.GetStaticField>(env, "LoadFromNetwork")); + case mbgl::TileOperation::StartParse: + return _class.Get(env, _class.GetStaticField>(env, "StartParse")); + case mbgl::TileOperation::EndParse: + return _class.Get(env, _class.GetStaticField>(env, "EndParse")); + case mbgl::TileOperation::Error: + return _class.Get(env, _class.GetStaticField>(env, "Error")); + case mbgl::TileOperation::Cancelled: + return _class.Get(env, _class.GetStaticField>(env, "Cancelled")); + case mbgl::TileOperation::NullOp: + return _class.Get(env, _class.GetStaticField>(env, "NullOp")); + } +} + +void TileOperation::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/MapLibreAndroid/src/cpp/tile/tile_operation.hpp b/platform/android/MapLibreAndroid/src/cpp/tile/tile_operation.hpp new file mode 100644 index 000000000..f82de4ac1 --- /dev/null +++ b/platform/android/MapLibreAndroid/src/cpp/tile/tile_operation.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace android { + +class TileOperation { +public: + static constexpr auto Name() { return "org/maplibre/android/tile/TileOperation"; }; + + static jni::Local> Create(jni::JNIEnv&, mbgl::TileOperation); + + static void registerNative(jni::JNIEnv& env); +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapChangeReceiver.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapChangeReceiver.java index 7807c3d32..68091dadb 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapChangeReceiver.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapChangeReceiver.java @@ -1,6 +1,7 @@ package org.maplibre.android.maps; import org.maplibre.android.log.Logger; +import org.maplibre.android.tile.TileOperation; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -35,6 +36,26 @@ class MapChangeReceiver implements NativeMapView.StateCallback { = new CopyOnWriteArrayList<>(); private final List onCanRemoveUnusedStyleImageListenerList = new CopyOnWriteArrayList<>(); + private final List onPreCompileShaderList + = new CopyOnWriteArrayList<>(); + private final List onPostCompileShaderList + = new CopyOnWriteArrayList<>(); + private final List onShaderCompileFailedList + = new CopyOnWriteArrayList<>(); + private final List onGlyphsLoadedList + = new CopyOnWriteArrayList<>(); + private final List onGlyphsErrorList + = new CopyOnWriteArrayList<>(); + private final List onGlyphsRequestedList + = new CopyOnWriteArrayList<>(); + private final List onTileActionList + = new CopyOnWriteArrayList<>(); + private final List onSpriteLoadedList + = new CopyOnWriteArrayList<>(); + private final List onSpriteErrorList + = new CopyOnWriteArrayList<>(); + private final List onSpriteRequestedList + = new CopyOnWriteArrayList<>(); @Override public void onCameraWillChange(boolean animated) { @@ -255,6 +276,146 @@ class MapChangeReceiver implements NativeMapView.StateCallback { return true; } + @Override + public void onPreCompileShader(int id, int type, String additionalDefines) { + try { + if (!onPreCompileShaderList.isEmpty()) { + for (MapView.OnPreCompileShaderListener listener : onPreCompileShaderList) { + listener.onPreCompileShader(id, type, additionalDefines); + } + } + } catch (Throwable err) { + Logger.e(TAG, "Exception in onPreCompileShader", err); + throw err; + } + } + + @Override + public void onPostCompileShader(int id, int type, String additionalDefines) { + try { + if (!onPostCompileShaderList.isEmpty()) { + for (MapView.OnPostCompileShaderListener listener : onPostCompileShaderList) { + listener.onPostCompileShader(id, type, additionalDefines); + } + } + } catch (Throwable err) { + Logger.e(TAG, "Exception in onPostCompileShader", err); + throw err; + } + } + + @Override + public void onShaderCompileFailed(int id, int type, String additionalDefines) { + try { + if (!onShaderCompileFailedList.isEmpty()) { + for (MapView.OnShaderCompileFailedListener listener : onShaderCompileFailedList) { + listener.onShaderCompileFailed(id, type, additionalDefines); + } + } + } catch (Throwable err) { + Logger.e(TAG, "Exception in onShaderCompileFailed", err); + throw err; + } + } + + @Override + public void onGlyphsLoaded(String[] stack, int rangeStart, int rangeEnd) { + try { + if (!onGlyphsLoadedList.isEmpty()) { + for (MapView.OnGlyphsLoadedListener listener : onGlyphsLoadedList) { + listener.onGlyphsLoaded(stack, rangeStart, rangeEnd); + } + } + } catch (Throwable err) { + Logger.e(TAG, "Exception in onGlyphsLoaded", err); + throw err; + } + } + + @Override + public void onGlyphsError(String[] stack, int rangeStart, int rangeEnd) { + try { + if (!onGlyphsErrorList.isEmpty()) { + for (MapView.OnGlyphsErrorListener listener : onGlyphsErrorList) { + listener.onGlyphsError(stack, rangeStart, rangeEnd); + } + } + } catch (Throwable err) { + Logger.e(TAG, "Exception in onGlyphsError", err); + throw err; + } + } + + @Override + public void onGlyphsRequested(String[] stack, int rangeStart, int rangeEnd) { + try { + if (!onGlyphsRequestedList.isEmpty()) { + for (MapView.OnGlyphsRequestedListener listener : onGlyphsRequestedList) { + listener.onGlyphsRequested(stack, rangeStart, rangeEnd); + } + } + } catch (Throwable err) { + Logger.e(TAG, "Exception in onGlyphsRequested", err); + throw err; + } + } + + @Override + public void onTileAction(TileOperation op, int x, int y, int z, int wrap, int overscaledZ, String sourceID) { + try { + if (!onTileActionList.isEmpty()) { + for (MapView.OnTileActionListener listener : onTileActionList) { + listener.onTileAction(op, x, y, z, wrap, overscaledZ, sourceID); + } + } + } catch (Throwable err) { + Logger.e(TAG, "Exception in onTileAction", err); + throw err; + } + } + + @Override + public void onSpriteLoaded(String id, String url) { + try { + if (!onSpriteLoadedList.isEmpty()) { + for (MapView.OnSpriteLoadedListener listener : onSpriteLoadedList) { + listener.onSpriteLoaded(id, url); + } + } + } catch (Throwable err) { + Logger.e(TAG, "Exception in onSpriteLoaded", err); + throw err; + } + } + + @Override + public void onSpriteError(String id, String url) { + try { + if (!onSpriteErrorList.isEmpty()) { + for (MapView.OnSpriteErrorListener listener : onSpriteErrorList) { + listener.onSpriteError(id, url); + } + } + } catch (Throwable err) { + Logger.e(TAG, "Exception in onSpriteError", err); + throw err; + } + } + + @Override + public void onSpriteRequested(String id, String url) { + try { + if (!onSpriteRequestedList.isEmpty()) { + for (MapView.OnSpriteRequestedListener listener : onSpriteRequestedList) { + listener.onSpriteRequested(id, url); + } + } + } catch (Throwable err) { + Logger.e(TAG, "Exception in onSpriteRequested", err); + throw err; + } + } + void addOnCameraWillChangeListener(MapView.OnCameraWillChangeListener listener) { onCameraWillChangeListenerList.add(listener); } @@ -375,6 +536,86 @@ class MapChangeReceiver implements NativeMapView.StateCallback { onCanRemoveUnusedStyleImageListenerList.remove(listener); } + public void addOnPreCompileShaderListener(MapView.OnPreCompileShaderListener callback) { + onPreCompileShaderList.add(callback); + } + + public void addOnPostCompileShaderListener(MapView.OnPostCompileShaderListener callback) { + onPostCompileShaderList.add(callback); + } + + public void addOnShaderCompileFailedListener(MapView.OnShaderCompileFailedListener callback) { + onShaderCompileFailedList.add(callback); + } + + public void addOnGlyphsLoadedListener(MapView.OnGlyphsLoadedListener callback) { + onGlyphsLoadedList.add(callback); + } + + public void addOnGlyphsErrorListener(MapView.OnGlyphsErrorListener callback) { + onGlyphsErrorList.add(callback); + } + + public void addOnGlyphsRequestedListener(MapView.OnGlyphsRequestedListener callback) { + onGlyphsRequestedList.add(callback); + } + + public void addOnTileActionListener(MapView.OnTileActionListener callback) { + onTileActionList.add(callback); + } + + public void addOnSpriteLoadedListener(MapView.OnSpriteLoadedListener callback) { + onSpriteLoadedList.add(callback); + } + + public void addOnSpriteErrorListener(MapView.OnSpriteErrorListener callback) { + onSpriteErrorList.add(callback); + } + + public void addOnSpriteRequestedListener(MapView.OnSpriteRequestedListener callback) { + onSpriteRequestedList.add(callback); + } + + public void removeOnPreCompileShaderListener(MapView.OnPreCompileShaderListener callback) { + onPreCompileShaderList.remove(callback); + } + + public void removeOnPostCompileShaderListener(MapView.OnPostCompileShaderListener callback) { + onPostCompileShaderList.remove(callback); + } + + public void removeOnShaderCompileFailedListener(MapView.OnShaderCompileFailedListener callback) { + onShaderCompileFailedList.remove(callback); + } + + public void removeOnGlyphsLoadedListener(MapView.OnGlyphsLoadedListener callback) { + onGlyphsLoadedList.remove(callback); + } + + public void removeOnGlyphsErrorListener(MapView.OnGlyphsErrorListener callback) { + onGlyphsErrorList.remove(callback); + } + + public void removeOnGlyphsRequestedListener(MapView.OnGlyphsRequestedListener callback) { + onGlyphsRequestedList.remove(callback); + } + + public void removeOnTileActionListener(MapView.OnTileActionListener callback) { + onTileActionList.remove(callback); + } + + public void removeOnSpriteLoadedListener(MapView.OnSpriteLoadedListener callback) { + onSpriteLoadedList.remove(callback); + } + + public void removeOnSpriteErrorListener(MapView.OnSpriteErrorListener callback) { + onSpriteErrorList.remove(callback); + } + + public void removeOnSpriteRequestedListener(MapView.OnSpriteRequestedListener callback) { + onSpriteRequestedList.remove(callback); + } + void clear() { onCameraWillChangeListenerList.clear(); onCameraIsChangingListenerList.clear(); @@ -391,5 +632,15 @@ class MapChangeReceiver implements NativeMapView.StateCallback { onSourceChangedListenerList.clear(); onStyleImageMissingListenerList.clear(); onCanRemoveUnusedStyleImageListenerList.clear(); + onPreCompileShaderList.clear(); + onPostCompileShaderList.clear(); + onShaderCompileFailedList.clear(); + onGlyphsLoadedList.clear(); + onGlyphsErrorList.clear(); + onGlyphsRequestedList.clear(); + onTileActionList.clear(); + onSpriteLoadedList.clear(); + onSpriteErrorList.clear(); + onSpriteRequestedList.clear(); } } \ No newline at end of file diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java index 58b883fda..bedfb0577 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java @@ -37,6 +37,7 @@ import org.maplibre.android.maps.widgets.CompassView; import org.maplibre.android.net.ConnectivityReceiver; import org.maplibre.android.storage.FileSource; import org.maplibre.android.utils.BitmapUtils; +import org.maplibre.android.tile.TileOperation; import java.util.ArrayList; import java.util.Iterator; @@ -906,6 +907,186 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { mapChangeReceiver.removeOnCanRemoveUnusedStyleImageListener(listener); } + /** + * Set a callback that's invoked before a shader is compiled. + * + * @param listener The callback that's invoked before a shader is compiled + */ + public void addOnPreCompileShaderListener(MapView.OnPreCompileShaderListener callback) { + mapChangeReceiver.addOnPreCompileShaderListener(callback); + } + + /** + * Removes a callback that's invoked before a shader is compiled. + * + * @param listener The callback that's invoked before a shader is compiled + */ + public void removeOnPreCompileShaderListener(MapView.OnPreCompileShaderListener callback) { + mapChangeReceiver.removeOnPreCompileShaderListener(callback); + } + + /** + * Set a callback that's invoked after a shader is compiled. + * + * @param listener The callback that's invoked after a shader is compiled + */ + public void addOnPostCompileShaderListener(MapView.OnPostCompileShaderListener callback) { + mapChangeReceiver.addOnPostCompileShaderListener(callback); + } + + /** + * Removes a callback that's invoked after a shader is compiled. + * + * @param listener The callback that's invoked after a shader is compiled + */ + public void removeOnPostCompileShaderListener(MapView.OnPostCompileShaderListener callback) { + mapChangeReceiver.removeOnPostCompileShaderListener(callback); + } + + /** + * Set a callback that's invoked after a shader failed to compile. + * + * @param listener The callback that's invoked after a shader failes to compile + */ + public void addOnShaderCompileFailedListener(MapView.OnShaderCompileFailedListener callback) { + mapChangeReceiver.addOnShaderCompileFailedListener(callback); + } + + /** + * Removes a callback that's invoked after a shader failed to compile. + * + * @param listener The callback that's invoked after a shader failes to compile + */ + public void removeOnShaderCompileFailedListener(MapView.OnShaderCompileFailedListener callback) { + mapChangeReceiver.removeOnShaderCompileFailedListener(callback); + } + + /** + * Set a callback that's invoked after a range of glyphs are loaded. + * + * @param listener The callback that's invoked after a range of glyphs are loaded + */ + public void addOnGlyphsLoadedListener(MapView.OnGlyphsLoadedListener callback) { + mapChangeReceiver.addOnGlyphsLoadedListener(callback); + } + + /** + * Removes a callback that's invoked after a range of glyphs are loaded. + * + * @param listener The callback that's invoked after a range of glyphs are loaded + */ + public void removeOnGlyphsLoadedListener(MapView.OnGlyphsLoadedListener callback) { + mapChangeReceiver.removeOnGlyphsLoadedListener(callback); + } + + /** + * Set a callback that's invoked after a range of glyphs fail to load. + * + * @param listener The callback that's invoked after a range of glyphs fail to load + */ + public void addOnGlyphsErrorListener(MapView.OnGlyphsErrorListener callback) { + mapChangeReceiver.addOnGlyphsErrorListener(callback); + } + + /** + * Removes a callback that's invoked after a range of glyphs fail to load. + * + * @param listener The callback that's invoked after a range of glyphs fail to load + */ + public void removeOnGlyphsErrorListener(MapView.OnGlyphsErrorListener callback) { + mapChangeReceiver.removeOnGlyphsErrorListener(callback); + } + + /** + * Set a callback that's invoked after a range of glyphs are requested. + * + * @param listener The callback that's invoked after a range of glyphs are requested + */ + public void addOnGlyphsRequestedListener(MapView.OnGlyphsRequestedListener callback) { + mapChangeReceiver.addOnGlyphsRequestedListener(callback); + } + + /** + * Removes a callback that's invoked after a range of glyphs are requested. + * + * @param listener The callback that's invoked after a range of glyphs are requested + */ + public void removeOnGlyphsRequestedListener(MapView.OnGlyphsRequestedListener callback) { + mapChangeReceiver.removeOnGlyphsRequestedListener(callback); + } + + /** + * Set a callback that's invoked after a tile action occurs. + * + * @param listener The callback that's invoked after a tile action occurs + */ + public void addOnTileActionListener(MapView.OnTileActionListener callback) { + mapChangeReceiver.addOnTileActionListener(callback); + } + + /** + * Remove's a callback that's invoked after a tile action occurs. + * + * @param listener The callback that's invoked after a tile action occurs + */ + public void removeOnTileActionListener(MapView.OnTileActionListener callback) { + mapChangeReceiver.removeOnTileActionListener(callback); + } + + /** + * Set a callback that's invoked after a sprite is loaded. + * + * @param listener The callback that's invoked after a sprite is loaded + */ + public void addOnSpriteLoadedListener(MapView.OnSpriteLoadedListener callback) { + mapChangeReceiver.addOnSpriteLoadedListener(callback); + } + + /** + * Removes a callback that's invoked after a sprite is loaded. + * + * @param listener The callback that's invoked after a sprite is loaded + */ + public void removeOnSpriteLoadedListener(MapView.OnSpriteLoadedListener callback) { + mapChangeReceiver.removeOnSpriteLoadedListener(callback); + } + + /** + * Set a callback that's invoked after a sprite fails to laod. + * + * @param listener The callback that's invoked after a sprite fails to load + */ + public void addOnSpriteErrorListener(MapView.OnSpriteErrorListener callback) { + mapChangeReceiver.addOnSpriteErrorListener(callback); + } + + /** + * Removes a callback that's invoked after a sprite fails to laod. + * + * @param listener The callback that's invoked after a sprite fails to load + */ + public void removeOnSpriteErrorListener(MapView.OnSpriteErrorListener callback) { + mapChangeReceiver.removeOnSpriteErrorListener(callback); + } + + /** + * Set a callback that's invoked after a sprite is requested. + * + * @param listener The callback that's invoked after a sprite is requested + */ + public void addOnSpriteRequestedListener(MapView.OnSpriteRequestedListener callback) { + mapChangeReceiver.addOnSpriteRequestedListener(callback); + } + + /** + * Removes a callback that's invoked after a sprite is requested. + * + * @param listener The callback that's invoked after a sprite is requested + */ + public void removeOnSpriteRequestedListener(MapView.OnSpriteRequestedListener callback) { + mapChangeReceiver.removeOnSpriteRequestedListener(callback); + } + /** * Interface definition for a callback to be invoked when the camera will change. *

@@ -1123,6 +1304,183 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { boolean onCanRemoveUnusedStyleImage(@NonNull String id); } + /** + * Interface definition for a callback to be invoked before a shader is compiled. + *

+ * {@link MapView#addOnPreCompileShaderListener(OnPreCompileShaderListener)} + *

+ */ + public interface OnPreCompileShaderListener { + /** + * Called before a shader is compiled. + * + * @param id of a shader type enumeration. See `mbgl::shaders::BuiltIn` for a list + * of possible values. + * @param type of graphics backend the shader is being compiled for. See + * `mbgl::gfx::Backend::Type` for a list of possible values. + * @param additionalDefines that specify the permutaion of the shader. + */ + void onPreCompileShader(int id, int type, String additionalDefines); + } + + /** + * Interface definition for a callback to be invoked after a shader is compiled. + *

+ * {@link MapView#addOnPostCompileShaderListener(OnPostCompileShaderListener)} + *

+ */ + public interface OnPostCompileShaderListener { + /** + * Called after a shader is compiled. + * + * @param id of a shader type enumeration. See `mbgl::shaders::BuiltIn` for a list + * of possible values. + * @param type of graphics backend the shader is being compiled for. See + * `mbgl::gfx::Backend::Type` for a list of possible values. + * @param additionalDefines that specify the permutation of the shader. + */ + void onPostCompileShader(int id, int type, String additionalDefines); + } + + /** + * Interface definition for a callback to be invoked after a shader failed to compile. + *

+ * {@link MapView#addOnShaderCompileFailedListener(OnShaderCompileFailedListener)} + *

+ */ + public interface OnShaderCompileFailedListener { + /** + * Called when a shader fails to compile. + * + * @param id of a shader type enumeration. See `mbgl::shaders::BuiltIn` for a list + * of possible values. + * @param type of graphics backend the shader is being compiled for. See + * `mbgl::gfx::Backend::Type` for a list of possible values. + * @param additionalDefines that specify the permutation of the shader. + */ + void onShaderCompileFailed(int id, int type, String additionalDefines); + } + + /** + * Interface definition for a callback to be invoked after a range of glyphs are loaded. + *

+ * {@link MapView#addOnGlyphsLoadedListener(OnGlyphsLoadedListener)} + *

+ */ + public interface OnGlyphsLoadedListener { + /** + * Called when a range of glyphs for a font stack are loaded. + * + * @param stack of font names. + * @param rangeStart of glyph indices being loaded. + * @param rangeEnd of glyph indices being loaded. + */ + void onGlyphsLoaded(@NonNull String[] stack, int rangeStart, int rangeEnd); + } + + /** + * Interface definition for a callback to be invoked after a range of glyphs fail to load. + *

+ * {@link MapView#addOnGlyphsErrorListener(OnGlyphsErrorListener)} + *

+ */ + public interface OnGlyphsErrorListener { + /** + * Called when a range of glyphs for a font stack failed to load. + * + * @param stack of font names. + * @param rangeStart of glyph indices that failed to load. + * @param rangeEnd of glyph indices that failed to load. + */ + void onGlyphsError(@NonNull String[] stack, int rangeStart, int rangeEnd); + } + + /** + * Interface definition for a callback to be invoked after a range of glyphs are requested. + *

+ * {@link MapView#addOnGlyphsRequestedListener(OnGlyphsRequestedListener)} + *

+ */ + public interface OnGlyphsRequestedListener { + /** + * Called when a range of glyphs for a font stack are requested. + * + * @param stack of font names. + * @param rangeStart of glyph indices that are being requested. + * @param rangeEnd of glyph indices that are being requested. + */ + void onGlyphsRequested(@NonNull String[] stack, int rangeStart, int rangeEnd); + } + + /** + * Interface definition for a callback to be invoked after a tile action occurs. + *

+ * {@link MapView#addOnTileActionListener(OnTileActionListener)} + *

+ */ + public interface OnTileActionListener { + /** + * Called when a tile action occurs. + * + * @param op identifying the tile action that occurred. + * @param x coordinate of the tile. + * @param y coordinate of the tile. + * @param z coordinate of the tile. + * @param wrap coordinate of the tile. + * @param overscaledZ coordinate of the tile. + * @param sourceID of the tile. + */ + void onTileAction(TileOperation op, int x, int y, int z, int wrap, int overscaledZ, String sourceID); + } + + /** + * Interface definition for a callback to be invoked after a sprite is requested. + *

+ * {@link MapView#addOnSpriteLoadedListener(OnSpriteLoadedListener)} + *

+ */ + public interface OnSpriteLoadedListener { + /** + * Called when a sprite is loaded. + * + * @param id of the sprite. + * @param url of the sprite. + */ + void onSpriteLoaded(@NonNull String id, @NonNull String url); + } + + /** + * Interface definition for a callback to be invoked after a sprite fails to load. + *

+ * {@link MapView#addOnSpriteErrorListener(OnSpriteErrorListener)} + *

+ */ + public interface OnSpriteErrorListener { + /** + * Called when a sprite fails to load. + * + * @param id of the sprite. + * @param url of the sprite. + */ + void onSpriteError(@NonNull String id, @NonNull String url); + } + + /** + * Interface definition for a callback to be invoked after a sprite is requested. + *

+ * {@link MapView#addOnSpriteRequestedListener(OnSpriteRequestedListener)} + *

+ */ + public interface OnSpriteRequestedListener { + /** + * Called when a sprite is requested. + * + * @param id of the sprite. + * @param url of the sprite. + */ + void onSpriteRequested(@NonNull String id, @NonNull String url); + } + /** * Sets a callback object which will be triggered when the {@link MapLibreMap} instance is ready to be used. * diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapView.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapView.java index 18879be31..09797905e 100755 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapView.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapView.java @@ -37,6 +37,7 @@ import org.maplibre.android.style.light.Light; import org.maplibre.android.style.sources.CannotAddSourceException; import org.maplibre.android.style.sources.Source; import org.maplibre.android.utils.BitmapUtils; +import org.maplibre.android.tile.TileOperation; import java.util.ArrayList; import java.util.Arrays; @@ -1194,6 +1195,76 @@ final class NativeMapView implements NativeMap { } } + @Keep + private void onPreCompileShader(int id, int type, String additionalDefines) { + if (stateCallback != null) { + stateCallback.onPreCompileShader(id, type, additionalDefines); + } + } + + @Keep + private void onPostCompileShader(int id, int type, String additionalDefines) { + if (stateCallback != null) { + stateCallback.onPostCompileShader(id, type, additionalDefines); + } + } + + @Keep + private void onShaderCompileFailed(int id, int type, String additionalDefines) { + if (stateCallback != null) { + stateCallback.onShaderCompileFailed(id, type, additionalDefines); + } + } + + @Keep + private void onGlyphsLoaded(String[] stack, int rangeStart, int rangeEnd) { + if (stateCallback != null) { + stateCallback.onGlyphsLoaded(stack, rangeStart, rangeEnd); + } + } + + @Keep + private void onGlyphsError(String[] stack, int rangeStart, int rangeEnd) { + if (stateCallback != null) { + stateCallback.onGlyphsError(stack, rangeStart, rangeEnd); + } + } + + @Keep + private void onGlyphsRequested(String[] stack, int rangeStart, int rangeEnd) { + if (stateCallback != null) { + stateCallback.onGlyphsRequested(stack, rangeStart, rangeEnd); + } + } + + @Keep + private void onTileAction(TileOperation op, int x, int y, int z, int wrap, int overscaledZ, String sourceID) { + if (stateCallback != null) { + stateCallback.onTileAction(op, x, y, z, wrap, overscaledZ, sourceID); + } + } + + @Keep + private void onSpriteLoaded(String id, String url) { + if (stateCallback != null) { + stateCallback.onSpriteLoaded(id, url); + } + } + + @Keep + private void onSpriteError(String id, String url) { + if (stateCallback != null) { + stateCallback.onSpriteError(id, url); + } + } + + @Keep + private void onSpriteRequested(String id, String url) { + if (stateCallback != null) { + stateCallback.onSpriteRequested(id, url); + } + } + // // JNI methods // @@ -1621,5 +1692,25 @@ final class NativeMapView implements NativeMap { void onStyleImageMissing(String imageId); boolean onCanRemoveUnusedStyleImage(String imageId); + + void onPreCompileShader(int id, int type, String additionalDefines); + + void onPostCompileShader(int id, int type, String additionalDefines); + + void onShaderCompileFailed(int id, int type, String additionalDefines); + + void onGlyphsLoaded(String[] stack, int rangeStart, int rangeEnd); + + void onGlyphsError(String[] stack, int rangeStart, int rangeEnd); + + void onGlyphsRequested(String[] stack, int rangeStart, int rangeEnd); + + void onTileAction(TileOperation op, int x, int y, int z, int wrap, int overscaledZ, String sourceID); + + void onSpriteLoaded(String id, String url); + + void onSpriteError(String id, String url); + + void onSpriteRequested(String id, String url); } } diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/tile/TileOperation.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/tile/TileOperation.java new file mode 100644 index 000000000..393e77592 --- /dev/null +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/tile/TileOperation.java @@ -0,0 +1,13 @@ +package org.maplibre.android.tile; + +public enum TileOperation { + RequestedFromCache, + RequestedFromNetwork, + LoadFromNetwork, + LoadFromCache, + StartParse, + EndParse, + Error, + Cancelled, + NullOp, +} diff --git a/platform/android/MapLibreAndroidTestApp/proguard-rules.pro b/platform/android/MapLibreAndroidTestApp/proguard-rules.pro index 6440487da..99ce15590 100644 --- a/platform/android/MapLibreAndroidTestApp/proguard-rules.pro +++ b/platform/android/MapLibreAndroidTestApp/proguard-rules.pro @@ -11,7 +11,7 @@ # GMS -dontnote com.google.android.gms.** --keep class com.mapbox.mapboxsdk.testapp.model.customlayer.ExampleCustomLayer { *; } +-keep class org.maplibre.android.testapp.model.customlayer.ExampleCustomLayer { *; } # okhttp -dontwarn org.bouncycastle.jsse.BCSSLSocket diff --git a/platform/android/MapLibreAndroidTestApp/src/main/AndroidManifest.xml b/platform/android/MapLibreAndroidTestApp/src/main/AndroidManifest.xml index 4ac146116..9be783948 100644 --- a/platform/android/MapLibreAndroidTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapLibreAndroidTestApp/src/main/AndroidManifest.xml @@ -874,6 +874,18 @@ + + + + = HashMap() + + public override fun onPreCompileShader(id: Int, type: Int, defines: String) { + shaderTimes["${id}-${type}-${defines}"] = TimeSource.Monotonic.markNow() + Logger.i(TAG, "A new shader is being compiled, shaderID:${id}, backend type:${type}, program configuration:${defines}") + } + + public override fun onPostCompileShader(id: Int, type: Int, defines: String) { + val startTime = shaderTimes.get("${id}-${type}-${defines}") + if (startTime != null) { + Logger.i(TAG, "A shader has been compiled in ${startTime.elapsedNow()}, shaderID:${id}, backend type:${type}, program configuration:${defines}") + } + } + + public override fun onGlyphsRequested(fontStack: Array, rangeStart: Int, rangeEnd: Int) { + Logger.i(TAG, "Glyphs are being requested for the font stack $fontStack, ranging from $rangeStart to $rangeEnd") + } + + public override fun onGlyphsLoaded(fontStack: Array, rangeStart: Int, rangeEnd: Int) { + Logger.i(TAG, "Glyphs have been loaded for the font stack $fontStack, ranging from $rangeStart to $rangeEnd") + } + + public override fun onSpriteRequested(id: String, url: String) { + Logger.i(TAG, "The sprite $id has been requested from $url") + } + + public override fun onSpriteLoaded(id: String, url: String) { + Logger.i(TAG, "The sprite $id has been loaded from $url") + } + + public override fun onTileAction(op: TileOperation, x: Int, y: Int, z: Int, wrap: Int, overscaledZ: Int, sourceID: String) { + val tile = "X:${x}, Y:${y}, Z:${z}, Wrap:${wrap}, OverscaledZ:${overscaledZ}, SourceID:${sourceID}" + when (op) { + TileOperation.RequestedFromCache -> Logger.i(TAG, "Requesting tile ${tile} from the cache") + TileOperation.RequestedFromNetwork -> Logger.i(TAG, "Requesting tile ${tile} from the network") + TileOperation.LoadFromCache -> Logger.i(TAG, "Loading tile ${tile}, requested from the cache") + TileOperation.LoadFromNetwork -> Logger.i(TAG, "Loading tile ${tile}, requested from the network") + TileOperation.StartParse -> Logger.i(TAG, "Parsing tile ${tile}") + TileOperation.EndParse -> Logger.i(TAG, "Completed parsing tile ${tile}") + TileOperation.Error -> Logger.e(TAG, "An error occured during proccessing for tile ${tile}") + TileOperation.Cancelled -> Logger.i(TAG, "Pending work on tile ${tile} was cancelled") + TileOperation.NullOp -> Logger.e(TAG, "An unknown tile operation was emitted for tile ${tile}") + } + } + + override fun onStart() { + super.onStart() + mapView.onStart() + } + + override fun onResume() { + super.onResume() + mapView.onResume() + } + + override fun onPause() { + super.onPause() + mapView.onPause() + } + + override fun onStop() { + super.onStop() + mapView.onStop() + } + + override fun onLowMemory() { + super.onLowMemory() + mapView.onLowMemory() + } + + override fun onDestroy() { + super.onDestroy() + mapView.onDestroy() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + mapView.onSaveInstanceState(outState) + } +} diff --git a/platform/android/MapLibreAndroidTestApp/src/main/res/values/categories.xml b/platform/android/MapLibreAndroidTestApp/src/main/res/values/categories.xml index 0c25c86c3..5facc7294 100644 --- a/platform/android/MapLibreAndroidTestApp/src/main/res/values/categories.xml +++ b/platform/android/MapLibreAndroidTestApp/src/main/res/values/categories.xml @@ -13,6 +13,7 @@ Styling Features Storage + Events Texture View Location _Integration diff --git a/platform/android/MapLibreAndroidTestApp/src/main/res/values/descriptions.xml b/platform/android/MapLibreAndroidTestApp/src/main/res/values/descriptions.xml index 56c96b21d..ac42003f1 100644 --- a/platform/android/MapLibreAndroidTestApp/src/main/res/values/descriptions.xml +++ b/platform/android/MapLibreAndroidTestApp/src/main/res/values/descriptions.xml @@ -72,6 +72,7 @@ Show how to load a local style with a Snapshot Use Android SDK Animators to animate camera position changes Use Android SDK Views as symbols + Use map observers to log map event information Use TextureView to render the map Resize a map rendered on a TextureView Animate a map rendered on a TextureView diff --git a/platform/android/MapLibreAndroidTestApp/src/main/res/values/titles.xml b/platform/android/MapLibreAndroidTestApp/src/main/res/values/titles.xml index 39b55ef5a..acb099cab 100644 --- a/platform/android/MapLibreAndroidTestApp/src/main/res/values/titles.xml +++ b/platform/android/MapLibreAndroidTestApp/src/main/res/values/titles.xml @@ -68,6 +68,7 @@ Map Snapshot with local style Animator animation SymbolGenerator + Log map observer events TextureView debug TextureView resize TextureView animation diff --git a/src/mbgl/annotation/annotation_tile.cpp b/src/mbgl/annotation/annotation_tile.cpp index c52fd9382..ad3496f81 100644 --- a/src/mbgl/annotation/annotation_tile.cpp +++ b/src/mbgl/annotation/annotation_tile.cpp @@ -7,8 +7,10 @@ namespace mbgl { -AnnotationTile::AnnotationTile(const OverscaledTileID& overscaledTileID, const TileParameters& parameters) - : GeometryTile(overscaledTileID, AnnotationManager::SourceID, parameters), +AnnotationTile::AnnotationTile(const OverscaledTileID& overscaledTileID, + const TileParameters& parameters, + TileObserver* observer_) + : GeometryTile(overscaledTileID, AnnotationManager::SourceID, parameters, observer_), annotationManager(parameters.annotationManager) { auto guard = annotationManager.lock(); if (annotationManager) { diff --git a/src/mbgl/annotation/annotation_tile.hpp b/src/mbgl/annotation/annotation_tile.hpp index 5fba88501..d63e6f34d 100644 --- a/src/mbgl/annotation/annotation_tile.hpp +++ b/src/mbgl/annotation/annotation_tile.hpp @@ -11,7 +11,7 @@ class TileParameters; class AnnotationTile : public GeometryTile { public: - AnnotationTile(const OverscaledTileID&, const TileParameters&); + AnnotationTile(const OverscaledTileID&, const TileParameters&, TileObserver*); ~AnnotationTile() override; private: diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp index e84a89c41..10d74e55e 100644 --- a/src/mbgl/annotation/render_annotation_source.cpp +++ b/src/mbgl/annotation/render_annotation_source.cpp @@ -32,18 +32,19 @@ void RenderAnnotationSource::update(Immutable baseImpl_, enabled = needsRendering; - tilePyramid.update( - layers, - needsRendering, - needsRelayout, - parameters, - *baseImpl, - util::tileSize_I, - // Zoom level 16 is typically sufficient for annotations. - // See https://github.com/mapbox/mapbox-gl-native/issues/10197 - {0, 16}, - std::nullopt, - [&](const OverscaledTileID& tileID) { return std::make_unique(tileID, parameters); }); + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + *baseImpl, + util::tileSize_I, + // Zoom level 16 is typically sufficient for annotations. + // See https://github.com/mapbox/mapbox-gl-native/issues/10197 + {0, 16}, + std::nullopt, + [&](const OverscaledTileID& tileID, TileObserver* observer_) { + return std::make_unique(tileID, parameters, observer_); + }); } std::unordered_map> RenderAnnotationSource::queryRenderedFeatures( diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index bc38dd251..ae3b50cf8 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -47,6 +47,8 @@ public: std::unique_ptr createCommandEncoder() override; + gfx::ContextObserver& getObserver() const { return *observer; } + void beginFrame() override; void endFrame() override; diff --git a/src/mbgl/map/map_impl.cpp b/src/mbgl/map/map_impl.cpp index 2fdc3a9b8..6b5a6454f 100644 --- a/src/mbgl/map/map_impl.cpp +++ b/src/mbgl/map/map_impl.cpp @@ -147,6 +147,18 @@ void Map::Impl::onStyleError(std::exception_ptr error) { observer.onDidFailLoadingMap(type, description); } +void Map::Impl::onSpriteLoaded(const std::optional& sprite) { + observer.onSpriteLoaded(sprite); +} + +void Map::Impl::onSpriteError(const std::optional& sprite, std::exception_ptr ex) { + observer.onSpriteError(sprite, ex); +} + +void Map::Impl::onSpriteRequested(const std::optional& sprite) { + observer.onSpriteRequested(sprite); +} + // MARK: - Map::Impl RendererObserver void Map::Impl::onInvalidate() { @@ -232,4 +244,38 @@ void Map::Impl::onRegisterShaders(gfx::ShaderRegistry& registry) { observer.onRegisterShaders(registry); } +void Map::Impl::onPreCompileShader(shaders::BuiltIn shaderID, + gfx::Backend::Type type, + const std::string& additionalDefines) { + observer.onPreCompileShader(shaderID, type, additionalDefines); +} + +void Map::Impl::onPostCompileShader(shaders::BuiltIn shaderID, + gfx::Backend::Type type, + const std::string& additionalDefines) { + observer.onPostCompileShader(shaderID, type, additionalDefines); +} + +void Map::Impl::onShaderCompileFailed(shaders::BuiltIn shaderID, + gfx::Backend::Type type, + const std::string& additionalDefines) { + observer.onShaderCompileFailed(shaderID, type, additionalDefines); +} + +void Map::Impl::onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& ranges) { + observer.onGlyphsLoaded(fontStack, ranges); +} + +void Map::Impl::onGlyphsError(const FontStack& fontStack, const GlyphRange& ranges, std::exception_ptr ex) { + observer.onGlyphsError(fontStack, ranges, ex); +} + +void Map::Impl::onGlyphsRequested(const FontStack& fontStack, const GlyphRange& ranges) { + observer.onGlyphsRequested(fontStack, ranges); +} + +void Map::Impl::onTileAction(TileOperation op, const OverscaledTileID& id, const std::string& sourceID) { + observer.onTileAction(op, id, sourceID); +} + } // namespace mbgl diff --git a/src/mbgl/map/map_impl.hpp b/src/mbgl/map/map_impl.hpp index 2d7794f5d..d093b30f2 100644 --- a/src/mbgl/map/map_impl.hpp +++ b/src/mbgl/map/map_impl.hpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace mbgl { @@ -40,6 +41,9 @@ public: void onStyleLoading() final; void onStyleLoaded() final; void onStyleError(std::exception_ptr) final; + void onSpriteLoaded(const std::optional&) final; + void onSpriteError(const std::optional&, std::exception_ptr) final; + void onSpriteRequested(const std::optional&) final; // RendererObserver void onInvalidate() final; @@ -52,6 +56,14 @@ public: void onRemoveUnusedStyleImages(const std::vector&) final; void onRegisterShaders(gfx::ShaderRegistry&) final; + void onPreCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) final; + void onPostCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) final; + void onShaderCompileFailed(shaders::BuiltIn, gfx::Backend::Type, const std::string&) final; + void onGlyphsLoaded(const FontStack&, const GlyphRange&) final; + void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) final; + void onGlyphsRequested(const FontStack&, const GlyphRange&) final; + void onTileAction(TileOperation op, const OverscaledTileID&, const std::string&) final; + // Map void jumpTo(const CameraOptions&); diff --git a/src/mbgl/mtl/context.cpp b/src/mbgl/mtl/context.cpp index 1d79df551..2710b9cc8 100644 --- a/src/mbgl/mtl/context.cpp +++ b/src/mbgl/mtl/context.cpp @@ -87,7 +87,8 @@ BufferResource Context::createBuffer( // `NS::Object::sendMessage`, and the alignment rules are different. We assume that, because // `NS::Object` does not use virtual methods and only serves as a way for C++ code to store // and pass ObjC `id`s to `objc_msgSend`, this won't cause any real problems. -UniqueShaderProgram Context::createProgram(std::string name, +UniqueShaderProgram Context::createProgram(shaders::BuiltIn shaderID, + std::string name, const std::string_view source, const std::string_view vertexName, const std::string_view fragmentName, @@ -99,17 +100,21 @@ UniqueShaderProgram Context::createProgram(std::string name, const auto& programDefines = programParameters.getDefines(); const auto numDefines = programDefines.size() + additionalDefines.size(); + std::string defineStr; std::vector rawDefines; rawDefines.reserve(2 * numDefines); - const auto addDefine = [&rawDefines](const auto& pair) { + const auto addDefine = [&rawDefines, &defineStr](const auto& pair) { const auto* nsKey = NS::String::string(pair.first.data(), NS::UTF8StringEncoding); const auto* nsVal = NS::String::string(pair.second.data(), NS::UTF8StringEncoding); rawDefines.insert(std::next(rawDefines.begin(), rawDefines.size() / 2), nsKey); rawDefines.insert(rawDefines.end(), nsVal); + defineStr += "#define " + pair.first + " " + pair.second + "\n"; }; std::for_each(programDefines.begin(), programDefines.end(), addDefine); std::for_each(additionalDefines.begin(), additionalDefines.end(), addDefine); + observer->onPreCompileShader(shaderID, gfx::Backend::Type::Metal, defineStr); + const auto nsDefines = NS::Dictionary::dictionary( &rawDefines[numDefines], rawDefines.data(), static_cast(numDefines)); rawDefines.clear(); @@ -142,6 +147,7 @@ UniqueShaderProgram Context::createProgram(std::string name, const auto errPtr = error ? error->localizedDescription()->utf8String() : nullptr; const auto errStr = (errPtr && errPtr[0]) ? ": " + std::string(errPtr) : std::string(); Log::Error(Event::Shader, name + " compile failed" + errStr); + observer->onShaderCompileFailed(shaderID, gfx::Backend::Type::Metal, defineStr); assert(false); return nullptr; } @@ -150,6 +156,7 @@ UniqueShaderProgram Context::createProgram(std::string name, MTLFunctionPtr vertexFunction = NS::TransferPtr(library->newFunction(nsVertName)); if (!vertexFunction) { Log::Error(Event::Shader, name + " missing vertex function " + vertexName.data()); + observer->onShaderCompileFailed(shaderID, gfx::Backend::Type::Metal, defineStr); assert(false); return nullptr; } @@ -161,13 +168,17 @@ UniqueShaderProgram Context::createProgram(std::string name, fragmentFunction = NS::TransferPtr(library->newFunction(nsFragName)); if (!fragmentFunction) { Log::Error(Event::Shader, name + " missing fragment function " + fragmentName.data()); + observer->onShaderCompileFailed(shaderID, gfx::Backend::Type::Metal, defineStr); assert(false); return nullptr; } } - return std::make_unique( + auto shader = std::make_unique( std::move(name), backend, std::move(vertexFunction), std::move(fragmentFunction)); + observer->onPostCompileShader(shaderID, gfx::Backend::Type::Metal, defineStr); + + return shader; } MTLTexturePtr Context::createMetalTexture(MTLTextureDescriptorPtr textureDescriptor) const { diff --git a/src/mbgl/programs/program_parameters.cpp b/src/mbgl/programs/program_parameters.cpp index 197f14e44..d5ce6182e 100644 --- a/src/mbgl/programs/program_parameters.cpp +++ b/src/mbgl/programs/program_parameters.cpp @@ -34,6 +34,12 @@ ProgramParameters ProgramParameters::withDefaultSource(const ProgramSource& sour return params; } +ProgramParameters ProgramParameters::withProgramType(shaders::BuiltIn type) const noexcept { + ProgramParameters params = *this; + params.programType = type; + return params; +} + const std::string& ProgramParameters::getDefinesString() const { if (definesStr.empty() && !defines.empty()) { definesStr.assign(defines.size() * 32, '\0'); diff --git a/src/mbgl/programs/program_parameters.hpp b/src/mbgl/programs/program_parameters.hpp index a4936a972..3ffcb29a8 100644 --- a/src/mbgl/programs/program_parameters.hpp +++ b/src/mbgl/programs/program_parameters.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -42,6 +43,13 @@ public: /// @return Mutated ProgramParameters ProgramParameters withDefaultSource(const ProgramSource& source) const noexcept; + /// @brief Provide a `BuiltIn` value used to indicate the program type + /// @param source ProgramSource + /// @return Mutated ProgramParameters + ProgramParameters withProgramType(shaders::BuiltIn type) const noexcept; + + shaders::BuiltIn getProgramType() const noexcept { return programType; } + /// @brief Get a list of built-in shader preprocessor defines /// @return Shader source string /// @todo With the addition of future backends, defines should also be backend-aware @@ -67,6 +75,7 @@ public: private: std::unordered_map defines; std::uint64_t definesHash; + shaders::BuiltIn programType = shaders::BuiltIn::None; // cached value of `defines` converted to string format mutable std::string definesStr; diff --git a/src/mbgl/renderer/render_orchestrator.cpp b/src/mbgl/renderer/render_orchestrator.cpp index 4d9ccd952..21ae10f54 100644 --- a/src/mbgl/renderer/render_orchestrator.cpp +++ b/src/mbgl/renderer/render_orchestrator.cpp @@ -1033,6 +1033,10 @@ void RenderOrchestrator::updateDebugLayerGroups(const RenderTree& renderTree, Pa #endif // MLN_DRAWABLE_RENDERER +void RenderOrchestrator::onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& range) { + observer->onGlyphsLoaded(fontStack, range); +} + void RenderOrchestrator::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { @@ -1045,6 +1049,10 @@ void RenderOrchestrator::onGlyphsError(const FontStack& fontStack, observer->onResourceError(error); } +void RenderOrchestrator::onGlyphsRequested(const FontStack& fontStack, const GlyphRange& range) { + observer->onGlyphsRequested(fontStack, range); +} + void RenderOrchestrator::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) { MLN_TRACE_FUNC(); @@ -1060,6 +1068,13 @@ void RenderOrchestrator::onTileChanged(RenderSource&, const OverscaledTileID&) { observer->onInvalidate(); } +void RenderOrchestrator::onTileAction(RenderSource&, + TileOperation op, + const OverscaledTileID& id, + const std::string& sourceID) { + observer->onTileAction(op, id, sourceID); +} + void RenderOrchestrator::onStyleImageMissing(const std::string& id, const std::function& done) { MLN_TRACE_FUNC(); diff --git a/src/mbgl/renderer/render_orchestrator.hpp b/src/mbgl/renderer/render_orchestrator.hpp index ab5bf3df8..56ee9790e 100644 --- a/src/mbgl/renderer/render_orchestrator.hpp +++ b/src/mbgl/renderer/render_orchestrator.hpp @@ -169,11 +169,13 @@ private: const std::unordered_map&) const; // GlyphManagerObserver implementation. + void onGlyphsLoaded(const FontStack&, const GlyphRange&) override; void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override; - + void onGlyphsRequested(const FontStack&, const GlyphRange&) override; // RenderSourceObserver implementation. void onTileChanged(RenderSource&, const OverscaledTileID&) override; void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override; + void onTileAction(RenderSource&, TileOperation, const OverscaledTileID&, const std::string&) override; // ImageManagerObserver implementation void onStyleImageMissing(const std::string&, const std::function&) override; diff --git a/src/mbgl/renderer/render_source.cpp b/src/mbgl/renderer/render_source.cpp index 8c0481e38..405030a76 100644 --- a/src/mbgl/renderer/render_source.cpp +++ b/src/mbgl/renderer/render_source.cpp @@ -78,6 +78,10 @@ void RenderSource::onTileError(Tile& tile, std::exception_ptr error) { observer->onTileError(*this, tile.id, error); } +void RenderSource::onTileAction(OverscaledTileID id, std::string sourceID, TileOperation op) { + observer->onTileAction(*this, op, id, sourceID); +} + bool RenderSource::isEnabled() const { return enabled; } diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index 0482acb49..58e6d129f 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -121,6 +121,7 @@ protected: void onTileChanged(Tile&) override; void onTileError(Tile&, std::exception_ptr) final; + void onTileAction(OverscaledTileID, std::string, TileOperation) override; }; } // namespace mbgl diff --git a/src/mbgl/renderer/render_source_observer.hpp b/src/mbgl/renderer/render_source_observer.hpp index 792a18db2..6fafac31f 100644 --- a/src/mbgl/renderer/render_source_observer.hpp +++ b/src/mbgl/renderer/render_source_observer.hpp @@ -1,5 +1,7 @@ #pragma once +#include +#include #include namespace mbgl { @@ -13,6 +15,7 @@ public: virtual void onTileChanged(RenderSource&, const OverscaledTileID&) {} virtual void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) {} + virtual void onTileAction(RenderSource&, TileOperation, const OverscaledTileID&, const std::string&) {} }; } // namespace mbgl diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 3bef358d2..2c4f23a93 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -68,6 +68,24 @@ Renderer::Impl::~Impl() { assert(gfx::BackendScope::exists()); }; +void Renderer::Impl::onPreCompileShader(shaders::BuiltIn shaderID, + gfx::Backend::Type type, + const std::string& additionalDefines) { + observer->onPreCompileShader(shaderID, type, additionalDefines); +} + +void Renderer::Impl::onPostCompileShader(shaders::BuiltIn shaderID, + gfx::Backend::Type type, + const std::string& additionalDefines) { + observer->onPostCompileShader(shaderID, type, additionalDefines); +} + +void Renderer::Impl::onShaderCompileFailed(shaders::BuiltIn shaderID, + gfx::Backend::Type type, + const std::string& additionalDefines) { + observer->onShaderCompileFailed(shaderID, type, additionalDefines); +} + void Renderer::Impl::setObserver(RendererObserver* observer_) { observer = observer_ ? observer_ : &nullObserver(); } @@ -76,6 +94,8 @@ void Renderer::Impl::render(const RenderTree& renderTree, [[maybe_unused]] const std::shared_ptr& updateParameters) { MLN_TRACE_FUNC(); auto& context = backend.getContext(); + context.setObserver(this); + #if MLN_RENDER_BACKEND_METAL if constexpr (EnableMetalCapture) { const auto& mtlBackend = static_cast(backend); diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index e67c0cc52..c34de65c3 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #if MLN_RENDER_BACKEND_METAL #include @@ -21,10 +22,15 @@ class RendererBackend; class ShadeRegistry; } // namespace gfx -class Renderer::Impl { +class Renderer::Impl : public gfx::ContextObserver { public: Impl(gfx::RendererBackend&, float pixelRatio_, const std::optional& localFontFamily_); - ~Impl(); + virtual ~Impl(); + + // ContextObserver + void onPreCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) override; + void onPostCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) override; + void onShaderCompileFailed(shaders::BuiltIn, gfx::Backend::Type, const std::string&) override; private: friend class Renderer; diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp index 13377ef42..7a169cc54 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp @@ -49,9 +49,9 @@ void RenderCustomGeometrySource::update(Immutable baseImpl_ impl().getTileOptions()->tileSize, impl().getZoomRange(), {}, - [&](const OverscaledTileID& tileID) { + [&](const OverscaledTileID& tileID, TileObserver* observer_) { return std::make_unique( - tileID, impl().id, parameters, impl().getTileOptions(), *tileLoader); + tileID, impl().id, parameters, impl().getTileOptions(), *tileLoader, observer_); }); } diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index 988931840..033822540 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.cpp +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -110,8 +110,8 @@ void RenderGeoJSONSource::update(Immutable baseImpl_, util::tileSize_I, impl().getZoomRange(), std::optional{}, - [&, data_](const OverscaledTileID& tileID) { - return std::make_unique(tileID, impl().id, parameters, data_); + [&, data_](const OverscaledTileID& tileID, TileObserver* observer_) { + return std::make_unique(tileID, impl().id, parameters, data_, observer_); }); } diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.cpp b/src/mbgl/renderer/sources/render_raster_dem_source.cpp index 8cb322ea2..ef95adbd3 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.cpp @@ -27,16 +27,17 @@ void RenderRasterDEMSource::updateInternal(const Tileset& tileset, const bool needsRendering, const bool needsRelayout, const TileParameters& parameters) { - tilePyramid.update( - layers, - needsRendering, - needsRelayout, - parameters, - *baseImpl, - impl().getTileSize(), - tileset.zoomRange, - tileset.bounds, - [&](const OverscaledTileID& tileID) { return std::make_unique(tileID, parameters, tileset); }); + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + *baseImpl, + impl().getTileSize(), + tileset.zoomRange, + tileset.bounds, + [&](const OverscaledTileID& tileID, TileObserver* observer_) { + return std::make_unique(tileID, baseImpl->id, parameters, tileset, observer_); + }); algorithm::updateTileMasks(tilePyramid.getRenderedTiles()); } diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index 30bf34151..0f32e51a4 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -24,16 +24,17 @@ void RenderRasterSource::updateInternal(const Tileset& tileset, const bool needsRendering, const bool needsRelayout, const TileParameters& parameters) { - tilePyramid.update( - layers, - needsRendering, - needsRelayout, - parameters, - *baseImpl, - impl().getTileSize(), - tileset.zoomRange, - tileset.bounds, - [&](const OverscaledTileID& tileID) { return std::make_unique(tileID, parameters, tileset); }); + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + *baseImpl, + impl().getTileSize(), + tileset.zoomRange, + tileset.bounds, + [&](const OverscaledTileID& tileID, TileObserver* observer_) { + return std::make_unique(tileID, baseImpl->id, parameters, tileset, observer_); + }); algorithm::updateTileMasks(tilePyramid.getRenderedTiles()); } diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index c53447eb8..2ccaf5492 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -28,8 +28,8 @@ void RenderVectorSource::updateInternal(const Tileset& tileset, util::tileSize_I, tileset.zoomRange, tileset.bounds, - [&](const OverscaledTileID& tileID) { - return std::make_unique(tileID, baseImpl->id, parameters, tileset); + [&](const OverscaledTileID& tileID, TileObserver* observer_) { + return std::make_unique(tileID, baseImpl->id, parameters, tileset, observer_); }); } diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index 71b3951d1..4533e6f75 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -59,7 +59,7 @@ void TilePyramid::update(const std::vector>& l const uint16_t tileSize, const Range zoomRange, std::optional bounds, - std::function(const OverscaledTileID&)> createTile) { + std::function(const OverscaledTileID&, TileObserver*)> createTile) { // If we need a relayout, abandon any cached tiles; they're now stale. if (needsRelayout) { cache.clear(); @@ -172,9 +172,8 @@ void TilePyramid::update(const std::vector>& l } std::unique_ptr tile = cache.pop(tileID); if (!tile) { - tile = createTile(tileID); + tile = createTile(tileID, observer); if (!tile) return nullptr; - tile->setObserver(observer); tile->setLayers(layers); } diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index 351c81043..9432d1862 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -42,7 +42,7 @@ public: uint16_t tileSize, Range zoomRange, std::optional bounds, - std::function(const OverscaledTileID&)> createTile); + std::function(const OverscaledTileID&, TileObserver*)> createTile); const std::map>& getRenderedTiles() const { return renderedTiles; } Tile* getTile(const OverscaledTileID&); diff --git a/src/mbgl/shaders/gl/shader_program_gl.cpp b/src/mbgl/shaders/gl/shader_program_gl.cpp index 90ef0a891..f56920b09 100644 --- a/src/mbgl/shaders/gl/shader_program_gl.cpp +++ b/src/mbgl/shaders/gl/shader_program_gl.cpp @@ -118,65 +118,77 @@ std::shared_ptr ShaderProgramGL::create( const std::string& vertexSource, const std::string& fragmentSource, const std::string& additionalDefines) noexcept(false) { - // throws on compile error - auto vertProg = context.createShader( - ShaderType::Vertex, - std::initializer_list{ - "#version 300 es\n", - programParameters.getDefinesString().c_str(), - additionalDefines.c_str(), - shaders::ShaderSource::vertex, - vertexSource.c_str()}); - auto fragProg = context.createShader( - ShaderType::Fragment, - {"#version 300 es\n", - programParameters.getDefinesString().c_str(), - additionalDefines.c_str(), - shaders::ShaderSource::fragment, - fragmentSource.c_str()}); - auto program = context.createProgram(vertProg, fragProg, firstAttribName.data()); + try { + context.getObserver().onPreCompileShader( + programParameters.getProgramType(), gfx::Backend::Type::OpenGL, additionalDefines); - UniformBlockArrayGL uniformBlocks; - for (const auto& blockInfo : uniformBlocksInfo) { - GLint index = MBGL_CHECK_ERROR(glGetUniformBlockIndex(program, blockInfo.name.data())); - GLint size = 0; - MBGL_CHECK_ERROR(glGetActiveUniformBlockiv(program, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size)); - assert(size > 0); - GLint binding = static_cast(blockInfo.binding); - MBGL_CHECK_ERROR(glUniformBlockBinding(program, index, binding)); - uniformBlocks.set(blockInfo.id, binding, size); - } + // throws on compile error + auto vertProg = context.createShader( + ShaderType::Vertex, + std::initializer_list{ + "#version 300 es\n", + programParameters.getDefinesString().c_str(), + additionalDefines.c_str(), + shaders::ShaderSource::vertex, + vertexSource.c_str()}); + auto fragProg = context.createShader( + ShaderType::Fragment, + {"#version 300 es\n", + programParameters.getDefinesString().c_str(), + additionalDefines.c_str(), + shaders::ShaderSource::fragment, + fragmentSource.c_str()}); + auto program = context.createProgram(vertProg, fragProg, firstAttribName.data()); - SamplerLocationArray samplerLocations; - for (const auto& textureInfo : texturesInfo) { - GLint location = MBGL_CHECK_ERROR(glGetUniformLocation(program, textureInfo.name.data())); - assert(location != -1); - if (location != -1) { - samplerLocations[textureInfo.id] = location; + context.getObserver().onPostCompileShader( + programParameters.getProgramType(), gfx::Backend::Type::OpenGL, additionalDefines); + + UniformBlockArrayGL uniformBlocks; + for (const auto& blockInfo : uniformBlocksInfo) { + GLint index = MBGL_CHECK_ERROR(glGetUniformBlockIndex(program, blockInfo.name.data())); + GLint size = 0; + MBGL_CHECK_ERROR(glGetActiveUniformBlockiv(program, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size)); + assert(size > 0); + GLint binding = static_cast(blockInfo.binding); + MBGL_CHECK_ERROR(glUniformBlockBinding(program, index, binding)); + uniformBlocks.set(blockInfo.id, binding, size); } - } - VertexAttributeArrayGL attrs; - GLint count = 0; - GLint maxLength = 0; - MBGL_CHECK_ERROR(glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &count)); - MBGL_CHECK_ERROR(glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength)); - auto name = std::vector(maxLength); - for (GLint index = 0; index < count; ++index) { - GLsizei length = 0; // "number of characters actually written in name (excluding the null terminator)" - GLint size = 0; // "size of the attribute variable, in units of the type returned in type" - GLenum glType = 0; - MBGL_CHECK_ERROR(glGetActiveAttrib(program, index, maxLength, &length, &size, &glType, name.data())); - if (!strncmp(name.data(), "gl_", 3)) { // Is there a better way to detect built-in attributes? - continue; + SamplerLocationArray samplerLocations; + for (const auto& textureInfo : texturesInfo) { + GLint location = MBGL_CHECK_ERROR(glGetUniformLocation(program, textureInfo.name.data())); + assert(location != -1); + if (location != -1) { + samplerLocations[textureInfo.id] = location; + } } - const GLint location = MBGL_CHECK_ERROR(glGetAttribLocation(program, name.data())); - assert(attributesInfo[location].name == std::string_view(name.data())); - addAttr(attrs, attributesInfo[location].id, location, length, size, glType); - } - return std::make_shared( - std::move(program), std::move(uniformBlocks), std::move(attrs), std::move(samplerLocations)); + VertexAttributeArrayGL attrs; + GLint count = 0; + GLint maxLength = 0; + MBGL_CHECK_ERROR(glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &count)); + MBGL_CHECK_ERROR(glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength)); + auto name = std::vector(maxLength); + for (GLint index = 0; index < count; ++index) { + GLsizei length = 0; // "number of characters actually written in name (excluding the null terminator)" + GLint size = 0; // "size of the attribute variable, in units of the type returned in type" + GLenum glType = 0; + MBGL_CHECK_ERROR(glGetActiveAttrib(program, index, maxLength, &length, &size, &glType, name.data())); + if (!strncmp(name.data(), "gl_", 3)) { // Is there a better way to detect built-in attributes? + continue; + } + const GLint location = MBGL_CHECK_ERROR(glGetAttribLocation(program, name.data())); + assert(attributesInfo[location].name == std::string_view(name.data())); + addAttr(attrs, attributesInfo[location].id, location, length, size, glType); + } + + return std::make_shared( + std::move(program), std::move(uniformBlocks), std::move(attrs), std::move(samplerLocations)); + } catch (const std::exception& e) { + context.getObserver().onShaderCompileFailed( + programParameters.getProgramType(), gfx::Backend::Type::OpenGL, additionalDefines); + std::rethrow_exception(std::current_exception()); + } } } // namespace gl diff --git a/src/mbgl/shaders/vulkan/shader_program.cpp b/src/mbgl/shaders/vulkan/shader_program.cpp index f86e07e24..bfa7aee53 100644 --- a/src/mbgl/shaders/vulkan/shader_program.cpp +++ b/src/mbgl/shaders/vulkan/shader_program.cpp @@ -26,26 +26,29 @@ namespace mbgl { namespace vulkan { -ShaderProgram::ShaderProgram(const std::string& name, +ShaderProgram::ShaderProgram(shaders::BuiltIn shaderID, + const std::string& name, const std::string_view& vertex, const std::string_view& fragment, const ProgramParameters& programParameters, const mbgl::unordered_map& additionalDefines, - RendererBackend& backend_) + RendererBackend& backend_, + gfx::ContextObserver& observer) : ShaderProgramBase(), shaderName(name), backend(backend_) { + std::string defineStr = programParameters.getDefinesString() + "\n\n"; + for (const auto& define : additionalDefines) { + defineStr += "#define " + define.first + " " + define.second + "\n"; + } + observer.onPreCompileShader(shaderID, gfx::Backend::Type::Metal, defineStr); + constexpr auto targetClientVersion = glslang::EShTargetVulkan_1_0; constexpr auto targetLanguageVersion = glslang::EShTargetSpv_1_0; constexpr auto defaultVersion = 450; constexpr auto messages = EShMsgSpvRules | EShMsgVulkanRules; const auto defaultResources = GetDefaultResources(); - std::string defineStr = programParameters.getDefinesString() + "\n\n"; - for (const auto& define : additionalDefines) { - defineStr += "#define " + define.first + " " + define.second + "\n"; - } - const auto compileGlsl = [&](const EShLanguage& language, const std::string_view& data, const char* prelude) { glslang::TShader glslShader(language); @@ -61,6 +64,7 @@ ShaderProgram::ShaderProgram(const std::string& name, if (!glslShader.parse(defaultResources, defaultVersion, ENoProfile, false, true, messages)) { mbgl::Log::Error(mbgl::Event::Shader, shaderName + " - " + glslShader.getInfoLog()); + observer.onShaderCompileFailed(shaderID, gfx::Backend::Type::Vulkan, defineStr); return std::vector(); } @@ -69,6 +73,7 @@ ShaderProgram::ShaderProgram(const std::string& name, if (!glslProgram.link(messages)) { mbgl::Log::Error(mbgl::Event::Shader, shaderName + " - " + glslProgram.getInfoLog()); + observer.onShaderCompileFailed(shaderID, gfx::Backend::Type::Vulkan, defineStr); return std::vector(); } @@ -100,6 +105,8 @@ ShaderProgram::ShaderProgram(const std::string& name, backend.setDebugName(vertexShader.get(), shaderName + ".vert"); backend.setDebugName(fragmentShader.get(), shaderName + ".frag"); + + observer.onPostCompileShader(shaderID, gfx::Backend::Type::Metal, defineStr); } ShaderProgram::~ShaderProgram() noexcept = default; diff --git a/src/mbgl/sprite/sprite_loader.cpp b/src/mbgl/sprite/sprite_loader.cpp index 7feb71a42..4d927d8c4 100644 --- a/src/mbgl/sprite/sprite_loader.cpp +++ b/src/mbgl/sprite/sprite_loader.cpp @@ -40,6 +40,8 @@ void SpriteLoader::load(const std::optional sprite, FileSource& f return; } + observer->onSpriteRequested(sprite); + std::string id = sprite->id; std::string url = sprite->spriteURL; diff --git a/src/mbgl/sprite/sprite_loader_observer.hpp b/src/mbgl/sprite/sprite_loader_observer.hpp index ad7257172..09e9a7829 100644 --- a/src/mbgl/sprite/sprite_loader_observer.hpp +++ b/src/mbgl/sprite/sprite_loader_observer.hpp @@ -21,6 +21,8 @@ public: virtual void onSpriteLoaded(std::optional, std::vector>) {} virtual void onSpriteError(std::optional, std::exception_ptr) {} + + virtual void onSpriteRequested(const std::optional&) {} }; } // namespace mbgl diff --git a/src/mbgl/style/observer.hpp b/src/mbgl/style/observer.hpp index cc6378b36..aaae426ef 100644 --- a/src/mbgl/style/observer.hpp +++ b/src/mbgl/style/observer.hpp @@ -1,8 +1,10 @@ #pragma once #include +#include #include +#include namespace mbgl { namespace style { @@ -14,6 +16,10 @@ public: virtual void onUpdate() {} virtual void onStyleError(std::exception_ptr) {} virtual void onResourceError(std::exception_ptr) {} + + virtual void onSpriteLoaded(const std::optional&) {} + virtual void onSpriteError(const std::optional&, std::exception_ptr) {} + virtual void onSpriteRequested(const std::optional&) {} }; } // namespace style diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp index 36676f8eb..b42deb21c 100644 --- a/src/mbgl/style/style_impl.cpp +++ b/src/mbgl/style/style_impl.cpp @@ -386,6 +386,7 @@ void Style::Impl::onSpriteLoaded(std::optional sprite, spritesLoadingStatus["default"] = true; } observer->onUpdate(); // For *-pattern properties. + observer->onSpriteLoaded(sprite); } void Style::Impl::onSpriteError(std::optional sprite, std::exception_ptr error) { @@ -399,6 +400,11 @@ void Style::Impl::onSpriteError(std::optional sprite, std::except } // Unblock rendering tiles (even though sprite request has failed). observer->onUpdate(); + observer->onSpriteError(sprite, error); +} + +void Style::Impl::onSpriteRequested(const std::optional& sprite) { + observer->onSpriteRequested(sprite); } void Style::Impl::onLayerChanged(Layer& layer) { diff --git a/src/mbgl/style/style_impl.hpp b/src/mbgl/style/style_impl.hpp index 04591b16b..e9ab3abc5 100644 --- a/src/mbgl/style/style_impl.hpp +++ b/src/mbgl/style/style_impl.hpp @@ -118,6 +118,7 @@ private: // SpriteLoaderObserver implementation. void onSpriteLoaded(std::optional sprite, std::vector>) override; void onSpriteError(std::optional sprite, std::exception_ptr) override; + void onSpriteRequested(const std::optional&) override; // SourceObserver implementation. void onSourceLoaded(Source&) override; diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp index 3f4a4b992..949df607f 100644 --- a/src/mbgl/text/glyph_manager.cpp +++ b/src/mbgl/text/glyph_manager.cpp @@ -76,6 +76,8 @@ void GlyphManager::requestRange(GlyphRequest& request, return; } + observer->onGlyphsRequested(fontStack, range); + request.req = fileSource.request( Resource::glyphs(glyphURL, fontStack, range), [this, fontStack, range](const Response& res) { processResponse(res, fontStack, range); }); diff --git a/src/mbgl/text/glyph_manager_observer.hpp b/src/mbgl/text/glyph_manager_observer.hpp index b8678e060..649a83e41 100644 --- a/src/mbgl/text/glyph_manager_observer.hpp +++ b/src/mbgl/text/glyph_manager_observer.hpp @@ -14,6 +14,7 @@ public: virtual void onGlyphsLoaded(const FontStack&, const GlyphRange&) {} virtual void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) {} + virtual void onGlyphsRequested(const FontStack&, const GlyphRange&) {} }; } // namespace mbgl diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp index 9aa1cdf2a..81765e665 100644 --- a/src/mbgl/tile/custom_geometry_tile.cpp +++ b/src/mbgl/tile/custom_geometry_tile.cpp @@ -18,8 +18,9 @@ CustomGeometryTile::CustomGeometryTile(const OverscaledTileID& overscaledTileID, std::string sourceID_, const TileParameters& parameters, Immutable options_, - ActorRef loader_) - : GeometryTile(overscaledTileID, std::move(sourceID_), parameters), + ActorRef loader_, + TileObserver* observer_) + : GeometryTile(overscaledTileID, std::move(sourceID_), parameters, observer_), necessity(TileNecessity::Optional), options(std::move(options_)), loader(std::move(loader_)), diff --git a/src/mbgl/tile/custom_geometry_tile.hpp b/src/mbgl/tile/custom_geometry_tile.hpp index f88d62196..e2a0633b9 100644 --- a/src/mbgl/tile/custom_geometry_tile.hpp +++ b/src/mbgl/tile/custom_geometry_tile.hpp @@ -17,10 +17,11 @@ class CustomTileLoader; class CustomGeometryTile : public GeometryTile { public: CustomGeometryTile(const OverscaledTileID&, - std::string sourceID, + std::string, const TileParameters&, Immutable, - ActorRef loader); + ActorRef loader, + TileObserver* observer = nullptr); ~CustomGeometryTile() override; void setTileData(const GeoJSON& geoJSON); @@ -32,6 +33,7 @@ public: private: bool stale = true; + std::string sourceID; TileNecessity necessity; Immutable options; ActorRef loader; diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index 803d12651..7a47d80de 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -11,8 +11,9 @@ namespace mbgl { GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID, std::string sourceID_, const TileParameters& parameters, - std::shared_ptr data_) - : GeometryTile(overscaledTileID, std::move(sourceID_), parameters) { + std::shared_ptr data_, + TileObserver* observer_) + : GeometryTile(overscaledTileID, std::move(sourceID_), parameters, observer_) { updateData(std::move(data_), false /*needsRelayout*/); } diff --git a/src/mbgl/tile/geojson_tile.hpp b/src/mbgl/tile/geojson_tile.hpp index 8a3898bd2..c9f46d861 100644 --- a/src/mbgl/tile/geojson_tile.hpp +++ b/src/mbgl/tile/geojson_tile.hpp @@ -15,7 +15,8 @@ public: GeoJSONTile(const OverscaledTileID&, std::string sourceID, const TileParameters&, - std::shared_ptr); + std::shared_ptr, + TileObserver* observer = nullptr); void updateData(std::shared_ptr data, bool needsRelayout = false); diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index e1ece707e..4c31203f4 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -166,10 +166,12 @@ const LayerRenderData* GeometryTileRenderData::getLayerRenderData(const style::L that could flag the tile as non-pending too early. */ -GeometryTile::GeometryTile(const OverscaledTileID& id_, std::string sourceID_, const TileParameters& parameters) - : Tile(Kind::Geometry, id_), +GeometryTile::GeometryTile(const OverscaledTileID& id_, + std::string sourceID_, + const TileParameters& parameters, + TileObserver* observer_) + : Tile(Kind::Geometry, id_, std::move(sourceID_), observer_), ImageRequestor(parameters.imageManager), - sourceID(std::move(sourceID_)), threadPool(parameters.threadPool), mailbox(std::make_shared(*Scheduler::GetCurrent())), worker(parameters.threadPool, // Scheduler reference for the Actor retainer @@ -195,6 +197,11 @@ GeometryTile::~GeometryTile() { glyphManager->removeRequestor(*this); imageManager->removeRequestor(*this); + if (pending) { + // This tile never finished loading or was abandoned, emit a cancellation event + observer->onTileAction(id, sourceID, TileOperation::Cancelled); + } + if (layoutResult) { threadPool.runOnRenderThread( [layoutResult_{std::move(layoutResult)}, atlasTextures_{std::move(atlasTextures)}]() {}); @@ -222,6 +229,10 @@ void GeometryTile::setData(std::unique_ptr data_) { return; } + if (data_) { + observer->onTileAction(id, sourceID, TileOperation::StartParse); + } + // Mark the tile as pending again if it was complete before to prevent // signaling a complete state despite pending parse operations. pending = true; @@ -238,6 +249,8 @@ void GeometryTile::reset() { // signaling a complete state despite pending parse operations. pending = true; + observer->onTileAction(id, sourceID, TileOperation::Cancelled); + ++correlationID; worker.self().invoke(&GeometryTileWorker::reset, correlationID); } @@ -319,6 +332,10 @@ void GeometryTile::onLayout(std::shared_ptr result, const uint64_t } } } + + if (!pending) { + observer->onTileAction(id, sourceID, TileOperation::EndParse); + } } void GeometryTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) { diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index fdbf4e970..549854d46 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -30,7 +30,10 @@ class GeometryTile : public Tile, public GlyphRequestor, public ImageRequestor { public: const std::thread::id renderThreadID = std::this_thread::get_id(); - GeometryTile(const OverscaledTileID&, std::string sourceID, const TileParameters&); + GeometryTile(const OverscaledTileID&, + std::string sourceID, + const TileParameters&, + TileObserver* observer = nullptr); ~GeometryTile() override; @@ -94,8 +97,6 @@ public: void performedFadePlacement() override; std::shared_ptr getFeatureIndex() const; - const std::string sourceID; - void setFeatureState(const LayerFeatureStates&) override; protected: diff --git a/src/mbgl/tile/raster_dem_tile.cpp b/src/mbgl/tile/raster_dem_tile.cpp index a624917f7..ff5cf8b60 100644 --- a/src/mbgl/tile/raster_dem_tile.cpp +++ b/src/mbgl/tile/raster_dem_tile.cpp @@ -14,8 +14,12 @@ namespace mbgl { -RasterDEMTile::RasterDEMTile(const OverscaledTileID& id_, const TileParameters& parameters, const Tileset& tileset) - : Tile(Kind::RasterDEM, id_), +RasterDEMTile::RasterDEMTile(const OverscaledTileID& id_, + std::string sourceID_, + const TileParameters& parameters, + const Tileset& tileset, + TileObserver* observer_) + : Tile(Kind::RasterDEM, id_, std::move(sourceID_), observer_), loader(*this, id_, parameters, tileset), threadPool(parameters.threadPool), mailbox(std::make_shared(*Scheduler::GetCurrent())), @@ -35,6 +39,11 @@ RasterDEMTile::RasterDEMTile(const OverscaledTileID& id_, const TileParameters& RasterDEMTile::~RasterDEMTile() { markObsolete(); + if (pending) { + // This tile never finished loading or was abandoned, emit a cancellation event + observer->onTileAction(id, sourceID, TileOperation::Cancelled); + } + // The bucket has resources that need to be released on the render thread. if (bucket) { threadPool.runOnRenderThread([bucket_{std::move(bucket)}]() {}); @@ -59,6 +68,11 @@ void RasterDEMTile::setData(const std::shared_ptr& data) { if (!obsolete) { pending = true; ++correlationID; + + if (data) { + observer->onTileAction(id, sourceID, TileOperation::StartParse); + } + worker.self().invoke(&RasterDEMTileWorker::parse, data, correlationID, encoding); } } @@ -72,6 +86,7 @@ void RasterDEMTile::onParsed(std::unique_ptr result, const uint } renderable = static_cast(bucket); observer->onTileChanged(*this); + observer->onTileAction(id, sourceID, TileOperation::EndParse); } } diff --git a/src/mbgl/tile/raster_dem_tile.hpp b/src/mbgl/tile/raster_dem_tile.hpp index ad0e5f191..afcdef74a 100644 --- a/src/mbgl/tile/raster_dem_tile.hpp +++ b/src/mbgl/tile/raster_dem_tile.hpp @@ -61,7 +61,8 @@ class Layer; class RasterDEMTile final : public Tile { public: - RasterDEMTile(const OverscaledTileID&, const TileParameters&, const Tileset&); + RasterDEMTile( + const OverscaledTileID&, std::string, const TileParameters&, const Tileset&, TileObserver* observer = nullptr); ~RasterDEMTile() override; std::unique_ptr createRenderData() override; diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp index b9f97e698..d89af17c3 100644 --- a/src/mbgl/tile/raster_tile.cpp +++ b/src/mbgl/tile/raster_tile.cpp @@ -14,8 +14,12 @@ namespace mbgl { -RasterTile::RasterTile(const OverscaledTileID& id_, const TileParameters& parameters, const Tileset& tileset) - : Tile(Kind::Raster, id_), +RasterTile::RasterTile(const OverscaledTileID& id_, + std::string sourceID_, + const TileParameters& parameters, + const Tileset& tileset, + TileObserver* observer_) + : Tile(Kind::Raster, id_, std::move(sourceID_), observer_), loader(*this, id_, parameters, tileset), threadPool(parameters.threadPool), mailbox(std::make_shared(*Scheduler::GetCurrent())), @@ -24,6 +28,11 @@ RasterTile::RasterTile(const OverscaledTileID& id_, const TileParameters& parame RasterTile::~RasterTile() { markObsolete(); + if (pending) { + // This tile never finished loading or was abandoned, emit a cancellation event + observer->onTileAction(id, sourceID, TileOperation::Cancelled); + } + // The bucket has resources that need to be released on the render thread. if (bucket) { threadPool.runOnRenderThread([bucket_{std::move(bucket)}]() {}); @@ -48,6 +57,11 @@ void RasterTile::setData(const std::shared_ptr& data) { if (!obsolete) { pending = true; ++correlationID; + + if (data) { + observer->onTileAction(id, sourceID, TileOperation::StartParse); + } + worker.self().invoke(&RasterTileWorker::parse, data, correlationID); } } @@ -61,6 +75,7 @@ void RasterTile::onParsed(std::unique_ptr result, const uint64_t r } renderable = static_cast(bucket); observer->onTileChanged(*this); + observer->onTileAction(id, sourceID, TileOperation::EndParse); } } diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp index 544540a12..bdb4c2185 100644 --- a/src/mbgl/tile/raster_tile.hpp +++ b/src/mbgl/tile/raster_tile.hpp @@ -17,7 +17,8 @@ class Layer; class RasterTile final : public Tile { public: - RasterTile(const OverscaledTileID&, const TileParameters&, const Tileset&); + RasterTile( + const OverscaledTileID&, std::string, const TileParameters&, const Tileset&, TileObserver* observer = nullptr); ~RasterTile() override; std::unique_ptr createRenderData() override; diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp index 306a86e1c..64c722903 100644 --- a/src/mbgl/tile/tile.cpp +++ b/src/mbgl/tile/tile.cpp @@ -8,10 +8,12 @@ namespace mbgl { static TileObserver nullObserver; -Tile::Tile(Kind kind_, OverscaledTileID id_) +Tile::Tile(Kind kind_, OverscaledTileID id_, std::string sourceID_, TileObserver* observer_) : kind(kind_), id(id_), - observer(&nullObserver) {} + sourceID(std::move(sourceID_)) { + observer = observer_ ? observer_ : &nullObserver; +} Tile::~Tile() = default; @@ -61,4 +63,8 @@ float Tile::getQueryPadding(const std::unordered_map&, const SourceQueryOptions&) {} +void Tile::onTileAction(TileOperation op) { + observer->onTileAction(id, sourceID, op); +}; + } // namespace mbgl diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index 73ab8140e..a3e9853ed 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -46,7 +47,7 @@ inline bool operator==(const TileUpdateParameters& a, const TileUpdateParameters inline bool operator!=(const TileUpdateParameters& a, const TileUpdateParameters& b) { return !(a == b); } -class Tile { +class Tile : public TileLoaderObserver { public: enum class Kind : uint8_t { Geometry, @@ -56,8 +57,8 @@ public: Tile(const Tile&) = delete; Tile& operator=(const Tile&) = delete; - Tile(Kind, OverscaledTileID); - virtual ~Tile(); + Tile(Kind, OverscaledTileID, std::string, TileObserver* observer = nullptr); + ~Tile() override; virtual std::unique_ptr createRenderData() = 0; @@ -135,8 +136,12 @@ public: void dumpDebugLogs() const; + // TileLoaderObserver + void onTileAction(TileOperation op) override; + const Kind kind; OverscaledTileID id; + const std::string sourceID; std::optional modified; std::optional expires; // Indicates whether this tile is used for the currently visible layers on diff --git a/src/mbgl/tile/tile_loader.hpp b/src/mbgl/tile/tile_loader.hpp index f728a169f..64657d1f7 100644 --- a/src/mbgl/tile/tile_loader.hpp +++ b/src/mbgl/tile/tile_loader.hpp @@ -39,7 +39,7 @@ private: void makeOptional(); void loadFromCache(); - void loadedData(const Response&); + void loadedData(const Response&, Resource::LoadingMethod); void loadFromNetwork(); bool hasPendingNetworkRequest() const { diff --git a/src/mbgl/tile/tile_loader_impl.hpp b/src/mbgl/tile/tile_loader_impl.hpp index ff9315b10..10b4f5549 100644 --- a/src/mbgl/tile/tile_loader_impl.hpp +++ b/src/mbgl/tile/tile_loader_impl.hpp @@ -106,6 +106,8 @@ void TileLoader::loadFromCache() { return; } + tile.onTileAction(TileOperation::RequestedFromCache); + resource.loadingMethod = Resource::LoadingMethod::CacheOnly; request = fileSource->request(resource, [this, shared_{shared}](const Response& res) { do { @@ -128,7 +130,7 @@ void TileLoader::loadFromCache() { resource.priorEtag = res.etag; resource.priorData = res.data; } else { - loadedData(res); + loadedData(res, Resource::LoadingMethod::CacheOnly); } if (necessity == TileNecessity::Required) { @@ -157,10 +159,19 @@ void TileLoader::makeOptional() { } template -void TileLoader::loadedData(const Response& res) { +void TileLoader::loadedData(const Response& res, Resource::LoadingMethod method) { if (res.error && res.error->reason != Response::Error::Reason::NotFound) { tile.setError(std::make_exception_ptr(std::runtime_error(res.error->message))); - } else if (res.notModified) { + tile.onTileAction(TileOperation::Error); + return; + } + if (method == Resource::LoadingMethod::NetworkOnly) { + tile.onTileAction(TileOperation::LoadFromNetwork); + } else if (method == Resource::LoadingMethod::CacheOnly) { + tile.onTileAction(TileOperation::LoadFromCache); + } + + if (res.notModified) { resource.priorExpires = res.expires; // Do not notify the tile; when we get this message, it already has the // current version of the data. @@ -182,6 +193,8 @@ void TileLoader::loadFromNetwork() { return; } + tile.onTileAction(TileOperation::RequestedFromNetwork); + // Instead of using Resource::LoadingMethod::All, we're first doing a // CacheOnly, and then a NetworkOnly request. resource.loadingMethod = Resource::LoadingMethod::NetworkOnly; @@ -196,7 +209,7 @@ void TileLoader::loadFromNetwork() { if (shared_->aborted) return; request.reset(); - loadedData(res); + loadedData(res, Resource::LoadingMethod::NetworkOnly); break; } } while (!shared_->aborted); diff --git a/src/mbgl/tile/tile_loader_observer.hpp b/src/mbgl/tile/tile_loader_observer.hpp new file mode 100644 index 000000000..9a423f4b1 --- /dev/null +++ b/src/mbgl/tile/tile_loader_observer.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace mbgl { + +class Tile; + +class TileLoaderObserver { +public: + virtual ~TileLoaderObserver() = default; + + virtual void onTileAction(TileOperation) {} +}; + +} // namespace mbgl diff --git a/src/mbgl/tile/tile_observer.hpp b/src/mbgl/tile/tile_observer.hpp index 837b47ae0..4e0a11f64 100644 --- a/src/mbgl/tile/tile_observer.hpp +++ b/src/mbgl/tile/tile_observer.hpp @@ -1,5 +1,7 @@ #pragma once +#include +#include #include namespace mbgl { @@ -12,6 +14,7 @@ public: virtual void onTileChanged(Tile&) {} virtual void onTileError(Tile&, std::exception_ptr) {} + virtual void onTileAction(OverscaledTileID, std::string, TileOperation) {} }; } // namespace mbgl diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp index 989c86fb1..f39559d38 100644 --- a/src/mbgl/tile/vector_tile.cpp +++ b/src/mbgl/tile/vector_tile.cpp @@ -9,8 +9,9 @@ namespace mbgl { VectorTile::VectorTile(const OverscaledTileID& id_, std::string sourceID_, const TileParameters& parameters, - const Tileset& tileset) - : GeometryTile(id_, std::move(sourceID_), parameters), + const Tileset& tileset, + TileObserver* observer_) + : GeometryTile(id_, std::move(sourceID_), parameters, observer_), loader(*this, id_, parameters, tileset) {} VectorTile::~VectorTile() { diff --git a/src/mbgl/tile/vector_tile.hpp b/src/mbgl/tile/vector_tile.hpp index 788e61996..ab8f96281 100644 --- a/src/mbgl/tile/vector_tile.hpp +++ b/src/mbgl/tile/vector_tile.hpp @@ -10,7 +10,11 @@ class TileParameters; class VectorTile : public GeometryTile { public: - VectorTile(const OverscaledTileID&, std::string sourceID, const TileParameters&, const Tileset&); + VectorTile(const OverscaledTileID&, + std::string sourceID, + const TileParameters&, + const Tileset&, + TileObserver* observer = nullptr); ~VectorTile() override; void setNecessity(TileNecessity) final; diff --git a/src/mbgl/vulkan/context.cpp b/src/mbgl/vulkan/context.cpp index d7eab8d7c..dd22f4223 100644 --- a/src/mbgl/vulkan/context.cpp +++ b/src/mbgl/vulkan/context.cpp @@ -252,12 +252,15 @@ BufferResource Context::createBuffer(const void* data, std::size_t size, std::ui return BufferResource(const_cast(*this), data, size, usage, persistent); } -UniqueShaderProgram Context::createProgram(std::string name, +UniqueShaderProgram Context::createProgram(shaders::BuiltIn shaderID, + std::string name, const std::string_view vertex, const std::string_view fragment, const ProgramParameters& programParameters, const mbgl::unordered_map& additionalDefines) { - return std::make_unique(name, vertex, fragment, programParameters, additionalDefines, backend); + auto program = std::make_unique( + shaderID, name, vertex, fragment, programParameters, additionalDefines, backend, *observer); + return program; } gfx::UniqueDrawableBuilder Context::createDrawableBuilder(std::string name) { diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index f271ce272..5cf770c83 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -28,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -1684,3 +1687,162 @@ TEST(Map, InvalidUTF8InTile) { test.runLoop.run(); } + +TEST(Map, ObserveTileLifecycle) { + util::RunLoop runLoop; + + struct TileEntry { + OverscaledTileID id; + std::string sourceID; + TileOperation op; + }; + std::mutex tileMutex; + std::vector tileOps; + + struct ShaderEntry { + shaders::BuiltIn id; + gfx::Backend::Type type; + std::string defines; + bool isPostCompile; + }; + std::vector shaderOps; + + StubMapObserver observer; + observer.onTileActionCallback = [&](TileOperation op, const OverscaledTileID& id, const std::string& sourceID) { + if (sourceID != "mapbox") return; + std::lock_guard lock(tileMutex); + tileOps.push_back(TileEntry{id, sourceID, op}); + }; + observer.onPreCompileShaderCallback = + [&](shaders::BuiltIn id, gfx::Backend::Type type, const std::string& additionalDefines) { + shaderOps.push_back(ShaderEntry{id, type, additionalDefines, false}); + }; + observer.onPostCompileShaderCallback = + [&](shaders::BuiltIn id, gfx::Backend::Type type, const std::string& additionalDefines) { + shaderOps.push_back(ShaderEntry{id, type, additionalDefines, true}); + }; + + HeadlessFrontend frontend{{512, 512}, 1}; + MapAdapter map( + frontend, + observer, + std::make_shared( + ResourceOptions().withCachePath(":memory:").withAssetPath("test/fixtures/api/assets"), ClientOptions()), + MapOptions().withMapMode(MapMode::Static).withSize(frontend.getSize())); + + map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); + auto layer = std::make_unique("landcover", "mapbox"); + layer->setSourceLayer("landcover"); + layer->setFillColor(Color{0.0, 1.0, 0.0, 1.0}); + map.getStyle().addLayer(std::move(layer)); + map.jumpTo(CameraOptions().withCenter(LatLng{37.8, -122.5}).withZoom(10.0)); + (void)frontend.render(map); + + // We expect to see a valid shader lifecycle for every entry in this list. + const std::vector> expectedShaders = { + {shaders::BuiltIn::FillShader, 16114602744458825542ULL}, + {shaders::BuiltIn::FillOutlineShader, 16114602744458825542ULL}, + }; + + for (const auto& [id, defineHash] : expectedShaders) { + bool seenPreEvent = false; + + for (const auto& op : shaderOps) { + if (op.id != id || std::hash()(op.defines) != defineHash) { + continue; + } + + if (!seenPreEvent) { + EXPECT_EQ(op.isPostCompile, false); + seenPreEvent = true; + } else { + EXPECT_EQ(op.isPostCompile, true); + break; + } + } + } + + // We expect to see a valid lifecycle for every tile in this list. + const std::vector expectedTiles = { + {10, 0, 10, 163, 395}, {10, 0, 10, 163, 396}, {10, 0, 10, 164, 395}, {10, 0, 10, 164, 396}, + // Lower zooms can also be seen, but not always, so we + // ignore them. + }; + + for (const auto& tile : expectedTiles) { + TileOperation stage = TileOperation::NullOp; + + for (const auto& op : tileOps) { + if (op.id != tile) continue; + switch (op.op) { + case TileOperation::RequestedFromCache: { + EXPECT_EQ(stage, TileOperation::NullOp); + stage = TileOperation::RequestedFromCache; + break; + } + case TileOperation::RequestedFromNetwork: { + EXPECT_THAT( + stage, + testing::AnyOf( + TileOperation::StartParse, TileOperation::EndParse, TileOperation::RequestedFromCache)); + stage = TileOperation::RequestedFromNetwork; + break; + } + case TileOperation::LoadFromNetwork: { + EXPECT_THAT(stage, TileOperation::RequestedFromNetwork); + stage = TileOperation::LoadFromNetwork; + break; + } + case TileOperation::LoadFromCache: { + EXPECT_EQ(stage, TileOperation::RequestedFromCache); + stage = TileOperation::LoadFromCache; + break; + } + case TileOperation::StartParse: { + EXPECT_THAT(stage, testing::AnyOf(TileOperation::LoadFromNetwork, TileOperation::LoadFromCache)); + stage = TileOperation::StartParse; + break; + } + case TileOperation::Cancelled: { + EXPECT_THAT(stage, + testing::AnyOf(TileOperation::RequestedFromCache, + TileOperation::RequestedFromNetwork, + TileOperation::LoadFromNetwork, + TileOperation::LoadFromCache, + TileOperation::StartParse)); + stage = TileOperation::Cancelled; + break; + } + case TileOperation::EndParse: { + // The following are both possible: + // * RequestedFromCache -> LoadFromCache -> StartParse -> RequestedFromNetwork -> EndParse -> + // LoadFromNetwork -> StartParse -> EndParse + // The parsing stage is initiated by the cached version. The network request doesn't arrive + // until after the cached version has completed parsing. A new parsing task is spawned, resulting + // in a pair of Start and EndParse events. + // + // * RequestedFromCache -> LoadFromCache -> StartParse -> + // RequestedFromNetwork -> LoadFromNetwork -> EndParse + // The parsing stage is initiated by the cached version before being interrupted + // by data from a new version downloaded over the network. This interruption doesn't cancel the + // ongoing parsing task, so ::EndParse will end up being emitted only once by completion of the + // newer tile's data. + EXPECT_THAT(stage, + testing::AnyOf(TileOperation::StartParse, + TileOperation::LoadFromNetwork, + TileOperation::RequestedFromNetwork)); + stage = TileOperation::EndParse; + break; + } + case TileOperation::NullOp: + [[fallthrough]]; + case TileOperation::Error: { + ADD_FAILURE(); + break; + } + } + } + + EXPECT_THAT(stage, testing::AnyOf(TileOperation::EndParse, TileOperation::Cancelled)); + } +} diff --git a/test/src/mbgl/test/stub_map_observer.hpp b/test/src/mbgl/test/stub_map_observer.hpp index 6967f8d79..76972a62d 100644 --- a/test/src/mbgl/test/stub_map_observer.hpp +++ b/test/src/mbgl/test/stub_map_observer.hpp @@ -50,6 +50,50 @@ public: } } + void onPreCompileShader(shaders::BuiltIn id, gfx::Backend::Type type, const std::string& additionalDefines) final { + if (onPreCompileShaderCallback) { + onPreCompileShaderCallback(id, type, additionalDefines); + } + } + + void onPostCompileShader(shaders::BuiltIn id, gfx::Backend::Type type, const std::string& additionalDefines) final { + if (onPostCompileShaderCallback) { + onPostCompileShaderCallback(id, type, additionalDefines); + } + } + + void onShaderCompileFailed(shaders::BuiltIn id, + gfx::Backend::Type type, + const std::string& additionalDefines) final { + if (onShaderCompileFailedCallback) { + onShaderCompileFailedCallback(id, type, additionalDefines); + } + } + + void onGlyphsLoaded(const FontStack& stack, const GlyphRange& range) final { + if (onGlyphsLoadedCallback) { + onGlyphsLoadedCallback(stack, range); + } + } + + void onGlyphsError(const FontStack& stack, const GlyphRange& range, std::exception_ptr ex) final { + if (onGlyphsErrorCallback) { + onGlyphsErrorCallback(stack, range, ex); + } + } + + void onGlyphsRequested(const FontStack& stack, const GlyphRange& range) final { + if (onGlyphsRequestedCallback) { + onGlyphsRequestedCallback(stack, range); + } + } + + void onTileAction(TileOperation op, const OverscaledTileID& id, const std::string& sourceID) final { + if (onTileActionCallback) { + onTileActionCallback(op, id, sourceID); + } + } + std::function willStartLoadingMapCallback; std::function didFinishLoadingMapCallback; std::function didFailLoadingMapCallback; @@ -57,6 +101,13 @@ public: std::function didFinishRenderingFrameCallback; std::function didBecomeIdleCallback; std::function onRegisterShadersCallback; + std::function onPreCompileShaderCallback; + std::function onPostCompileShaderCallback; + std::function onShaderCompileFailedCallback; + std::function onGlyphsLoadedCallback; + std::function onGlyphsErrorCallback; + std::function onGlyphsRequestedCallback; + std::function onTileActionCallback; }; } // namespace mbgl diff --git a/test/style/source.test.cpp b/test/style/source.test.cpp index 0d33eb57a..7e1b61832 100644 --- a/test/style/source.test.cpp +++ b/test/style/source.test.cpp @@ -765,8 +765,8 @@ class FakeTileSource; class FakeTile : public Tile { public: - FakeTile(FakeTileSource& source_, const OverscaledTileID& tileID) - : Tile(Tile::Kind::Geometry, tileID), + FakeTile(FakeTileSource& source_, const OverscaledTileID& tileID, TileObserver* observer_) + : Tile(Tile::Kind::Geometry, tileID, "FakeTileSource", observer_), source(source_) { renderable = true; } @@ -802,7 +802,9 @@ public: util::tileSize_I, tileset.zoomRange, tileset.bounds, - [&](const OverscaledTileID& tileID) { return std::make_unique(*this, tileID); }); + [&](const OverscaledTileID& tileID, TileObserver* observer_) { + return std::make_unique(*this, tileID, observer_); + }); } const std::optional& getTileset() const override { diff --git a/test/tile/raster_dem_tile.test.cpp b/test/tile/raster_dem_tile.test.cpp index 98f4a9b7b..23d67190b 100644 --- a/test/tile/raster_dem_tile.test.cpp +++ b/test/tile/raster_dem_tile.test.cpp @@ -43,7 +43,7 @@ public: TEST(RasterDEMTile, setError) { RasterDEMTileTest test; - RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); + RasterDEMTile tile(OverscaledTileID(0, 0, 0), "testSource", test.tileParameters, test.tileset); tile.setError(std::make_exception_ptr(std::runtime_error("test"))); EXPECT_FALSE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); @@ -52,7 +52,7 @@ TEST(RasterDEMTile, setError) { TEST(RasterDEMTile, onError) { RasterDEMTileTest test; - RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); + RasterDEMTile tile(OverscaledTileID(0, 0, 0), "testSource", test.tileParameters, test.tileset); tile.onError(std::make_exception_ptr(std::runtime_error("test")), 0); EXPECT_FALSE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); @@ -61,7 +61,7 @@ TEST(RasterDEMTile, onError) { TEST(RasterDEMTile, onParsed) { RasterDEMTileTest test; - RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); + RasterDEMTile tile(OverscaledTileID(0, 0, 0), "testSource", test.tileParameters, test.tileset); tile.onParsed(std::make_unique(PremultipliedImage({16, 16}), Tileset::DEMEncoding::Mapbox), 0); EXPECT_TRUE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); @@ -85,7 +85,7 @@ TEST(RasterDEMTile, onParsed) { TEST(RasterDEMTile, onParsedEmpty) { RasterDEMTileTest test; - RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); + RasterDEMTile tile(OverscaledTileID(0, 0, 0), "testSource", test.tileParameters, test.tileset); tile.onParsed(nullptr, 0); EXPECT_FALSE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); diff --git a/test/tile/raster_tile.test.cpp b/test/tile/raster_tile.test.cpp index 26a239608..5f0922266 100644 --- a/test/tile/raster_tile.test.cpp +++ b/test/tile/raster_tile.test.cpp @@ -43,7 +43,7 @@ public: TEST(RasterTile, setError) { RasterTileTest test; - RasterTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); + RasterTile tile(OverscaledTileID(0, 0, 0), "testSource", test.tileParameters, test.tileset); tile.setError(std::make_exception_ptr(std::runtime_error("test"))); EXPECT_FALSE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); @@ -52,7 +52,7 @@ TEST(RasterTile, setError) { TEST(RasterTile, onError) { RasterTileTest test; - RasterTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); + RasterTile tile(OverscaledTileID(0, 0, 0), "testSource", test.tileParameters, test.tileset); tile.onError(std::make_exception_ptr(std::runtime_error("test")), 0); EXPECT_FALSE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); @@ -61,7 +61,7 @@ TEST(RasterTile, onError) { TEST(RasterTile, onParsed) { RasterTileTest test; - RasterTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); + RasterTile tile(OverscaledTileID(0, 0, 0), "testSource", test.tileParameters, test.tileset); tile.onParsed(std::make_unique(PremultipliedImage{}), 0); EXPECT_TRUE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); @@ -85,7 +85,7 @@ TEST(RasterTile, onParsed) { TEST(RasterTile, onParsedEmpty) { RasterTileTest test; - RasterTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); + RasterTile tile(OverscaledTileID(0, 0, 0), "testSource", test.tileParameters, test.tileset); tile.onParsed(nullptr, 0); EXPECT_FALSE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded());