function process_effects(root) {
	/** @type {Effect[]} */
	var effects = [];

	/** @type {Effect | null} */
	var effect = root;

	while (effect !== null) {
		var flags = effect.f;
		var is_branch = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) !== 0;
		var is_skippable_branch = is_branch && (flags & CLEAN) !== 0;

		if (!is_skippable_branch && (flags & INERT) === 0) {
			if ((flags & EFFECT) !== 0) {
				effects.push(effect);
			} else if (is_branch) {
				effect.f ^= CLEAN;
			} else {
				if (check_dirtiness(effect)) {
					update_effect(effect);
				}
			}

			/** @type {Effect | null} */
			var child = effect.first;

			if (child !== null) {
				effect = child;
				continue;
			}
		}

		var parent = effect.parent;
		effect = effect.next;

		while (effect === null && parent !== null) {
			effect = parent.next;
			parent = parent.parent;
		}
	}

	return effects;
}
This function both runs render effects and collects user effects in topological order
from the starting effect passed in. Effects will be collected when they match the filtered
bitwise flag passed in only. The collected effects array will be populated with all the user
effects to be flushed.

@param {Effect} root
@returns {Effect[]}