test: add core particle render layer validation
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled

This commit is contained in:
OpenAI Codex
2026-08-29 12:55:54 +08:00
parent 74be2a5052
commit d046be5e52
23 changed files with 1640 additions and 1 deletions

View File

@@ -0,0 +1,25 @@
#include <mbgl/layermanager/particle_validation_layer_factory.hpp>
#include <mbgl/renderer/layers/render_particle_validation_layer.hpp>
#include <mbgl/style/layers/particle_validation_layer.hpp>
#include <mbgl/style/layers/particle_validation_layer_impl.hpp>
namespace mbgl {
const style::LayerTypeInfo* ParticleValidationLayerFactory::getTypeInfo() const noexcept {
return style::ParticleValidationLayer::Impl::staticTypeInfo();
}
std::unique_ptr<style::Layer> ParticleValidationLayerFactory::createLayer(
const std::string& id, const style::conversion::Convertible&) noexcept {
return std::make_unique<style::ParticleValidationLayer>(id);
}
std::unique_ptr<RenderLayer> ParticleValidationLayerFactory::createRenderLayer(
Immutable<style::Layer::Impl> impl) noexcept {
assert(impl->getTypeInfo() == getTypeInfo());
return std::make_unique<RenderParticleValidationLayer>(
staticImmutableCast<style::ParticleValidationLayer::Impl>(impl));
}
} // namespace mbgl

View File

@@ -0,0 +1,132 @@
#include <mbgl/renderer/layers/render_particle_validation_layer.hpp>
#include <mbgl/gfx/context.hpp>
#include <mbgl/gfx/drawable.hpp>
#include <mbgl/gfx/drawable_builder.hpp>
#include <mbgl/gfx/drawable_tweaker.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/renderer/layer_group.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_pass.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/util/logging.hpp>
#include <sstream>
namespace mbgl {
namespace {
const style::ParticleValidationLayer::Impl& impl(const Immutable<style::Layer::Impl>& value) {
assert(value->getTypeInfo() == style::ParticleValidationLayer::Impl::staticTypeInfo());
return static_cast<const style::ParticleValidationLayer::Impl&>(*value);
}
class ParticleValidationDrawableTweaker final : public gfx::DrawableTweaker {
public:
ParticleValidationDrawableTweaker(std::string id_, bool active_, int32_t layerIndex_)
: id(std::move(id_)),
active(active_),
layerIndex(layerIndex_) {}
~ParticleValidationDrawableTweaker() override {
Log::Info(Event::Render,
"ParticleValidation: validation drawable destroy id=" + id +
" validationFrames=" + std::to_string(validationFrameCount));
}
void init(gfx::Drawable&) override {}
void execute(gfx::Drawable&, PaintParameters& parameters) override {
if (parameters.pass != RenderPass::Translucent) {
return;
}
++validationFrameCount;
if (validationFrameCount != 1 && validationFrameCount % 60 != 0) {
return;
}
const auto size = parameters.state.getSize();
const auto& matrix = parameters.transformParams.projMatrix;
std::ostringstream message;
message << "ParticleValidation: render frame=" << validationFrameCount << " mapFrame=" << parameters.frameCount
<< " state=" << (active ? "ACTIVE" : "STATIC") << " repaint=" << (active ? "requested" : "stopped")
<< " pass=translucent" << " layerIndex=" << layerIndex << " currentLayer=" << parameters.currentLayer
<< " viewport=" << size.width << 'x' << size.height << " zoom=" << parameters.state.getZoom()
<< " bearing=" << parameters.state.getBearing() << " pitch=" << parameters.state.getPitch() << " proj=["
<< matrix[0] << ',' << matrix[5] << ',' << matrix[12] << ',' << matrix[13] << ']';
Log::Info(Event::Render, message.str());
}
private:
std::string id;
bool active;
int32_t layerIndex;
uint64_t validationFrameCount = 0;
};
} // namespace
RenderParticleValidationLayer::RenderParticleValidationLayer(Immutable<style::ParticleValidationLayer::Impl> impl_)
: RenderLayer(makeMutable<style::ParticleValidationLayerProperties>(impl_, impl_->active)) {
Log::Info(Event::Render,
"ParticleValidation: RenderLayer create id=" + getID() + " source-less=true pass=translucent");
}
RenderParticleValidationLayer::~RenderParticleValidationLayer() {
Log::Info(Event::Render, "ParticleValidation: RenderLayer destroy id=" + getID());
}
void RenderParticleValidationLayer::evaluate(const PropertyEvaluationParameters&) {
const bool active = impl(baseImpl).active;
auto properties = makeMutable<style::ParticleValidationLayerProperties>(
staticImmutableCast<style::ParticleValidationLayer::Impl>(baseImpl), active);
passes = RenderPass::Translucent;
properties->renderPasses = mbgl::underlying_type(passes);
evaluatedProperties = std::move(properties);
Log::Info(Event::Render,
"ParticleValidation: state=" + std::string(active ? "ACTIVE" : "STATIC") + " id=" + getID() +
" layerIndex=" + std::to_string(getLayerIndex()));
}
bool RenderParticleValidationLayer::needsRepaint() const {
const auto& properties = static_cast<const style::ParticleValidationLayerProperties&>(*evaluatedProperties);
return properties.active && baseImpl->visibility != style::VisibilityType::None;
}
void RenderParticleValidationLayer::update(gfx::ShaderRegistry&,
gfx::Context& context,
const TransformState&,
const std::shared_ptr<UpdateParameters>&,
const RenderTree&,
UniqueChangeRequestVec& changes) {
if (!layerGroup) {
if (auto group = context.createLayerGroup(layerIndex, 1, getID())) {
setLayerGroup(std::move(group), changes);
}
}
if (!layerGroup) {
return;
}
auto* group = static_cast<LayerGroup*>(layerGroup.get());
if (group->getDrawableCount() != 0) {
return;
}
const bool active = static_cast<const style::ParticleValidationLayerProperties&>(*evaluatedProperties).active;
auto builder = context.createDrawableBuilder(getID());
auto& drawable = builder->getCurrentDrawable(true);
drawable->setIsCustom(true);
drawable->setRenderPass(RenderPass::Translucent);
drawable->addTweaker(std::make_shared<ParticleValidationDrawableTweaker>(getID(), active, getLayerIndex()));
auto drawables = builder->clearDrawables();
group->addDrawable(std::move(drawables.front()));
++stats.drawablesAdded;
Log::Info(Event::Render,
"ParticleValidation: validation drawable create id=" + getID() +
" layerIndex=" + std::to_string(getLayerIndex()) + " pass=translucent");
}
} // namespace mbgl

