Circle layer final (#1227)

This commit is contained in:
Alex Cristici
2023-06-09 20:49:46 +03:00
committed by Bart Louwers
parent 008f584b9e
commit 52b9e099e5
31 changed files with 605 additions and 402 deletions

View File

@@ -148,6 +148,7 @@ list(APPEND INCLUDE_FILES
${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_group.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/gfx/shader_registry.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/gfx/shader.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/gfx/uniform_block.hpp
@@ -190,6 +191,7 @@ list(APPEND INCLUDE_FILES
${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer_observer.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer_state.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/shader_program_base.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/shader_group_gl.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/shader_program_gl.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/shader_program_mtl.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/storage/database_file_source.hpp
@@ -393,6 +395,7 @@ list(APPEND SRC_FILES
${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_group.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

View File

@@ -41,6 +41,7 @@ enum class AttributeDataType : uint8_t {
UShort2,
UShort3,
UShort4,
UShort8,
Int,
Int2,

View File

@@ -0,0 +1,153 @@
#pragma once
#include <mbgl/gfx/shader.hpp>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <unordered_map>
#include <vector>
namespace mbgl {
namespace gfx {
class Context;
/// @brief A ShaderGroup contains a collection of gfx::Shader.
/// Using the group, shaders may be dynamically registered or replaced
/// at runtime.
class ShaderGroup {
public:
ShaderGroup() = default;
ShaderGroup(const ShaderGroup&) = delete;
ShaderGroup(ShaderGroup&&) noexcept = delete;
ShaderGroup& operator=(const ShaderGroup&) = delete;
ShaderGroup& operator=(ShaderGroup&&) noexcept = delete;
virtual ~ShaderGroup() = default;
/// @brief Checks if a shader exists in the group 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 group 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 group with the provided
/// instance. Shader type-names must match.
/// @param shader A `gfx::Shader`. The ShaderGroup 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 Replace a matching shader in the group with the provided
/// instance. Shader type-names must match.
/// This variant replaces by explicit name.
/// @param shader A `gfx::Shader`. The ShaderGroup will take ownership.
/// @param shaderName Unique name to register the shader under.
/// @return True if a match was found and the shader was replaced, false
/// otherwise.
[[nodiscard]] virtual bool replaceShader(std::shared_ptr<Shader>&& shader, const std::string& shaderName) noexcept;
/// @brief Register a new shader with the group. If a shader is present
/// in the group with a conflicting name, registration will fail.
/// @param shader A `gfx::Shader` to register. The ShaderGroup 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 Register a new shader with the group. If a shader is present
/// in the group with a conflicting name, registration will fail.
/// This variant registers using an explicit name.
/// @param shader A `gfx::Shader` to register. The ShaderGroup will
/// take ownership.
/// @param shaderName Unique name to register the shader under.
/// @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, const std::string& shaderName) noexcept;
/// @brief Shorthand helper to quickly get a derived type from the group.
/// @tparam T Derived type, inheriting `gfx::Shader`
/// @param shaderName The group name to look up
/// @return T or nullptr if not found in the group
template <typename T, typename std::enable_if_t<is_shader_v<T>, bool>* = nullptr>
std::shared_ptr<T> get(const std::string& shaderName) noexcept {
auto shader = getShader(shaderName);
if (!shader || shader->typeName() != T::Name) {
return nullptr;
}
return std::static_pointer_cast<T>(shader);
}
/// @brief Shorthand helper to quickly get a derived type from the group.
/// This variant looks up shaders only by type name.
/// @tparam T Derived type, inheriting `gfx::Shader`
/// @return T or nullptr if not found in the group
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->typeName() != T::Name) {
return nullptr;
}
return std::static_pointer_cast<T>(shader);
}
/// @brief Ensure the destination 'to' is populated with the requested
/// shader. If already non-null, does nothing.
/// @tparam T Derived type, inheriting `gfx::Shader`
/// @param to Location to store the shader
/// @param shaderName The group name to look up
/// @return True if 'to' has a valid program object, false otherwise.
template <typename T, typename std::enable_if_t<is_shader_v<T>, bool>* = nullptr>
bool populate(std::shared_ptr<T>& to, const std::string& shaderName) noexcept {
if (to) {
return true;
}
auto shader = getShader(shaderName);
if (!shader || shader->typeName() != T::Name) {
return false;
}
to = std::static_pointer_cast<T>(shader);
return true;
}
/// @brief Ensure the destination 'to' is populated with the requested
/// shader. If already non-null, does nothing. This variant looks up
/// shaders only by type name.
/// @tparam T Derived type, inheriting `gfx::Shader`
/// @param to Location to store the shader
/// @return True if 'to' has a valid program object, false otherwise.
template <typename T, typename std::enable_if_t<is_shader_v<T>, bool>* = nullptr>
bool populate(std::shared_ptr<T>& to) noexcept {
if (to) {
return true;
}
auto shader = getShader(std::string(T::Name));
if (!shader || shader->typeName() != T::Name) {
return false;
}
to = std::static_pointer_cast<T>(shader);
return true;
}
/// @brief Get a shader from the group by its set of data driven properties as uniforms.
/// if no shader is found, create and register the shader.
/// @param propertiesAsUniforms Vector of data driven properties as uniforms.
/// @return A `gfx::Shader`
virtual const std::shared_ptr<gfx::Shader> getOrCreateShader(gfx::Context&, const std::vector<std::string>&) {
return nullptr;
}
private:
std::unordered_map<std::string, std::shared_ptr<gfx::Shader>> programs;
mutable std::shared_mutex programLock;
};
} // namespace gfx
} // namespace mbgl

View File

@@ -1,6 +1,6 @@
#pragma once
#include <mbgl/gfx/shader.hpp>
#include <mbgl/gfx/shader_group.hpp>
#include <memory>
#include <mutex>
@@ -10,8 +10,10 @@
namespace mbgl {
namespace gfx {
/// @brief A ShaderRegistry contains a collection of gfx::Shader instances.
/// Using the registry, shaders may be dynamically registered or replaced
using ShaderGroupPtr = std::shared_ptr<gfx::ShaderGroup>;
/// @brief A ShaderRegistry contains a collection of gfx::ShaderGroup instances.
/// Using the registry, shader groups may be dynamically registered or replaced
/// at runtime.
class ShaderRegistry {
public:
@@ -22,120 +24,44 @@ public:
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 the legacy shader group.
/// @return A `gfx::ShaderGroup`
[[nodiscard]] ShaderGroup& getLegacyGroup() 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
/// @brief Checks if a shader group exists in the registry for the given name.
/// @param shaderGroupName Name of shader group
/// @return If a shader group is found, true
[[nodiscard]] virtual bool isShaderGroup(const std::string& shaderGroupName) const noexcept;
/// @brief Get a shader group from the registry by name.
/// @param shaderGroupName Name of shader group
/// @return A `gfx::ShaderGroup` or `nullptr` if no shader group is found with the
/// given name
[[nodiscard]] virtual const std::shared_ptr<gfx::Shader> getShader(const std::string& shaderName) const noexcept;
[[nodiscard]] virtual const ShaderGroupPtr getShaderGroup(const std::string& shaderGroupName) const noexcept;
/// @brief Replace a matching shader in the registry with the provided
/// instance. Shader type-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
/// @brief Replace a matching shader group in the registry with the provided
/// name.
/// @param shaderGroup A `gfx::ShaderGroup`. The ShaderRegistry will take ownership.
/// @param shaderGroupName Unique name to register the shader group under.
/// @return True if a match was found and the shader group was replaced, false
/// otherwise.
[[nodiscard]] virtual bool replaceShader(std::shared_ptr<Shader>&& shader) noexcept;
[[nodiscard]] virtual bool replaceShader(ShaderGroupPtr&& shaderGroup, const std::string& shaderName) noexcept;
/// @brief Replace a matching shader in the registry with the provided
/// instance. Shader type-names must match.
/// This variant replaces by explicit name.
/// @param shader A `gfx::Shader`. The ShaderRegistry will take ownership.
/// @param shaderName Unique name to register the shader under.
/// @return True if a match was found and the shader was replaced, false
/// otherwise.
[[nodiscard]] virtual bool replaceShader(std::shared_ptr<Shader>&& shader, const std::string& shaderName) 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
/// @brief Register a new shader group with the registry for the given name.
/// If a shader group is present in the registry with a conflicting name,
/// registration will fail.
/// @param shaderGroup A `gfx::ShaderGroup` to register. The ShaderRegistry will
/// take ownership.
/// @return True if the shader was registered, false if another shader is
/// @param shaderGroupName Unique name to register the shader group under.
/// @return True if the shader group was registered, false if another shader group is
/// already present with a conflicting name.
[[nodiscard]] virtual bool registerShader(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.
/// This variant registers using an explicit name.
/// @param shader A `gfx::Shader` to register. The ShaderRegistry will
/// take ownership.
/// @param shaderName Unique name to register the shader under.
/// @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, const std::string& shaderName) noexcept;
/// @brief Shorthand helper to quickly get a derived type from the registry.
/// @tparam T Derived type, inheriting `gfx::Shader`
/// @param shaderName The registry name to look up
/// @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(const std::string& shaderName) noexcept {
auto shader = getShader(shaderName);
if (!shader || shader->typeName() != T::Name) {
return nullptr;
}
return std::static_pointer_cast<T>(shader);
}
/// @brief Shorthand helper to quickly get a derived type from the registry.
/// This variant looks up shaders only by type name.
/// @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->typeName() != T::Name) {
return nullptr;
}
return std::static_pointer_cast<T>(shader);
}
/// @brief Ensure the destination 'to' is populated with the requested
/// shader. If already non-null, does nothing.
/// @tparam T Derived type, inheriting `gfx::Shader`
/// @param to Location to store the shader
/// @param shaderName The registry name to look up
/// @return True if 'to' has a valid program object, false otherwise.
template <typename T, typename std::enable_if_t<is_shader_v<T>, bool>* = nullptr>
bool populate(std::shared_ptr<T>& to, const std::string& shaderName) noexcept {
if (to) {
return true;
}
auto shader = getShader(shaderName);
if (!shader || shader->typeName() != T::Name) {
return false;
}
to = std::static_pointer_cast<T>(shader);
return true;
}
/// @brief Ensure the destination 'to' is populated with the requested
/// shader. If already non-null, does nothing. This variant looks up
/// shaders only by type name.
/// @tparam T Derived type, inheriting `gfx::Shader`
/// @param to Location to store the shader
/// @return True if 'to' has a valid program object, false otherwise.
template <typename T, typename std::enable_if_t<is_shader_v<T>, bool>* = nullptr>
bool populate(std::shared_ptr<T>& to) noexcept {
if (to) {
return true;
}
auto shader = getShader(std::string(T::Name));
if (!shader || shader->typeName() != T::Name) {
return false;
}
to = std::static_pointer_cast<T>(shader);
return true;
}
[[nodiscard]] virtual bool registerShaderGroup(ShaderGroupPtr&& shaderGroup,
const std::string& shaderGroupName) noexcept;
private:
std::unordered_map<std::string, std::shared_ptr<gfx::Shader>> programs;
mutable std::shared_mutex programLock;
gfx::ShaderGroup legacyGroup;
std::unordered_map<std::string, ShaderGroupPtr> shaderGroups;
mutable std::shared_mutex shaderGroupLock;
};
} // namespace gfx

View File

@@ -33,8 +33,10 @@ public:
using int2 = std::array<std::int32_t, 2>;
using int3 = std::array<std::int32_t, 3>;
using int4 = std::array<std::int32_t, 4>;
using ushort8 = std::array<std::uint16_t, 8>;
using ElementType = std::variant<std::int32_t, int2, int3, int4, float, float2, float3, float4, matf3, matf4>;
using ElementType =
std::variant<std::int32_t, ushort8, int2, int3, int4, float, float2, float3, float4, matf3, matf4>;
VertexAttribute(int index_, AttributeDataType dataType_, int size_, std::size_t count_, std::size_t stride_)
: index(index_),
@@ -106,6 +108,19 @@ public:
rawData.clear();
}
template <std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type set(std::size_t, std::tuple<Tp...>, std::size_t) {}
template <std::size_t I = 0, typename... Tp>
inline typename std::enable_if <
I<sizeof...(Tp), void>::type set(std::size_t i, std::tuple<Tp...> tuple, std::size_t tupleIndex) {
if (tupleIndex == 0) {
set(i, std::get<I>(tuple).a1);
} else {
set<I + 1, Tp...>(i, tuple, tupleIndex - 1);
}
}
protected:
VertexAttribute& operator=(const VertexAttribute&) = default;
VertexAttribute& operator=(VertexAttribute&& other) {
@@ -219,6 +234,33 @@ public:
return newAttrs;
}
template <class... DataDrivenPaintProperty, class Binders, class Evaluated>
std::vector<std::string> readDataDrivenPaintProperties(const Binders& binders, const Evaluated& evaluated) {
std::vector<std::string> propertiesAsUniforms;
(
[&](const auto& attributeNames) {
for (std::size_t attrIndex = 0; attrIndex < attributeNames.size(); ++attrIndex) {
const auto& attributeName = std::string(attributeNames[attrIndex]);
if (auto& binder = binders.template get<DataDrivenPaintProperty>()) {
const auto vertexCount = binder->getVertexCount();
const auto isConstant = evaluated.template get<DataDrivenPaintProperty>().isConstant();
if (vertexCount > 0 && !isConstant) {
if (auto& attr = getOrAdd("a_" + attributeName)) {
for (std::size_t i = 0; i < vertexCount; ++i) {
attr->set(i, binder->getVertexValue(i), attrIndex);
}
}
propertiesAsUniforms.emplace_back("");
} else {
propertiesAsUniforms.emplace_back(attributeName);
}
}
}
}(DataDrivenPaintProperty::AttributeNames),
...);
return propertiesAsUniforms;
}
protected:
const std::unique_ptr<VertexAttribute>& add(std::string name, std::unique_ptr<VertexAttribute>&& attr) {
const auto result = attrs.insert(std::make_pair(std::move(name), std::unique_ptr<VertexAttribute>()));

View File

@@ -0,0 +1,53 @@
#pragma once
#include <mbgl/gfx/shader_group.hpp>
#include <mbgl/shaders/gl/shader_program_gl.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/programs/program_parameters.hpp>
namespace mbgl {
namespace gl {
template <shaders::BuiltIn ShaderID>
class ShaderGroupGL final : public gfx::ShaderGroup {
public:
ShaderGroupGL(const ProgramParameters& programParameters_)
: ShaderGroup(),
programParameters(programParameters_){};
~ShaderGroupGL() noexcept override = default;
const std::shared_ptr<gfx::Shader> getOrCreateShader(
gfx::Context& context, const std::vector<std::string>& propertiesAsUniforms) override {
const auto& name = shaders::ShaderSource<ShaderID, gfx::Backend::Type::OpenGL>::name;
const auto& vert = shaders::ShaderSource<ShaderID, gfx::Backend::Type::OpenGL>::vertex;
const auto& frag = shaders::ShaderSource<ShaderID, gfx::Backend::Type::OpenGL>::fragment;
uint32_t key = 0;
std::string additionalDefines;
for (unsigned int i = 0; i < propertiesAsUniforms.size(); i++) {
if (propertiesAsUniforms[i].empty()) {
continue;
}
key |= 1 << i;
additionalDefines += "#define HAS_UNIFORM_u_";
additionalDefines += propertiesAsUniforms[i];
additionalDefines += "\n";
}
std::string shaderName = std::string(name) + "#" + std::to_string(key);
auto shader = get<gl::ShaderProgramGL>(shaderName);
if (!shader) {
shader = ShaderProgramGL::create(
static_cast<gl::Context&>(context), programParameters, shaderName, vert, frag, additionalDefines);
if (!registerShader(shader, shaderName)) {
throw std::runtime_error("Failed to register " + shaderName + " with shader group!");
}
}
return shader;
}
private:
ProgramParameters programParameters;
};
} // namespace gl
} // namespace mbgl

View File

@@ -1,6 +1,7 @@
#pragma once
#include <array>
#include <algorithm>
namespace mbgl {
@@ -10,4 +11,14 @@ using vec3f = std::array<float, 3>;
using vec3i = std::array<int, 3>;
using vec4 = std::array<double, 4>;
} // namespace mbgl
template <typename Type, std::size_t... sizes>
auto concatenate(const std::array<Type, sizes>&... arrays) {
std::array<Type, (sizes + ...)> result;
std::size_t index{};
((std::copy_n(arrays.begin(), sizes, result.begin() + index), index += sizes), ...);
return result;
}
} // namespace mbgl

View File

@@ -63,6 +63,8 @@ struct AttributeDataTypeOf<uint16_t, 3> : std::integral_constant<AttributeDataTy
template <>
struct AttributeDataTypeOf<uint16_t, 4> : std::integral_constant<AttributeDataType, AttributeDataType::UShort4> {};
template <>
struct AttributeDataTypeOf<uint16_t, 8> : std::integral_constant<AttributeDataType, AttributeDataType::UShort8> {};
template <>
struct AttributeDataTypeOf<int32_t, 1> : std::integral_constant<AttributeDataType, AttributeDataType::Int> {};
template <>
struct AttributeDataTypeOf<int32_t, 2> : std::integral_constant<AttributeDataType, AttributeDataType::Int2> {};

View File

@@ -0,0 +1,51 @@
#include <mbgl/gfx/shader_group.hpp>
#include <mbgl/gfx/shader.hpp>
namespace mbgl {
namespace gfx {
bool ShaderGroup::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> ShaderGroup::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 ShaderGroup::replaceShader(std::shared_ptr<gfx::Shader>&& shader) noexcept {
return replaceShader(std::move(shader), std::string{shader->typeName()});
}
bool ShaderGroup::replaceShader(std::shared_ptr<Shader>&& shader, const std::string& shaderName) noexcept {
std::unique_lock<std::shared_mutex> writerLock(programLock);
if (programs.find(shaderName) == programs.end()) {
return false;
}
programs[shaderName] = std::move(shader);
return true;
}
bool ShaderGroup::registerShader(std::shared_ptr<gfx::Shader>&& shader) noexcept {
return registerShader(std::move(shader), std::string{shader->typeName()});
}
bool ShaderGroup::registerShader(std::shared_ptr<Shader>&& shader, const std::string& shaderName) noexcept {
std::unique_lock<std::shared_mutex> writerLock(programLock);
if (programs.find(shaderName) != programs.end()) {
return false;
}
programs.emplace(shaderName, std::move(shader));
return true;
}
} // namespace gfx
} // namespace mbgl

View File

@@ -1,51 +1,48 @@
#include <mbgl/gfx/shader_registry.hpp>
#include <mbgl/gfx/shader.hpp>
#include <mbgl/gfx/shader_group.hpp>
namespace mbgl {
namespace gfx {
ShaderRegistry::ShaderRegistry() {}
ShaderRegistry::ShaderRegistry()
: legacyGroup() {}
bool ShaderRegistry::isShader(const std::string& shaderName) const noexcept {
std::shared_lock<std::shared_mutex> readerLock(programLock);
return programs.find(shaderName) != programs.end();
ShaderGroup& ShaderRegistry::getLegacyGroup() noexcept {
return legacyGroup;
}
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()) {
bool ShaderRegistry::isShaderGroup(const std::string& shaderGroupName) const noexcept {
std::shared_lock<std::shared_mutex> readerLock(shaderGroupLock);
return shaderGroups.find(shaderGroupName) != shaderGroups.end();
}
const ShaderGroupPtr ShaderRegistry::getShaderGroup(const std::string& shaderGroupName) const noexcept {
std::shared_lock<std::shared_mutex> readerLock(shaderGroupLock);
const auto it = shaderGroups.find(shaderGroupName);
if (it == shaderGroups.end()) {
return nullptr;
}
return it->second;
}
bool ShaderRegistry::replaceShader(std::shared_ptr<gfx::Shader>&& shader) noexcept {
return replaceShader(std::move(shader), std::string{shader->typeName()});
}
bool ShaderRegistry::replaceShader(std::shared_ptr<Shader>&& shader, const std::string& shaderName) noexcept {
std::unique_lock<std::shared_mutex> writerLock(programLock);
if (programs.find(shaderName) == programs.end()) {
bool ShaderRegistry::replaceShader(ShaderGroupPtr&& shader, const std::string& shaderGroupName) noexcept {
std::unique_lock<std::shared_mutex> writerLock(shaderGroupLock);
if (shaderGroups.find(shaderGroupName) == shaderGroups.end()) {
return false;
}
programs[shaderName] = std::move(shader);
shaderGroups[shaderGroupName] = std::move(shader);
return true;
}
bool ShaderRegistry::registerShader(std::shared_ptr<gfx::Shader>&& shader) noexcept {
return registerShader(std::move(shader), std::string{shader->typeName()});
}
bool ShaderRegistry::registerShader(std::shared_ptr<Shader>&& shader, const std::string& shaderName) noexcept {
std::unique_lock<std::shared_mutex> writerLock(programLock);
if (programs.find(shaderName) != programs.end()) {
bool ShaderRegistry::registerShaderGroup(ShaderGroupPtr&& shader, const std::string& shaderGroupName) noexcept {
std::unique_lock<std::shared_mutex> writerLock(shaderGroupLock);
if (shaderGroups.find(shaderGroupName) != shaderGroups.end()) {
return false;
}
programs.emplace(shaderName, std::move(shader));
shaderGroups.emplace(shaderGroupName, std::move(shader));
return true;
}

View File

@@ -468,7 +468,9 @@ gfx::UniformBufferPtr Context::createUniformBuffer(const void* data, std::size_t
}
gfx::ShaderProgramBasePtr Context::getGenericShader(gfx::ShaderRegistry& shaders, const std::string& name) {
return shaders.get<gl::ShaderProgramGL>(name);
std::vector<std::string> emptyAttributes(0);
return std::static_pointer_cast<gfx::ShaderProgramBase>(
shaders.getShaderGroup(name)->getOrCreateShader(*this, emptyAttributes));
}
TileLayerGroupPtr Context::createTileLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) {

View File

@@ -4,7 +4,7 @@
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/extension.hpp>
#include <mbgl/shaders/shader_manifest.hpp>
#include <mbgl/shaders/gl/shader_program_gl.hpp>
#include <mbgl/shaders/gl/shader_group_gl.hpp>
#include <mbgl/util/logging.hpp>
#include <cassert>
@@ -69,10 +69,9 @@ RendererBackend::~RendererBackend() = default;
/// @brief Register a list of types with a shader registry instance
/// @tparam ...ShaderID Pack of BuiltIn:: shader IDs
/// @param registry A shader registry instance
/// @param glContext The GL context instance
/// @param programParameters ProgramParameters used to initialize each instance
template <shaders::BuiltIn... ShaderID>
void registerTypes(gfx::ShaderRegistry& registry, gl::Context& glContext, const ProgramParameters& programParameters) {
void registerTypes(gfx::ShaderRegistry& registry, const ProgramParameters& programParameters) {
/// The following fold expression will create a shader for every type
/// in the parameter pack and register it with the shader registry.
@@ -80,15 +79,12 @@ void registerTypes(gfx::ShaderRegistry& registry, gl::Context& glContext, const
/// failure, we shouldn't expect registration to faill unless the shader
/// registry instance provided already has conflicting programs present.
(
[&](const std::string& name, const std::string& vert, const std::string& frag) {
[&]() {
using Ty = shaders::ShaderSource<ShaderID, gfx::Backend::Type::OpenGL>;
if (!registry.registerShader(gl::ShaderProgramGL::create(glContext, programParameters, name, vert, frag),
name)) {
if (!registry.registerShaderGroup(std::make_shared<ShaderGroupGL<ShaderID>>(programParameters), Ty::name)) {
throw std::runtime_error("Failed to register " + std::string(Ty::name) + " with shader registry!");
}
}(shaders::ShaderSource<ShaderID, gfx::Backend::Type::OpenGL>::name,
shaders::ShaderSource<ShaderID, gfx::Backend::Type::OpenGL>::vertex,
shaders::ShaderSource<ShaderID, gfx::Backend::Type::OpenGL>::fragment),
}(),
...);
}
@@ -100,7 +96,7 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar
shaders::BuiltIn::FillOutlineShader,
shaders::BuiltIn::FillOutlinePatternShader,
shaders::BuiltIn::FillPatternShader,
shaders::BuiltIn::RasterShader>(shaders, static_cast<gl::Context&>(*context), programParameters);
shaders::BuiltIn::RasterShader>(shaders, programParameters);
}
} // namespace gl

View File

@@ -37,7 +37,7 @@ void registerTypes(gfx::ShaderRegistry& registry, const ProgramParameters& progr
if (!expr) {
throw std::runtime_error("Failed to register " + std::string(T::Name) + " with shader registry!");
}
}(registry.registerShader(std::make_shared<T>(programParameters_))),
}(registry.getLegacyGroup().registerShader(std::make_shared<T>(programParameters_))),
...);
}

View File

@@ -40,17 +40,9 @@ struct alignas(16) CircleEvaluatedPropsUBO {
};
static_assert(sizeof(CircleEvaluatedPropsUBO) % 16 == 0);
struct alignas(16) CircleInterpolateUBO {
float color_t;
float radius_t;
float blur_t;
float opacity_t;
float stroke_color_t;
float stroke_width_t;
float stroke_opacity_t;
float padding;
};
static_assert(sizeof(CircleInterpolateUBO) % 16 == 0);
static constexpr std::string_view CircleDrawableUBOName = "CircleDrawableUBO";
static constexpr std::string_view CirclePaintParamsUBOName = "CirclePaintParamsUBO";
static constexpr std::string_view CircleEvaluatedPropsUBOName = "CircleEvaluatedPropsUBO";
void CircleLayerTweaker::execute(LayerGroup& layerGroup,
const RenderTree& renderTree,
@@ -66,9 +58,10 @@ void CircleLayerTweaker::execute(LayerGroup& layerGroup,
const auto debugGroup = parameters.encoder->createDebugGroup(label.c_str());
#endif
CirclePaintParamsUBO paintParamsUBO;
paintParamsUBO.camera_to_center_distance = parameters.state.getCameraToCenterDistance();
paintParamsUBO.device_pixel_ratio = parameters.pixelRatio;
CirclePaintParamsUBO paintParamsUBO = {
/* .camera_to_center_distance = */ parameters.state.getCameraToCenterDistance(),
/* .device_pixel_ratio = */ parameters.pixelRatio,
/* .padding = */ {0}};
if (!paintParamsUniformBuffer) {
paintParamsUniformBuffer = parameters.context.createUniformBuffer(&paintParamsUBO, sizeof(paintParamsUBO));
@@ -79,39 +72,25 @@ void CircleLayerTweaker::execute(LayerGroup& layerGroup,
const bool pitchWithMap = evaluated.get<CirclePitchAlignment>() == AlignmentType::Map;
if (!evaluatedPropsUniformBuffer) {
CircleEvaluatedPropsUBO evaluatedPropsUBO;
evaluatedPropsUBO.color = evaluated.get<CircleColor>().constantOr(Color());
evaluatedPropsUBO.radius = evaluated.get<CircleRadius>().constantOr(0);
evaluatedPropsUBO.blur = evaluated.get<CircleBlur>().constantOr(0);
evaluatedPropsUBO.opacity = evaluated.get<CircleOpacity>().constantOr(0);
evaluatedPropsUBO.stroke_color = evaluated.get<CircleStrokeColor>().constantOr(Color());
evaluatedPropsUBO.stroke_width = evaluated.get<CircleStrokeWidth>().constantOr(0);
evaluatedPropsUBO.stroke_opacity = evaluated.get<CircleStrokeOpacity>().constantOr(0);
evaluatedPropsUBO.scale_with_map = evaluated.get<CirclePitchScale>() == CirclePitchScaleType::Map;
evaluatedPropsUBO.pitch_with_map = pitchWithMap;
CircleEvaluatedPropsUBO evaluatedPropsUBO = {
/* .color = */ evaluated.get<CircleColor>().constantOr(CircleColor::defaultValue()),
/* .stroke_color = */ evaluated.get<CircleStrokeColor>().constantOr(CircleStrokeColor::defaultValue()),
/* .radius = */ evaluated.get<CircleRadius>().constantOr(CircleRadius::defaultValue()),
/* .blur = */ evaluated.get<CircleBlur>().constantOr(CircleBlur::defaultValue()),
/* .opacity = */ evaluated.get<CircleOpacity>().constantOr(CircleOpacity::defaultValue()),
/* .stroke_width = */ evaluated.get<CircleStrokeWidth>().constantOr(CircleStrokeWidth::defaultValue()),
/* .stroke_opacity = */
evaluated.get<CircleStrokeOpacity>().constantOr(CircleStrokeOpacity::defaultValue()),
/* .scale_with_map = */ evaluated.get<CirclePitchScale>() == CirclePitchScaleType::Map,
/* .pitch_with_map = */ pitchWithMap,
/* .padding = */ 0};
evaluatedPropsUniformBuffer = parameters.context.createUniformBuffer(&evaluatedPropsUBO,
sizeof(evaluatedPropsUBO));
}
CircleInterpolateUBO interpolateUBO;
interpolateUBO.color_t = 0;
interpolateUBO.radius_t = 0;
interpolateUBO.blur_t = 0;
interpolateUBO.opacity_t = 0;
interpolateUBO.stroke_color_t = 0;
interpolateUBO.stroke_width_t = 0;
interpolateUBO.stroke_opacity_t = 0;
if (!interpolateUniformBuffer) {
interpolateUniformBuffer = parameters.context.createUniformBuffer(&interpolateUBO, sizeof(interpolateUBO));
} else {
interpolateUniformBuffer->update(&interpolateUBO, sizeof(CircleInterpolateUBO));
}
layerGroup.observeDrawables([&](gfx::Drawable& drawable) {
drawable.mutableUniformBuffers().addOrReplace("CirclePaintParamsUBO", paintParamsUniformBuffer);
drawable.mutableUniformBuffers().addOrReplace("CircleEvaluatedPropsUBO", evaluatedPropsUniformBuffer);
drawable.mutableUniformBuffers().addOrReplace("CircleInterpolateUBO", interpolateUniformBuffer);
drawable.mutableUniformBuffers().addOrReplace(CirclePaintParamsUBOName, paintParamsUniformBuffer);
drawable.mutableUniformBuffers().addOrReplace(CircleEvaluatedPropsUBOName, evaluatedPropsUniformBuffer);
if (!drawable.getTileID()) {
return;
@@ -124,20 +103,15 @@ void CircleLayerTweaker::execute(LayerGroup& layerGroup,
const auto matrix = getTileMatrix(
tileID, renderTree, parameters.state, translation, anchor, inViewportPixelUnits);
CircleDrawableUBO drawableUBO;
drawableUBO.matrix = util::cast<float>(matrix);
drawableUBO.extrude_scale =
pitchWithMap
? std::array<float, 2>{{tileID.pixelsToTileUnits(1.0f, static_cast<float>(parameters.state.getZoom())),
tileID.pixelsToTileUnits(1.0f, static_cast<float>(parameters.state.getZoom()))}}
: parameters.pixelsToGLUnits;
const auto pixelsToTileUnits = tileID.pixelsToTileUnits(1.0f, static_cast<float>(parameters.state.getZoom()));
if (auto& ubo = drawable.mutableUniformBuffers().get("CircleDrawableUBO")) {
ubo->update(&drawableUBO, sizeof(drawableUBO));
} else {
auto drawableUniformBuffer = parameters.context.createUniformBuffer(&drawableUBO, sizeof(drawableUBO));
drawable.mutableUniformBuffers().addOrReplace("CircleDrawableUBO", drawableUniformBuffer);
}
CircleDrawableUBO drawableUBO = {
/* .matrix = */ util::cast<float>(matrix),
/* .extrude_scale = */
pitchWithMap ? std::array<float, 2>{{pixelsToTileUnits}} : parameters.pixelsToGLUnits,
/* .padding = */ {0}};
drawable.mutableUniformBuffers().createOrUpdate(CircleDrawableUBOName, &drawableUBO, parameters.context);
});
}

View File

@@ -23,9 +23,8 @@ public:
void execute(LayerGroup&, const RenderTree&, const PaintParameters&) override;
protected:
gfx::UniformBufferPtr paintParamsUniformBuffer = nullptr;
gfx::UniformBufferPtr evaluatedPropsUniformBuffer = nullptr;
gfx::UniformBufferPtr interpolateUniformBuffer = nullptr;
gfx::UniformBufferPtr paintParamsUniformBuffer;
gfx::UniformBufferPtr evaluatedPropsUniformBuffer;
};
} // namespace mbgl

View File

@@ -79,8 +79,8 @@ void RenderBackgroundLayer::render(PaintParameters& parameters) {
// is drawn with glClear rather than this method.
// Ensure programs are available
if (!parameters.shaders.populate(backgroundProgram)) return;
if (!parameters.shaders.populate(backgroundPatternProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(backgroundProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(backgroundPatternProgram)) return;
const Properties<>::PossiblyEvaluated properties;
const BackgroundProgram::Binders paintAttributeData(properties, 0);

View File

@@ -8,9 +8,11 @@
#include <mbgl/programs/circle_program.hpp>
#include <mbgl/tile/tile.hpp>
#include <mbgl/style/layers/circle_layer_impl.hpp>
#include <mbgl/shaders/shader_program_base.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/gfx/drawable_builder.hpp>
#include <mbgl/gfx/cull_face_mode.hpp>
#include <mbgl/gfx/shader_group.hpp>
#include <mbgl/gfx/shader_registry.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/intersection_tests.hpp>
@@ -92,7 +94,7 @@ void RenderCircleLayer::render(PaintParameters& parameters) {
return;
}
if (!parameters.shaders.populate(circleProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(circleProgram)) return;
const auto drawTile = [&](const RenderTile& tile, const LayerRenderData* data, const auto& segments) {
auto& circleBucket = static_cast<CircleBucket&>(*data->bucket);
@@ -255,18 +257,37 @@ void RenderCircleLayer::removeTile(RenderPass renderPass, const OverscaledTileID
stats.tileDrawablesRemoved += tileLayerGroup->removeDrawables(renderPass, tileID).size();
}
struct alignas(16) CircleInterpolateUBO {
float color_t;
float radius_t;
float blur_t;
float opacity_t;
float stroke_color_t;
float stroke_width_t;
float stroke_opacity_t;
float padding;
};
static_assert(sizeof(CircleInterpolateUBO) % 16 == 0);
static const std::string CircleShaderGroupName = "CircleShader";
static constexpr std::string_view CircleInterpolateUBOName = "CircleInterpolateUBO";
void RenderCircleLayer::update(gfx::ShaderRegistry& shaders,
gfx::Context& context,
const TransformState& /*state*/,
const TransformState& state,
[[maybe_unused]] const RenderTree& renderTree,
UniqueChangeRequestVec& changes) {
std::unique_lock<std::mutex> guard(mutex);
if (!renderTiles || renderTiles->empty()) {
const auto removeAll = [&]() {
if (tileLayerGroup) {
stats.tileDrawablesRemoved += tileLayerGroup->getDrawableCount();
tileLayerGroup->clearDrawables();
}
};
if (!renderTiles || renderTiles->empty()) {
removeAll();
return;
}
@@ -280,8 +301,12 @@ void RenderCircleLayer::update(gfx::ShaderRegistry& shaders,
changes.emplace_back(std::make_unique<AddLayerGroupRequest>(tileLayerGroup, /*canReplace=*/true));
}
if (!circleShader) {
circleShader = context.getGenericShader(shaders, "CircleShader");
if (!circleShaderGroup) {
circleShaderGroup = shaders.getShaderGroup(CircleShaderGroupName);
}
if (!circleShaderGroup) {
removeAll();
return;
}
std::unordered_set<OverscaledTileID> newTileIDs(renderTiles->size());
@@ -308,13 +333,11 @@ void RenderCircleLayer::update(gfx::ShaderRegistry& shaders,
}
});
const auto& evaluated = static_cast<const CircleLayerProperties&>(*evaluatedProperties).evaluated;
for (const RenderTile& tile : *renderTiles) {
const auto& tileID = tile.getOverscaledTileID();
if (tileLayerGroup->getDrawableCount(renderPass, tileID) > 0) {
continue;
}
const LayerRenderData* renderData = getRenderDataForPass(tile, renderPass);
if (!renderData) {
removeTile(renderPass, tileID);
@@ -322,6 +345,45 @@ void RenderCircleLayer::update(gfx::ShaderRegistry& shaders,
}
auto& bucket = static_cast<CircleBucket&>(*renderData->bucket);
const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID());
float zoom = static_cast<float>(state.getZoom());
const CircleInterpolateUBO interpolateUBO = {
/* .color_t = */ std::get<0>(paintPropertyBinders.get<CircleColor>()->interpolationFactor(zoom)),
/* .radius_t = */ std::get<0>(paintPropertyBinders.get<CircleRadius>()->interpolationFactor(zoom)),
/* .blur_t = */ std::get<0>(paintPropertyBinders.get<CircleBlur>()->interpolationFactor(zoom)),
/* .opacity_t = */ std::get<0>(paintPropertyBinders.get<CircleOpacity>()->interpolationFactor(zoom)),
/* .stroke_color_t = */
std::get<0>(paintPropertyBinders.get<CircleStrokeColor>()->interpolationFactor(zoom)),
/* .stroke_width_t = */
std::get<0>(paintPropertyBinders.get<CircleStrokeWidth>()->interpolationFactor(zoom)),
/* .stroke_opacity_t = */
std::get<0>(paintPropertyBinders.get<CircleStrokeOpacity>()->interpolationFactor(zoom)),
/* .padding = */ 0};
tileLayerGroup->observeDrawables(renderPass, tileID, [&](gfx::Drawable& drawable) {
drawable.mutableUniformBuffers().createOrUpdate(CircleInterpolateUBOName, &interpolateUBO, context);
});
if (tileLayerGroup->getDrawableCount(renderPass, tileID) > 0) {
continue;
}
circleVertexAttrs.clear();
auto propertiesAsUniforms = circleVertexAttrs.readDataDrivenPaintProperties<CircleColor,
CircleRadius,
CircleBlur,
CircleOpacity,
CircleStrokeColor,
CircleStrokeWidth,
CircleStrokeOpacity>(
paintPropertyBinders, evaluated);
auto circleShader = circleShaderGroup->getOrCreateShader(context, propertiesAsUniforms);
if (!circleShader) {
continue;
}
std::vector<std::array<int16_t, 2>> rawVerts;
const auto buildVertices = [&]() {
@@ -333,94 +395,8 @@ void RenderCircleLayer::update(gfx::ShaderRegistry& shaders,
}
};
circleVertexAttrs.clear();
const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID());
if (auto& binder = paintPropertyBinders.get<CircleColor>()) {
const auto count = binder->getVertexCount();
if (auto& attr = circleVertexAttrs.getOrAdd("a_color")) {
for (std::size_t i = 0; i < count; ++i) {
const auto& packedColor = static_cast<const gfx::detail::VertexType<gfx::AttributeType<float, 2>>*>(
binder->getVertexValue(i))
->a1;
attr->set<gfx::VertexAttribute::float4>(
i, {packedColor[0], packedColor[1], packedColor[0], packedColor[1]});
}
}
}
if (auto& binder = paintPropertyBinders.get<CircleRadius>()) {
const auto count = binder->getVertexCount();
if (auto& attr = circleVertexAttrs.getOrAdd("a_radius")) {
for (std::size_t i = 0; i < count; ++i) {
const auto& radius = static_cast<const gfx::detail::VertexType<gfx::AttributeType<float, 1>>*>(
binder->getVertexValue(i))
->a1;
attr->set<gfx::VertexAttribute::float2>(i, {radius[0], radius[0]});
}
}
}
if (auto& binder = paintPropertyBinders.get<CircleBlur>()) {
const auto count = binder->getVertexCount();
if (auto& attr = circleVertexAttrs.getOrAdd("a_blur")) {
for (std::size_t i = 0; i < count; ++i) {
const auto& blur = static_cast<const gfx::detail::VertexType<gfx::AttributeType<float, 1>>*>(
binder->getVertexValue(i))
->a1;
attr->set<gfx::VertexAttribute::float2>(i, {blur[0], blur[0]});
}
}
}
if (auto& binder = paintPropertyBinders.get<CircleOpacity>()) {
const auto count = binder->getVertexCount();
if (auto& attr = circleVertexAttrs.getOrAdd("a_opacity")) {
for (std::size_t i = 0; i < count; ++i) {
const auto& opacity = static_cast<const gfx::detail::VertexType<gfx::AttributeType<float, 1>>*>(
binder->getVertexValue(i))
->a1;
attr->set<gfx::VertexAttribute::float2>(i, {opacity[0], opacity[0]});
}
}
}
if (auto& binder = paintPropertyBinders.get<CircleStrokeColor>()) {
const auto count = binder->getVertexCount();
if (auto& attr = circleVertexAttrs.getOrAdd("a_stroke_color")) {
for (std::size_t i = 0; i < count; ++i) {
const auto& packedStrokeColor =
static_cast<const gfx::detail::VertexType<gfx::AttributeType<float, 2>>*>(
binder->getVertexValue(i))
->a1;
attr->set<gfx::VertexAttribute::float4>(
i, {packedStrokeColor[0], packedStrokeColor[1], packedStrokeColor[0], packedStrokeColor[1]});
}
}
}
if (auto& binder = paintPropertyBinders.get<CircleStrokeWidth>()) {
const auto count = binder->getVertexCount();
if (auto& attr = circleVertexAttrs.getOrAdd("a_stroke_width")) {
for (std::size_t i = 0; i < count; ++i) {
const auto& strokeWidth = static_cast<const gfx::detail::VertexType<gfx::AttributeType<float, 1>>*>(
binder->getVertexValue(i))
->a1;
attr->set<gfx::VertexAttribute::float2>(i, {strokeWidth[0], strokeWidth[0]});
}
}
}
if (auto& binder = paintPropertyBinders.get<CircleStrokeOpacity>()) {
const auto count = binder->getVertexCount();
if (auto& attr = circleVertexAttrs.getOrAdd("a_stroke_opacity")) {
for (std::size_t i = 0; i < count; ++i) {
const auto& strokeOpacity =
static_cast<const gfx::detail::VertexType<gfx::AttributeType<float, 1>>*>(
binder->getVertexValue(i))
->a1;
attr->set<gfx::VertexAttribute::float2>(i, {strokeOpacity[0], strokeOpacity[0]});
}
}
}
circleBuilder = context.createDrawableBuilder("circle");
circleBuilder->setShader(circleShader);
circleBuilder->setShader(std::static_pointer_cast<gfx::ShaderProgramBase>(circleShader));
circleBuilder->setColorAttrMode(gfx::DrawableBuilder::ColorAttrMode::None);
circleBuilder->setDepthType((renderPass == RenderPass::Opaque) ? gfx::DepthMaskType::ReadWrite
: gfx::DepthMaskType::ReadOnly);
@@ -432,14 +408,15 @@ void RenderCircleLayer::update(gfx::ShaderRegistry& shaders,
buildVertices();
circleBuilder->addVertices(rawVerts, 0, rawVerts.size());
for (const auto& seg : bucket.segments) {
circleBuilder->addTriangles(bucket.triangles.vector(), seg.indexOffset, seg.indexLength);
}
circleBuilder->setSegments(gfx::Triangles(),
bucket.triangles.vector(),
reinterpret_cast<const std::vector<Segment<void>>&>(bucket.segments));
circleBuilder->flush();
for (auto& drawable : circleBuilder->clearDrawables()) {
drawable->setTileID(tileID);
drawable->mutableUniformBuffers().createOrUpdate(CircleInterpolateUBOName, &interpolateUBO, context);
tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
++stats.tileDrawablesAdded;

View File

@@ -8,6 +8,13 @@ namespace mbgl {
class CircleProgram;
namespace gfx {
class ShaderGroup;
class UniformBuffer;
using ShaderGroupPtr = std::shared_ptr<ShaderGroup>;
using UniformBufferPtr = std::shared_ptr<UniformBuffer>;
} // namespace gfx
class RenderCircleLayer final : public RenderLayer {
public:
explicit RenderCircleLayer(Immutable<style::CircleLayer::Impl>);
@@ -46,7 +53,7 @@ private:
// Programs
std::shared_ptr<CircleProgram> circleProgram;
gfx::ShaderProgramBasePtr circleShader;
gfx::ShaderGroupPtr circleShaderGroup;
};
} // namespace mbgl

View File

@@ -71,8 +71,8 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters) {
return;
}
if (!parameters.shaders.populate(fillExtrusionProgram)) return;
if (!parameters.shaders.populate(fillExtrusionPatternProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(fillExtrusionProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(fillExtrusionPatternProgram)) return;
const auto& evaluated = static_cast<const FillExtrusionLayerProperties&>(*evaluatedProperties).evaluated;
const auto& crossfade = static_cast<const FillExtrusionLayerProperties&>(*evaluatedProperties).crossfade;

View File

@@ -84,10 +84,10 @@ bool RenderFillLayer::hasCrossfade() const {
void RenderFillLayer::render(PaintParameters& parameters) {
assert(renderTiles);
if (!parameters.shaders.populate(fillProgram)) return;
if (!parameters.shaders.populate(fillPatternProgram)) return;
if (!parameters.shaders.populate(fillOutlineProgram)) return;
if (!parameters.shaders.populate(fillOutlinePatternProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(fillProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(fillPatternProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(fillOutlineProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(fillOutlinePatternProgram)) return;
if (unevaluated.get<FillPattern>().isUndefined()) {
parameters.renderTileClippingMasks(renderTiles);
@@ -301,10 +301,8 @@ bool bindFloat(const FloatBinder& binder, gfx::VertexAttributeArray& attrs, std:
const auto count = binder.getVertexCount();
if (auto& attr = attrs.getOrAdd(std::move(attributeName))) {
for (std::size_t i = 0; i < count; ++i) {
const auto& value = static_cast<const FloatVertex*>(binder.getVertexValue(i))->a1;
const auto& from = value; // TODO: should be two different values, right?
const auto& to = value;
attr->set(i, util::concat(from, to));
const auto& value = std::get<0>(binder.getVertexValue(i)).a1;
attr->set(i, value);
}
return true;
}
@@ -321,10 +319,8 @@ bool bindFloatPair(const FloatPairBinder& binder, gfx::VertexAttributeArray& att
// check that vertexVector.elements() == sum(segments.vertexLength)
if (auto& attr = attrs.getOrAdd(std::move(attributeName))) {
for (std::size_t i = 0; i < count; ++i) {
const auto& value = static_cast<const FloatPairVertex*>(binder.getVertexValue(i))->a1;
const auto& from = value; // TODO: should be two different values, right?
const auto& to = value;
attr->set(i, util::concat(from, to));
const auto& value = std::get<0>(binder.getVertexValue(i)).a1;
attr->set(i, value);
}
return true;
}
@@ -341,10 +337,8 @@ bool bindColor(const ColorBinder& binder, gfx::VertexAttributeArray& attrs, std:
// check that vertexVector.elements() == sum(segments.vertexLength)
if (auto& attr = attrs.getOrAdd(std::move(attributeName))) {
for (std::size_t i = 0; i < count; ++i) {
const auto& value = static_cast<const FloatPairVertex*>(binder.getVertexValue(i))->a1;
const auto& from = value; // TODO: should be two different values, right?
const auto& to = value;
attr->set(i, util::concat(from, to));
const auto& value = std::get<0>(binder.getVertexValue(i)).a1;
attr->set(i, value);
}
return true;
}
@@ -361,8 +355,8 @@ bool bindImage(const ImageBinder& binder, gfx::VertexAttributeArray& attrs, std:
// check that vertexVector.elements() == sum(segments.vertexLength)
if (auto& attr = attrs.getOrAdd(std::move(attributeName))) {
for (std::size_t i = 0; i < count; ++i) {
const auto& value = static_cast<const UShortQuadVertex*>(binder.getVertexValue(i))->a1;
attr->set(i, util::cast<int32_t>(value));
const auto& value = std::get<0>(binder.getVertexValue(i)).a1;
attr->set(i, value);
}
return true;
}

View File

@@ -69,8 +69,8 @@ void RenderHeatmapLayer::render(PaintParameters& parameters) {
return;
}
if (!parameters.shaders.populate(heatmapProgram)) return;
if (!parameters.shaders.populate(heatmapTextureProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(heatmapProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(heatmapTextureProgram)) return;
if (parameters.pass == RenderPass::Pass3D) {
const auto& viewportSize = parameters.staticData.backendSize;

View File

@@ -79,8 +79,8 @@ void RenderHillshadeLayer::render(PaintParameters& parameters) {
assert(renderTiles);
if (parameters.pass != RenderPass::Translucent && parameters.pass != RenderPass::Pass3D) return;
if (!parameters.shaders.populate(hillshadeProgram)) return;
if (!parameters.shaders.populate(hillshadePrepareProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(hillshadeProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(hillshadePrepareProgram)) return;
const auto& evaluated = static_cast<const HillshadeLayerProperties&>(*evaluatedProperties).evaluated;
auto draw = [&](const mat4& matrix,

View File

@@ -95,10 +95,10 @@ void RenderLineLayer::render(PaintParameters& parameters) {
return;
}
if (!parameters.shaders.populate(lineProgram)) return;
if (!parameters.shaders.populate(lineGradientProgram)) return;
if (!parameters.shaders.populate(lineSDFProgram)) return;
if (!parameters.shaders.populate(linePatternProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(lineProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(lineGradientProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(lineSDFProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(linePatternProgram)) return;
parameters.renderTileClippingMasks(renderTiles);

View File

@@ -96,7 +96,7 @@ void RenderRasterLayer::render(PaintParameters& parameters) {
return;
}
if (!parameters.shaders.populate(rasterProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(rasterProgram)) return;
const auto& evaluated = static_cast<const RasterLayerProperties&>(*evaluatedProperties).evaluated;
RasterProgram::Binders paintAttributeData{evaluated, 0};

View File

@@ -359,12 +359,12 @@ void RenderSymbolLayer::render(PaintParameters& parameters) {
return;
}
if (!parameters.shaders.populate(programs.symbolIconProgram)) return;
if (!parameters.shaders.populate(programs.symbolSDFIconProgram)) return;
if (!parameters.shaders.populate(programs.symbolSDFTextProgram)) return;
if (!parameters.shaders.populate(programs.symbolTextAndIconProgram)) return;
if (!parameters.shaders.populate(programs.collisionBoxProgram)) return;
if (!parameters.shaders.populate(programs.collisionCircleProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(programs.symbolIconProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(programs.symbolSDFIconProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(programs.symbolSDFTextProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(programs.symbolTextAndIconProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(programs.collisionBoxProgram)) return;
if (!parameters.shaders.getLegacyGroup().populate(programs.collisionCircleProgram)) return;
const bool sortFeaturesByKey = !impl_cast(baseImpl).layout.get<SymbolSortKey>().isUndefined();
std::multiset<RenderableSegment> renderableSegments;

View File

@@ -138,7 +138,8 @@ void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFun
clearStencil();
}
auto program = staticData.shaders->get<ClippingMaskProgram>();
auto program = staticData.shaders->getLegacyGroup().get<ClippingMaskProgram>();
if (!program) {
return;
}

View File

@@ -1,21 +1,20 @@
#pragma once
#include <mbgl/gfx/attribute.hpp>
#include <mbgl/gfx/context.hpp>
#include <mbgl/gfx/uniform.hpp>
#include <mbgl/gfx/attribute.hpp>
#include <mbgl/gfx/upload_pass.hpp>
#include <mbgl/layout/pattern_layout.hpp>
#include <mbgl/programs/attributes.hpp>
#include <mbgl/renderer/cross_faded_property_evaluator.hpp>
#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/renderer/paint_property_statistics.hpp>
#include <mbgl/renderer/possibly_evaluated_property_value.hpp>
#include <mbgl/util/indexed_tuple.hpp>
#include <mbgl/util/literal.hpp>
#include <mbgl/util/type_list.hpp>
#include <mbgl/renderer/possibly_evaluated_property_value.hpp>
#include <mbgl/renderer/paint_property_statistics.hpp>
#include <mbgl/renderer/cross_faded_property_evaluator.hpp>
#include <mbgl/util/variant.hpp>
#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/util/indexed_tuple.hpp>
#include <mbgl/layout/pattern_layout.hpp>
#include <bitset>
#include <mbgl/util/vectors.hpp>
namespace mbgl {
@@ -38,6 +37,9 @@ template <class AttributeType>
using ZoomInterpolatedAttributeType =
gfx::AttributeType<typename AttributeType::ElementType, AttributeType::Dimensions * 2>;
template <class A>
using ZoomInterpolatedVertexType = typename gfx::detail::VertexType<ZoomInterpolatedAttributeType<A>>;
inline std::array<float, 1> attributeValue(float v) {
return {{v}};
}
@@ -129,8 +131,8 @@ public:
const PossiblyEvaluatedType& currentValue) const = 0;
virtual std::size_t getVertexCount() const = 0;
virtual const /*std::tuple<ExpandToType<As, UniformValueType>...>&*/ void* getVertexValue(
std::size_t index) const = 0;
virtual std::tuple<ZoomInterpolatedVertexType<As>...> getVertexValue(std::size_t index) const = 0;
static std::unique_ptr<PaintPropertyBinder> create(const PossiblyEvaluatedType& value, float zoom, T defaultValue);
@@ -170,10 +172,10 @@ public:
using BaseAttributeType = A;
using BaseVertex = gfx::VertexType<BaseAttributeType>;
std::size_t getVertexCount() const override { return 1; }
const /*std::tuple<ExpandToType<A, T>>&*/ void* getVertexValue(std::size_t) const override {
convertedValue = BaseVertex{attributeValue(constant)};
return &convertedValue;
std::size_t getVertexCount() const override { return 0; }
std::tuple<ZoomInterpolatedVertexType<A>> getVertexValue(std::size_t) const override {
return {ZoomInterpolatedVertexType<A>{0}};
}
private:
@@ -222,9 +224,10 @@ public:
return constantPatternPositions;
}
std::size_t getVertexCount() const override { return 1; }
const /*std::tuple<ExpandToType<A, T>>&*/ void* getVertexValue(std::size_t) const override {
return &constantPatternPositions;
std::size_t getVertexCount() const override { return 0; }
std::tuple<ZoomInterpolatedVertexType<As>...> getVertexValue(std::size_t) const override {
return {ZoomInterpolatedVertexType<As>{0}...};
}
private:
@@ -238,8 +241,6 @@ public:
using BaseAttributeType = A;
using BaseVertex = gfx::VertexType<BaseAttributeType>;
using AttributeType = ZoomInterpolatedAttributeType<A>;
SourceFunctionPaintPropertyBinder(style::PropertyExpression<T> expression_, T defaultValue_)
: expression(std::move(expression_)),
defaultValue(std::move(defaultValue_)) {}
@@ -326,8 +327,10 @@ public:
}
std::size_t getVertexCount() const override { return vertexVector.elements(); }
const /*std::tuple<ExpandToType<A, T>>&*/ void* getVertexValue(std::size_t index) const override {
return &vertexVector.at(index);
std::tuple<ZoomInterpolatedVertexType<A>> getVertexValue(std::size_t index) const override {
const BaseVertex& value = vertexVector.at(index);
return {ZoomInterpolatedVertexType<A>{concatenate(value.a1, value.a1)}};
}
private:
@@ -450,8 +453,9 @@ public:
}
std::size_t getVertexCount() const override { return vertexVector.elements(); }
const /*std::tuple<ExpandToType<A, T>>&*/ void* getVertexValue(std::size_t index) const override {
return &vertexVector.at(index);
std::tuple<ZoomInterpolatedVertexType<A>> getVertexValue(std::size_t index) const override {
return {vertexVector.at(index)};
}
private:
@@ -467,9 +471,6 @@ template <class T, class A1, class A2>
class CompositeCrossFadedPaintPropertyBinder final
: public PaintPropertyBinder<T, std::array<uint16_t, 4>, PossiblyEvaluatedPropertyValue<Faded<T>>, A1, A2> {
public:
using AttributeType = ZoomInterpolatedAttributeType<A1>;
using AttributeType2 = ZoomInterpolatedAttributeType<A2>;
using BaseAttributeType = A1;
using BaseAttributeType2 = A2;
@@ -564,8 +565,16 @@ public:
return {};
}
std::size_t getVertexCount() const override { return 0; }
const /*std::tuple<ExpandToType<A, T>>&*/ void* getVertexValue(std::size_t) const override { return nullptr; }
std::size_t getVertexCount() const override { return patternToVertexVector.elements(); }
std::tuple<ZoomInterpolatedVertexType<A1>, ZoomInterpolatedVertexType<A2>> getVertexValue(
std::size_t index) const override {
const Vertex& patternValue = patternToVertexVector.at(index);
const Vertex2& zoomValue = crossfade.fromScale == 2 ? zoomInVertexVector.at(index)
: zoomOutVertexVector.at(index);
return {ZoomInterpolatedVertexType<A1>{concatenate(patternValue.a1, patternValue.a1)},
ZoomInterpolatedVertexType<A2>{concatenate(zoomValue.a1, zoomValue.a1)}};
}
private:
style::PropertyExpression<T> expression;

View File

@@ -171,7 +171,7 @@ void RenderTile::finishRender(PaintParameters& parameters) const {
static const style::Properties<>::PossiblyEvaluated properties{};
static const DebugProgram::Binders paintAttributeData(properties, 0);
auto program = parameters.shaders.get<DebugProgram>();
auto program = parameters.shaders.getLegacyGroup().get<DebugProgram>();
if (!program) {
return;
}

View File

@@ -39,7 +39,7 @@ void ImageSourceRenderData::render(PaintParameters& parameters) const {
static const style::Properties<>::PossiblyEvaluated properties{};
static const DebugProgram::Binders paintAttributeData(properties, 0);
auto programInstance = parameters.shaders.get<DebugProgram>();
auto programInstance = parameters.shaders.getLegacyGroup().get<DebugProgram>();
if (!programInstance) {
return;
}

View File

@@ -39,6 +39,8 @@ public:
using AttributeList = TypeList<A>;
using Uniform = U;
using UniformList = TypeList<U>;
static constexpr const std::array<std::string_view, 1> AttributeNames = {A::name()};
};
template <class T, class A1, class U1, class A2, class U2>
@@ -56,6 +58,8 @@ public:
using AttributeList = TypeList<A1, A2>;
using Uniform = U1;
using UniformList = TypeList<U1, U2>;
static constexpr const std::array<std::string_view, 2> AttributeNames = {A1::name(), A2::name()};
};
template <class T>

View File

@@ -82,7 +82,7 @@ class StubShaderConsumer {
public:
template <typename T>
uint32_t useShader(gfx::ShaderRegistry& registry) {
auto program = registry.get<T>();
auto program = registry.getLegacyGroup().get<T>();
return program ? program->draw() : 0;
}
};
@@ -94,17 +94,17 @@ TEST(ShaderRegistry, RegisterShader) {
gfx::ShaderRegistry registry;
// The registry should start empty
ASSERT_FALSE(registry.isShader(std::string{StubProgram_1::Name}));
ASSERT_FALSE(registry.getLegacyGroup().isShader(std::string{StubProgram_1::Name}));
// Register the program
auto program = std::make_shared<StubProgram_1>();
ASSERT_TRUE(registry.registerShader(program));
ASSERT_TRUE(registry.getLegacyGroup().registerShader(program));
// We can't re-register the same program name
ASSERT_FALSE(registry.registerShader(program));
ASSERT_FALSE(registry.getLegacyGroup().registerShader(program));
// Ensure the shader is present in the registry now
ASSERT_TRUE(registry.isShader(std::string{StubProgram_1::Name}));
ASSERT_TRUE(registry.getLegacyGroup().isShader(std::string{StubProgram_1::Name}));
// And we can fetch it
ASSERT_EQ(registry.get<StubProgram_1>(), program);
ASSERT_EQ(registry.getLegacyGroup().get<StubProgram_1>(), program);
// Make sure downcasting to program1 works as expected
StubShaderConsumer consumer;
@@ -115,13 +115,13 @@ TEST(ShaderRegistry, RegisterShader) {
TEST(ShaderRegistry, ReplaceShaderType) {
gfx::ShaderRegistry registry;
ASSERT_FALSE(registry.isShader(std::string{StubProgram_1::Name}));
ASSERT_FALSE(registry.getLegacyGroup().isShader(std::string{StubProgram_1::Name}));
auto program = std::make_shared<StubProgram_1>();
ASSERT_TRUE(registry.registerShader(program));
ASSERT_TRUE(registry.getLegacyGroup().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->typeName()}));
ASSERT_TRUE(registry.getLegacyGroup().isShader(std::string{StubProgram_1::Name}));
ASSERT_TRUE(registry.getLegacyGroup().isShader(std::string{program->typeName()}));
// Make sure downcasting to program1 works as expected
StubShaderConsumer consumer;
@@ -131,13 +131,13 @@ TEST(ShaderRegistry, ReplaceShaderType) {
// 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));
ASSERT_FALSE(registry.getLegacyGroup().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_TRUE(registry.getLegacyGroup().replaceShader(program3));
// Assert the new program downcasts from the registry as expected
ASSERT_EQ(consumer.useShader<StubProgram_1>(registry), 30);
}
@@ -156,28 +156,29 @@ TEST(ShaderRegistry, ShaderRTTI) {
TEST(ShaderRegistry, MultiRegister) {
gfx::ShaderRegistry registry;
ASSERT_TRUE(registry.registerShader(std::make_shared<StubProgram_1>()));
ASSERT_TRUE(registry.registerShader(std::make_shared<StubProgram_1>(), "SecondProgram"));
ASSERT_TRUE(registry.getLegacyGroup().registerShader(std::make_shared<StubProgram_1>()));
ASSERT_TRUE(registry.getLegacyGroup().registerShader(std::make_shared<StubProgram_1>(), "SecondProgram"));
// Default option, register as the type name
ASSERT_NE(registry.get<StubProgram_1>(), nullptr);
ASSERT_NE(registry.getLegacyGroup().get<StubProgram_1>(), nullptr);
// Register with an explicit name
ASSERT_NE(registry.get<StubProgram_1>("SecondProgram"), nullptr);
ASSERT_NE(registry.get<StubProgram_1>(), registry.get<StubProgram_1>("SecondProgram"));
ASSERT_NE(registry.getLegacyGroup().get<StubProgram_1>("SecondProgram"), nullptr);
ASSERT_NE(registry.getLegacyGroup().get<StubProgram_1>(),
registry.getLegacyGroup().get<StubProgram_1>("SecondProgram"));
}
// Test fetching
TEST(ShaderRegistry, RegistryFetch) {
gfx::ShaderRegistry registry;
ASSERT_TRUE(registry.registerShader(std::make_shared<StubProgram_1>()));
ASSERT_TRUE(registry.registerShader(std::make_shared<StubProgram_1>(), "SecondProgram"));
ASSERT_TRUE(registry.getLegacyGroup().registerShader(std::make_shared<StubProgram_1>()));
ASSERT_TRUE(registry.getLegacyGroup().registerShader(std::make_shared<StubProgram_1>(), "SecondProgram"));
std::shared_ptr<StubProgram_1> progA;
std::shared_ptr<StubProgram_1> progB;
ASSERT_TRUE(registry.populate(progA));
ASSERT_TRUE(registry.populate(progB, "SecondProgram"));
ASSERT_TRUE(registry.getLegacyGroup().populate(progA));
ASSERT_TRUE(registry.getLegacyGroup().populate(progB, "SecondProgram"));
ASSERT_NE(progA, progB);
ASSERT_NE(progA, nullptr);
ASSERT_NE(progB, nullptr);
@@ -188,17 +189,17 @@ TEST(ShaderRegistry, NamedReplace) {
gfx::ShaderRegistry registry;
// Register
ASSERT_TRUE(registry.registerShader(std::make_shared<StubProgram_1>(), "CustomName"));
ASSERT_TRUE(registry.getLegacyGroup().registerShader(std::make_shared<StubProgram_1>(), "CustomName"));
std::shared_ptr<StubProgram_1> progA;
ASSERT_TRUE(registry.populate(progA, "CustomName"));
ASSERT_TRUE(registry.getLegacyGroup().populate(progA, "CustomName"));
ASSERT_NE(progA, nullptr);
// Replace it with a new instance
ASSERT_TRUE(registry.replaceShader(std::make_shared<StubProgram_1>(), "CustomName"));
ASSERT_TRUE(registry.getLegacyGroup().replaceShader(std::make_shared<StubProgram_1>(), "CustomName"));
std::shared_ptr<StubProgram_1> progB;
ASSERT_TRUE(registry.populate(progB, "CustomName"));
ASSERT_TRUE(registry.getLegacyGroup().populate(progB, "CustomName"));
ASSERT_NE(progB, nullptr);
// Should be different instances
@@ -213,7 +214,7 @@ TEST(ShaderRegistry, GLSLReplacement_NoOp) {
// Just replace with a default instance
observer.registerShaders = [&](gfx::ShaderRegistry& registry) {
if (!registry.replaceShader(std::make_shared<FillProgram>(ProgramParameters(1.0f, false)))) {
if (!registry.getLegacyGroup().replaceShader(std::make_shared<FillProgram>(ProgramParameters(1.0f, false)))) {
throw std::runtime_error("Failed to register shader!");
}
};
@@ -239,7 +240,7 @@ TEST(ShaderRegistry, GLSLReplacement1) {
// Replace with an instance that only renders blue
observer.registerShaders = [&](gfx::ShaderRegistry& registry) {
if (!registry.replaceShader(std::make_shared<FillProgram>(
if (!registry.getLegacyGroup().replaceShader(std::make_shared<FillProgram>(
ProgramParameters(1.0f, false)
.withShaderSource(ProgramParameters::ProgramSource(gfx::Backend::Type::OpenGL,
"",
@@ -273,7 +274,7 @@ TEST(ShaderRegistry, GLSLReplacement2) {
// Replace with an instance that adds some red and green
observer.registerShaders = [&](gfx::ShaderRegistry& registry) {
if (!registry.replaceShader(std::make_shared<FillProgram>(
if (!registry.getLegacyGroup().replaceShader(std::make_shared<FillProgram>(
ProgramParameters(1.0f, false)
.withShaderSource(ProgramParameters::ProgramSource(gfx::Backend::Type::OpenGL,
"",