feat: draw all raster particle states
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled

This commit is contained in:
OpenAI Codex
2026-09-03 15:04:05 +08:00
parent c0d1ad43c4
commit a0aae48ec8
7 changed files with 277 additions and 8 deletions

View File

@@ -6,6 +6,7 @@
#include <mbgl/gfx/drawable.hpp>
#include <mbgl/gfx/drawable_builder.hpp>
#include <mbgl/gfx/drawable_tweaker.hpp>
#include <mbgl/gfx/index_vector.hpp>
#include <mbgl/gfx/shader_registry.hpp>
#include <mbgl/gfx/texture2d.hpp>
#include <mbgl/map/transform_state.hpp>
@@ -34,6 +35,7 @@
#include <array>
#include <atomic>
#include <cmath>
#include <limits>
#include <sstream>
#include <vector>
@@ -73,6 +75,7 @@ enum class ValidationPass : uint8_t {
Trail = 1,
Composite = 2,
FrozenComposite = 3,
TrailParticles = 4,
};
const char* slotName(const uint8_t slot) {
@@ -206,7 +209,10 @@ public:
std::shared_ptr<PremultipliedImage> nextFieldImage;
std::shared_ptr<PremultipliedImage> maskFixture;
std::shared_ptr<gfx::VertexVector<HeatmapTextureLayoutVertex>> vertices;
std::shared_ptr<gfx::VertexVector<HeatmapTextureLayoutVertex>> particleVertices;
gfx::IndexVectorBasePtr particleIndices;
SegmentVector segments;
SegmentVector particleSegments;
uint8_t stateWrite() const { return static_cast<uint8_t>(1 - stateRead); }
uint8_t trailWrite() const { return static_cast<uint8_t>(1 - trailRead); }
@@ -255,6 +261,33 @@ public:
namespace {
constexpr uint32_t MaxParticleVerticesPerSegment = std::numeric_limits<uint16_t>::max() + 1u;
void rebuildParticleDrawResources(const std::shared_ptr<ParticleGpuResources>& resources) {
auto vertices = std::make_shared<gfx::VertexVector<HeatmapTextureLayoutVertex>>();
vertices->reserve(resources->particleCount);
for (uint32_t particleIndex = 0; particleIndex < resources->particleCount; ++particleIndex) {
const auto x = static_cast<int16_t>(particleIndex % resources->stateSize.width);
const auto y = static_cast<int16_t>(particleIndex / resources->stateSize.width);
vertices->emplace_back(HeatmapBucket::textureVertex({x, y}));
}
auto indices = std::make_shared<gfx::IndexVectorBase>();
indices->reserve(std::min(resources->particleCount, MaxParticleVerticesPerSegment));
for (uint32_t index = 0; index < std::min(resources->particleCount, MaxParticleVerticesPerSegment); ++index) {
indices->extend(1, static_cast<uint16_t>(index));
}
SegmentVector segments;
for (uint32_t offset = 0; offset < resources->particleCount; offset += MaxParticleVerticesPerSegment) {
const auto count = std::min(resources->particleCount - offset, MaxParticleVerticesPerSegment);
segments.emplace_back(offset, 0, count, count);
}
resources->particleVertices = std::move(vertices);
resources->particleIndices = std::move(indices);
resources->particleSegments = std::move(segments);
}
class ParticleGpuValidationTweaker final : public gfx::DrawableTweaker {
public:
ParticleGpuValidationTweaker(std::shared_ptr<ParticleGpuResources> state_,
@@ -311,8 +344,8 @@ public:
const shaders::ParticleGpuValidationUBO ubo = {
.pass = {static_cast<float>(pass), frame, maskMode == 0 ? 0.0f : 1.0f, static_cast<float>(maskMode)},
.marker = {markerX, markerY, 0.035f, 0.965f},
.mask = {static_cast<float>(resources->maskGeneration),
static_cast<float>(worldWrap),
.mask = {static_cast<float>(resources->stateSize.width),
static_cast<float>(resources->stateSize.height),
resources->formalDisplay ? 1.0f : 0.0f,
seedState ? 1.0f : 0.0f},
.matrix = util::cast<float>(parameters.matrixForTile({worldWrap, {0, 0, 0}})),
@@ -527,6 +560,63 @@ bool buildQuad(gfx::Context& context,
return true;
}
bool buildParticlePoints(gfx::Context& context,
const std::shared_ptr<ParticleGpuResources>& resources,
LayerGroup& group,
const std::string& name,
const gfx::Texture2DPtr& stateTexture,
const gfx::Texture2DPtr& target,
const uint8_t stateSlot,
const uint8_t trailSlot) {
if (!resources->particleVertices || !resources->particleIndices || resources->particleSegments.empty() ||
!stateTexture || !target) {
Log::Error(Event::Render, "ParticleGPU: event=PARTICLE_DRAW_REJECTED reason=MISSING_DRAW_RESOURCES");
return false;
}
auto attributes = context.createVertexAttributeArray();
if (const auto& attribute = attributes->set(shaders::idParticleGpuValidationPosVertexAttribute)) {
attribute->setSharedRawData(resources->particleVertices,
offsetof(HeatmapTextureLayoutVertex, a1),
0,
sizeof(HeatmapTextureLayoutVertex),
gfx::AttributeDataType::Short2);
}
auto builder = context.createDrawableBuilder(name);
builder->setShader(resources->shader);
builder->setRenderPass(RenderPass::Translucent);
builder->setEnableDepth(false);
builder->setColorMode(gfx::ColorMode::unblended());
builder->setCullFaceMode(gfx::CullFaceMode::disabled());
builder->setVertexAttributes(std::move(attributes));
builder->setRawVertices({}, resources->particleVertices->elements(), gfx::AttributeDataType::Short2);
builder->setSegments(gfx::Points(3.0f),
resources->particleIndices,
resources->particleSegments.data(),
resources->particleSegments.size());
builder->setTexture(stateTexture, shaders::idParticleGpuValidationPreviousTexture);
builder->setTexture(stateTexture, shaders::idParticleGpuValidationStateTexture);
builder->addTweaker(std::make_shared<ParticleGpuValidationTweaker>(resources,
ValidationPass::TrailParticles,
stateSlot,
trailSlot,
stateTexture,
target,
resources->maskTexture,
stateTexture,
0.0f,
0,
0));
builder->flush(context);
auto drawables = builder->clearDrawables();
if (drawables.size() != 1 || !drawables.front()) {
Log::Error(Event::Render, "ParticleGPU: event=PARTICLE_DRAW_REJECTED reason=DRAWABLE_FINALIZE_FAILED");
return false;
}
group.addDrawable(std::move(drawables.front()));
return true;
}
} // namespace
RenderRasterParticleLayer::RenderRasterParticleLayer(Immutable<style::Layer::Impl> impl_)
@@ -596,6 +686,10 @@ void RenderRasterParticleLayer::layerRemoved(UniqueChangeRequestVec& changes) {
"ParticleGPU: event=PARTICLE_RESOURCES_RELEASE reason=LAYER_REMOVED particleCount=" +
std::to_string(gpuResources->particleCount) +
" resourceGeneration=" + std::to_string(gpuResources->generation));
Log::Info(Event::Render,
"ParticleGPU: event=PARTICLE_DRAW_RESOURCES_RELEASE reason=LAYER_REMOVED trailDrawCount=" +
std::to_string(gpuResources->particleVertices ? gpuResources->particleVertices->elements() : 0) +
" fullParticleDraw=true resourceGeneration=" + std::to_string(gpuResources->generation));
removeTargets(gpuResources->registeredTargets, changes);
gpuResources->registeredTargets.clear();
}
@@ -609,6 +703,10 @@ void RenderRasterParticleLayer::markContextDestroyed() {
"ParticleGPU: event=PARTICLE_RESOURCES_RELEASE reason=CONTEXT_DESTROYED particleCount=" +
std::to_string(gpuResources->particleCount) +
" resourceGeneration=" + std::to_string(gpuResources->generation));
Log::Info(Event::Render,
"ParticleGPU: event=PARTICLE_DRAW_RESOURCES_RELEASE reason=CONTEXT_DESTROYED trailDrawCount=" +
std::to_string(gpuResources->particleVertices ? gpuResources->particleVertices->elements() : 0) +
" fullParticleDraw=true resourceGeneration=" + std::to_string(gpuResources->generation));
Log::Info(
Event::Render,
"ParticleValidation30_02: context lost discard generation=" + std::to_string(gpuResources->generation) +
@@ -665,6 +763,7 @@ void RenderRasterParticleLayer::update(gfx::ShaderRegistry& shaders,
owned->segments = RenderStaticData::heatmapTextureSegments();
owned->particleCount = particleCount;
owned->stateSize = particleStateSize(owned->particleCount);
rebuildParticleDrawResources(owned);
for (uint8_t i = 0; i < 2; ++i) {
owned->stateTargets[i] = createValidationTarget(
context, owned->stateSize, getID() + "-state-" + slotName(i), gfx::TextureFilterType::Nearest);
@@ -749,6 +848,13 @@ void RenderRasterParticleLayer::update(gfx::ShaderRegistry& shaders,
" effectiveParticleCount=" + std::to_string(resources->particleCount) + " stateGrid=" +
std::to_string(resources->stateSize.width) + "x" + std::to_string(resources->stateSize.height) +
" resourceGeneration=" + std::to_string(resources->generation));
Log::Info(Event::Render,
"ParticleGPU: event=PARTICLE_DRAW_RESOURCES_CREATE effectiveParticleCount=" +
std::to_string(resources->particleCount) + " stateGrid=" +
std::to_string(resources->stateSize.width) + "x" + std::to_string(resources->stateSize.height) +
" trailDrawCount=" + std::to_string(resources->particleVertices->elements()) +
" particleIndexCount=" + std::to_string(resources->particleVertices->elements()) +
" fullParticleDraw=true resourceGeneration=" + std::to_string(resources->generation));
Log::Info(
Event::Render,
"ParticleField: fixture loaded id=" + resources->field.fixtureID + " type=" + resources->field.fieldType +
@@ -806,10 +912,15 @@ void RenderRasterParticleLayer::update(gfx::ShaderRegistry& shaders,
std::to_string(previousCount) + " stateGrid=" + std::to_string(previousSize.width) + "x" +
std::to_string(previousSize.height) +
" resourceGeneration=" + std::to_string(resources->generation));
Log::Info(Event::Render,
"ParticleGPU: event=PARTICLE_DRAW_RESOURCES_RELEASE reason=PARTICLE_COUNT_CHANGE trailDrawCount=" +
std::to_string(resources->particleVertices ? resources->particleVertices->elements() : 0) +
" fullParticleDraw=true resourceGeneration=" + std::to_string(resources->generation));
removeTargets(resources->stateTargets, changes);
resources->stateTargets = std::move(replacementTargets);
resources->particleCount = particleCount;
resources->stateSize = requestedSize;
rebuildParticleDrawResources(resources);
resources->resetSequence("PARTICLE_COUNT_CHANGE");
Log::Info(Event::Render,
"ParticleGPU: event=PARTICLE_RESOURCES_REBUILD reason=PARTICLE_COUNT_CHANGE requestedParticleCount=" +
@@ -817,6 +928,13 @@ void RenderRasterParticleLayer::update(gfx::ShaderRegistry& shaders,
" effectiveParticleCount=" + std::to_string(resources->particleCount) + " stateGrid=" +
std::to_string(resources->stateSize.width) + "x" + std::to_string(resources->stateSize.height) +
" resourceGeneration=" + std::to_string(resources->generation));
Log::Info(Event::Render,
"ParticleGPU: event=PARTICLE_DRAW_RESOURCES_REBUILD effectiveParticleCount=" +
std::to_string(resources->particleCount) + " stateGrid=" +
std::to_string(resources->stateSize.width) + "x" + std::to_string(resources->stateSize.height) +
" trailDrawCount=" + std::to_string(resources->particleVertices->elements()) +
" particleIndexCount=" + std::to_string(resources->particleVertices->elements()) +
" fullParticleDraw=true resourceGeneration=" + std::to_string(resources->generation));
}
const bool sourceChanged = formalSource && sourceField && sourceNextField &&
@@ -1028,6 +1146,15 @@ void RenderRasterParticleLayer::update(gfx::ShaderRegistry& shaders,
0,
maskMode,
true);
const bool particlesBuilt = !resources->formalDisplay ||
buildParticlePoints(context,
resources,
trailGroup,
getID() + "-static-bootstrap-trail-particles",
resources->stateTargets[stateWrite]->getTexture(),
resources->trailTargets[trailWrite]->getTexture(),
stateWrite,
trailWrite);
compositeGroup.clearDrawables();
bool compositeBuilt = true;
const auto center = transformState.getLatLng(LatLng::Unwrapped);
@@ -1050,7 +1177,7 @@ void RenderRasterParticleLayer::update(gfx::ShaderRegistry& shaders,
false) &&
compositeBuilt;
}
if (!stateBuilt || !trailBuilt || !compositeBuilt) {
if (!stateBuilt || !trailBuilt || !particlesBuilt || !compositeBuilt) {
compositeGroup.clearDrawables();
return;
}
@@ -1136,6 +1263,15 @@ void RenderRasterParticleLayer::update(gfx::ShaderRegistry& shaders,
0,
maskMode,
true);
const bool particlesBuilt = !resources->formalDisplay ||
buildParticlePoints(context,
resources,
trailGroup,
getID() + "-trail-particles",
resources->stateTargets[stateWrite]->getTexture(),
resources->trailTargets[trailWrite]->getTexture(),
stateWrite,
trailWrite);
compositeGroup.clearDrawables();
bool compositeBuilt = true;
const auto center = transformState.getLatLng(LatLng::Unwrapped);
@@ -1158,7 +1294,7 @@ void RenderRasterParticleLayer::update(gfx::ShaderRegistry& shaders,
false) &&
compositeBuilt;
}
if (!stateBuilt || !trailBuilt || !compositeBuilt) {
if (!stateBuilt || !trailBuilt || !particlesBuilt || !compositeBuilt) {
compositeGroup.clearDrawables();
return;
}