Add shader registry (#924)
This commit is contained in:
@@ -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
|
||||
|
||||
49
include/mbgl/gfx/shader.hpp
Normal file
49
include/mbgl/gfx/shader.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
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<typename T>
|
||||
inline constexpr bool is_shader_v =
|
||||
std::is_base_of_v<gfx::Shader, T> &&
|
||||
std::is_same_v<
|
||||
std::remove_cv_t<decltype(T::Name)>,
|
||||
std::string_view> &&
|
||||
std::is_final_v<T>;
|
||||
|
||||
|
||||
/// @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<typename T,
|
||||
typename std::enable_if_t<is_shader_v<T>, bool>* = nullptr>
|
||||
T* to() noexcept {
|
||||
if (name() != T::Name) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mbgl
|
||||
77
include/mbgl/gfx/shader_registry.hpp
Normal file
77
include/mbgl/gfx/shader_registry.hpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/gfx/shader.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
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<gfx::Shader> 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>&& 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>&& 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<typename T,
|
||||
typename std::enable_if_t<is_shader_v<T>, bool>* = nullptr>
|
||||
std::shared_ptr<T> get() noexcept {
|
||||
auto shader = getShader(std::string(T::Name));
|
||||
if (!shader || (shader->name() != T::Name)) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::static_pointer_cast<T>(shader);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<
|
||||
std::string,
|
||||
std::shared_ptr<gfx::Shader>
|
||||
> programs;
|
||||
mutable std::shared_mutex programLock;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mbgl
|
||||
@@ -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
|
||||
|
||||
@@ -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<void()>;
|
||||
virtual void onStyleImageMissing(const std::string&, const StyleImageMissingCallback& done) { done(); }
|
||||
virtual void onRemoveUnusedStyleImages(const std::vector<std::string>&) {}
|
||||
|
||||
// Entry point for custom shader registration
|
||||
virtual void onRegisterShaders(gfx::ShaderRegistry&) {};
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
55
src/mbgl/gfx/shader_registry.cpp
Normal file
55
src/mbgl/gfx/shader_registry.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <mbgl/gfx/shader_registry.hpp>
|
||||
#include <mbgl/gfx/shader.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace gfx {
|
||||
|
||||
ShaderRegistry::ShaderRegistry() {}
|
||||
|
||||
bool ShaderRegistry::isShader(const std::string& shaderName)
|
||||
const noexcept
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> readerLock(programLock);
|
||||
return programs.find(shaderName) != programs.end();
|
||||
}
|
||||
|
||||
const std::shared_ptr<gfx::Shader> ShaderRegistry::getShader(const std::string& shaderName)
|
||||
const noexcept
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> readerLock(programLock);
|
||||
const auto it = programs.find(shaderName);
|
||||
if (it == programs.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool ShaderRegistry::replaceShader(
|
||||
std::shared_ptr<gfx::Shader>&& shader) noexcept
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> 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<gfx::Shader>&& shader) noexcept
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> 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
|
||||
@@ -60,18 +60,24 @@ public:
|
||||
const ProgramParameters& programParameters,
|
||||
const std::string& additionalDefines) {
|
||||
// Compile the shader
|
||||
const std::initializer_list<const char*> vertexSource = {
|
||||
std::initializer_list<const char*> 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<const char*> fragmentSource = {
|
||||
|
||||
std::initializer_list<const char*> 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<Instance>(context, vertexSource, fragmentSource);
|
||||
}
|
||||
|
||||
|
||||
@@ -182,4 +182,8 @@ void Map::Impl::onRemoveUnusedStyleImages(const std::vector<std::string>& unused
|
||||
}
|
||||
}
|
||||
|
||||
void Map::Impl::onRegisterShaders(gfx::ShaderRegistry& registry) {
|
||||
observer.onRegisterShaders(registry);
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -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<void()>&) final;
|
||||
void onRemoveUnusedStyleImages(const std::vector<std::string>&) final;
|
||||
void onRegisterShaders(gfx::ShaderRegistry&) final;
|
||||
|
||||
// Map
|
||||
void jumpTo(const CameraOptions&);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <mbgl/programs/program_parameters.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <string_view>
|
||||
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<BackgroundLayerPrograms>(context, programParameters);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <mbgl/programs/clipping_mask_program.hpp>
|
||||
#include <mbgl/programs/debug_program.hpp>
|
||||
#include <mbgl/programs/program_parameters.hpp>
|
||||
#include <mbgl/gfx/shader_registry.hpp>
|
||||
#include <memory>
|
||||
|
||||
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;
|
||||
|
||||
@@ -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 }};
|
||||
|
||||
|
||||
@@ -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<float, 2> 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;
|
||||
|
||||
@@ -49,8 +49,10 @@ static gfx::VertexVector<HeatmapTextureLayoutVertex> heatmapTextureVertices() {
|
||||
return result;
|
||||
}
|
||||
|
||||
RenderStaticData::RenderStaticData(gfx::Context& context, float pixelRatio)
|
||||
RenderStaticData::RenderStaticData(gfx::Context& context, float pixelRatio,
|
||||
std::unique_ptr<gfx::ShaderRegistry>&& shaders_)
|
||||
: programs(context, ProgramParameters{pixelRatio, false}),
|
||||
shaders(std::move(shaders_)),
|
||||
clippingMaskSegments(tileTriangleSegments())
|
||||
#ifndef NDEBUG
|
||||
,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <mbgl/gfx/vertex_buffer.hpp>
|
||||
#include <mbgl/gfx/index_buffer.hpp>
|
||||
#include <mbgl/gfx/renderbuffer.hpp>
|
||||
#include <mbgl/gfx/shader_registry.hpp>
|
||||
#include <mbgl/programs/background_program.hpp>
|
||||
#include <mbgl/programs/heatmap_texture_program.hpp>
|
||||
#include <mbgl/programs/programs.hpp>
|
||||
@@ -19,7 +20,8 @@ class UploadPass;
|
||||
|
||||
class RenderStaticData {
|
||||
public:
|
||||
RenderStaticData(gfx::Context&, float pixelRatio);
|
||||
RenderStaticData(gfx::Context&, float pixelRatio,
|
||||
std::unique_ptr<gfx::ShaderRegistry>&& 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<gfx::ShaderRegistry> shaders;
|
||||
|
||||
const SegmentVector<BackgroundAttributes> clippingMaskSegments;
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <mbgl/gfx/cull_face_mode.hpp>
|
||||
#include <mbgl/gfx/context.hpp>
|
||||
#include <mbgl/gfx/renderable.hpp>
|
||||
#include <mbgl/programs/programs.hpp>
|
||||
#include <mbgl/renderer/pattern_atlas.hpp>
|
||||
#include <mbgl/renderer/renderer_observer.hpp>
|
||||
#include <mbgl/renderer/render_static_data.hpp>
|
||||
@@ -47,7 +48,10 @@ void Renderer::Impl::render(const RenderTree& renderTree) {
|
||||
const auto& renderTreeParameters = renderTree.getParameters();
|
||||
|
||||
if (!staticData) {
|
||||
staticData = std::make_unique<RenderStaticData>(backend.getContext(), pixelRatio);
|
||||
staticData = std::make_unique<RenderStaticData>(backend.getContext(), pixelRatio,
|
||||
std::make_unique<gfx::ShaderRegistry>());
|
||||
staticData->programs.registerWith(*staticData->shaders);
|
||||
observer->onRegisterShaders(*staticData->shaders);
|
||||
}
|
||||
staticData->has3D = renderTreeParameters.has3D;
|
||||
|
||||
|
||||
@@ -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&);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <mbgl/gfx/backend_scope.hpp>
|
||||
#include <mbgl/gfx/headless_frontend.hpp>
|
||||
#include <mbgl/gfx/shader_registry.hpp>
|
||||
#include <mbgl/gl/context.hpp>
|
||||
#include <mbgl/map/map_options.hpp>
|
||||
#include <mbgl/math/log2.hpp>
|
||||
@@ -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);
|
||||
}
|
||||
113
test/renderer/shader_registry.test.cpp
Normal file
113
test/renderer/shader_registry.test.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#include <mbgl/test/util.hpp>
|
||||
|
||||
#include <mbgl/gfx/shader_registry.hpp>
|
||||
#include <mbgl/gfx/shader.hpp>
|
||||
|
||||
#include <string_view>
|
||||
#include <iostream>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
namespace {
|
||||
|
||||
template<uint32_t token_value>
|
||||
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<typename T>
|
||||
uint32_t useShader(gfx::ShaderRegistry& registry) {
|
||||
auto program = registry.get<T>();
|
||||
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<StubProgram_1>();
|
||||
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<StubProgram_1>(), program);
|
||||
|
||||
// Make sure downcasting to program1 works as expected
|
||||
StubShaderConsumer consumer;
|
||||
ASSERT_EQ(consumer.useShader<StubProgram_1>(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<StubProgram_1>();
|
||||
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<StubProgram_1>(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<StubProgram_2>();
|
||||
ASSERT_FALSE(registry.replaceShader(program2));
|
||||
|
||||
// Make a second instance of program1 and change the token
|
||||
auto program3 = std::make_shared<StubProgram_1>();
|
||||
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<StubProgram_1>(registry), 30);
|
||||
}
|
||||
|
||||
// Validate shader type info and downcasting are functioning
|
||||
TEST(ShaderRegistry, ShaderRTTI) {
|
||||
auto program1 = StubProgram_1();
|
||||
auto asBase = static_cast<gfx::Shader*>(&program1);
|
||||
|
||||
// Should convert to program 1 but not program 2
|
||||
ASSERT_EQ(asBase->to<StubProgram_1>(), &program1);
|
||||
ASSERT_EQ(asBase->to<StubProgram_2>(), nullptr);
|
||||
}
|
||||
@@ -44,12 +44,19 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void onRegisterShaders(gfx::ShaderRegistry& registry) final {
|
||||
if (onRegisterShadersCallback) {
|
||||
onRegisterShadersCallback(registry);
|
||||
}
|
||||
}
|
||||
|
||||
std::function<void()> willStartLoadingMapCallback;
|
||||
std::function<void()> didFinishLoadingMapCallback;
|
||||
std::function<void()> didFailLoadingMapCallback;
|
||||
std::function<void()> didFinishLoadingStyleCallback;
|
||||
std::function<void(RenderFrameStatus)> didFinishRenderingFrameCallback;
|
||||
std::function<void()> didBecomeIdleCallback;
|
||||
std::function<void(gfx::ShaderRegistry&)> onRegisterShadersCallback;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user