From 37a9c089a2c39062e725a6fdedb5625ea477d0a8 Mon Sep 17 00:00:00 2001 From: Tim Sylvester Date: Tue, 14 Nov 2023 15:43:56 -0800 Subject: [PATCH] Separate properties-as-uniforms per drawable (#1856) --- include/mbgl/renderer/layer_tweaker.hpp | 7 +++- include/mbgl/util/tiny_unordered_map.hpp | 18 +++++++- src/mbgl/gfx/symbol_drawable_data.hpp | 11 ++++- src/mbgl/renderer/layer_tweaker.cpp | 9 ---- .../renderer/layers/render_symbol_layer.cpp | 21 +++++++--- .../renderer/layers/symbol_layer_tweaker.cpp | 42 +++++++++---------- 6 files changed, 67 insertions(+), 41 deletions(-) diff --git a/include/mbgl/renderer/layer_tweaker.hpp b/include/mbgl/renderer/layer_tweaker.hpp index 200e05131..ae333a934 100644 --- a/include/mbgl/renderer/layer_tweaker.hpp +++ b/include/mbgl/renderer/layer_tweaker.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -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::AttributeSource getAttributeSource(size_t index) { diff --git a/include/mbgl/util/tiny_unordered_map.hpp b/include/mbgl/util/tiny_unordered_map.hpp index 3fa4b8bf4..99fea52e2 100644 --- a/include/mbgl/util/tiny_unordered_map.hpp +++ b/include/mbgl/util/tiny_unordered_map.hpp @@ -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, LinearThreshold> keys; diff --git a/src/mbgl/gfx/symbol_drawable_data.hpp b/src/mbgl/gfx/symbol_drawable_data.hpp index 17b2fa729..6febd039c 100644 --- a/src/mbgl/gfx/symbol_drawable_data.hpp +++ b/src/mbgl/gfx/symbol_drawable_data.hpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -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; + 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; diff --git a/src/mbgl/renderer/layer_tweaker.cpp b/src/mbgl/renderer/layer_tweaker.cpp index 57e3278fb..85e8aa2c8 100644 --- a/src/mbgl/renderer/layer_tweaker.cpp +++ b/src/mbgl/renderer/layer_tweaker.cpp @@ -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& props) { diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 753cad18e..377b48a1b 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -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& set) { + // can we do this without allocating? + auto values = std::vector(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(renderData.layerProperties); propertiesAsUniforms.clear(); + + const auto& evaluated = getEvaluated(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(*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().constantOr(Color::black()).a > 0.0f && evaluated.get().constantOr(1); @@ -1394,7 +1404,8 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders, /*.pitchAlignment=*/values.pitchAlignment, /*.rotationAlignment=*/values.rotationAlignment, /*.placement=*/layout.get(), - /*.textFit=*/layout.get()); + /*.textFit=*/layout.get(), + /*propertiesAsUniforms=*/toMap(propertiesAsUniforms)); const auto tileUBO = buildTileUBO(bucket, *drawData, currentZoom); drawable->setData(std::move(drawData)); diff --git a/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp b/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp index 915229657..3f062a9aa 100644 --- a/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp @@ -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(5), /*.expression=*/{}}, - /* .halo_color = */ {/*.source=*/getAttributeSource(6), /*.expression=*/{}}, - /* .opacity = */ {/*.source=*/getAttributeSource(7), /*.expression=*/{}}, - /* .halo_width = */ {/*.source=*/getAttributeSource(8), /*.expression=*/{}}, - /* .halo_blur = */ {/*.source=*/getAttributeSource(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; + 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 }); }