def iterate_scene_tasks(
    scene_dict,
    args,
    # if True, enumerate scenes that we might have launched earlier,
    # even if we wouldnt launch them now (due to crashes etc)
    monitor_all,
    # provided by gin
    global_tasks,
    view_dependent_tasks,
    camera_dependent_tasks,
    frame_range,
    n_camera_rigs,
    n_subcams,
    point_trajectory_src_frame=1,
    num_resamples=1,
    render_frame_range=None,
    finalize_tasks=[],
    view_block_size=1,  # how many frames should share each `view_dependent_task`
    cam_block_size=None,  # how many frames should share each `camera_dependent_task`
    # cleanup_viewdep=False, # TODO fix. Should cleanup the results of `view_dependent_tasks` once each view iter is done?
    viewdep_paralell=True,  # can we work on multiple view depenendent tasks (usually `fine`) in paralell?
    camdep_paralell=True,  # can we work on multiple camera dependent tasks (usually render/gt) in paralell?
):
    """
    This function is a generator which yields all scenes we might want to consider
    monitoring or running for a particular scene

    It `yield`s the available scenes, regardless of whether they are already running etc
    """

    for task in global_tasks + view_dependent_tasks + camera_dependent_tasks:
        if "_" in task["name"]:
            raise ValueError(
                f'{task=} with {task["name"]=} is invalid, must not contain underscores'
            )

    if cam_block_size is None:
        cam_block_size = view_block_size

    assert n_camera_rigs >= 1
    assert n_subcams >= 1
    assert view_block_size >= 1
    assert cam_block_size >= 1
    if cam_block_size > view_block_size:
        cam_block_size = view_block_size
    seed = scene_dict["seed"]

    scene_folder = args.output_folder / seed
    get_task_state = partial(
        get_scene_state, scene=scene_dict, scene_folder=scene_folder
    )

    global_overrides = [
        f"execute_tasks.frame_range={repr(list(frame_range))}",
        "execute_tasks.camera_id=[0, 0]",
    ]
    global_configs = scene_dict.get("configs", []) + args.configs
    global_iter = iterate_sequential_tasks(
        global_tasks,
        get_task_state,
        overrides=args.overrides + global_overrides,
        configs=global_configs,
    )

    for state, *rest in global_iter:
        yield state, *rest
    if not state == JobState.Succeeded:
        return

    # blender frame_range is inclusive, but python's range is end-exclusive
    view_range = render_frame_range if render_frame_range is not None else frame_range
    view_frames = range(view_range[0], view_range[1] + 1, view_block_size)
    resamples = range(num_resamples)
    cam_rigs = range(n_camera_rigs)
    subcams = range(n_subcams)

    running_views = 0
    for cam_rig, view_frame in itertools.product(cam_rigs, view_frames):
        logger.debug(f"Checking {seed=} {cam_rig=} {view_frame=}")

        view_frame_range = [
            view_frame,
            min(frame_range[1], view_frame + view_block_size - 1),
        ]
        view_overrides = [
            f"execute_tasks.frame_range=[{view_frame_range[0]},{view_frame_range[1]}]",
            f"execute_tasks.camera_id=[{cam_rig},{0}]",
        ]

        view_idxs = dict(cam_rig=cam_rig, frame=view_frame)
        view_tasks_iter = iterate_sequential_tasks(
            view_dependent_tasks,
            get_task_state,
            overrides=args.overrides + view_overrides,
            configs=global_configs,
            output_indices=view_idxs,
        )

        state = JobState.Succeeded
        for state, *rest in view_tasks_iter:
            yield state, *rest

        if state not in CONCLUDED_JOBSTATES:
            if viewdep_paralell:
                running_views += 1
                continue
            else:
                return
        elif state == JobState.Failed and not monitor_all:
            return

        running_blocks = 0
        for subcam, resample_idx in itertools.product(subcams, resamples):
            for cam_frame in range(
                view_frame_range[0], view_frame_range[1] + 1, cam_block_size
            ):
                cam_frame_range = [
                    cam_frame,
                    min(view_frame_range[1], cam_frame + cam_block_size - 1),
                ]  # blender frame_end is INCLUSIVE
                cam_overrides = [
                    f"execute_tasks.frame_range=[{cam_frame_range[0]},{cam_frame_range[1]}]",
                    f"execute_tasks.camera_id=[{cam_rig},{subcam}]",
                    f"execute_tasks.resample_idx={resample_idx}",
                    f"execute_tasks.point_trajectory_src_frame={point_trajectory_src_frame}",
                ]

                camdep_indices = dict(
                    cam_rig=cam_rig,
                    frame=cam_frame,
                    subcam=subcam,
                    resample=resample_idx,
                )
                # extra semi-redundant info needed for openglgt mostly
                extra_indices = dict(
                    view_first_frame=view_frame_range[0],
                    last_view_frame=view_frame_range[1],
                    last_cam_frame=cam_frame_range[1],
                )
                camera_dep_iter = iterate_sequential_tasks(
                    camera_dependent_tasks,
                    get_task_state,
                    overrides=args.overrides + cam_overrides,
                    configs=global_configs,
                    input_indices=view_idxs if len(view_dependent_tasks) else None,
                    output_indices={**camdep_indices, **extra_indices},
                )
                state = JobState.Succeeded
                for state, *rest in camera_dep_iter:
                    yield state, *rest
                if state not in CONCLUDED_JOBSTATES:
                    if camdep_paralell:
                        running_blocks += 1
                        continue
                    else:
                        return
                elif state == JobState.Failed and not monitor_all:
                    return

        if running_blocks > 0:
            running_views += 1
            continue

    if running_views > 0:
        return

    finalize_iter = iterate_sequential_tasks(
        finalize_tasks,
        get_task_state,
        overrides=args.overrides + global_overrides,
        configs=global_configs,
    )
    for state, *rest in finalize_iter:
        yield state, *rest
    if not state == JobState.Succeeded:
        return

    if scene_dict["all_done"] == SceneState.NotDone:
        on_scene_termination(args, scene_dict, crashed=False)

    This function is a generator which yields all scenes we might want to consider
    monitoring or running for a particular scene

    It `yield`s the available scenes, regardless of whether they are already running etc
    
infinigen/datagen/monitor_tasks.py