From 4532b180530b7f0dd388610f8b26b069739b720f Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Fri, 5 Jul 2019 17:31:37 +0300 Subject: [core] createRenderer() and createLoader() for RenderLineLayer --- src/mbgl/renderer/layers/render_line_layer.cpp | 330 ++++++++++++++----------- src/mbgl/renderer/layers/render_line_layer.hpp | 21 +- src/mbgl/renderer/render_layer.cpp | 27 ++ src/mbgl/renderer/render_layer.hpp | 5 + 4 files changed, 228 insertions(+), 155 deletions(-) diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp index 595140634d..05412fa49a 100644 --- a/src/mbgl/renderer/layers/render_line_layer.cpp +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -20,18 +20,194 @@ namespace mbgl { using namespace style; +ColorRampRenderData::ColorRampRenderData(const style::ColorRampPropertyValue& value) + : image({256, 1}) { + assert(!value.isUndefined()); + const auto length = image.bytes(); + + for (uint32_t i = 0; i < length; i += 4) { + const auto color = value.evaluate(static_cast(i) / length); + image.data[i] = std::floor(color.r * 255); + image.data[i + 1] = std::floor(color.g * 255); + image.data[i + 2] = std::floor(color.b * 255); + image.data[i + 3] = std::floor(color.a * 255); + } +} + +void ColorRampRenderData::upload(gfx::UploadPass& uploadPass) { + if (!texture) { + texture = uploadPass.createTexture(image); + } +} + inline const LineLayer::Impl& impl(const Immutable& impl) { return static_cast(*impl); } RenderLineLayer::RenderLineLayer(Immutable _impl) : RenderLayer(makeMutable(std::move(_impl))), - unevaluated(impl(baseImpl).paint.untransitioned()), - colorRamp({256, 1}) { + unevaluated(impl(baseImpl).paint.untransitioned()) { } RenderLineLayer::~RenderLineLayer() = default; +LayerRenderer RenderLineLayer::createRenderer() { + const unsigned char kDrawPattern = 1 << 0; + const unsigned char kDrawGradient = 1 << 1; + // Minimize size of the catured parameters in order to + // avoid heap allocations at std::function construction. + unsigned char drawFlags = 0; + + if (!unevaluated.get().isUndefined()) { + drawFlags |= kDrawPattern; + } else if (!unevaluated.get().getValue().isUndefined()) { + drawFlags |= kDrawGradient; + } + + return [drawFlags, colorRampData = colorRamp](PaintParameters& parameters, const LayerRenderItem& renderItem) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + const auto& renderTiles = renderItem.renderTiles; + const auto layerId = renderItem.evaluatedProperties->baseImpl->id; + + parameters.renderTileClippingMasks(renderTiles); + + for (const RenderTile& tile : renderTiles) { + const LayerRenderData* renderData = tile.getLayerRenderData(*renderItem.evaluatedProperties->baseImpl); + if (!renderData) { + continue; + } + auto& bucket = static_cast(*renderData->bucket); + const auto& evaluated = getEvaluated(renderData->layerProperties); + const auto& crossfade = getCrossfade(renderData->layerProperties); + + const bool drawDash = !evaluated.get().from.empty(); + + auto draw = [&](auto& programInstance, + auto&& uniformValues, + const optional& patternPositionA, + const optional& patternPositionB, auto&& textureBindings) { + const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(layerId); + + paintPropertyBinders.setPatternParameters(patternPositionA, patternPositionB, crossfade); + + const auto allUniformValues = programInstance.computeAllUniformValues( + std::move(uniformValues), + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( + *bucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + renderItem.checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + *parameters.renderPass, + gfx::Triangles(), + parameters.depthModeForSublayer(0, gfx::DepthMaskType::ReadOnly), + parameters.stencilModeForClipping(tile.id), + parameters.colorModeForRenderPass(), + gfx::CullFaceMode::disabled(), + *bucket.indexBuffer, + bucket.segments, + allUniformValues, + allAttributeBindings, + std::move(textureBindings), + layerId + ); + }; + + if (drawDash) { + const LinePatternCap cap = bucket.layout.get() == LineCapType::Round + ? LinePatternCap::Round : LinePatternCap::Square; + LinePatternPos posA = parameters.lineAtlas.getDashPosition(evaluated.get().from, cap); + LinePatternPos posB = parameters.lineAtlas.getDashPosition(evaluated.get().to, cap); + + draw( + parameters.programs.getLineLayerPrograms().lineSDF, + LineSDFProgram::layoutUniformValues( + evaluated, + parameters.pixelRatio, + tile, + parameters.state, + parameters.pixelsToGLUnits, + posA, + posB, + crossfade, + parameters.lineAtlas.getSize().width), + {}, + {}, + LineSDFProgram::TextureBindings{ + parameters.lineAtlas.textureBinding(), + }); + + } else if (drawFlags & kDrawPattern) { + const auto& linePatternValue = evaluated.get().constantOr(Faded>{ "", ""}); + const Size& texsize = tile.getIconAtlasTexture().size; + + optional posA = tile.getPattern(linePatternValue.from); + optional posB = tile.getPattern(linePatternValue.to); + + draw(parameters.programs.getLineLayerPrograms().linePattern, + LinePatternProgram::layoutUniformValues( + evaluated, + tile, + parameters.state, + parameters.pixelsToGLUnits, + parameters.pixelRatio, + texsize, + crossfade), + posA, + posB, + LinePatternProgram::TextureBindings{ + textures::image::Value{ tile.getIconAtlasTexture().getResource(), gfx::TextureFilterType::Linear }, + }); + } else if (drawFlags & kDrawGradient) { + assert(colorRampData); + assert(colorRampData->texture); + draw(parameters.programs.getLineLayerPrograms().lineGradient, + LineGradientProgram::layoutUniformValues( + evaluated, + tile, + parameters.state, + parameters.pixelsToGLUnits, + parameters.pixelRatio), + {}, + {}, + LineGradientProgram::TextureBindings{ + textures::image::Value{ colorRampData->texture->getResource(), gfx::TextureFilterType::Linear }, + }); + } else { + draw(parameters.programs.getLineLayerPrograms().line, + LineProgram::layoutUniformValues( + evaluated, + tile, + parameters.state, + parameters.pixelsToGLUnits, + parameters.pixelRatio), + {}, + {}, + LineProgram::TextureBindings{}); + } + } + }; +} + +LayerUploader RenderLineLayer::createUploader() { + if (!unevaluated.get().getValue().isUndefined()) { + return [colorRampData = colorRamp] (gfx::UploadPass& uploadPass) { + if (colorRampData) colorRampData->upload(uploadPass); + }; + } + return {}; +} + void RenderLineLayer::transition(const TransitionParameters& parameters) { unevaluated = impl(baseImpl).paint.transitioned(parameters, std::move(unevaluated)); updateColorRamp(); @@ -77,141 +253,6 @@ void RenderLineLayer::prepare(const LayerPrepareParameters& params) { } } -void RenderLineLayer::upload(gfx::UploadPass& uploadPass) { - if (!unevaluated.get().getValue().isUndefined() && !colorRampTexture) { - colorRampTexture = uploadPass.createTexture(colorRamp); - } -} - -void RenderLineLayer::render(PaintParameters& parameters) { - if (parameters.pass == RenderPass::Opaque) { - return; - } - - parameters.renderTileClippingMasks(renderTiles); - - for (const RenderTile& tile : renderTiles) { - const LayerRenderData* renderData = tile.getLayerRenderData(*baseImpl); - if (!renderData) { - continue; - } - auto& bucket = static_cast(*renderData->bucket); - const auto& evaluated = getEvaluated(renderData->layerProperties); - const auto& crossfade = getCrossfade(renderData->layerProperties); - - auto draw = [&](auto& programInstance, - auto&& uniformValues, - const optional& patternPositionA, - const optional& patternPositionB, auto&& textureBindings) { - const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); - - paintPropertyBinders.setPatternParameters(patternPositionA, patternPositionB, crossfade); - - const auto allUniformValues = programInstance.computeAllUniformValues( - std::move(uniformValues), - paintPropertyBinders, - evaluated, - parameters.state.getZoom() - ); - const auto allAttributeBindings = programInstance.computeAllAttributeBindings( - *bucket.vertexBuffer, - paintPropertyBinders, - evaluated - ); - - checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); - - programInstance.draw( - parameters.context, - *parameters.renderPass, - gfx::Triangles(), - parameters.depthModeForSublayer(0, gfx::DepthMaskType::ReadOnly), - parameters.stencilModeForClipping(tile.id), - parameters.colorModeForRenderPass(), - gfx::CullFaceMode::disabled(), - *bucket.indexBuffer, - bucket.segments, - allUniformValues, - allAttributeBindings, - std::move(textureBindings), - getID() - ); - }; - - if (!evaluated.get().from.empty()) { - const LinePatternCap cap = bucket.layout.get() == LineCapType::Round - ? LinePatternCap::Round : LinePatternCap::Square; - LinePatternPos posA = parameters.lineAtlas.getDashPosition(evaluated.get().from, cap); - LinePatternPos posB = parameters.lineAtlas.getDashPosition(evaluated.get().to, cap); - - draw(parameters.programs.getLineLayerPrograms().lineSDF, - LineSDFProgram::layoutUniformValues( - evaluated, - parameters.pixelRatio, - tile, - parameters.state, - parameters.pixelsToGLUnits, - posA, - posB, - crossfade, - parameters.lineAtlas.getSize().width), - {}, - {}, - LineSDFProgram::TextureBindings{ - parameters.lineAtlas.textureBinding(), - }); - - } else if (!unevaluated.get().isUndefined()) { - const auto& linePatternValue = evaluated.get().constantOr(Faded>{ "", ""}); - const Size& texsize = tile.getIconAtlasTexture().size; - - optional posA = tile.getPattern(linePatternValue.from); - optional posB = tile.getPattern(linePatternValue.to); - - draw(parameters.programs.getLineLayerPrograms().linePattern, - LinePatternProgram::layoutUniformValues( - evaluated, - tile, - parameters.state, - parameters.pixelsToGLUnits, - parameters.pixelRatio, - texsize, - crossfade), - posA, - posB, - LinePatternProgram::TextureBindings{ - textures::image::Value{ tile.getIconAtlasTexture().getResource(), gfx::TextureFilterType::Linear }, - }); - } else if (!unevaluated.get().getValue().isUndefined()) { - assert(colorRampTexture); - - draw(parameters.programs.getLineLayerPrograms().lineGradient, - LineGradientProgram::layoutUniformValues( - evaluated, - tile, - parameters.state, - parameters.pixelsToGLUnits, - parameters.pixelRatio), - {}, - {}, - LineGradientProgram::TextureBindings{ - textures::image::Value{ colorRampTexture->getResource(), gfx::TextureFilterType::Linear }, - }); - } else { - draw(parameters.programs.getLineLayerPrograms().line, - LineProgram::layoutUniformValues( - evaluated, - tile, - parameters.state, - parameters.pixelsToGLUnits, - parameters.pixelRatio), - {}, - {}, - LineProgram::TextureBindings{}); - } - } -} - optional offsetLine(const GeometryCollection& rings, const double offset) { if (offset == 0) return {}; @@ -278,20 +319,7 @@ void RenderLineLayer::updateColorRamp() { if (colorValue.isUndefined()) { return; } - - const auto length = colorRamp.bytes(); - - for (uint32_t i = 0; i < length; i += 4) { - const auto color = colorValue.evaluate(static_cast(i) / length); - colorRamp.data[i] = std::floor(color.r * 255); - colorRamp.data[i + 1] = std::floor(color.g * 255); - colorRamp.data[i + 2] = std::floor(color.b * 255); - colorRamp.data[i + 3] = std::floor(color.a * 255); - } - - if (colorRampTexture) { - colorRampTexture = nullopt; - } + colorRamp = std::make_shared(colorValue); } float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature, const float zoom) const { diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp index 4454d215d9..cb2d7ebda2 100644 --- a/src/mbgl/renderer/layers/render_line_layer.hpp +++ b/src/mbgl/renderer/layers/render_line_layer.hpp @@ -10,19 +10,33 @@ namespace mbgl { +namespace style { +class ColorRampPropertyValue; +} // namespace style + +class ColorRampRenderData { +public: + explicit ColorRampRenderData(const style::ColorRampPropertyValue&); + void upload(gfx::UploadPass&); + const PremultipliedImage image; + optional texture; +}; + class RenderLineLayer final : public RenderLayer { public: explicit RenderLineLayer(Immutable); ~RenderLineLayer() override; private: + // LayerRenderItem createRenderItem() override; + LayerRenderer createRenderer() override; + LayerUploader createUploader() override; void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; bool hasCrossfade() const override; void prepare(const LayerPrepareParameters&) override; - void upload(gfx::UploadPass&) override; - void render(PaintParameters&) override; + void render(PaintParameters&) override {} bool queryIntersectsFeature( const GeometryCoordinates&, @@ -38,8 +52,7 @@ private: float getLineWidth(const GeometryTileFeature&, const float) const; void updateColorRamp(); - PremultipliedImage colorRamp; - optional colorRampTexture; + std::shared_ptr colorRamp; }; } // namespace mbgl diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp index 396fc64b2d..7d02ccb050 100644 --- a/src/mbgl/renderer/render_layer.cpp +++ b/src/mbgl/renderer/render_layer.cpp @@ -31,6 +31,33 @@ LayerRenderItem RenderLayer::createRenderItem() { }; } +void LayerRenderItem::checkRenderability(const PaintParameters& parameters, + const uint32_t activeBindingCount) const { + // Only warn once for every layer. + if (hasRenderFailures) { + return; + } + std::string layerId = evaluatedProperties->baseImpl->id; + if (activeBindingCount > parameters.context.maximumVertexBindingCount) { + Log::Info(Event::OpenGL, + "The layer '%s' uses more data-driven properties than the current device " + "supports, and will have rendering errors. To ensure compatibility with this " + "device, use %d fewer data driven properties in this layer.", + layerId.c_str(), + activeBindingCount - parameters.context.minimumRequiredVertexBindingCount); + hasRenderFailures = true; + } else if (activeBindingCount > parameters.context.minimumRequiredVertexBindingCount) { + Log::Info(Event::OpenGL, + "The layer '%s' uses more data-driven properties than some devices may support. " + "Though it will render correctly on this device, it may have rendering errors " + "on other devices. To ensure compatibility with all devices, use %d fewer " + "data-driven properties in this layer.", + layerId.c_str(), + activeBindingCount - parameters.context.minimumRequiredVertexBindingCount); + hasRenderFailures = true; + } +} + LayerRenderer RenderLayer::createRenderer() { return [this](PaintParameters& pass, const LayerRenderItem&){ render(pass); }; } diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp index 6837d22072..6752fd3dc8 100644 --- a/src/mbgl/renderer/render_layer.hpp +++ b/src/mbgl/renderer/render_layer.hpp @@ -60,6 +60,7 @@ public: LayerUploader uploader; // optionally initialized. Immutable evaluatedProperties; + void checkRenderability(const PaintParameters&, uint32_t activeBindingCount) const; private: bool hasRenderPass(RenderPass pass) const override { return bool(renderPass & pass); } void upload(gfx::UploadPass& pass) const override { if (uploader) uploader(pass);} @@ -69,6 +70,10 @@ private: } const std::string& getName() const override { return evaluatedProperties->baseImpl->id; } RenderPass renderPass; + // Some layers may not render correctly on some hardware when the vertex attribute limit of + // that GPU is exceeded. More attributes are used when adding many data driven paint properties + // to a layer. + mutable bool hasRenderFailures = false; }; class RenderLayer { -- cgit v1.2.1