#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 RasterLayer::Impl& impl_cast(const Immutable& impl) { assert(impl->getTypeInfo() == RasterLayer::Impl::staticTypeInfo()); return static_cast(*impl); } } // namespace RenderRasterLayer::RenderRasterLayer(Immutable _impl) : RenderLayer(makeMutable(std::move(_impl))), unevaluated(impl_cast(baseImpl).paint.untransitioned()) {} RenderRasterLayer::~RenderRasterLayer() = default; void RenderRasterLayer::transition(const TransitionParameters& parameters) { unevaluated = impl_cast(baseImpl).paint.transitioned(parameters, std::move(unevaluated)); } void RenderRasterLayer::evaluate(const PropertyEvaluationParameters& parameters) { auto properties = makeMutable(staticImmutableCast(baseImpl), unevaluated.evaluate(parameters)); passes = properties->evaluated.get() > 0 ? RenderPass::Translucent : RenderPass::None; properties->renderPasses = mbgl::underlying_type(passes); evaluatedProperties = std::move(properties); if (tileLayerGroup && tileLayerGroup->getLayerTweaker()) { tileLayerGroup->setLayerTweaker(std::make_shared(evaluatedProperties)); } } bool RenderRasterLayer::hasTransition() const { return unevaluated.hasTransition(); } bool RenderRasterLayer::hasCrossfade() const { return false; } static float saturationFactor(float saturation) { if (saturation > 0) { return 1.f - 1.f / (1.001f - saturation); } else { return -saturation; } } static float contrastFactor(float contrast) { if (contrast > 0) { return 1 / (1 - contrast); } else { return 1 + contrast; } } static std::array spinWeights(float spin) { spin = util::deg2radf(spin); float s = std::sin(spin); float c = std::cos(spin); std::array spin_weights = { {(2 * c + 1) / 3, (-std::sqrt(3.0f) * s - c + 1) / 3, (std::sqrt(3.0f) * s - c + 1) / 3}}; return spin_weights; } void RenderRasterLayer::prepare(const LayerPrepareParameters& params) { renderTiles = params.source->getRenderTiles(); imageData = params.source->getImageRenderData(); // It is possible image data is not available until the source loads it. assert(renderTiles || imageData || !params.source->isEnabled()); } void RenderRasterLayer::render(PaintParameters& parameters) { if (parameters.pass != RenderPass::Translucent || (!renderTiles && !imageData)) { return; } if (!parameters.shaders.populate(rasterProgram)) return; const auto& evaluated = static_cast(*evaluatedProperties).evaluated; RasterProgram::Binders paintAttributeData{evaluated, 0}; auto draw = [&](const mat4& matrix, const auto& vertexBuffer, const auto& indexBuffer, const auto& segments, const auto& textureBindings, const std::string& drawScopeID) { const auto allUniformValues = RasterProgram::computeAllUniformValues( RasterProgram::LayoutUniformValues{ uniforms::matrix::Value(matrix), uniforms::opacity::Value(evaluated.get()), uniforms::fade_t::Value(1), uniforms::brightness_low::Value(evaluated.get()), uniforms::brightness_high::Value(evaluated.get()), uniforms::saturation_factor::Value(saturationFactor(evaluated.get())), uniforms::contrast_factor::Value(contrastFactor(evaluated.get())), uniforms::spin_weights::Value(spinWeights(evaluated.get())), uniforms::buffer_scale::Value(1.0f), uniforms::scale_parent::Value(1.0f), uniforms::tl_parent::Value(std::array{{0.0f, 0.0f}}), }, paintAttributeData, evaluated, static_cast(parameters.state.getZoom())); const auto allAttributeBindings = RasterProgram::computeAllAttributeBindings( vertexBuffer, paintAttributeData, evaluated); checkRenderability(parameters, RasterProgram::activeBindingCount(allAttributeBindings)); rasterProgram->draw(parameters.context, *parameters.renderPass, gfx::Triangles(), parameters.depthModeForSublayer(0, gfx::DepthMaskType::ReadOnly), gfx::StencilMode::disabled(), parameters.colorModeForRenderPass(), gfx::CullFaceMode::disabled(), indexBuffer, segments, allUniformValues, allAttributeBindings, textureBindings, getID() + "/" + drawScopeID); }; const gfx::TextureFilterType filter = evaluated.get() == RasterResamplingType::Nearest ? gfx::TextureFilterType::Nearest : gfx::TextureFilterType::Linear; if (imageData && !imageData->bucket->needsUpload()) { RasterBucket& bucket = *imageData->bucket; assert(bucket.texture); size_t i = 0; for (const auto& matrix_ : imageData->matrices) { draw(matrix_, *bucket.vertexBuffer, *bucket.indexBuffer, bucket.segments, RasterProgram::TextureBindings{ textures::image0::Value{bucket.texture->getResource(), filter}, textures::image1::Value{bucket.texture->getResource(), filter}, }, std::to_string(i++)); } } else if (renderTiles) { for (const RenderTile& tile : *renderTiles) { auto* bucket_ = tile.getBucket(*baseImpl); if (!bucket_) { continue; } auto& bucket = static_cast(*bucket_); if (!bucket.hasData()) continue; assert(bucket.texture); if (bucket.vertexBuffer && bucket.indexBuffer) { // Draw only the parts of the tile that aren't drawn by another tile in the layer. draw(parameters.matrixForTile(tile.id, !parameters.state.isChanging()), *bucket.vertexBuffer, *bucket.indexBuffer, bucket.segments, RasterProgram::TextureBindings{ textures::image0::Value{bucket.texture->getResource(), filter}, textures::image1::Value{bucket.texture->getResource(), filter}, }, "image"); } else { // Draw the full tile. if (bucket.segments.empty()) { // Copy over the segments so that we can create our own DrawScopes. bucket.segments = RenderStaticData::rasterSegments(); } draw(parameters.matrixForTile(tile.id, !parameters.state.isChanging()), *parameters.staticData.rasterVertexBuffer, *parameters.staticData.quadTriangleIndexBuffer, bucket.segments, RasterProgram::TextureBindings{ textures::image0::Value{bucket.texture->getResource(), filter}, textures::image1::Value{bucket.texture->getResource(), filter}, }, "image"); } } } } void RenderRasterLayer::layerRemoved(UniqueChangeRequestVec& changes) { // Remove everything if (tileLayerGroup) { changes.emplace_back(std::make_unique(tileLayerGroup->getLayerIndex())); tileLayerGroup.reset(); } } void RenderRasterLayer::removeTile(RenderPass renderPass, const OverscaledTileID& tileID) { stats.tileDrawablesRemoved += tileLayerGroup->removeDrawables(renderPass, tileID).size(); } void RenderRasterLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& /*state*/, [[maybe_unused]] const RenderTree& renderTree, [[maybe_unused]] UniqueChangeRequestVec& changes) { std::unique_lock guard(mutex); if ((!renderTiles || renderTiles->empty()) && !imageData) { if (tileLayerGroup) { stats.tileDrawablesRemoved += tileLayerGroup->getDrawableCount(); tileLayerGroup->clearDrawables(); } return; } // Set up a layer group if (!tileLayerGroup) { tileLayerGroup = context.createTileLayerGroup(layerIndex, /*initialCapacity=*/64, getID()); if (!tileLayerGroup) { return; } tileLayerGroup->setLayerTweaker(std::make_shared(evaluatedProperties)); changes.emplace_back(std::make_unique(tileLayerGroup, /*canReplace=*/true)); } auto renderPass = RenderPass::Translucent; if (!rasterShader) { rasterShader = context.getGenericShader(shaders, "RasterShader"); } const auto& evaluated = static_cast(*evaluatedProperties).evaluated; RasterProgram::Binders paintAttributeData{evaluated, 0}; const gfx::TextureFilterType filter = evaluated.get() == RasterResamplingType::Nearest ? gfx::TextureFilterType::Nearest : gfx::TextureFilterType::Linear; (void)filter; if (imageData && !imageData->bucket->needsUpload()) { // TODO: implement arbitrary image // RasterBucket& bucket = *imageData->bucket; assert(imageData->bucket->texture); // size_t i = 0; for (const auto& matrix_ : imageData->matrices) { /*draw(matrix_, *bucket.vertexBuffer, *bucket.indexBuffer, bucket.segments, RasterProgram::TextureBindings{ textures::image0::Value{bucket.texture->getResource(), filter}, textures::image1::Value{bucket.texture->getResource(), filter}, }, std::to_string(i++));*/ (void)matrix_; } } else if (renderTiles) { tileLayerGroup->observeDrawables([&](gfx::UniqueDrawable& drawable) { // Has this tile dropped out of the cover set? if (const auto it = std::find_if(renderTiles->begin(), renderTiles->end(), [&drawable](const auto& renderTile) { return drawable->getTileID() == renderTile.get().getOverscaledTileID(); }); it == renderTiles->end()) { // remove it drawable.reset(); ++stats.tileDrawablesRemoved; } }); for (const RenderTile& tile : *renderTiles) { const auto& tileID = tile.getOverscaledTileID(); auto* bucket_ = tile.getBucket(*baseImpl); if (!bucket_) { continue; } auto& bucket = static_cast(*bucket_); if (!bucket.hasData()) continue; if (tileLayerGroup->getDrawableCount(renderPass, tileID) > 0) continue; if (bucket.image) { std::unique_ptr builder{context.createDrawableBuilder("raster")}; builder->setShader(rasterShader); builder->setRenderPass(renderPass); builder->setColorAttrMode(gfx::DrawableBuilder::ColorAttrMode::None); builder->setSubLayerIndex(0); builder->setDepthType((renderPass == RenderPass::Opaque) ? gfx::DepthMaskType::ReadWrite : gfx::DepthMaskType::ReadOnly); builder->setCullFaceMode(gfx::CullFaceMode::disabled()); builder->setVertexAttrName("a_pos"); // render data auto buildRenderData = [](const TileMask& mask, std::vector>& vertices, std::vector>& attributes, std::vector& indices, std::vector>& segments) { constexpr const uint16_t vertexLength = 4; // Create the vertex buffer for the specified tile mask. for (const auto& id : mask) { // Create a quad for every masked tile. const int32_t vertexExtent = util::EXTENT >> id.z; const Point tlVertex = {static_cast(id.x * vertexExtent), static_cast(id.y * vertexExtent)}; const Point brVertex = {static_cast(tlVertex.x + vertexExtent), static_cast(tlVertex.y + vertexExtent)}; if (segments.empty() || (segments.back().vertexLength + vertexLength > std::numeric_limits::max())) { // Move to a new segments because the old one can't hold the geometry. segments.emplace_back(vertices.size(), indices.size()); } vertices.emplace_back(std::array{{tlVertex.x, tlVertex.y}}); attributes.emplace_back(std::array{{tlVertex.x, tlVertex.y}}); vertices.emplace_back(std::array{{brVertex.x, tlVertex.y}}); attributes.emplace_back(std::array{{brVertex.x, tlVertex.y}}); vertices.emplace_back(std::array{{tlVertex.x, brVertex.y}}); attributes.emplace_back(std::array{{tlVertex.x, brVertex.y}}); vertices.emplace_back(std::array{{brVertex.x, brVertex.y}}); attributes.emplace_back(std::array{{brVertex.x, brVertex.y}}); auto& segment = segments.back(); assert(segment.vertexLength <= std::numeric_limits::max()); const auto offset = static_cast(segment.vertexLength); // 0, 1, 2 // 1, 2, 3 indices.insert(indices.end(), {offset, static_cast(offset + 1), static_cast(offset + 2u)}); indices.insert(indices.end(), {static_cast(offset + 1), static_cast(offset + 2), static_cast(offset + 3)}); segment.vertexLength += vertexLength; segment.indexLength += 6; } }; std::vector> vertices, attributes; std::vector indices; std::vector> segments; buildRenderData(bucket.mask, vertices, attributes, indices, segments); builder->addVertices(vertices, 0, vertices.size()); builder->setSegments(gfx::Triangles(), indices, segments); // attributes { gfx::VertexAttributeArray vertexAttrs; if (auto& attr = vertexAttrs.getOrAdd("a_texture_pos")) { std::size_t index{0}; for (auto& a : attributes) { attr->set(index++, {a[0], a[1]}); } } builder->setVertexAttributes(std::move(vertexAttrs)); } // textures // TODO: move texture creation into gfx::Context std::shared_ptr tex0 = context.createTexture2D(); tex0->setImage(bucket.image).create(); auto location0 = rasterShader->getSamplerLocation("u_image0"); if (location0.has_value()) { builder->setTexture(tex0, location0.value()); } std::shared_ptr tex1 = context.createTexture2D(); tex1->setImage(bucket.image).create(); auto location1 = rasterShader->getSamplerLocation("u_image1"); if (location1.has_value()) { builder->setTexture(tex1, location1.value()); } // finish builder->flush(); for (auto& drawable : builder->clearDrawables()) { drawable->setTileID(tileID); tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable)); ++stats.tileDrawablesAdded; } }; } } } } // namespace mbgl