#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()); } constexpr auto shaderName = "background_generic"; void RenderFillLayer::layerRemoved(UniqueChangeRequestVec& changes) { // TODO: This isn't happening on style change, so old tile drawables are being left active // Remove everything decltype(tileDrawables) localDrawables; { std::unique_lock guard(mutex); localDrawables = std::move(tileDrawables); } removeDrawables( localDrawables.cbegin(), localDrawables.cend(), changes, [](auto& ii) { return ii->second->getId(); }); } 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, shaderName); } // const auto& evaluated = getEvaluated(evaluatedProperties); // std::optional color; // if (evaluated.get().from.empty()) { // const auto opacity = evaluated.get(); // if (opacity > 0.0f) { // color = evaluated.get() * evaluated.get(); // } // } // // // If the result is transparent or missing, just remove any existing drawables and stop // if (!color) { // removeDrawables( // tileDrawables.cbegin(), tileDrawables.cend(), changes, [](auto& ii) { return ii->second->getId(); }); // tileDrawables.clear(); // return; // } // // const bool colorChange = (color != lastColor); // const bool layerChange = (layerIndex != lastLayerIndex); // lastColor = color; // lastLayerIndex = layerIndex; // // const auto zoom = state.getIntegerZoom(); // const auto tileCover = util::tileCover(state, zoom); // // // Drawables per overscaled or canonical tile? // // const UnwrappedTileID unwrappedTileID = tileID.toUnwrapped(); // // // Put the tile cover into a searchable form. // // TODO: Likely better to sort and `std::binary_search` the vector. // // If it's returned in a well-defined order, we might not even need to sort. // const std::unordered_set newTileIDs(tileCover.begin(), tileCover.end()); // // // For each existing tile drawable... // for (auto iter = tileDrawables.begin(); iter != tileDrawables.end();) { // const auto& drawable = iter->second; // // // Has this tile dropped out of the cover set? // if (newTileIDs.find(iter->first) == newTileIDs.end()) { // // remove it // changes.emplace_back(std::make_unique(drawable->getId())); // // Log::Warning(Event::General, "Removing drawable for " + util::toString(iter->first) + " total " + // // std::to_string(stats.tileDrawablesRemoved+1)); // iter = tileDrawables.erase(iter); // ++stats.tileDrawablesRemoved; // continue; // } // ++iter; // // // If the color evaluated to a new value, update all vertexes of the drawable to the new color // if (colorChange) { // drawable->resetColor(*color); // } // if (layerChange) { // drawable->setLayerIndex(layerIndex); // } // } // // std::unique_ptr builder; // // // For each tile in the cover set, add a tile drawable if one doesn't already exist. // // We currently assume only one drawable per tile. // for (const auto& tileID : tileCover) { // const auto result = tileDrawables.insert(std::make_pair(tileID, gfx::DrawablePtr())); // if (!result.second) { // // Already present // // TODO: Update matrix here or in the tweaker? // continue; // } // // // We actually need to build things, so set up a builder if we haven't already // if (!builder) { // builder = context.createDrawableBuilder("background"); // builder->setShader(shader); // builder->addTweaker(context.createDrawableTweaker()); // builder->setColor(*color); // builder->setColorMode(gfx::DrawableBuilder::ColorMode::PerDrawable); // builder->setDepthType(gfx::DepthMaskType::ReadWrite); // builder->setLayerIndex(layerIndex); // } // // // Tile coordinates are fixed... // builder->addQuad(0, 0, util::EXTENT, util::EXTENT); // // // ... they're placed with the matrix in the uniforms, which changes with the view // builder->setMatrix(/*parameters.matrixForTile(tileID.toUnwrapped())*/ matrix::identity4()); // // builder->flush(); // // auto drawables = builder->clearDrawables(); // if (!drawables.empty()) { // auto& drawable = drawables[0]; // drawable->setTileID(tileID); // result.first->second = drawable; // changes.emplace_back(std::make_unique(std::move(drawable))); // ++stats.tileDrawablesAdded; // // Log::Warning(Event::General, "Adding drawable for " + util::toString(tileID) + " total " + // // std::to_string(stats.tileDrawablesAdded+1)); // } // } 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(); }); // For each existing tile drawable... for (auto iter = tileDrawables.begin(); iter != tileDrawables.end();) { const auto& drawable = iter->second; // Has this tile dropped out of the cover set? if (newTileIDs.find(iter->first) == newTileIDs.end()) { // remove it if (drawable) { changes.emplace_back(std::make_unique(drawable->getId())); } // Log::Warning(Event::General, "Removing drawable for " + util::toString(iter->first) + " total " + // std::to_string(stats.tileDrawablesRemoved+1)); iter = tileDrawables.erase(iter); ++stats.tileDrawablesRemoved; continue; } ++iter; // update... } std::unique_ptr builder; if (unevaluated.get().isUndefined()) { // parameters.renderTileClippingMasks(renderTiles); for (const RenderTile& tile : *renderTiles) { const auto& tileID = tile.getOverscaledTileID(); const auto hit = tileDrawables.find(tileID); const auto renderPass = RenderPass::Translucent; const LayerRenderData* renderData = getRenderDataForPass(tile, renderPass); if (!renderData) { // Remove the tile if it was previously present if (hit != tileDrawables.end()) { if (hit->second) { changes.emplace_back(std::make_unique(hit->second->getId())); } tileDrawables.erase(hit); ++stats.tileDrawablesRemoved; } continue; } if (hit != tileDrawables.end()) { // already present continue; } auto& bucket = static_cast(*renderData->bucket); const auto& evaluated = getEvaluated(renderData->layerProperties); if (!builder) { builder = context.createDrawableBuilder("fill"); builder->setShader(shader); builder->addTweaker(context.createDrawableTweaker()); builder->setColorMode(gfx::DrawableBuilder::ColorMode::PerDrawable); builder->setDepthType(gfx::DepthMaskType::ReadWrite); builder->setLayerIndex(layerIndex); } const auto fillRenderPass = (evaluated.get().constantOr(Color()).a >= 1.0f && evaluated.get().constantOr(0) >= 1.0f /* && parameters.currentLayer >= parameters.opaquePassCutoff*/) ? RenderPass::Opaque : RenderPass::Translucent; builder->setRenderPass(fillRenderPass); 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. #if !defined(NDEBUG) const auto result = #endif tileDrawables.insert(std::make_pair(tileID, drawable)); // This should always insert because we checked previously. assert(result.second); changes.emplace_back(std::make_unique(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