Separate properties-as-uniforms per drawable (#1856)

This commit is contained in:
Tim Sylvester
2023-11-14 15:43:56 -08:00
committed by GitHub
parent 9884b1e40f
commit 37a9c089a2
6 changed files with 67 additions and 41 deletions

View File

@@ -1,5 +1,6 @@
#pragma once
#include <mbgl/shaders/layer_ubo.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/util/immutable.hpp>
@@ -53,8 +54,10 @@ public:
static shaders::ExpressionInputsUBO buildExpressionUBO(double zoom, uint64_t frameCount);
/// @brief Check whether a property name exists within the previously set collection.
bool hasPropertyAsUniform(StringIdentity) const;
shaders::AttributeSource getAttributeSource(StringIdentity) const;
shaders::AttributeSource getAttributeSource(const StringIdentity id) {
return propertiesAsUniforms.count(id) ? shaders::AttributeSource::Constant
: shaders::AttributeSource::PerVertex;
}
template <shaders::BuiltIn ShaderType>
shaders::AttributeSource getAttributeSource(size_t index) {

View File

@@ -78,11 +78,11 @@ public:
/// Move constructor
TinyUnorderedMap(TinyUnorderedMap&& rhs)
: Super(std::move(rhs)),
linearSize(rhs.linearSize),
: linearSize(rhs.linearSize),
keys(std::move(rhs.keys)),
values(std::move(rhs.values)) {
rhs.linearSize = 0;
Super::operator=(std::move(rhs));
}
/// Copy constructor
@@ -159,6 +159,20 @@ public:
this->Super::clear();
}
bool operator==(const TinyUnorderedMap& other) const {
if (size() != other.size()) return false;
assert(linearSize == other.linearSize);
if (linearSize) {
return std::equal(
keys.begin(), keys.begin() + linearSize, other.keys.begin(), other.keys.begin() + linearSize) &&
std::equal(values.begin(),
values.begin() + linearSize,
other.values.begin(),
other.values.begin() + linearSize);
}
return std::operator==(*this, other);
}
private:
std::size_t linearSize = 0;
std::array<std::optional<Key>, LinearThreshold> keys;

View File

@@ -2,6 +2,7 @@
#include <mbgl/gfx/drawable_data.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/util/tiny_unordered_map.hpp>
#include <memory>
@@ -12,20 +13,25 @@ enum class SymbolType : uint8_t;
namespace gfx {
struct SymbolDrawableData : public DrawableData {
constexpr static std::size_t LinearThreshold = 10;
using PropertyMapType = util::TinyUnorderedMap<StringIdentity, bool, LinearThreshold>;
SymbolDrawableData(const bool isHalo_,
const bool bucketVariablePlacement_,
const style::SymbolType symbolType_,
const style::AlignmentType pitchAlignment_,
const style::AlignmentType rotationAlignment_,
const style::SymbolPlacementType placement_,
const style::IconTextFitType textFit_)
const style::IconTextFitType textFit_,
PropertyMapType&& propertiesAsUniforms_)
: isHalo(isHalo_),
bucketVariablePlacement(bucketVariablePlacement_),
symbolType(symbolType_),
pitchAlignment(pitchAlignment_),
rotationAlignment(rotationAlignment_),
placement(placement_),
textFit(textFit_) {}
textFit(textFit_),
propertiesAsUniforms(std::move(propertiesAsUniforms_)) {}
~SymbolDrawableData() override = default;
const bool isHalo;
@@ -35,6 +41,7 @@ struct SymbolDrawableData : public DrawableData {
const style::AlignmentType rotationAlignment;
const style::SymbolPlacementType placement;
const style::IconTextFitType textFit;
const PropertyMapType propertiesAsUniforms;
};
using UniqueSymbolDrawableData = std::unique_ptr<SymbolDrawableData>;

View File

@@ -64,15 +64,6 @@ shaders::ExpressionInputsUBO LayerTweaker::buildExpressionUBO(double zoom, uint6
/* .pad = */ 0,
0};
}
bool LayerTweaker::hasPropertyAsUniform(const StringIdentity attrNameID) const {
return propertiesAsUniforms.find(attrNameID) != propertiesAsUniforms.end();
}
using namespace shaders;
AttributeSource LayerTweaker::getAttributeSource(const StringIdentity attribNameID) const {
return hasPropertyAsUniform(attribNameID) ? AttributeSource::Constant : AttributeSource::PerVertex;
}
#endif // MLN_RENDER_BACKEND_METAL
void LayerTweaker::setPropertiesAsUniforms([[maybe_unused]] const std::unordered_set<StringIdentity>& props) {

View File

@@ -748,6 +748,13 @@ SymbolDrawableTilePropsUBO buildTileUBO(const SymbolBucket& bucket,
};
}
// Convert a properties-as-uniforms set to the type expected by `SymbolDrawableData`
gfx::SymbolDrawableData::PropertyMapType toMap(const std::unordered_set<StringIdentity>& set) {
// can we do this without allocating?
auto values = std::vector<bool>(set.size());
return gfx::SymbolDrawableData::PropertyMapType(set.begin(), set.end(), values.begin(), values.end());
}
const auto idDataAttibName = stringIndexer().get("a_data");
const auto posOffsetAttribName = "a_pos_offset";
const auto idPosOffsetAttribName = stringIndexer().get(posOffsetAttribName);
@@ -1198,8 +1205,9 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders,
return;
}
const auto& evaluated = getEvaluated<SymbolLayerProperties>(renderData.layerProperties);
propertiesAsUniforms.clear();
const auto& evaluated = getEvaluated<SymbolLayerProperties>(renderData.layerProperties);
updateTileDrawable(drawable,
context,
bucket,
@@ -1209,6 +1217,11 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders,
textInterpUBO,
iconInterpUBO,
propertiesAsUniforms);
// We assume that the properties-as-uniforms doesn't change without the style changing.
// That would require updating the shader as well.
[[maybe_unused]] const auto& drawData = static_cast<gfx::SymbolDrawableData&>(*drawable.getData());
assert(drawData.propertiesAsUniforms == toMap(propertiesAsUniforms));
});
// re-create collision drawables
@@ -1295,9 +1308,6 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders,
propertiesAsUniforms.clear();
gfx::VertexAttributeArray attribs;
updateTileAttributes(buffer, isText, bucketPaintProperties, evaluated, attribs, propertiesAsUniforms);
if (layerTweaker) {
layerTweaker->setPropertiesAsUniforms(propertiesAsUniforms);
}
const auto textHalo = evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0.0f &&
evaluated.get<style::TextHaloWidth>().constantOr(1);
@@ -1394,7 +1404,8 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders,
/*.pitchAlignment=*/values.pitchAlignment,
/*.rotationAlignment=*/values.rotationAlignment,
/*.placement=*/layout.get<SymbolPlacement>(),
/*.textFit=*/layout.get<IconTextFit>());
/*.textFit=*/layout.get<IconTextFit>(),
/*propertiesAsUniforms=*/toMap(propertiesAsUniforms));
const auto tileUBO = buildTileUBO(bucket, *drawData, currentZoom);
drawable->setData(std::move(drawData));

