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>
This commit is contained in:
mwilsnd
2024-09-03 17:48:03 -04:00
committed by GitHub
parent b90b2df920
commit bb34568af2
93 changed files with 1951 additions and 177 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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",

View File

@@ -3,6 +3,7 @@
#include <mbgl/actor/scheduler.hpp>
#include <mbgl/gfx/backend.hpp>
#include <mbgl/gfx/command_encoder.hpp>
#include <mbgl/gfx/context_observer.hpp>
#include <mbgl/gfx/draw_scope.hpp>
#include <mbgl/gfx/program.hpp>
#include <mbgl/gfx/renderbuffer.hpp>
@@ -51,10 +52,15 @@ using UniqueDrawableBuilder = std::unique_ptr<DrawableBuilder>;
using VertexAttributeArrayPtr = std::shared_ptr<VertexAttributeArray>;
#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<DrawScopeResource> createDrawScopeResource() = 0;
gfx::RenderingStats stats;
ContextObserver* observer;
};
} // namespace gfx

View File

@@ -0,0 +1,19 @@
#pragma once
#include <mbgl/gfx/backend.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <string>
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

View File

@@ -1,7 +1,12 @@
#pragma once
#include <mbgl/tile/tile_operation.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/style/source.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/style/sprite.hpp>
#include <mbgl/text/glyph.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <cstdint>
#include <string>
@@ -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<style::Sprite>&) {}
virtual void onSpriteError(const std::optional<style::Sprite>&, std::exception_ptr) {}
virtual void onSpriteRequested(const std::optional<style::Sprite>&) {}
};
} // namespace mbgl

View File

@@ -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,

View File

@@ -1,5 +1,12 @@
#pragma once
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/font_stack.hpp>
#include <mbgl/text/glyph_range.hpp>
#include <mbgl/tile/tile_operation.hpp>
#include <mbgl/gfx/backend.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <cstdint>
#include <exception>
#include <functional>
@@ -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

View File

@@ -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,

View File

@@ -65,7 +65,7 @@ public:
auto& context = static_cast<Context&>(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);

View File

@@ -61,7 +61,7 @@ public:
addAdditionalDefines(propertiesAsUniforms, additionalDefines);
auto& context = static_cast<Context&>(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);

View File

@@ -51,12 +51,14 @@ using UniqueShaderProgram = std::unique_ptr<ShaderProgram>;
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<std::string, std::string>& additionalDefines,
RendererBackend& backend);
RendererBackend& backend,
gfx::ContextObserver& observer);
~ShaderProgram() noexcept override;
static constexpr std::string_view Name{"GenericVulkanShader"};

View File

@@ -0,0 +1,16 @@
#pragma once
#include <cstdint>
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

View File

@@ -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,

View File

@@ -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

View File

@@ -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)

View File

@@ -1,5 +1,6 @@
#include "android_renderer_frontend.hpp"
#include <mbgl/tile/tile_operation.hpp>
#include <mbgl/actor/scheduler.hpp>
#include <mbgl/renderer/renderer.hpp>
#include <mbgl/renderer/renderer_observer.hpp>
@@ -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> mailbox;
ActorRef<RendererObserver> delegate;

View File

@@ -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);

View File

