diff --git a/CMakeLists.txt b/CMakeLists.txt index 8de0d0cb3..7bf7ecb10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,8 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/renderable.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/renderer_backend.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/rendering_stats.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/shader_registry.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/shader.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/background_layer_factory.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/circle_layer_factory.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/fill_extrusion_layer_factory.hpp @@ -349,6 +351,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/renderbuffer.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/renderer_backend.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/rendering_stats.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/shader_registry.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/stencil_mode.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/texture.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/types.hpp diff --git a/include/mbgl/gfx/shader.hpp b/include/mbgl/gfx/shader.hpp new file mode 100644 index 000000000..4c0631701 --- /dev/null +++ b/include/mbgl/gfx/shader.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace gfx { + +class Shader; + +// Assert that a type is a valid shader for downcasting. +// A valid shader must: +// * Inherit gfx::Shader +// * Have properly called DECLARE_SHADER_TYPEINFO in the class body +// * Be a final class +template +inline constexpr bool is_shader_v = + std::is_base_of_v && + std::is_same_v< + std::remove_cv_t, + std::string_view> && + std::is_final_v; + + +/// @brief A shader is used as the base class for all programs across any supported +/// backend API. Shaders are registered with a `gfx::ShaderRegistry` instance. +class Shader { + public: + virtual ~Shader() = default; + + /// @brief Get the name of this shader + /// @return Shader name + virtual const std::string_view name() const noexcept = 0; + + /// @brief Downcast to a type + /// @tparam T Derived type + /// @return Type or nullptr if type info was not a match + template, bool>* = nullptr> + T* to() noexcept { + if (name() != T::Name) { + return nullptr; + } + return static_cast(this); + } +}; + +} // namespace gfx +} // namespace mbgl diff --git a/include/mbgl/gfx/shader_registry.hpp b/include/mbgl/gfx/shader_registry.hpp new file mode 100644 index 000000000..04c876a0f --- /dev/null +++ b/include/mbgl/gfx/shader_registry.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include + +#include +#include +#include +#include + +namespace mbgl { +namespace gfx { + +/// @brief A ShaderRegistry contains a collection of gfx::Shader instances. +/// Using the registry, shaders may be dynamically registered or replaced +/// at runtime. +class ShaderRegistry { + public: + explicit ShaderRegistry(); + ShaderRegistry(const ShaderRegistry&) = delete; + ShaderRegistry(ShaderRegistry&&) noexcept = delete; + ShaderRegistry& operator=(const ShaderRegistry&) = delete; + ShaderRegistry& operator=(ShaderRegistry&&) noexcept = delete; + virtual ~ShaderRegistry() = default; + + /// @brief Checks if a shader exists in the registry for the given name. + /// @param shaderName Name of shader + /// @return If a shader is found, true + [[nodiscard]] virtual bool isShader(const std::string& shaderName) + const noexcept; + + /// @brief Get a shader from the registry by name. + /// @param shaderName Name of shader + /// @return A `gfx::Shader` or `nullptr` if no shader is found with the + /// given name + [[nodiscard]] virtual const std::shared_ptr getShader( + const std::string& shaderName) const noexcept; + + /// @brief Replace a matching shader in the registry with the provided + /// instance. Shader names must match. + /// @param shader A `gfx::Shader`. The ShaderRegistry will take ownership. + /// @return True if a match was found and the shader was replaced, false + /// otherwise. + [[nodiscard]] virtual bool replaceShader( + std::shared_ptr&& shader) noexcept; + + /// @brief Register a new shader with the registry. If a shader is present + /// in the registry with a conflicting name, registration will fail. + /// @param shader A `gfx::Shader` to register. The ShaderRegistry will + /// take ownership. + /// @return True if the shader was registered, false if another shader is + /// already present with a conflicting name. + [[nodiscard]] virtual bool registerShader( + std::shared_ptr&& shader) noexcept; + + /// @brief Shorthand helper to quickly get a derived type from the registry. + /// @tparam T Derived type, inheriting `gfx::Shader` + /// @return T or nullptr if not found in the registry + template, bool>* = nullptr> + std::shared_ptr get() noexcept { + auto shader = getShader(std::string(T::Name)); + if (!shader || (shader->name() != T::Name)) { + return nullptr; + } + return std::static_pointer_cast(shader); + } + + private: + std::unordered_map< + std::string, + std::shared_ptr + > programs; + mutable std::shared_mutex programLock; +}; + +} // namespace gfx +} // namespace mbgl diff --git a/include/mbgl/map/map_observer.hpp b/include/mbgl/map/map_observer.hpp index c3f0b84a6..7a400bc74 100644 --- a/include/mbgl/map/map_observer.hpp +++ b/include/mbgl/map/map_observer.hpp @@ -8,6 +8,10 @@ namespace mbgl { +namespace gfx { +class ShaderRegistry; +} + enum class MapLoadError { StyleParseError, StyleLoadError, @@ -57,6 +61,9 @@ public: /// This method should return true if unused image can be removed, /// false otherwise. By default, unused image will be removed. 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&) {}; }; } // namespace mbgl diff --git a/include/mbgl/renderer/renderer_observer.hpp b/include/mbgl/renderer/renderer_observer.hpp index 5b9052e18..d0bb74754 100644 --- a/include/mbgl/renderer/renderer_observer.hpp +++ b/include/mbgl/renderer/renderer_observer.hpp @@ -5,6 +5,10 @@ namespace mbgl { +namespace gfx { +class ShaderRegistry; +} + class RendererObserver { public: virtual ~RendererObserver() = default; @@ -36,6 +40,9 @@ public: using StyleImageMissingCallback = std::function; virtual void onStyleImageMissing(const std::string&, const StyleImageMissingCallback& done) { done(); } virtual void onRemoveUnusedStyleImages(const std::vector&) {} + + // Entry point for custom shader registration + virtual void onRegisterShaders(gfx::ShaderRegistry&) {}; }; } // namespace mbgl diff --git a/src/mbgl/gfx/shader_registry.cpp b/src/mbgl/gfx/shader_registry.cpp new file mode 100644 index 000000000..a1cab8214 --- /dev/null +++ b/src/mbgl/gfx/shader_registry.cpp @@ -0,0 +1,55 @@ +#include +#include + +namespace mbgl { +namespace gfx { + +ShaderRegistry::ShaderRegistry() {} + +bool ShaderRegistry::isShader(const std::string& shaderName) + const noexcept +{ + std::shared_lock readerLock(programLock); + return programs.find(shaderName) != programs.end(); +} + +const std::shared_ptr ShaderRegistry::getShader(const std::string& shaderName) + const noexcept +{ + std::shared_lock readerLock(programLock); + const auto it = programs.find(shaderName); + if (it == programs.end()) { + return nullptr; + } + + return it->second; +} + +bool ShaderRegistry::replaceShader( + std::shared_ptr&& shader) noexcept +{ + std::unique_lock writerLock(programLock); + const std::string programName{shader->name()}; // TODO: C++ 20 heterogenous lookup + if (programs.find(programName) == programs.end()) { + return false; + } + + programs[programName] = std::move(shader); + return true; +} + +bool ShaderRegistry::registerShader( + std::shared_ptr&& shader) noexcept +{ + std::unique_lock writerLock(programLock); + const std::string programName{shader->name()}; + if (programs.find(programName) != programs.end()) { + return false; + } + + programs.emplace(programName, std::move(shader)); + return true; +} + +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp index dbac6841f..a42598b91 100644 --- a/src/mbgl/gl/program.hpp +++ b/src/mbgl/gl/program.hpp @@ -60,18 +60,24 @@ public: const ProgramParameters& programParameters, const std::string& additionalDefines) { // Compile the shader - const std::initializer_list vertexSource = { + std::initializer_list vertexSource = { programParameters.getDefines().c_str(), additionalDefines.c_str(), (programs::gl::shaderSource() + programs::gl::vertexPreludeOffset), - (programs::gl::shaderSource() + vertexOffset) + programParameters.vertexSource().length() == 0 ? + (programs::gl::shaderSource() + vertexOffset) : + programParameters.vertexSource().c_str() }; - const std::initializer_list fragmentSource = { + + std::initializer_list fragmentSource = { programParameters.getDefines().c_str(), additionalDefines.c_str(), (programs::gl::shaderSource() + programs::gl::fragmentPreludeOffset), - (programs::gl::shaderSource() + fragmentOffset) + programParameters.fragmentSource().length() == 0 ? + (programs::gl::shaderSource() + fragmentOffset) : + programParameters.fragmentSource().c_str() }; + return std::make_unique(context, vertexSource, fragmentSource); } diff --git a/src/mbgl/map/map_impl.cpp b/src/mbgl/map/map_impl.cpp index cef939fc2..4fd9ba63a 100644 --- a/src/mbgl/map/map_impl.cpp +++ b/src/mbgl/map/map_impl.cpp @@ -182,4 +182,8 @@ void Map::Impl::onRemoveUnusedStyleImages(const std::vector& unused } } +void Map::Impl::onRegisterShaders(gfx::ShaderRegistry& registry) { + observer.onRegisterShaders(registry); +} + } // namespace mbgl diff --git a/src/mbgl/map/map_impl.hpp b/src/mbgl/map/map_impl.hpp index 16ce40ff4..97f42763f 100644 --- a/src/mbgl/map/map_impl.hpp +++ b/src/mbgl/map/map_impl.hpp @@ -18,6 +18,10 @@ namespace mbgl { class FileSource; class ResourceTransform; +namespace gfx { +class ShaderRegistry; +} + struct StillImageRequest { StillImageRequest(Map::StillImageCallback&& callback_) : callback(std::move(callback_)) { @@ -47,6 +51,7 @@ public: void onDidFinishRenderingMap() final; void onStyleImageMissing(const std::string&, const std::function&) final; void onRemoveUnusedStyleImages(const std::vector&) final; + void onRegisterShaders(gfx::ShaderRegistry&) final; // Map void jumpTo(const CameraOptions&); diff --git a/src/mbgl/programs/program_parameters.cpp b/src/mbgl/programs/program_parameters.cpp index 95d2d0f66..a61bb884e 100644 --- a/src/mbgl/programs/program_parameters.cpp +++ b/src/mbgl/programs/program_parameters.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace mbgl { @@ -15,11 +16,28 @@ ProgramParameters::ProgramParameters(const float pixelRatio, result += "#define OVERDRAW_INSPECTOR\n"; } return result; - }()) { + }()) +{} + +ProgramParameters ProgramParameters::withShaderSource(std::string_view vertexSource, + std::string_view fragmentSource) const noexcept +{ + ProgramParameters params = *this; + params.vertexSource_ = vertexSource; + params.fragmentSource_ = fragmentSource; + return params; } const std::string& ProgramParameters::getDefines() const { return defines; } +const std::string& ProgramParameters::vertexSource() const noexcept { + return vertexSource_; +} + +const std::string& ProgramParameters::fragmentSource() const noexcept { + return fragmentSource_; +} + } // namespace mbgl diff --git a/src/mbgl/programs/program_parameters.hpp b/src/mbgl/programs/program_parameters.hpp index b471471b1..99e554aff 100644 --- a/src/mbgl/programs/program_parameters.hpp +++ b/src/mbgl/programs/program_parameters.hpp @@ -8,10 +8,18 @@ class ProgramParameters { public: ProgramParameters(float pixelRatio, bool overdraw); + ProgramParameters withShaderSource(std::string_view vertexSource, + std::string_view fragmentSource) const noexcept; + const std::string& getDefines() const; + const std::string& vertexSource() const noexcept; + const std::string& fragmentSource() const noexcept; + private: std::string defines; + std::string vertexSource_{""}; + std::string fragmentSource_{""}; }; } // namespace mbgl diff --git a/src/mbgl/programs/programs.cpp b/src/mbgl/programs/programs.cpp index ce5bbd7ee..14af28d37 100644 --- a/src/mbgl/programs/programs.cpp +++ b/src/mbgl/programs/programs.cpp @@ -20,6 +20,8 @@ Programs::Programs(gfx::Context& context_, const ProgramParameters& programParam Programs::~Programs() = default; +void Programs::registerWith([[maybe_unused]] gfx::ShaderRegistry& registry) noexcept {} + BackgroundLayerPrograms& Programs::getBackgroundLayerPrograms() noexcept { if (!backgroundPrograms) { backgroundPrograms = std::make_unique(context, programParameters); diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp index ca9359b26..21a0ac516 100644 --- a/src/mbgl/programs/programs.hpp +++ b/src/mbgl/programs/programs.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include namespace mbgl { @@ -23,6 +24,10 @@ public: Programs(gfx::Context&, const ProgramParameters&); ~Programs(); + /// @brief Registers built-in programs with the provided registry. + /// @param registry gfx::ShaderRegistry to populate with built-in programs. + void registerWith(gfx::ShaderRegistry& registry) noexcept; + BackgroundLayerPrograms& getBackgroundLayerPrograms() noexcept; RasterLayerPrograms& getRasterLayerPrograms() noexcept; HeatmapLayerPrograms& getHeatmapLayerPrograms() noexcept; diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp index 51a3e52a8..5e46d105d 100644 --- a/src/mbgl/renderer/paint_parameters.cpp +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -51,10 +51,11 @@ PaintParameters::PaintParameters(gfx::Context& context_, timePoint(timePoint_), pixelRatio(pixelRatio_), #ifndef NDEBUG - programs((debugOptions & MapDebugOptions::Overdraw) ? staticData_.overdrawPrograms : staticData_.programs) + programs((debugOptions & MapDebugOptions::Overdraw) ? staticData_.overdrawPrograms : staticData_.programs), #else - programs(staticData_.programs) + programs(staticData_.programs), #endif + shaders(*staticData_.shaders) { pixelsToGLUnits = {{ 2.0f / state.getSize().width, -2.0f / state.getSize().height }}; diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index a043ad557..75c28c17e 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -30,6 +30,7 @@ class Context; class RendererBackend; class CommandEncoder; class RenderPass; +class ShaderRegistry; } // namespace gfx @@ -78,7 +79,10 @@ public: float pixelRatio; std::array pixelsToGLUnits; + // Programs is, in effect, an immutable shader registry Programs& programs; + // We're migrating to a dynamic one + gfx::ShaderRegistry& shaders; gfx::DepthMode depthModeForSublayer(uint8_t n, gfx::DepthMaskType) const; gfx::DepthMode depthModeFor3D() const; diff --git a/src/mbgl/renderer/render_static_data.cpp b/src/mbgl/renderer/render_static_data.cpp index f7d9c18a7..72de16346 100644 --- a/src/mbgl/renderer/render_static_data.cpp +++ b/src/mbgl/renderer/render_static_data.cpp @@ -49,8 +49,10 @@ static gfx::VertexVector heatmapTextureVertices() { return result; } -RenderStaticData::RenderStaticData(gfx::Context& context, float pixelRatio) +RenderStaticData::RenderStaticData(gfx::Context& context, float pixelRatio, + std::unique_ptr&& shaders_) : programs(context, ProgramParameters{pixelRatio, false}), + shaders(std::move(shaders_)), clippingMaskSegments(tileTriangleSegments()) #ifndef NDEBUG , diff --git a/src/mbgl/renderer/render_static_data.hpp b/src/mbgl/renderer/render_static_data.hpp index 28296dada..c2c509022 100644 --- a/src/mbgl/renderer/render_static_data.hpp +++ b/src/mbgl/renderer/render_static_data.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,8 @@ class UploadPass; class RenderStaticData { public: - RenderStaticData(gfx::Context&, float pixelRatio); + RenderStaticData(gfx::Context&, float pixelRatio, + std::unique_ptr&& shaders_); void upload(gfx::UploadPass&); @@ -40,8 +42,11 @@ public: bool uploaded = false; Size backendSize; + // @TODO: Migrate away from and remove `Programs` Programs programs; + std::unique_ptr shaders; + const SegmentVector clippingMaskSegments; #ifndef NDEBUG diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 25a8020c8..3029988e6 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -47,7 +48,10 @@ void Renderer::Impl::render(const RenderTree& renderTree) { const auto& renderTreeParameters = renderTree.getParameters(); if (!staticData) { - staticData = std::make_unique(backend.getContext(), pixelRatio); + staticData = std::make_unique(backend.getContext(), pixelRatio, + std::make_unique()); + staticData->programs.registerWith(*staticData->shaders); + observer->onRegisterShaders(*staticData->shaders); } staticData->has3D = renderTreeParameters.has3D; diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 2a058c605..4057864a7 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -13,6 +13,7 @@ class RenderTree; namespace gfx { class RendererBackend; +class ShadeRegistry; } // namespace gfx class Renderer::Impl { @@ -22,7 +23,7 @@ public: private: friend class Renderer; - + void setObserver(RendererObserver*); void render(const RenderTree&); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 61e757724..1100c2bc4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -21,6 +21,7 @@ add_library( ${PROJECT_SOURCE_DIR}/test/programs/symbol_program.test.cpp ${PROJECT_SOURCE_DIR}/test/renderer/image_manager.test.cpp ${PROJECT_SOURCE_DIR}/test/renderer/pattern_atlas.test.cpp + ${PROJECT_SOURCE_DIR}/test/renderer/shader_registry.test.cpp ${PROJECT_SOURCE_DIR}/test/sprite/sprite_loader.test.cpp ${PROJECT_SOURCE_DIR}/test/sprite/sprite_parser.test.cpp ${PROJECT_SOURCE_DIR}/test/src/mbgl/test/fixture_log_observer.cpp diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index 9ae9d88ac..ee9a7eeb6 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -1576,3 +1577,23 @@ TEST(Map, VolatileSource) { test.runLoop.run(); EXPECT_EQ(8, requestedTiles); } + +TEST(Map, ObserveShaderRegistration) { + MapTest<> test; + + bool observedRegistry = false; + test.observer.onRegisterShadersCallback = [&observedRegistry](gfx::ShaderRegistry&) { + observedRegistry = true; + }; + + test.map.jumpTo(CameraOptions().withZoom(10)); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + + test.frontend.render(test.map); + EXPECT_EQ(observedRegistry, true); + + // Should not see it again + observedRegistry = false; + test.frontend.render(test.map); + EXPECT_EQ(observedRegistry, false); +} \ No newline at end of file diff --git a/test/renderer/shader_registry.test.cpp b/test/renderer/shader_registry.test.cpp new file mode 100644 index 000000000..092dbdead --- /dev/null +++ b/test/renderer/shader_registry.test.cpp @@ -0,0 +1,113 @@ +#include + +#include +#include + +#include +#include + +using namespace mbgl; + +namespace { + +template +class StubProgramBase : public gfx::Shader { + public: + static constexpr auto Token = token_value; + virtual uint32_t draw() { return token; } + + protected: + uint32_t token{token_value}; +}; + +class StubProgram_1 final : public StubProgramBase<10> { + public: + static constexpr std::string_view Name{"StubProgram_1"}; + const std::string_view name() const noexcept override { + return Name; + } + + void setToken(uint32_t tok) { token = tok; } +}; + +class StubProgram_2 final : public StubProgramBase<20> { + public: + static constexpr std::string_view Name{"StubProgram_2"}; + const std::string_view name() const noexcept override { + return Name; + } +}; + +class StubShaderConsumer { + public: + template + uint32_t useShader(gfx::ShaderRegistry& registry) { + auto program = registry.get(); + return program ? program->draw() : 0; + } +}; + +} // namespace + +// Ensure we can register a gfx::Shader-based type with a registry object +TEST(ShaderRegistry, RegisterShader) { + gfx::ShaderRegistry registry; + + // The registry should start empty + ASSERT_FALSE(registry.isShader(std::string{StubProgram_1::Name})); + + // Register the program + auto program = std::make_shared(); + ASSERT_TRUE(registry.registerShader(program)); + // We can't re-register the same program name + ASSERT_FALSE(registry.registerShader(program)); + // Ensure the shader is present in the registry now + ASSERT_TRUE(registry.isShader(std::string{StubProgram_1::Name})); + // And we can fetch it + ASSERT_EQ(registry.get(), program); + + // Make sure downcasting to program1 works as expected + StubShaderConsumer consumer; + ASSERT_EQ(consumer.useShader(registry), 10); +} + +// Validate replacing a shader by name with a different instance +TEST(ShaderRegistry, ReplaceShaderType) { + gfx::ShaderRegistry registry; + + ASSERT_FALSE(registry.isShader(std::string{StubProgram_1::Name})); + + auto program = std::make_shared(); + ASSERT_TRUE(registry.registerShader(program)); + // Ensure the shader is present in the registry now + ASSERT_TRUE(registry.isShader(std::string{StubProgram_1::Name})); + ASSERT_TRUE(registry.isShader(std::string{program->name()})); + + // Make sure downcasting to program1 works as expected + StubShaderConsumer consumer; + ASSERT_EQ(consumer.useShader(registry), 10); + + // StubProgram_1 and StubProgram_2 are disconnected types, + // and as such this replacement should fail as StubProgram_2 + // has never been registered. + auto program2 = std::make_shared(); + ASSERT_FALSE(registry.replaceShader(program2)); + + // Make a second instance of program1 and change the token + auto program3 = std::make_shared(); + program3->setToken(30); + // Replace it in the registry + ASSERT_TRUE(registry.replaceShader(program3)); + // Assert the new program downcasts from the registry as expected + ASSERT_EQ(consumer.useShader(registry), 30); +} + +// Validate shader type info and downcasting are functioning +TEST(ShaderRegistry, ShaderRTTI) { + auto program1 = StubProgram_1(); + auto asBase = static_cast(&program1); + + // Should convert to program 1 but not program 2 + ASSERT_EQ(asBase->to(), &program1); + ASSERT_EQ(asBase->to(), nullptr); +} diff --git a/test/src/mbgl/test/stub_map_observer.hpp b/test/src/mbgl/test/stub_map_observer.hpp index 63e42fa6a..f93c0d647 100644 --- a/test/src/mbgl/test/stub_map_observer.hpp +++ b/test/src/mbgl/test/stub_map_observer.hpp @@ -44,12 +44,19 @@ public: } } + void onRegisterShaders(gfx::ShaderRegistry& registry) final { + if (onRegisterShadersCallback) { + onRegisterShadersCallback(registry); + } + } + std::function willStartLoadingMapCallback; std::function didFinishLoadingMapCallback; std::function didFailLoadingMapCallback; std::function didFinishLoadingStyleCallback; std::function didFinishRenderingFrameCallback; std::function didBecomeIdleCallback; + std::function onRegisterShadersCallback; };