View File

@@ -0,0 +1,30 @@
#pragma once
#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/style/layers/particle_validation_layer_impl.hpp>
namespace mbgl {
// VALIDATION ONLY: proves Core RenderLayer/frame-transform/repaint integration.
class RenderParticleValidationLayer final : public RenderLayer {
public:
explicit RenderParticleValidationLayer(Immutable<style::ParticleValidationLayer::Impl>);
~RenderParticleValidationLayer() override;
void update(gfx::ShaderRegistry&,
gfx::Context&,
const TransformState&,
const std::shared_ptr<UpdateParameters>&,
const RenderTree&,
UniqueChangeRequestVec&) override;
private:
void transition(const TransitionParameters&) override {}
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override { return false; }
bool hasCrossfade() const override { return false; }
bool needsRepaint() const override;
void prepare(const LayerPrepareParameters&) override {}
};
} // namespace mbgl

View File

@@ -106,6 +106,9 @@ public:
// Returns true if the layer has a pattern property and is actively crossfading.
virtual bool hasCrossfade() const = 0;
// Returns true when an active animated layer needs another MapLibre frame.
virtual bool needsRepaint() const { return false; }
// Returns true if layer writes to depth buffer by drawing using PaintParameters::depthModeFor3D().
virtual bool is3D() const { return false; }

View File