View File

@@ -89,26 +89,6 @@ void SymbolLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamete
const auto zoom = parameters.state.getZoom();
#if MLN_RENDER_BACKEND_METAL
if (permutationUpdated) {
const SymbolPermutationUBO permutationUBO = {
/* .fill_color = */ {/*.source=*/getAttributeSource<BuiltIn::SymbolSDFIconShader>(5), /*.expression=*/{}},
/* .halo_color = */ {/*.source=*/getAttributeSource<BuiltIn::SymbolSDFIconShader>(6), /*.expression=*/{}},
/* .opacity = */ {/*.source=*/getAttributeSource<BuiltIn::SymbolSDFIconShader>(7), /*.expression=*/{}},
/* .halo_width = */ {/*.source=*/getAttributeSource<BuiltIn::SymbolSDFIconShader>(8), /*.expression=*/{}},
/* .halo_blur = */ {/*.source=*/getAttributeSource<BuiltIn::SymbolSDFIconShader>(9), /*.expression=*/{}},
/* .overdrawInspector = */ overdrawInspector,
/* .pad = */ 0,
0,
0};
if (permutationUniformBuffer) {
permutationUniformBuffer->update(&permutationUBO, sizeof(permutationUBO));
} else {
permutationUniformBuffer = context.createUniformBuffer(&permutationUBO, sizeof(permutationUBO));
}
permutationUpdated = false;
}
if (!expressionUniformBuffer) {
const auto expressionUBO = buildExpressionUBO(zoom, parameters.frameCount);
expressionUniformBuffer = context.createUniformBuffer(&expressionUBO, sizeof(expressionUBO));
@@ -201,8 +181,28 @@ void SymbolLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParamete
uniforms.addOrReplace(idSymbolDrawablePaintUBOName, isText ? textPaintBuffer : iconPaintBuffer);
#if MLN_RENDER_BACKEND_METAL
assert(propertiesAsUniforms.empty());
using namespace shaders;
using ShaderClass = ShaderSource<BuiltIn::SymbolSDFIconShader, gfx::Backend::Type::Metal>;
const auto source = [&](int index) {
const auto nameID = ShaderClass::attributes[index].nameID;
const bool uniform = symbolData.propertiesAsUniforms.count(nameID);
return uniform ? AttributeSource::Constant : AttributeSource::PerVertex;
};
const SymbolPermutationUBO permutationUBO = {/* .fill_color = */ {/*.source=*/source(5), /*.expression=*/{}},
/* .halo_color = */ {/*.source=*/source(6), /*.expression=*/{}},
/* .opacity = */ {/*.source=*/source(7), /*.expression=*/{}},
/* .halo_width = */ {/*.source=*/source(8), /*.expression=*/{}},
/* .halo_blur = */ {/*.source=*/source(9), /*.expression=*/{}},
/* .overdrawInspector = */ overdrawInspector,
/* .pad = */ 0,
0,
0};
uniforms.createOrUpdate(idSymbolPermutationUBOName, &permutationUBO, context);
uniforms.addOrReplace(idExpressionInputsUBOName, expressionUniformBuffer);
uniforms.addOrReplace(idSymbolPermutationUBOName, permutationUniformBuffer);
#endif // MLN_RENDER_BACKEND_METAL
});
}