#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { using namespace style; namespace { inline const FillLayer::Impl& impl_cast(const Immutable& impl) { assert(impl->getTypeInfo() == FillLayer::Impl::staticTypeInfo()); return static_cast(*impl); } } // namespace RenderFillLayer::RenderFillLayer(Immutable _impl) : RenderLayer(makeMutable(std::move(_impl))), unevaluated(impl_cast(baseImpl).paint.untransitioned()) {} RenderFillLayer::~RenderFillLayer() = default; void RenderFillLayer::transition(const TransitionParameters& parameters) { unevaluated = impl_cast(baseImpl).paint.transitioned(parameters, std::move(unevaluated)); } void RenderFillLayer::evaluate(const PropertyEvaluationParameters& parameters) { auto properties = makeMutable(staticImmutableCast(baseImpl), parameters.getCrossfadeParameters(), unevaluated.evaluate(parameters)); auto& evaluated = properties->evaluated; if (unevaluated.get().isUndefined()) { evaluated.get() = evaluated.get(); } passes = RenderPass::Translucent; if (!(!unevaluated.get().isUndefined() || evaluated.get().constantOr(Color()).a < 1.0f || evaluated.get().constantOr(0) < 1.0f)) { // Supply both - evaluated based on opaquePassCutoff in render(). passes |= RenderPass::Opaque; } properties->renderPasses = mbgl::underlying_type(passes); evaluatedProperties = std::move(properties); } bool RenderFillLayer::hasTransition() const { return unevaluated.hasTransition(); } bool RenderFillLayer::hasCrossfade() const { return getCrossfade(evaluatedProperties).t != 1; } 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 (unevaluated.get().isUndefined()) { parameters.renderTileClippingMasks(renderTiles); for (const RenderTile& tile : *renderTiles) { const LayerRenderData* renderData = getRenderDataForPass(tile, parameters.pass); if (!renderData) { continue; } auto& bucket = static_cast(*renderData->bucket); const auto& evaluated = getEvaluated(renderData->layerProperties); auto draw = [&](auto& programInstance, const auto& drawMode, const auto& depthMode, const auto& indexBuffer, const auto& segments, auto&& textureBindings) { const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); const auto allUniformValues = programInstance.computeAllUniformValues( FillProgram::LayoutUniformValues{ uniforms::matrix::Value(tile.translatedMatrix( evaluated.get(), evaluated.get(), parameters.state)), uniforms::world::Value(parameters.backend.getDefaultRenderable().getSize()), }, paintPropertyBinders, evaluated, static_cast(parameters.state.getZoom())); const auto allAttributeBindings = programInstance.computeAllAttributeBindings( *bucket.vertexBuffer, paintPropertyBinders, evaluated); checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); programInstance.draw(parameters.context, *parameters.renderPass, drawMode, depthMode, parameters.stencilModeForClipping(tile.id), parameters.colorModeForRenderPass(), gfx::CullFaceMode::disabled(), indexBuffer, segments, allUniformValues, allAttributeBindings, std::forward(textureBindings), getID()); }; auto fillRenderPass = (evaluated.get().constantOr(Color()).a >= 1.0f && evaluated.get().constantOr(0) >= 1.0f && parameters.currentLayer >= parameters.opaquePassCutoff) ? RenderPass::Opaque : RenderPass::Translucent; if (bucket.triangleIndexBuffer && parameters.pass == fillRenderPass) { draw(*fillProgram, gfx::Triangles(), parameters.depthModeForSublayer(1, parameters.pass == RenderPass::Opaque ? gfx::DepthMaskType::ReadWrite : gfx::DepthMaskType::ReadOnly), *bucket.triangleIndexBuffer, bucket.triangleSegments, FillProgram::TextureBindings{}); } if (evaluated.get() && parameters.pass == RenderPass::Translucent) { draw(*fillOutlineProgram, gfx::Lines{2.0f}, parameters.depthModeForSublayer(unevaluated.get().isUndefined() ? 2 : 0, gfx::DepthMaskType::ReadOnly), *bucket.lineIndexBuffer, bucket.lineSegments, FillOutlineProgram::TextureBindings{}); } } } else { if (parameters.pass != RenderPass::Translucent) { return; } parameters.renderTileClippingMasks(renderTiles); for (const RenderTile& tile : *renderTiles) { const LayerRenderData* renderData = getRenderDataForPass(tile, parameters.pass); if (!renderData) { continue; } auto& bucket = static_cast(*renderData->bucket); const auto& evaluated = getEvaluated(renderData->layerProperties); const auto& crossfade = getCrossfade(renderData->layerProperties); const auto& fillPatternValue = evaluated.get().constantOr(Faded{"", ""}); std::optional patternPosA = tile.getPattern(fillPatternValue.from.id()); std::optional patternPosB = tile.getPattern(fillPatternValue.to.id()); auto draw = [&](auto& programInstance, const auto& drawMode, const auto& depthMode, const auto& indexBuffer, const auto& segments, auto&& textureBindings) { const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); paintPropertyBinders.setPatternParameters(patternPosA, patternPosB, crossfade); const auto allUniformValues = programInstance.computeAllUniformValues( FillPatternProgram::layoutUniformValues( tile.translatedMatrix( evaluated.get(), evaluated.get(), parameters.state), parameters.backend.getDefaultRenderable().getSize(), tile.getIconAtlasTexture().size, crossfade, tile.id, parameters.state, parameters.pixelRatio), paintPropertyBinders, evaluated, static_cast(parameters.state.getZoom())); const auto allAttributeBindings = programInstance.computeAllAttributeBindings( *bucket.vertexBuffer, paintPropertyBinders, evaluated); checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); programInstance.draw(parameters.context, *parameters.renderPass, drawMode, depthMode, parameters.stencilModeForClipping(tile.id), parameters.colorModeForRenderPass(), gfx::CullFaceMode::disabled(), indexBuffer, segments, allUniformValues, allAttributeBindings, std::forward(textureBindings), getID()); }; if (bucket.triangleIndexBuffer) { draw(*fillPatternProgram, gfx::Triangles(), parameters.depthModeForSublayer(1, gfx::DepthMaskType::ReadWrite), *bucket.triangleIndexBuffer, bucket.triangleSegments, FillPatternProgram::TextureBindings{ textures::image::Value{tile.getIconAtlasTexture().getResource(), gfx::TextureFilterType::Linear}, }); } if (evaluated.get() && unevaluated.get().isUndefined()) { draw(*fillOutlinePatternProgram, gfx::Lines{2.0f}, parameters.depthModeForSublayer(2, gfx::DepthMaskType::ReadOnly), *bucket.lineIndexBuffer, bucket.lineSegments, FillOutlinePatternProgram::TextureBindings{ textures::image::Value{tile.getIconAtlasTexture().getResource(), gfx::TextureFilterType::Linear}, }); } } } } bool RenderFillLayer::queryIntersectsFeature(const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float, const TransformState& transformState, const float pixelsToTileUnits, const mat4&, const FeatureState&) const { const auto& evaluated = getEvaluated(evaluatedProperties); auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(queryGeometry, evaluated.get(), evaluated.get(), static_cast(transformState.getBearing()), pixelsToTileUnits); return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); } void RenderFillLayer::layerRemoved(UniqueChangeRequestVec& changes) { // Remove everything if (tileLayerGroup) { changes.emplace_back(std::make_unique(tileLayerGroup->getLayerIndex())); tileLayerGroup.reset(); } } void RenderFillLayer::update(const int32_t layerIndex, gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& /*state*/, UniqueChangeRequestVec& changes) { std::unique_lock guard(mutex); if (!shader) { shader = context.getGenericShader(shaders, "FillShader"); } if (!shader || !renderTiles || renderTiles->empty()) { if (tileLayerGroup) { stats.tileDrawablesRemoved += tileLayerGroup->getDrawableCount(); tileLayerGroup->clearDrawables(); } return; } // Set up a layer group if (!tileLayerGroup) { tileLayerGroup = context.createTileLayerGroup(layerIndex, /*initialCapacity=*/64); if (!tileLayerGroup) { return; } changes.emplace_back(std::make_unique(tileLayerGroup, /*canReplace=*/true)); } std::unordered_set newTileIDs(renderTiles->size()); std::transform(renderTiles->begin(), renderTiles->end(), std::inserter(newTileIDs, newTileIDs.begin()), [](const auto& renderTile) -> OverscaledTileID { return renderTile.get().getOverscaledTileID(); }); std::unique_ptr builder; std::vector newTiles; for (const auto renderPass : {RenderPass::Opaque, RenderPass::Translucent}) { tileLayerGroup->observeDrawables([&](gfx::UniqueDrawable& drawable) { // Has this tile dropped out of the cover set? const auto tileID = drawable->getTileID(); if (tileID && newTileIDs.find(*tileID) == newTileIDs.end()) { // remove it drawable.reset(); ++stats.tileDrawablesRemoved; } }); if (unevaluated.get().isUndefined()) { // parameters.renderTileClippingMasks(renderTiles); for (const RenderTile& tile : *renderTiles) { const auto& tileID = tile.getOverscaledTileID(); auto& tileDrawable = tileLayerGroup->getDrawable(renderPass, tileID); const auto removeTile = [&]() { if (tileDrawable) { tileLayerGroup->removeDrawable(renderPass, tileID); ++stats.tileDrawablesRemoved; } }; const LayerRenderData* renderData = getRenderDataForPass(tile, renderPass); if (!renderData) { removeTile(); continue; } auto& bucket = static_cast(*renderData->bucket); const auto& evaluated = getEvaluated(renderData->layerProperties); const auto evalColor = evaluated.get().constantOr(Color()); const auto fillOpacity = evaluated.get().constantOr(0); const auto fillColor = evalColor * (fillOpacity > 0.0f ? fillOpacity : 1.0f); const auto fillRenderPass = (evalColor.a >= 1.0f && fillOpacity >= 1.0f /* && parameters.currentLayer >= parameters.opaquePassCutoff*/) ? RenderPass::Opaque : RenderPass::Translucent; if (fillRenderPass != renderPass) { removeTile(); continue; } if (tileDrawable) { // needed and already present, update? continue; } if (!builder) { builder = context.createDrawableBuilder("fill"); builder->setShader(shader); builder->addTweaker(context.createDrawableTweaker()); builder->setColorMode(gfx::DrawableBuilder::ColorMode::PerVertex); builder->setDepthType(gfx::DepthMaskType::ReadWrite); builder->setLayerIndex(layerIndex); } // tile.translatedMatrix(evaluated.get(), evaluated.get(), // parameters.state) builder->setColor(fillColor); builder->setRenderPass(renderPass); const std::vector>>::Vertex>& verts = bucket.vertices.vector(); std::vector> rawVerts(verts.size()); std::transform(verts.begin(), verts.end(), rawVerts.begin(), [](const auto& x) { return x.a1; }); for (const auto& seg : bucket.triangleSegments) { builder->addTriangles(rawVerts, seg.vertexOffset, seg.vertexLength, bucket.triangles.vector(), seg.indexOffset, seg.indexLength); } // evaluated.get(), // evaluated.get(), // parameters.stencilModeForClipping(tile.id), // parameters.colorModeForRenderPass(), // gfx::CullFaceMode::disabled(), // // const auto isOpaque = (evaluated.get().constantOr(Color()).a >= 1.0f && // evaluated.get().constantOr(0) >= 1.0f // );// && layerIndex >= parameters.opaquePassCutoff); // const auto fillRenderPass = isOpaque ? RenderPass::Opaque : RenderPass::Translucent; // // if (renderPass == fillRenderPass) { // } // const auto depthMask = (renderPass == RenderPass::Opaque) ? gfx::DepthMaskType::ReadWrite // : gfx::DepthMaskType::ReadOnly; // const auto depthMode = parameters.depthModeForSublayer(1, depthMask); // if (evaluated.get() && parameters.pass == RenderPass::Translucent) { // draw(*fillOutlineProgram, // gfx::Lines{2.0f}, // parameters.depthModeForSublayer(unevaluated.get().isUndefined() // ? 2 : 0, // gfx::DepthMaskType::ReadOnly), // *bucket.lineIndexBuffer, // bucket.lineSegments, // FillOutlineProgram::TextureBindings{}); // } builder->flush(); auto newDrawables = builder->clearDrawables(); if (!newDrawables.empty()) { auto& drawable = newDrawables[0]; drawable->setTileID(tileID); // Track it. //tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable)); ++stats.tileDrawablesAdded; // Log::Warning(Event::General, "Adding drawable for " + util::toString(tileID) + " total " + // std::to_string(stats.tileDrawablesAdded+1)); } } } else { // if (parameters.pass != RenderPass::Translucent) { // return; // } // // parameters.renderTileClippingMasks(renderTiles); // // for (const RenderTile& tile : *renderTiles) { // const LayerRenderData* renderData = getRenderDataForPass(tile, parameters.pass); // if (!renderData) { // continue; // } // auto& bucket = static_cast(*renderData->bucket); // const auto& evaluated = getEvaluated(renderData->layerProperties); // const auto& crossfade = getCrossfade(renderData->layerProperties); // // const auto& fillPatternValue = // evaluated.get().constantOr(Faded{"", ""}); // std::optional patternPosA = tile.getPattern(fillPatternValue.from.id()); // std::optional patternPosB = tile.getPattern(fillPatternValue.to.id()); // // auto draw = [&](auto& programInstance, // const auto& drawMode, // const auto& depthMode, // const auto& indexBuffer, // const auto& segments, // auto&& textureBindings) { // const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); // paintPropertyBinders.setPatternParameters(patternPosA, patternPosB, crossfade); // // const auto allUniformValues = programInstance.computeAllUniformValues( // FillPatternProgram::layoutUniformValues( // tile.translatedMatrix( // evaluated.get(), evaluated.get(), // parameters.state), // parameters.backend.getDefaultRenderable().getSize(), // tile.getIconAtlasTexture().size, // crossfade, // tile.id, // parameters.state, // parameters.pixelRatio), // paintPropertyBinders, // evaluated, // static_cast(parameters.state.getZoom())); // const auto allAttributeBindings = programInstance.computeAllAttributeBindings( // *bucket.vertexBuffer, paintPropertyBinders, evaluated); // // checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); // // programInstance.draw(parameters.context, // *parameters.renderPass, // drawMode, // depthMode, // parameters.stencilModeForClipping(tile.id), // parameters.colorModeForRenderPass(), // gfx::CullFaceMode::disabled(), // indexBuffer, // segments, // allUniformValues, // allAttributeBindings, // std::forward(textureBindings), // getID()); // }; // // if (bucket.triangleIndexBuffer) { // draw(*fillPatternProgram, // gfx::Triangles(), // parameters.depthModeForSublayer(1, gfx::DepthMaskType::ReadWrite), // *bucket.triangleIndexBuffer, // bucket.triangleSegments, // FillPatternProgram::TextureBindings{ // textures::image::Value{tile.getIconAtlasTexture().getResource(), // gfx::TextureFilterType::Linear}, // }); // } // if (evaluated.get() && unevaluated.get().isUndefined()) { // draw(*fillOutlinePatternProgram, // gfx::Lines{2.0f}, // parameters.depthModeForSublayer(2, gfx::DepthMaskType::ReadOnly), // *bucket.lineIndexBuffer, // bucket.lineSegments, // FillOutlinePatternProgram::TextureBindings{ // textures::image::Value{tile.getIconAtlasTexture().getResource(), // gfx::TextureFilterType::Linear}, // }); // } // } } } } } // namespace mbgl