@@ -818,7 +818,7 @@ bool RenderOrchestrator::hasTransitions(TimePoint timePoint) const {
}
for (const auto& entry : renderLayers) {
if (entry.second->hasTransition()) {
if (entry.second->hasTransition() || entry.second->needsRepaint()) {
return true;
}
}

View File

@@ -0,0 +1,110 @@
#include <mbgl/style/layers/particle_validation_layer.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion_impl.hpp>
#include <mbgl/style/layer_observer.hpp>
#include <mbgl/style/layers/particle_validation_layer_impl.hpp>
#include <mbgl/util/logging.hpp>
namespace mbgl {
namespace style {
namespace {
constexpr const char* ActiveProperty = "particle-validation-active";
const LayerTypeInfo typeInfo{.type = "particle-validation",
.source = LayerTypeInfo::Source::NotRequired,
.pass3d = LayerTypeInfo::Pass3D::NotRequired,
.layout = LayerTypeInfo::Layout::NotRequired,
.fadingTiles = LayerTypeInfo::FadingTiles::NotRequired,
.crossTileIndex = LayerTypeInfo::CrossTileIndex::NotRequired,
.tileKind = LayerTypeInfo::TileKind::NotRequired};
} // namespace
ParticleValidationLayer::Impl::Impl(const std::string& id)
: Layer::Impl(id, std::string()) {}
bool ParticleValidationLayer::Impl::hasLayoutDifference(const Layer::Impl&) const {
return false;
}
void ParticleValidationLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {}
const LayerTypeInfo* ParticleValidationLayer::Impl::staticTypeInfo() noexcept {
return &typeInfo;
}
ParticleValidationLayer::ParticleValidationLayer(const std::string& layerID)
: Layer(makeMutable<Impl>(layerID)) {
Log::Info(Event::Render, "ParticleValidation: Style Layer create id=" + layerID + " (VALIDATION ONLY)");
}
ParticleValidationLayer::ParticleValidationLayer(Immutable<Impl> impl_)
: Layer(std::move(impl_)) {}
ParticleValidationLayer::~ParticleValidationLayer() {
Log::Info(Event::Render, "ParticleValidation: Style Layer destroy id=" + getID());
weakFactory.invalidateWeakPtrs();
}
const ParticleValidationLayer::Impl& ParticleValidationLayer::impl() const {
return static_cast<const Impl&>(*baseImpl);
}
Mutable<ParticleValidationLayer::Impl> ParticleValidationLayer::mutableImpl() const {
return makeMutable<Impl>(impl());
}
bool ParticleValidationLayer::isActive() const {
return impl().active;
}
void ParticleValidationLayer::setActive(bool active) {
if (active == isActive()) {
return;
}
auto impl_ = mutableImpl();
impl_->active = active;
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
std::optional<conversion::Error> ParticleValidationLayer::setPropertyInternal(const std::string& name,
const conversion::Convertible& value) {
if (name != ActiveProperty) {
return conversion::Error{"layer doesn't support this property"};
}
conversion::Error error;
auto converted = conversion::convert<bool>(value, error);
if (!converted) {
return error;
}
setActive(*converted);
return std::nullopt;
}
StyleProperty ParticleValidationLayer::getProperty(const std::string& name) const {
if (name == ActiveProperty) {
return StyleProperty(Value{isActive()}, StyleProperty::Kind::Constant);
}
return {};
}
Value ParticleValidationLayer::serialize() const {
auto result = Layer::serialize();
serializeProperty(result, getProperty(ActiveProperty), ActiveProperty, true);
return result;
}
std::unique_ptr<Layer> ParticleValidationLayer::cloneRef(const std::string& id) const {
auto impl_ = mutableImpl();
impl_->id = id;
return std::unique_ptr<Layer>(new ParticleValidationLayer(std::move(impl_)));
}
Mutable<Layer::Impl> ParticleValidationLayer::mutableBaseImpl() const {
return staticMutableCast<Layer::Impl>(mutableImpl());
}
} // namespace style
} // namespace mbgl

View File

@@ -0,0 +1,36 @@
#pragma once
#include <mbgl/style/layer_impl.hpp>
#include <mbgl/style/layer_properties.hpp>
#include <mbgl/style/layers/particle_validation_layer.hpp>
namespace mbgl {
namespace style {
// VALIDATION ONLY: no particle data or production API belongs here.
class ParticleValidationLayer::Impl final : public Layer::Impl {
public:
explicit Impl(const std::string& id);
Impl(const Impl&) = default;
bool hasLayoutDifference(const Layer::Impl&) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
bool active = true;
DECLARE_LAYER_TYPE_INFO;
};
class ParticleValidationLayerProperties final : public LayerProperties {
public:
ParticleValidationLayerProperties(Immutable<ParticleValidationLayer::Impl> impl, bool active_)
: LayerProperties(std::move(impl)),
active(active_) {}
expression::Dependency getDependencies() const noexcept override { return expression::Dependency::None; }
bool active;
};
} // namespace style
} // namespace mbgl