@@ -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<NativeMapView>::Singleton(*_env);
static auto onPreCompileShader = javaClass.GetMethod<void(jni::jint, jni::jint, jni::String)>(*_env,
"onPreCompileShader");
auto weakReference = javaPeer.get(*_env);
if (weakReference) {
weakReference.Call(*_env,
onPreCompileShader,
static_cast<jni::jint>(id),
static_cast<jni::jint>(type),
jni::Make<jni::String>(*_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<NativeMapView>::Singleton(*_env);
static auto onPostCompileShader = javaClass.GetMethod<void(jni::jint, jni::jint, jni::String)>(
*_env, "onPostCompileShader");
auto weakReference = javaPeer.get(*_env);
if (weakReference) {
weakReference.Call(*_env,
onPostCompileShader,
static_cast<jni::jint>(id),
static_cast<jni::jint>(type),
jni::Make<jni::String>(*_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<NativeMapView>::Singleton(*_env);
static auto onShaderCompileFailed = javaClass.GetMethod<void(jni::jint, jni::jint, jni::String)>(
*_env, "onShaderCompileFailed");
auto weakReference = javaPeer.get(*_env);
if (weakReference) {
weakReference.Call(*_env,
onShaderCompileFailed,
static_cast<jni::jint>(id),
static_cast<jni::jint>(type),
jni::Make<jni::String>(*_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<NativeMapView>::Singleton(*_env);
static auto onGlyphsLoaded = javaClass.GetMethod<void(jni::Array<jni::String>, jni::jint, jni::jint)>(
*_env, "onGlyphsLoaded");
auto fontStack = jni::Array<jni::String>::New(*_env, stack.size());
for (std::size_t i = 0; i < stack.size(); i++) {
fontStack.Set(*_env, i, jni::Make<jni::String>(*_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<NativeMapView>::Singleton(*_env);
static auto onGlyphsError = javaClass.GetMethod<void(jni::Array<jni::String>, jni::jint, jni::jint)>(
*_env, "onGlyphsError");
auto fontStack = jni::Array<jni::String>::New(*_env, stack.size());
for (std::size_t i = 0; i < stack.size(); i++) {
fontStack.Set(*_env, i, jni::Make<jni::String>(*_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<NativeMapView>::Singleton(*_env);
static auto onGlyphsRequested = javaClass.GetMethod<void(jni::Array<jni::String>, jni::jint, jni::jint)>(
*_env, "onGlyphsRequested");
auto fontStack = jni::Array<jni::String>::New(*_env, stack.size());
for (std::size_t i = 0; i < stack.size(); i++) {
fontStack.Set(*_env, i, jni::Make<jni::String>(*_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<NativeMapView>::Singleton(*_env);
static auto onTileAction = javaClass.GetMethod<void(
jni::Object<mbgl::android::TileOperation>, 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<jni::jint>(id.canonical.x),
static_cast<jni::jint>(id.canonical.y),
static_cast<jni::jint>(id.canonical.z),
static_cast<jni::jint>(id.wrap),
static_cast<jni::jint>(id.overscaledZ),
jni::Make<jni::String>(*_env, sourceID));
}
}
// Sprite requests
void NativeMapView::onSpriteLoaded(const std::optional<style::Sprite>& sprite) {
assert(vm != nullptr);
android::UniqueEnv _env = android::AttachEnv();
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
static auto onSpriteLoaded = javaClass.GetMethod<void(jni::String, jni::String)>(*_env, "onSpriteLoaded");
auto weakReference = javaPeer.get(*_env);
if (weakReference && sprite) {
weakReference.Call(*_env,
onSpriteLoaded,
jni::Make<jni::String>(*_env, sprite->id),
jni::Make<jni::String>(*_env, sprite->spriteURL));
} else {
weakReference.Call(*_env, onSpriteLoaded, jni::Make<jni::String>(*_env, ""), jni::Make<jni::String>(*_env, ""));
}
}
void NativeMapView::onSpriteError(const std::optional<style::Sprite>& sprite, std::exception_ptr ex) {
assert(vm != nullptr);
android::UniqueEnv _env = android::AttachEnv();
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
static auto onSpriteError = javaClass.GetMethod<void(jni::String, jni::String)>(*_env, "onSpriteError");
auto weakReference = javaPeer.get(*_env);
if (weakReference && sprite) {
weakReference.Call(*_env,
onSpriteError,
jni::Make<jni::String>(*_env, sprite->id),
jni::Make<jni::String>(*_env, sprite->spriteURL));
} else {
weakReference.Call(*_env, onSpriteError, jni::Make<jni::String>(*_env, ""), jni::Make<jni::String>(*_env, ""));
}
}
void NativeMapView::onSpriteRequested(const std::optional<style::Sprite>& sprite) {
assert(vm != nullptr);
android::UniqueEnv _env = android::AttachEnv();
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
static auto onSpriteRequested = javaClass.GetMethod<void(jni::String, jni::String)>(*_env, "onSpriteRequested");
auto weakReference = javaPeer.get(*_env);
if (weakReference && sprite) {
weakReference.Call(*_env,
onSpriteRequested,
jni::Make<jni::String>(*_env, sprite->id),
jni::Make<jni::String>(*_env, sprite->spriteURL));
} else {
weakReference.Call(
*_env, onSpriteRequested, jni::Make<jni::String>(*_env, ""), jni::Make<jni::String>(*_env, ""));
}
}
} // namespace android
} // namespace mbgl

View File

@@ -5,6 +5,7 @@
#include <mbgl/map/map.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/tile/tile_operation.hpp>
#include <mbgl/storage/network_status.hpp>
#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<mbgl::style::Sprite>&) override;
void onSpriteError(const std::optional<mbgl::style::Sprite>&, std::exception_ptr) override;
void onSpriteRequested(const std::optional<mbgl::style::Sprite>&) override;
private:
std::unique_ptr<AndroidRendererFrontend> rendererFrontend;

View File

@@ -0,0 +1,35 @@
#include "tile_operation.hpp"
namespace mbgl {
namespace android {
jni::Local<jni::Object<TileOperation>> TileOperation::Create(jni::JNIEnv& env, mbgl::TileOperation op) {
static auto& _class = jni::Class<mbgl::android::TileOperation>::Singleton(env);
switch (op) {
case mbgl::TileOperation::RequestedFromNetwork:
return _class.Get(env, _class.GetStaticField<jni::Object<TileOperation>>(env, "RequestedFromNetwork"));
case mbgl::TileOperation::RequestedFromCache:
return _class.Get(env, _class.GetStaticField<jni::Object<TileOperation>>(env, "RequestedFromCache"));
case mbgl::TileOperation::LoadFromCache:
return _class.Get(env, _class.GetStaticField<jni::Object<TileOperation>>(env, "LoadFromCache"));
case mbgl::TileOperation::LoadFromNetwork:
return _class.Get(env, _class.GetStaticField<jni::Object<TileOperation>>(env, "LoadFromNetwork"));
case mbgl::TileOperation::StartParse:
return _class.Get(env, _class.GetStaticField<jni::Object<TileOperation>>(env, "StartParse"));
case mbgl::TileOperation::EndParse:
return _class.Get(env, _class.GetStaticField<jni::Object<TileOperation>>(env, "EndParse"));
case mbgl::TileOperation::Error:
return _class.Get(env, _class.GetStaticField<jni::Object<TileOperation>>(env, "Error"));
case mbgl::TileOperation::Cancelled:
return _class.Get(env, _class.GetStaticField<jni::Object<TileOperation>>(env, "Cancelled"));
case mbgl::TileOperation::NullOp:
return _class.Get(env, _class.GetStaticField<jni::Object<TileOperation>>(env, "NullOp"));
}
}
void TileOperation::registerNative(jni::JNIEnv& env) {
jni::Class<TileOperation>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,20 @@
#pragma once
#include <mbgl/tile/tile_operation.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class TileOperation {
public:
static constexpr auto Name() { return "org/maplibre/android/tile/TileOperation"; };
static jni::Local<jni::Object<TileOperation>> Create(jni::JNIEnv&, mbgl::TileOperation);
static void registerNative(jni::JNIEnv& env);
};
} // namespace android
} // namespace mbgl

View File

@@ -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<MapView.OnCanRemoveUnusedStyleImageListener> onCanRemoveUnusedStyleImageListenerList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnPreCompileShaderListener> onPreCompileShaderList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnPostCompileShaderListener> onPostCompileShaderList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnShaderCompileFailedListener> onShaderCompileFailedList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnGlyphsLoadedListener> onGlyphsLoadedList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnGlyphsErrorListener> onGlyphsErrorList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnGlyphsRequestedListener> onGlyphsRequestedList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnTileActionListener> onTileActionList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnSpriteLoadedListener> onSpriteLoadedList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnSpriteErrorListener> onSpriteErrorList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnSpriteRequestedListener> 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();
}
}

View File

@@ -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.
* <p>
@@ -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.
* <p>
* {@link MapView#addOnPreCompileShaderListener(OnPreCompileShaderListener)}
* </p>
*/
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.
* <p>
* {@link MapView#addOnPostCompileShaderListener(OnPostCompileShaderListener)}
* </p>
*/
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.
* <p>
* {@link MapView#addOnShaderCompileFailedListener(OnShaderCompileFailedListener)}
* </p>
*/
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.
* <p>
* {@link MapView#addOnGlyphsLoadedListener(OnGlyphsLoadedListener)}
* </p>
*/
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.
* <p>
* {@link MapView#addOnGlyphsErrorListener(OnGlyphsErrorListener)}
* </p>
*/
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.
* <p>
* {@link MapView#addOnGlyphsRequestedListener(OnGlyphsRequestedListener)}
* </p>
*/
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.
* <p>
* {@link MapView#addOnTileActionListener(OnTileActionListener)}
* </p>
*/
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.
* <p>
* {@link MapView#addOnSpriteLoadedListener(OnSpriteLoadedListener)}
* </p>
*/
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.
* <p>
* {@link MapView#addOnSpriteErrorListener(OnSpriteErrorListener)}
* </p>
*/
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.
* <p>
* {@link MapView#addOnSpriteRequestedListener(OnSpriteRequestedListener)}
* </p>
*/
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.
*

View File

@@ -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);
}
}

View File

@@ -0,0 +1,13 @@
package org.maplibre.android.tile;
public enum TileOperation {
RequestedFromCache,
RequestedFromNetwork,
LoadFromNetwork,
LoadFromCache,
StartParse,
EndParse,
Error,
Cancelled,
NullOp,
}

View File

@@ -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

View File

@@ -874,6 +874,18 @@
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity" />
</activity> <!-- Events -->
<activity
android:name="org.maplibre.android.testapp.activity.events.ObserverActivity"
android:description="@string/description_observer_activity"
android:exported="true"
android:label="@string/activity_observer_activity" >
<meta-data
android:name="@string/category"
android:value="@string/category_events" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity" />
</activity> <!-- TextureView -->
<activity
android:name=".activity.textureview.TextureViewDebugModeActivity"

View File

@@ -0,0 +1,137 @@
package org.maplibre.android.testapp.activity.events
import android.app.ActivityManager
import android.os.Build
import android.os.Bundle
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import org.maplibre.android.MapLibre
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.Style
import org.maplibre.android.tile.TileOperation
import org.maplibre.android.testapp.R
import org.maplibre.android.testapp.styles.TestStyles
import timber.log.Timber
import java.util.*
import kotlin.time.TimeMark
import kotlin.time.TimeSource
import kotlin.time.TimeSource.Monotonic
import org.maplibre.android.log.Logger
import org.maplibre.android.log.Logger.INFO
/**
* Test activity showcasing logging observer actions from the core
*/
class ObserverActivity : AppCompatActivity(),
MapView.OnPreCompileShaderListener,
MapView.OnPostCompileShaderListener,
MapView.OnTileActionListener,
MapView.OnGlyphsLoadedListener,
MapView.OnGlyphsRequestedListener,
MapView.OnSpriteLoadedListener,
MapView.OnSpriteRequestedListener {
private lateinit var mapView: MapView
companion object {
const val TAG = "ObserverActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map_simple)
mapView = findViewById(R.id.mapView)
mapView.onCreate(savedInstanceState)
mapView.addOnPreCompileShaderListener(this)
mapView.addOnPostCompileShaderListener(this)
mapView.addOnTileActionListener(this)
mapView.addOnGlyphsLoadedListener(this)
mapView.addOnGlyphsRequestedListener(this)
mapView.addOnSpriteLoadedListener(this)
mapView.addOnSpriteRequestedListener(this)
mapView.getMapAsync {
it.setStyle(
Style.Builder().fromUri(TestStyles.getPredefinedStyleWithFallback("Streets"))
)
}
}
private val shaderTimes: HashMap<String, TimeMark> = 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<String>, 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<String>, 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)
}
}

View File

@@ -13,6 +13,7 @@
<string name="category_style">Styling</string>
<string name="category_features">Features</string>
<string name="category_storage">Storage</string>
<string name="category_events">Events</string>
<string name="category_textureview">Texture View</string>
<string name="category_location">Location</string>
<string name="category_integration">_Integration</string>

View File

@@ -72,6 +72,7 @@
<string name="description_map_snapshotter_local_style">Show how to load a local style with a Snapshot</string>
<string name="description_camera_animator">Use Android SDK Animators to animate camera position changes</string>
<string name="description_symbol_generator">Use Android SDK Views as symbols</string>
<string name="description_observer_activity">Use map observers to log map event information</string>
<string name="description_textureview_debug">Use TextureView to render the map</string>
<string name="description_textureview_resize">Resize a map rendered on a TextureView</string>
<string name="description_textureview_animate">Animate a map rendered on a TextureView</string>

View File

@@ -68,6 +68,7 @@
<string name="activity_map_snapshotter_local_style">Map Snapshot with local style</string>
<string name="activity_camera_animator">Animator animation</string>
<string name="activity_symbol_generator">SymbolGenerator</string>
<string name="activity_observer_activity">Log map observer events</string>
<string name="activity_textureview_debug">TextureView debug</string>
<string name="activity_textureview_resize">TextureView resize</string>
<string name="activity_textureview_animate">TextureView animation</string>

View File

@@ -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) {

View File

@@ -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:

View File

@@ -32,18 +32,19 @@ void RenderAnnotationSource::update(Immutable<style::Source::Impl> 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<AnnotationTile>(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<AnnotationTile>(tileID, parameters, observer_);
});
}
std::unordered_map<std::string, std::vector<Feature>> RenderAnnotationSource::queryRenderedFeatures(

View File

@@ -47,6 +47,8 @@ public:
std::unique_ptr<gfx::CommandEncoder> createCommandEncoder() override;
gfx::ContextObserver& getObserver() const { return *observer; }
void beginFrame() override;
void endFrame() override;

View File

@@ -147,6 +147,18 @@ void Map::Impl::onStyleError(std::exception_ptr error) {
observer.onDidFailLoadingMap(type, description);
}
void Map::Impl::onSpriteLoaded(const std::optional<style::Sprite>& sprite) {
observer.onSpriteLoaded(sprite);
}
void Map::Impl::onSpriteError(const std::optional<style::Sprite>& sprite, std::exception_ptr ex) {
observer.onSpriteError(sprite, ex);
}
void Map::Impl::onSpriteRequested(const std::optional<style::Sprite>& 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

View File

@@ -12,6 +12,7 @@
#include <mbgl/style/source.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/util/size.hpp>
#include <mbgl/tile/tile_operation.hpp>
namespace mbgl {
@@ -40,6 +41,9 @@ public:
void onStyleLoading() final;
void onStyleLoaded() final;
void onStyleError(std::exception_ptr) final;
void onSpriteLoaded(const std::optional<style::Sprite>&) final;
void onSpriteError(const std::optional<style::Sprite>&, std::exception_ptr) final;
void onSpriteRequested(const std::optional<style::Sprite>&) final;
// RendererObserver
void onInvalidate() final;
@@ -52,6 +56,14 @@ public:
void onRemoveUnusedStyleImages(const std::vector<std::string>&) 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&);

View File

@@ -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<const NS::Object*> 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<NS::UInteger>(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<ShaderProgram>(
auto shader = std::make_unique<ShaderProgram>(
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 {

View File

@@ -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');

View File

@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/gfx/backend.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <array>
#include <cassert>
@@ -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<std::string, std::string> defines;
std::uint64_t definesHash;
shaders::BuiltIn programType = shaders::BuiltIn::None;
// cached value of `defines` converted to string format
mutable std::string definesStr;

View File

@@ -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<void()>& done) {
MLN_TRACE_FUNC();

View File

@@ -169,11 +169,13 @@ private:
const std::unordered_map<std::string, const RenderLayer*>&) 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<void()>&) override;

View File

@@ -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;
}

View File

@@ -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

View File

@@ -1,5 +1,7 @@
#pragma once
#include <mbgl/tile/tile_operation.hpp>
#include <string>
#include <exception>
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

View File

@@ -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>& updateParameters) {
MLN_TRACE_FUNC();
auto& context = backend.getContext();
context.setObserver(this);
#if MLN_RENDER_BACKEND_METAL
if constexpr (EnableMetalCapture) {
const auto& mtlBackend = static_cast<mtl::RendererBackend&>(backend);

View File

@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/renderer/render_orchestrator.hpp>
#include <mbgl/gfx/context_observer.hpp>
#if MLN_RENDER_BACKEND_METAL
#include <mbgl/mtl/mtl_fwd.hpp>
@@ -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<std::string>& 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;

View File

@@ -49,9 +49,9 @@ void RenderCustomGeometrySource::update(Immutable<style::Source::Impl> baseImpl_
impl().getTileOptions()->tileSize,
impl().getZoomRange(),
{},
[&](const OverscaledTileID& tileID) {
[&](const OverscaledTileID& tileID, TileObserver* observer_) {
return std::make_unique<CustomGeometryTile>(
tileID, impl().id, parameters, impl().getTileOptions(), *tileLoader);
tileID, impl().id, parameters, impl().getTileOptions(), *tileLoader, observer_);
});
}

View File

@@ -110,8 +110,8 @@ void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_,
util::tileSize_I,
impl().getZoomRange(),
std::optional<LatLngBounds>{},
[&, data_](const OverscaledTileID& tileID) {
return std::make_unique<GeoJSONTile>(tileID, impl().id, parameters, data_);
[&, data_](const OverscaledTileID& tileID, TileObserver* observer_) {
return std::make_unique<GeoJSONTile>(tileID, impl().id, parameters, data_, observer_);
});
}

View File

@@ -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<RasterDEMTile>(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<RasterDEMTile>(tileID, baseImpl->id, parameters, tileset, observer_);
});
algorithm::updateTileMasks(tilePyramid.getRenderedTiles());
}

View File

@@ -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<RasterTile>(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<RasterTile>(tileID, baseImpl->id, parameters, tileset, observer_);
});
algorithm::updateTileMasks(tilePyramid.getRenderedTiles());
}

View File

@@ -28,8 +28,8 @@ void RenderVectorSource::updateInternal(const Tileset& tileset,
util::tileSize_I,
tileset.zoomRange,
tileset.bounds,
[&](const OverscaledTileID& tileID) {
return std::make_unique<VectorTile>(tileID, baseImpl->id, parameters, tileset);
[&](const OverscaledTileID& tileID, TileObserver* observer_) {
return std::make_unique<VectorTile>(tileID, baseImpl->id, parameters, tileset, observer_);
});
}

View File

@@ -59,7 +59,7 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
const uint16_t tileSize,
const Range<uint8_t> zoomRange,
std::optional<LatLngBounds> bounds,
std::function<std::unique_ptr<Tile>(const OverscaledTileID&)> createTile) {
std::function<std::unique_ptr<Tile>(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<Immutable<style::LayerProperties>>& l
}
std::unique_ptr<Tile> tile = cache.pop(tileID);
if (!tile) {
tile = createTile(tileID);
tile = createTile(tileID, observer);
if (!tile) return nullptr;
tile->setObserver(observer);
tile->setLayers(layers);
}

View File

@@ -42,7 +42,7 @@ public:
uint16_t tileSize,
Range<uint8_t> zoomRange,
std::optional<LatLngBounds> bounds,
std::function<std::unique_ptr<Tile>(const OverscaledTileID&)> createTile);
std::function<std::unique_ptr<Tile>(const OverscaledTileID&, TileObserver*)> createTile);
const std::map<UnwrappedTileID, std::reference_wrapper<Tile>>& getRenderedTiles() const { return renderedTiles; }
Tile* getTile(const OverscaledTileID&);

View File

@@ -118,65 +118,77 @@ std::shared_ptr<ShaderProgramGL> 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<const char*>{
"#version 300 es\n",
programParameters.getDefinesString().c_str(),
additionalDefines.c_str(),
shaders::ShaderSource<shaders::BuiltIn::Prelude, gfx::Backend::Type::OpenGL>::vertex,
vertexSource.c_str()});
auto fragProg = context.createShader(
ShaderType::Fragment,
{"#version 300 es\n",
programParameters.getDefinesString().c_str(),
additionalDefines.c_str(),
shaders::ShaderSource<shaders::BuiltIn::Prelude, gfx::Backend::Type::OpenGL>::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<GLint>(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<const char*>{
"#version 300 es\n",
programParameters.getDefinesString().c_str(),
additionalDefines.c_str(),
shaders::ShaderSource<shaders::BuiltIn::Prelude, gfx::Backend::Type::OpenGL>::vertex,
vertexSource.c_str()});
auto fragProg = context.createShader(
ShaderType::Fragment,
{"#version 300 es\n",
programParameters.getDefinesString().c_str(),
additionalDefines.c_str(),
shaders::ShaderSource<shaders::BuiltIn::Prelude, gfx::Backend::Type::OpenGL>::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<GLint>(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<GLchar>(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<ShaderProgramGL>(
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<GLchar>(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<ShaderProgramGL>(
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

View File

@@ -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<std::string, std::string>& 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<uint32_t>();
}
@@ -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<uint32_t>();
}
@@ -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;

View File

@@ -40,6 +40,8 @@ void SpriteLoader::load(const std::optional<style::Sprite> sprite, FileSource& f
return;
}
observer->onSpriteRequested(sprite);
std::string id = sprite->id;
std::string url = sprite->spriteURL;

View File

@@ -21,6 +21,8 @@ public:
virtual void onSpriteLoaded(std::optional<style::Sprite>, std::vector<Immutable<style::Image::Impl>>) {}
virtual void onSpriteError(std::optional<style::Sprite>, std::exception_ptr) {}
virtual void onSpriteRequested(const std::optional<style::Sprite>&) {}
};
} // namespace mbgl

View File

@@ -1,8 +1,10 @@
#pragma once
#include <mbgl/style/source_observer.hpp>
#include <mbgl/style/sprite.hpp>
#include <exception>
#include <optional>
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<Sprite>&) {}
virtual void onSpriteError(const std::optional<Sprite>&, std::exception_ptr) {}
virtual void onSpriteRequested(const std::optional<Sprite>&) {}
};
} // namespace style

View File

@@ -386,6 +386,7 @@ void Style::Impl::onSpriteLoaded(std::optional<style::Sprite> sprite,
spritesLoadingStatus["default"] = true;
}
observer->onUpdate(); // For *-pattern properties.
observer->onSpriteLoaded(sprite);
}
void Style::Impl::onSpriteError(std::optional<style::Sprite> sprite, std::exception_ptr error) {
@@ -399,6 +400,11 @@ void Style::Impl::onSpriteError(std::optional<style::Sprite> 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<style::Sprite>& sprite) {
observer->onSpriteRequested(sprite);
}
void Style::Impl::onLayerChanged(Layer& layer) {

View File

@@ -118,6 +118,7 @@ private:
// SpriteLoaderObserver implementation.
void onSpriteLoaded(std::optional<style::Sprite> sprite, std::vector<Immutable<style::Image::Impl>>) override;
void onSpriteError(std::optional<style::Sprite> sprite, std::exception_ptr) override;
void onSpriteRequested(const std::optional<style::Sprite>&) override;
// SourceObserver implementation.
void onSourceLoaded(Source&) override;

View File

@@ -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); });

View File

@@ -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

View File

@@ -18,8 +18,9 @@ CustomGeometryTile::CustomGeometryTile(const OverscaledTileID& overscaledTileID,
std::string sourceID_,
const TileParameters& parameters,
Immutable<style::CustomGeometrySource::TileOptions> options_,
ActorRef<style::CustomTileLoader> loader_)
: GeometryTile(overscaledTileID, std::move(sourceID_), parameters),
ActorRef<style::CustomTileLoader> loader_,
TileObserver* observer_)
: GeometryTile(overscaledTileID, std::move(sourceID_), parameters, observer_),
necessity(TileNecessity::Optional),
options(std::move(options_)),
loader(std::move(loader_)),

View File

@@ -17,10 +17,11 @@ class CustomTileLoader;
class CustomGeometryTile : public GeometryTile {
public:
CustomGeometryTile(const OverscaledTileID&,
std::string sourceID,
std::string,
const TileParameters&,
Immutable<style::CustomGeometrySource::TileOptions>,
ActorRef<style::CustomTileLoader> loader);
ActorRef<style::CustomTileLoader> 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<style::CustomGeometrySource::TileOptions> options;
ActorRef<style::CustomTileLoader> loader;

View File

@@ -11,8 +11,9 @@ namespace mbgl {
GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID,
std::string sourceID_,
const TileParameters& parameters,
std::shared_ptr<style::GeoJSONData> data_)
: GeometryTile(overscaledTileID, std::move(sourceID_), parameters) {
std::shared_ptr<style::GeoJSONData> data_,
TileObserver* observer_)
: GeometryTile(overscaledTileID, std::move(sourceID_), parameters, observer_) {
updateData(std::move(data_), false /*needsRelayout*/);
}

View File

@@ -15,7 +15,8 @@ public:
GeoJSONTile(const OverscaledTileID&,
std::string sourceID,
const TileParameters&,
std::shared_ptr<style::GeoJSONData>);
std::shared_ptr<style::GeoJSONData>,
TileObserver* observer = nullptr);
void updateData(std::shared_ptr<style::GeoJSONData> data, bool needsRelayout = false);

View File

@@ -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<Mailbox>(*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<const GeometryTileData> 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<LayoutResult> result, const uint64_t
}
}
}
if (!pending) {
observer->onTileAction(id, sourceID, TileOperation::EndParse);
}
}
void GeometryTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) {

View File

@@ -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<FeatureIndex> getFeatureIndex() const;
const std::string sourceID;
void setFeatureState(const LayerFeatureStates&) override;
protected:

View File

@@ -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<Mailbox>(*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<const std::string>& 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<HillshadeBucket> result, const uint
}
renderable = static_cast<bool>(bucket);
observer->onTileChanged(*this);
observer->onTileAction(id, sourceID, TileOperation::EndParse);
}
}

View File

@@ -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<TileRenderData> createRenderData() override;

View File

@@ -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<Mailbox>(*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<const std::string>& 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<RasterBucket> result, const uint64_t r
}
renderable = static_cast<bool>(bucket);
observer->onTileChanged(*this);
observer->onTileAction(id, sourceID, TileOperation::EndParse);
}
}

View File

@@ -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<TileRenderData> createRenderData() override;

View File

@@ -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<std::string, const RenderLa
void Tile::querySourceFeatures(std::vector<Feature>&, const SourceQueryOptions&) {}
void Tile::onTileAction(TileOperation op) {
observer->onTileAction(id, sourceID, op);
};
} // namespace mbgl

View File

@@ -6,6 +6,7 @@
#include <mbgl/util/tile_coordinate.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/tile/tile_necessity.hpp>
#include <mbgl/tile/tile_loader_observer.hpp>
#include <mbgl/renderer/tile_mask.hpp>
#include <mbgl/renderer/bucket.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
@@ -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<TileRenderData> 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<Timestamp> modified;
std::optional<Timestamp> expires;
// Indicates whether this tile is used for the currently visible layers on

View File

@@ -39,7 +39,7 @@ private:
void makeOptional();
void loadFromCache();
void loadedData(const Response&);
void loadedData(const Response&, Resource::LoadingMethod);
void loadFromNetwork();
bool hasPendingNetworkRequest() const {

View File

@@ -106,6 +106,8 @@ void TileLoader<T>::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<T>::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<T>::makeOptional() {
}
template <typename T>
void TileLoader<T>::loadedData(const Response& res) {
void TileLoader<T>::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<T>::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<T>::loadFromNetwork() {
if (shared_->aborted) return;
request.reset();
loadedData(res);
loadedData(res, Resource::LoadingMethod::NetworkOnly);
break;
}
} while (!shared_->aborted);

View File

@@ -0,0 +1,17 @@
#pragma once
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/tile/tile_operation.hpp>
namespace mbgl {
class Tile;
class TileLoaderObserver {
public:
virtual ~TileLoaderObserver() = default;
virtual void onTileAction(TileOperation) {}
};
} // namespace mbgl

View File

@@ -1,5 +1,7 @@
#pragma once
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/tile/tile_operation.hpp>
#include <exception>
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

View File

@@ -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() {

View File

@@ -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;

View File

@@ -252,12 +252,15 @@ BufferResource Context::createBuffer(const void* data, std::size_t size, std::ui
return BufferResource(const_cast<Context&>(*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<std::string, std::string>& additionalDefines) {
return std::make_unique<ShaderProgram>(name, vertex, fragment, programParameters, additionalDefines, backend);
auto program = std::make_unique<ShaderProgram>(
shaderID, name, vertex, fragment, programParameters, additionalDefines, backend, *observer);
return program;
}
gfx::UniqueDrawableBuilder Context::createDrawableBuilder(std::string name) {

View File

@@ -1,3 +1,5 @@
#include <gmock/gmock.h>
#include <mbgl/test/util.hpp>
#include <mbgl/test/stub_file_source.hpp>
#include <mbgl/test/stub_map_observer.hpp>
@@ -28,6 +30,7 @@
#include <mbgl/style/sources/image_source.hpp>
#include <mbgl/style/sources/vector_source.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_impl.hpp>
#include <mbgl/util/async_task.hpp>
#include <mbgl/util/client_options.hpp>
#include <mbgl/util/color.hpp>
@@ -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<TileEntry> tileOps;
struct ShaderEntry {
shaders::BuiltIn id;
gfx::Backend::Type type;
std::string defines;
bool isPostCompile;
};
std::vector<ShaderEntry> shaderOps;
StubMapObserver observer;
observer.onTileActionCallback = [&](TileOperation op, const OverscaledTileID& id, const std::string& sourceID) {
if (sourceID != "mapbox") return;
std::lock_guard<std::mutex> 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<MainResourceLoader>(
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<FillLayer>("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<std::pair<shaders::BuiltIn, size_t>> 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<std::string>()(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<OverscaledTileID> 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));
}
}

View File

@@ -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<void()> willStartLoadingMapCallback;
std::function<void()> didFinishLoadingMapCallback;
std::function<void()> didFailLoadingMapCallback;
@@ -57,6 +101,13 @@ public:
std::function<void(RenderFrameStatus)> didFinishRenderingFrameCallback;
std::function<void()> didBecomeIdleCallback;
std::function<void(gfx::ShaderRegistry&)> onRegisterShadersCallback;
std::function<void(shaders::BuiltIn, gfx::Backend::Type, const std::string&)> onPreCompileShaderCallback;
std::function<void(shaders::BuiltIn, gfx::Backend::Type, const std::string&)> onPostCompileShaderCallback;
std::function<void(shaders::BuiltIn, gfx::Backend::Type, const std::string&)> onShaderCompileFailedCallback;
std::function<void(const FontStack&, const GlyphRange&)> onGlyphsLoadedCallback;
std::function<void(const FontStack&, const GlyphRange&, std::exception_ptr)> onGlyphsErrorCallback;
std::function<void(const FontStack&, const GlyphRange&)> onGlyphsRequestedCallback;
std::function<void(TileOperation, const OverscaledTileID&, const std::string&)> onTileActionCallback;
};
} // namespace mbgl

View File

@@ -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<FakeTile>(*this, tileID); });
[&](const OverscaledTileID& tileID, TileObserver* observer_) {
return std::make_unique<FakeTile>(*this, tileID, observer_);
});
}
const std::optional<Tileset>& getTileset() const override {

View File

@@ -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<HillshadeBucket>(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());

View File

@@ -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<RasterBucket>(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());