`PostProcessSequentialGroup` skips earlier post processes when the subtree is rendered more than once
Los mantenedores suelen responder en 1 día
Evaluación
Este issue todavía no se ha evaluado.
Descripción
What happened?
When a post process in a PostProcessSequentialGroup renders its subtree more than once, only the first call runs the post processes before it in the group. Every later call renders the raw tree and silently skips them.
Rendering the subtree more than once is common for multi-pass effects, and the PostProcess docs explicitly allow it. A drop shadow, for example, renders the subtree once as a dark, blurred shadow and then again for the scene itself. In PostProcessSequentialGroup([Grayscale(), DropShadow()]), the grayscale is only applied to the shadow pass, so the scene itself stays in full color and the grayscale effect appears to do nothing. Nothing is thrown.
https://github.com/user-attachments/assets/7315eab9-d002-490a-a954-368d15e5dc82
What do you expect?
Every call to renderSubtree or rasterizeSubtree should run the full chain of post processes before it in the group. In the example below, the whole scene should be gray, with a shadow, the same as nesting the two post processes by hand.
How can we reproduce this?
- Create a
PostProcessSequentialGroupwith a post process that renders its subtree twice, such as a drop shadow, after another post process, such as a grayscale filter. - Set it as
camera.postProcess, with some colored components in the world. - The shadows are drawn, but the components stay in color.
What steps should take to fix this?
PostProcessSequentialGroup.render (post_process.dart#L245-L260) builds a single list and removes post processes from it as it goes:
final stack = postProcesses.reversed.toList();
void runNext(Canvas c) {
if (stack.isEmpty) {
renderTree(c);
return;
}
final postProcess = stack.removeAt(0);
postProcess.render(c, size, runNext, updateContext);
}
The first subtree render empties the list, so the next one finds it empty and goes straight to renderTree.
Do have an example of where the bug occurs?
import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/post_process.dart';
import 'package:flutter/material.dart' show Colors;
import 'package:flutter/widgets.dart';
void main() => runApp(GameWidget(game: ReproGame()));
class ReproGame extends FlameGame {
@override
Future<void> onLoad() async {
world.addAll([
CircleComponent(
radius: 40,
position: Vector2(-160, -40),
paint: Paint()..color = Colors.red,
),
RectangleComponent(
size: Vector2.all(80),
position: Vector2(-40, -40),
paint: Paint()..color = Colors.green,
),
CircleComponent(
radius: 40,
position: Vector2(80, -40),
paint: Paint()..color = Colors.blue,
),
]);
camera.postProcess = PostProcessSequentialGroup(
// Grayscale is applied first, then DropShadow, so the whole scene
// should be gray, with a shadow.
postProcesses: [Grayscale(), DropShadow()],
);
}
}
/// Renders its subtree in grayscale.
class Grayscale extends PostProcess {
final _paint = Paint()
..colorFilter = const ColorFilter.matrix([
0.2126, 0.7152, 0.0722, 0, 0, //
0.2126, 0.7152, 0.0722, 0, 0, //
0.2126, 0.7152, 0.0722, 0, 0, //
0, 0, 0, 1, 0, //
]);
@override
void postProcess(Vector2 size, Canvas canvas) {
canvas.saveLayer(null, _paint);
renderSubtree(canvas);
canvas.restore();
}
}
/// Renders its subtree as a dark, blurred shadow, then again on top.
class DropShadow extends PostProcess {
final _shadowPaint = Paint()
..colorFilter = const ColorFilter.mode(Color(0xAA000000), BlendMode.srcIn)
..imageFilter = ImageFilter.blur(sigmaX: 3, sigmaY: 3);
@override
void postProcess(Vector2 size, Canvas canvas) {
// First pass: the shadow.
canvas
..save()
..translate(8, 10)
..saveLayer(null, _shadowPaint);
renderSubtree(canvas);
canvas
..restore()
..restore();
// Second pass: the scene itself.
renderSubtree(canvas);
}
}
Expected: gray shapes with shadows. Actual: red, green and blue shapes with shadows. Replacing the group with Grayscale() alone turns the shapes gray, so the filter itself works.
Relevant log output
Nothing is logged or thrown.
Execute in a terminal and put output into the code block below
[√] Flutter (Channel stable, 3.47.8, on Microsoft Windows [Version 10.0.22631.6199], locale en-US)
• Flutter version 3.47.8 on channel stable
• Framework revision 737ba8f885 (8 days ago), 2026-10-22 21:37:31 -0700
• Engine revision cf636b1dfb
• Dart version 3.13.7
• DevTools version 2.60.0
[√] Windows Version (11 Pro 64-bit, 23H2, 2009)
[√] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Community 2026 18.11.2)
[√] Connected device (3 available)
[√] Network resources
• No issues found!
Affected platforms
All
Are you interested in working on a PR for this?
- I want to work on this
- Lenguaje dominante
- Dart
- Estrellas
- 10.8k
- Forks
- 1.1k
- Merge medio
- 1 d 21 h
- PR fusionados (30 d)
- 62
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de flame-engine/flame
-
`Viewfinder.visibleGameSize` is not reapplied when the viewport changes sizePosiblemente ocupada @ufrshubham la tomó hace 1 día. Abiertobug
flame-engine/flame#4204 · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
`Viewfinder.visibleGameSize` zooms in too far with a `FixedResolutionViewport`Posiblemente ocupada @ufrshubham la tomó hace 1 día. Abiertobug
flame-engine/flame#4203 · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Camera bounds with `considerViewport: true` are not updated when the viewport is resizedPosiblemente ocupada @ufrshubham la tomó hace 1 día. Abiertobug
flame-engine/flame#4202 · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
`CameraComponent.setBounds` with `considerViewport: true` only works for bounds that start at (0, 0), at zoom 1Posiblemente ocupada @ufrshubham la tomó hace 1 día. Abiertobug
flame-engine/flame#4201 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
`findPostProcessFromContext` returns `null` for components rendered with `CameraComponent.postProcess`Posiblemente ocupada @ufrshubham la tomó hace 1 día. Abiertobug
flame-engine/flame#4200 · 1 asignado ·
Los mantenedores suelen responder en 1 día
Todos los issues de flame-engine/flame
Issues similares
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
InlitX/GymMane#311 · 1 reacción ·
Los mantenedores suelen responder en 4 días
-
Remove Info buttonAbiertoenhancement user request
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
mill-road-winter-fair/mill_road_winter_fair_app#646 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
[CarouselView] Crash (RangeError / ArgumentError) when children or itemCount is empty with infinite: true or CarouselController.animateToItemPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abiertop: material_ui package platform-android platform-ios platform-linux platform-macos platform-web team-design
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
flutter/flutter#195878 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Add an NCBA Bank packAbiertogood first issue help wanted parser-pack
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
pesaclarity/ujumbe-parsers#30 ·
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
AimesSoft/NipaPlay-Reload#1092 ·
Los mantenedores suelen responder en 1 día