#include #include #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 BackgroundLayer::Impl& impl_cast(const Immutable& impl) { assert(impl->getTypeInfo() == BackgroundLayer::Impl::staticTypeInfo()); return static_cast(*impl); } } // namespace RenderBackgroundLayer::RenderBackgroundLayer(Immutable _impl) : RenderLayer(makeMutable(std::move(_impl))), unevaluated(impl_cast(baseImpl).paint.untransitioned()) {} RenderBackgroundLayer::~RenderBackgroundLayer() = default; void RenderBackgroundLayer::transition(const TransitionParameters& parameters) { unevaluated = impl_cast(baseImpl).paint.transitioned(parameters, std::move(unevaluated)); } void RenderBackgroundLayer::evaluate(const PropertyEvaluationParameters& parameters) { auto evaluated = unevaluated.evaluate(parameters); auto properties = makeMutable( staticImmutableCast(baseImpl), parameters.getCrossfadeParameters(), evaluated); passes = properties->evaluated.get() == 0.0f ? RenderPass::None : (!unevaluated.get().isUndefined() || properties->evaluated.get() < 1.0f || properties->evaluated.get().a < 1.0f) ? RenderPass::Translucent // Supply both - evaluated based on opaquePassCutoff in render(). : RenderPass::Opaque | RenderPass::Translucent; properties->renderPasses = mbgl::underlying_type(passes); evaluatedProperties = std::move(properties); if (tileLayerGroup) { tileLayerGroup->setLayerTweaker(std::make_shared(evaluatedProperties)); } } bool RenderBackgroundLayer::hasTransition() const { return unevaluated.hasTransition(); } bool RenderBackgroundLayer::hasCrossfade() const { return getCrossfade(evaluatedProperties).t != 1; } void RenderBackgroundLayer::render(PaintParameters& parameters) { // Note that for bottommost layers without a pattern, the background color // is drawn with glClear rather than this method. // Ensure programs are available if (!parameters.shaders.getLegacyGroup().populate(backgroundProgram)) return; if (!parameters.shaders.getLegacyGroup().populate(backgroundPatternProgram)) return; const Properties<>::PossiblyEvaluated properties; const BackgroundProgram::Binders paintAttributeData(properties, 0); auto draw = [&](auto& program, auto&& uniformValues, const auto& textureBindings, const uint32_t id) { const auto allUniformValues = program.computeAllUniformValues( std::forward(uniformValues), paintAttributeData, properties, static_cast(parameters.state.getZoom())); const auto allAttributeBindings = program.computeAllAttributeBindings( *parameters.staticData.tileVertexBuffer, paintAttributeData, properties); checkRenderability(parameters, program.activeBindingCount(allAttributeBindings)); program.draw( parameters.context, *parameters.renderPass, gfx::Triangles(), parameters.depthModeForSublayer( 0, parameters.pass == RenderPass::Opaque ? gfx::DepthMaskType::ReadWrite : gfx::DepthMaskType::ReadOnly), gfx::StencilMode::disabled(), parameters.colorModeForRenderPass(), gfx::CullFaceMode::disabled(), *parameters.staticData.quadTriangleIndexBuffer, segments, allUniformValues, allAttributeBindings, textureBindings, util::toString(id)); }; if (segments.empty()) { segments = RenderStaticData::tileTriangleSegments(); } const auto& evaluated = static_cast(*evaluatedProperties).evaluated; const auto& crossfade = static_cast(*evaluatedProperties).crossfade; if (!evaluated.get().to.empty()) { std::optional imagePosA = parameters.patternAtlas.getPattern( evaluated.get().from.id()); std::optional imagePosB = parameters.patternAtlas.getPattern( evaluated.get().to.id()); if (!imagePosA || !imagePosB) return; uint32_t i = 0; for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { const UnwrappedTileID unwrappedTileID = tileID.toUnwrapped(); draw(*backgroundPatternProgram, BackgroundPatternProgram::layoutUniformValues(parameters.matrixForTile(unwrappedTileID), evaluated.get(), parameters.patternAtlas.getPixelSize(), *imagePosA, *imagePosB, crossfade, unwrappedTileID, parameters.state), BackgroundPatternProgram::TextureBindings{ textures::image::Value{parameters.patternAtlas.textureBinding()}, }, i++); } } else { auto backgroundRenderPass = (evaluated.get().a >= 1.0f && evaluated.get() >= 1.0f && parameters.currentLayer >= parameters.opaquePassCutoff) ? RenderPass::Opaque : RenderPass::Translucent; if (parameters.pass != backgroundRenderPass) { return; } uint32_t i = 0; for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { draw(*backgroundProgram, BackgroundProgram::LayoutUniformValues{ uniforms::matrix::Value(parameters.matrixForTile(tileID.toUnwrapped())), uniforms::color::Value(evaluated.get()), uniforms::opacity::Value(evaluated.get()), }, BackgroundProgram::TextureBindings{}, i++); } } } std::optional RenderBackgroundLayer::getSolidBackground() const { const auto& evaluated = getEvaluated(evaluatedProperties); if (!evaluated.get().from.empty() || evaluated.get() <= 0.0f) { return std::nullopt; } return {evaluated.get() * evaluated.get()}; } namespace { void addPatternIfNeeded(const std::string& id, const LayerPrepareParameters& params) { if (!params.patternAtlas.getPattern(id)) { if (auto* image = params.imageManager.getImage(id)) { params.patternAtlas.addPattern(*image); } } } } // namespace void RenderBackgroundLayer::prepare(const LayerPrepareParameters& params) { const auto& evaluated = getEvaluated(evaluatedProperties); if (!evaluated.get().to.empty()) { // Ensures that the pattern bitmap gets copied to atlas bitmap. // Atlas bitmap is uploaded to atlas texture in upload. addPatternIfNeeded(evaluated.get().from.id(), params); addPatternIfNeeded(evaluated.get().to.id(), params); } } static constexpr std::string_view BackgroundPlainShaderName = "BackgroundShader"; static constexpr std::string_view BackgroundPatternShaderName = "BackgroundPatternShader"; void RenderBackgroundLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& state, [[maybe_unused]] const RenderTree& renderTree, [[maybe_unused]] UniqueChangeRequestVec& changes) { std::unique_lock guard(mutex); const auto zoom = state.getIntegerZoom(); const auto tileCover = util::tileCover(state, zoom); // renderTiles is always empty, we use tileCover instead if (tileCover.empty()) { removeAllTiles(); return; } const auto& evaluated = getEvaluated(evaluatedProperties); const bool hasPattern = !evaluated.get().to.empty(); // TODO: If background is solid, we can skip drawables and rely on the clear color const auto drawPasses = evaluated.get() == 0.0f ? RenderPass::None : (!unevaluated.get().isUndefined() || evaluated.get() < 1.0f || evaluated.get().a < 1.0f) ? RenderPass::Translucent : RenderPass::Opaque | RenderPass::Translucent; // evaluated based on opaquePassCutoff in render() // If the result is transparent or missing, just remove any existing drawables and stop if (drawPasses == RenderPass::None) { removeAllTiles(); return; } if (!tileLayerGroup) { tileLayerGroup = context.createTileLayerGroup(layerIndex, /*initialCapacity=*/64, getID()); if (!tileLayerGroup) { return; } tileLayerGroup->setLayerTweaker(std::make_shared(evaluatedProperties)); } if (!hasPattern && !plainShader) { plainShader = context.getGenericShader(shaders, std::string(BackgroundPlainShaderName)); } if (hasPattern && !patternShader) { patternShader = context.getGenericShader(shaders, std::string(BackgroundPatternShaderName)); } const auto& curShader = hasPattern ? patternShader : plainShader; if (!curShader) { removeAllTiles(); return; } // 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()); std::unique_ptr builder; 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()) { drawable.reset(); ++stats.tileDrawablesRemoved; return; } }); // For each tile in the cover set, add a tile drawable if one doesn't already exist. for (const auto& tileID : tileCover) { // If we already have drawables for this tile, skip. // If a drawable needs to be updated, that's handled in the layer tweaker. if (tileLayerGroup->getDrawableCount(renderPass, tileID) > 0) { continue; } // We actually need to build things, so set up a builder if we haven't already if (!builder) { builder = context.createDrawableBuilder("background"); builder->setRenderPass(drawPasses); builder->setShader(curShader); builder->setColorAttrMode(gfx::DrawableBuilder::ColorAttrMode::PerDrawable); builder->setDepthType(gfx::DepthMaskType::ReadWrite); } // Tile coordinates are fixed. builder->addQuad(0, 0, util::EXTENT, util::EXTENT); builder->flush(); for (auto& drawable : builder->clearDrawables()) { drawable->setTileID(tileID); tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable)); ++stats.tileDrawablesAdded; } } } } } // namespace mbgl