Files
MapLibreParticleLab/src/mbgl/renderer/layers/render_raster_layer.cpp
2023-08-31 15:36:19 +02:00

465 lines
20 KiB
C++

#include <mbgl/renderer/layers/render_raster_layer.hpp>
#include <mbgl/renderer/layers/raster_layer_tweaker.hpp>
#include <mbgl/renderer/buckets/raster_bucket.hpp>
#include <mbgl/renderer/layer_group.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_static_data.hpp>
#include <mbgl/renderer/sources/render_image_source.hpp>
#include <mbgl/programs/programs.hpp>
#include <mbgl/programs/raster_program.hpp>
#include <mbgl/tile/tile.hpp>
#include <mbgl/gfx/context.hpp>
#include <mbgl/gfx/drawable_builder.hpp>
#include <mbgl/gfx/cull_face_mode.hpp>
#include <mbgl/math/angles.hpp>
#include <mbgl/style/layers/raster_layer_impl.hpp>
#include <mbgl/shaders/shader_program_base.hpp>
#include <mbgl/gfx/image_drawable_data.hpp>
namespace mbgl {
using namespace style;
namespace {
inline const RasterLayer::Impl& impl_cast(const Immutable<style::Layer::Impl>& impl) {
assert(impl->getTypeInfo() == RasterLayer::Impl::staticTypeInfo());
return static_cast<const RasterLayer::Impl&>(*impl);
}
} // namespace
RenderRasterLayer::RenderRasterLayer(Immutable<style::RasterLayer::Impl> _impl)
: RenderLayer(makeMutable<RasterLayerProperties>(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<RasterLayerProperties>(staticImmutableCast<RasterLayer::Impl>(baseImpl),
unevaluated.evaluate(parameters));
passes = properties->evaluated.get<style::RasterOpacity>() > 0 ? RenderPass::Translucent : RenderPass::None;
properties->renderPasses = mbgl::underlying_type(passes);
evaluatedProperties = std::move(properties);
if (tileLayerGroup && tileLayerGroup->getLayerTweaker()) {
tileLayerGroup->setLayerTweaker(std::make_shared<RasterLayerTweaker>(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<float, 3> spinWeights(float spin) {
spin = util::deg2radf(spin);
float s = std::sin(spin);
float c = std::cos(spin);
std::array<float, 3> 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.getLegacyGroup().populate(rasterProgram)) return;
const auto& evaluated = static_cast<const RasterLayerProperties&>(*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<RasterOpacity>()),
uniforms::fade_t::Value(1),
uniforms::brightness_low::Value(evaluated.get<RasterBrightnessMin>()),
uniforms::brightness_high::Value(evaluated.get<RasterBrightnessMax>()),
uniforms::saturation_factor::Value(saturationFactor(evaluated.get<RasterSaturation>())),
uniforms::contrast_factor::Value(contrastFactor(evaluated.get<RasterContrast>())),
uniforms::spin_weights::Value(spinWeights(evaluated.get<RasterHueRotate>())),
uniforms::buffer_scale::Value(1.0f),
uniforms::scale_parent::Value(1.0f),
uniforms::tl_parent::Value(std::array<float, 2>{{0.0f, 0.0f}}),
},
paintAttributeData,
evaluated,
static_cast<float>(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<RasterResampling>() == 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<RasterBucket&>(*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<RemoveLayerGroupRequest>(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<std::mutex> guard(mutex);
if ((!renderTiles || renderTiles->empty()) && !imageData) {
if (tileLayerGroup) {
stats.tileDrawablesRemoved += tileLayerGroup->getDrawableCount();
tileLayerGroup->clearDrawables();
}
if (imageLayerGroup) {
// stats.tileDrawablesRemoved += layerScene->getDrawableCount();
imageLayerGroup->clearDrawables();
}
return;
}
auto renderPass = RenderPass::Translucent;
if (!rasterShader) {
rasterShader = context.getGenericShader(shaders, "RasterShader");
}
const auto& evaluated = static_cast<const RasterLayerProperties&>(*evaluatedProperties).evaluated;
RasterProgram::Binders paintAttributeData{evaluated, 0};
const gfx::TextureFilterType filter = evaluated.get<RasterResampling>() == RasterResamplingType::Nearest
? gfx::TextureFilterType::Nearest
: gfx::TextureFilterType::Linear;
auto createBuilder = [&context, &renderPass, this]() -> std::unique_ptr<gfx::DrawableBuilder> {
std::unique_ptr<gfx::DrawableBuilder> 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");
return builder;
};
auto setTextures = [&context, &filter, this](std::unique_ptr<gfx::DrawableBuilder>& builder, RasterBucket& bucket) {
// textures
auto location0 = rasterShader->getSamplerLocation("u_image0");
if (location0.has_value()) {
std::shared_ptr<gfx::Texture2D> tex0 = context.createTexture2D();
tex0->setImage(bucket.image);
tex0->setSamplerConfiguration({filter, gfx::TextureWrapType::Clamp, gfx::TextureWrapType::Clamp});
builder->setTexture(tex0, location0.value());
}
auto location1 = rasterShader->getSamplerLocation("u_image1");
if (location1.has_value()) {
std::shared_ptr<gfx::Texture2D> tex1 = context.createTexture2D();
tex1->setImage(bucket.image);
tex1->setSamplerConfiguration({filter, gfx::TextureWrapType::Clamp, gfx::TextureWrapType::Clamp});
builder->setTexture(tex1, location1.value());
}
};
auto buildTileDrawables = [&setTextures](std::unique_ptr<gfx::DrawableBuilder>& builder, RasterBucket& bucket) {
auto buildRenderData = [](const TileMask& mask,
std::vector<std::array<int16_t, 2>>& vertices,
std::vector<std::array<int16_t, 2>>& attributes,
std::vector<uint16_t>& indices,
std::vector<SegmentBase>& 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<int16_t> tlVertex = {static_cast<int16_t>(id.x * vertexExtent),
static_cast<int16_t>(id.y * vertexExtent)};
const Point<int16_t> brVertex = {static_cast<int16_t>(tlVertex.x + vertexExtent),
static_cast<int16_t>(tlVertex.y + vertexExtent)};
if (segments.empty() ||
(segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::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<int16_t, 2>{{tlVertex.x, tlVertex.y}});
attributes.emplace_back(std::array<int16_t, 2>{{tlVertex.x, tlVertex.y}});
vertices.emplace_back(std::array<int16_t, 2>{{brVertex.x, tlVertex.y}});
attributes.emplace_back(std::array<int16_t, 2>{{brVertex.x, tlVertex.y}});
vertices.emplace_back(std::array<int16_t, 2>{{tlVertex.x, brVertex.y}});
attributes.emplace_back(std::array<int16_t, 2>{{tlVertex.x, brVertex.y}});
vertices.emplace_back(std::array<int16_t, 2>{{brVertex.x, brVertex.y}});
attributes.emplace_back(std::array<int16_t, 2>{{brVertex.x, brVertex.y}});
auto& segment = segments.back();
assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max());
const auto offset = static_cast<uint16_t>(segment.vertexLength);
// 0, 1, 2
// 1, 2, 3
indices.insert(indices.end(),
{offset, static_cast<uint16_t>(offset + 1), static_cast<uint16_t>(offset + 2u)});
indices.insert(indices.end(),
{static_cast<uint16_t>(offset + 1),
static_cast<uint16_t>(offset + 2),
static_cast<uint16_t>(offset + 3)});
segment.vertexLength += vertexLength;
segment.indexLength += 6;
}
};
std::vector<std::array<int16_t, 2>> vertices, attributes;
std::vector<uint16_t> indices;
std::vector<SegmentBase> 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<gfx::VertexAttribute::int2>(index++, {a[0], a[1]});
}
}
builder->setVertexAttributes(std::move(vertexAttrs));
}
// textures
setTextures(builder, bucket);
};
auto buildImageDrawables = [&setTextures](std::unique_ptr<gfx::DrawableBuilder>& builder, RasterBucket& bucket) {
std::vector<std::array<int16_t, 2>> vertices(bucket.vertices.vector().size());
std::transform(
bucket.vertices.vector().begin(), bucket.vertices.vector().end(), vertices.begin(), [](const auto& x) {
return x.a1;
});
builder->addVertices(vertices, 0, vertices.size());
builder->setSegments(gfx::Triangles(), bucket.indices.vector(), bucket.segments.data(), bucket.segments.size());
// attributes
{
gfx::VertexAttributeArray vertexAttrs;
if (auto& attr = vertexAttrs.getOrAdd("a_texture_pos")) {
std::size_t index{0};
for (auto& v : bucket.vertices.vector()) {
attr->set<gfx::VertexAttribute::int2>(index++, {v.a2[0], v.a2[1]});
}
}
builder->setVertexAttributes(std::move(vertexAttrs));
}
// textures
setTextures(builder, bucket);
};
if (imageData) {
RasterBucket& bucket = *imageData->bucket;
if (!bucket.vertices.empty()) {
if (imageLayerGroup) {
imageLayerGroup->clearDrawables();
} else {
// Set up a layer group
imageLayerGroup = context.createLayerGroup(layerIndex, /*initialCapacity=*/64, getID());
imageLayerGroup->setLayerTweaker(std::make_shared<RasterLayerTweaker>(evaluatedProperties));
changes.emplace_back(std::make_unique<AddLayerGroupRequest>(imageLayerGroup, /*canReplace=*/true));
}
auto builder = createBuilder();
for (const auto& matrix_ : imageData->matrices) {
buildImageDrawables(builder, bucket);
// finish
builder->flush();
for (auto& drawable : builder->clearDrawables()) {
drawable->setData(std::make_unique<gfx::ImageDrawableData>(matrix_));
imageLayerGroup->addDrawable(std::move(drawable));
++stats.tileDrawablesAdded;
}
}
}
} else if (renderTiles) {
if (tileLayerGroup) {
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;
}
});
} else {
// Set up a tile layer group
tileLayerGroup = context.createTileLayerGroup(layerIndex, /*initialCapacity=*/64, getID());
tileLayerGroup->setLayerTweaker(std::make_shared<RasterLayerTweaker>(evaluatedProperties));
changes.emplace_back(std::make_unique<AddLayerGroupRequest>(tileLayerGroup, /*canReplace=*/true));
}
auto builder = createBuilder();
for (const RenderTile& tile : *renderTiles) {
const auto& tileID = tile.getOverscaledTileID();
auto* bucket_ = tile.getBucket(*baseImpl);
if (!bucket_) {
continue;
}
auto& bucket = static_cast<RasterBucket&>(*bucket_);
if (!bucket.hasData()) continue;
if (tileLayerGroup->getDrawableCount(renderPass, tileID) > 0) continue;
if (bucket.image) {
buildTileDrawables(builder, bucket);
// finish
builder->flush();
for (auto& drawable : builder->clearDrawables()) {
drawable->setTileID(tileID);
tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
++stats.tileDrawablesAdded;
}
};
}
}
}
} // namespace mbgl