From 364b8f381e27eee36c4895084b2f0578a023fa61 Mon Sep 17 00:00:00 2001 From: OpenAI Codex Date: Sun, 30 Aug 2026 07:32:09 +0800 Subject: [PATCH] fix: freeze particle gpu state in static mode --- .../render_particle_validation_layer.cpp | 59 +++++++++++++++++-- .../render_particle_validation_layer.hpp | 3 +- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/mbgl/renderer/layers/render_particle_validation_layer.cpp b/src/mbgl/renderer/layers/render_particle_validation_layer.cpp index 0a919478e..c0016ffac 100644 --- a/src/mbgl/renderer/layers/render_particle_validation_layer.cpp +++ b/src/mbgl/renderer/layers/render_particle_validation_layer.cpp @@ -46,6 +46,7 @@ enum class ValidationPass : uint8_t { State = 0, Trail = 1, Composite = 2, + FrozenComposite = 3, }; const char* slotName(const uint8_t slot) { @@ -76,6 +77,7 @@ public: bool trailSeeded = false; bool pending = false; bool contextDestroyed = false; + bool staticFreezeLogged = false; Size trailSize{}; std::array stateTargets; @@ -147,7 +149,8 @@ public: drawable.setTexture(source, shaders::idParticleGpuValidationPreviousTexture); - const float frame = static_cast(resources->committedFrame + 1); + const bool frozen = pass == ValidationPass::FrozenComposite; + const float frame = static_cast(resources->committedFrame + (frozen ? 0 : 1)); const float markerX = 0.5f + 0.32f * std::sin(frame * 0.071f); const float markerY = 0.5f + 0.32f * std::cos(frame * 0.053f); const shaders::ParticleGpuValidationUBO ubo = { @@ -156,7 +159,23 @@ public: }; drawable.mutableUniformBuffers().createOrUpdate(shaders::idParticleGpuValidationUBO, &ubo, parameters.context); - if (pass == ValidationPass::Composite) { + if (pass == ValidationPass::Composite || pass == ValidationPass::FrozenComposite) { + if (pass == ValidationPass::FrozenComposite) { + if (!resources->staticFreezeLogged) { + const auto size = parameters.state.getSize(); + std::ostringstream message; + message << "ParticleValidation30_02: STATIC frozen composite generation=" << resources->generation + << " frame=" << resources->committedFrame << " stateRead=" << slotName(resources->stateRead) + << " trailRead=" << slotName(resources->trailRead) + << " offscreenPasses=0 swap=false viewport=" << size.width << 'x' << size.height + << " zoom=" << parameters.state.getZoom() << " bearing=" << parameters.state.getBearing() + << " pitch=" << parameters.state.getPitch(); + Log::Info(Event::Render, message.str()); + resources->staticFreezeLogged = true; + } + return; + } + const auto beforeState = resources->stateRead; const auto beforeTrail = resources->trailRead; resources->commit(); @@ -241,8 +260,9 @@ bool buildQuad(gfx::Context& context, builder->setShader(resources->shader); builder->setRenderPass(RenderPass::Translucent); builder->setEnableDepth(false); - builder->setColorMode(pass == ValidationPass::Composite ? gfx::ColorMode::alphaBlended() - : gfx::ColorMode::unblended()); + builder->setColorMode(pass == ValidationPass::Composite || pass == ValidationPass::FrozenComposite + ? gfx::ColorMode::alphaBlended() + : gfx::ColorMode::unblended()); builder->setCullFaceMode(gfx::CullFaceMode::disabled()); builder->setVertexAttributes(std::move(attributes)); builder->setRawVertices({}, resources->vertices->elements(), gfx::AttributeDataType::Short2); @@ -345,8 +365,16 @@ void RenderParticleValidationLayer::update(gfx::ShaderRegistry& shaders, return; } + const bool active = static_cast(*evaluatedProperties).active; auto resources = gpuValidation; if (!resources) { + if (!active) { + // STATIC without a preceding ACTIVE frame has no trail to show. Do not create, + // clear, or advance offscreen resources merely because the map redraws. + static_cast(*layerGroup).clearDrawables(); + return; + } + auto owned = std::make_shared(); owned->generation = ++nextGpuGeneration; owned->shader = context.getGenericShader(shaders, ShaderName); @@ -371,6 +399,29 @@ void RenderParticleValidationLayer::update(gfx::ShaderRegistry& shaders, removeTargets(resources->registeredTargets, changes); resources->registeredTargets.clear(); + if (!active) { + // Map gestures must still redraw the main pass, but STATIC is a simulation freeze: + // no State/Trail RenderTarget is registered, no pending swap survives, and the + // composite samples the last committed Trail slot with the current map frame. + resources->pending = false; + auto& compositeGroup = static_cast(*layerGroup); + if (resources->trailSeeded && resources->trailTargets[resources->trailRead]) { + buildQuad(context, + resources, + compositeGroup, + getID() + "-frozen-composite-pass", + resources->trailTargets[resources->trailRead]->getTexture(), + {}, + ValidationPass::FrozenComposite, + resources->trailRead, + resources->trailRead); + } else { + compositeGroup.clearDrawables(); + } + return; + } + resources->staticFreezeLogged = false; + const auto viewport = validViewportSize(transformState.getSize()); if (!resources->trailTargets[0] || !resources->trailTargets[1] || resources->trailSize != viewport) { const auto previousSize = resources->trailSize; diff --git a/src/mbgl/renderer/layers/render_particle_validation_layer.hpp b/src/mbgl/renderer/layers/render_particle_validation_layer.hpp index 632c58c6b..7099c0c65 100644 --- a/src/mbgl/renderer/layers/render_particle_validation_layer.hpp +++ b/src/mbgl/renderer/layers/render_particle_validation_layer.hpp @@ -7,7 +7,8 @@ namespace mbgl { class ParticleGpuValidationResources; -// VALIDATION ONLY: proves Core RenderLayer/frame-transform/repaint integration. +// VALIDATION ONLY: proves Core RenderLayer/GPU ping-pong integration. +// STATIC redraws only the frozen Trail composite; it never advances State/Trail slots. class RenderParticleValidationLayer final : public RenderLayer { public: explicit RenderParticleValidationLayer(Immutable);