def do_pass(self, key_k, key_v, idx, forward=True, step_cb=None):
        """
        Do a complete pass that includes propagation and fusion
        key_k/key_v -  memory feature of the starting frame
        idx - Frame index of the starting frame
        forward - forward/backward propagation
        step_cb - Callback function used for GUI (progress bar) only
        """

        # Pointer in the memory bank
        num_certain_keys = self.certain_mem_k.shape[2]
        m_front = num_certain_keys

        # Determine the required size of the memory bank
        if forward:
            closest_ti = min([ti for ti in self.interacted
                              if ti > idx] + [self.t])
            total_m = (closest_ti - idx - 1
                       ) // self.mem_freq + 1 + num_certain_keys
        else:
            closest_ti = max([ti for ti in self.interacted if ti < idx] + [-1])
            total_m = (idx - closest_ti - 1
                       ) // self.mem_freq + 1 + num_certain_keys
        K, CK, _, H, W = key_k.shape
        _, CV, _, _, _ = key_v.shape

        # Pre-allocate keys/values memory
        keys = paddle.empty((K, CK, total_m, H, W), dtype='float32')
        values = paddle.empty((K, CV, total_m, H, W), dtype='float32')

        # Initial key/value passed in
        keys[:, :, 0:num_certain_keys] = self.certain_mem_k
        values[:, :, 0:num_certain_keys] = self.certain_mem_v
        prev_in_mem = True
        last_ti = idx

        # Note that we never reach closest_ti, just the frame before it
        if forward:
            this_range = range(idx + 1, closest_ti)
            step = +1
            end = closest_ti - 1
        else:
            this_range = range(idx - 1, closest_ti, -1)
            step = -1
            end = closest_ti + 1

        for ti in this_range:
            if prev_in_mem:
                this_k = keys[:, :, :m_front]
                this_v = values[:, :, :m_front]
            else:
                this_k = keys[:, :, :m_front + 1]
                this_v = values[:, :, :m_front + 1]

            out_mask, quary_key = self.get_query_kv_mask(ti, this_k, this_v)
            out_mask = aggregate_wbg(paddle.to_tensor(out_mask), keep_bg=True)

            if ti != end:
                keys[:, :, m_front:m_front +
                     1], values[:, :, m_front:m_front + 1] = calculate_memorize(
                         self.prop_net_memory,
                         self.get_image_buffered(ti).numpy(),
                         out_mask[1:].numpy())
                if abs(ti - last_ti) >= self.mem_freq:
                    # Memorize the frame
                    m_front += 1
                    last_ti = ti
                    prev_in_mem = True
                else:
                    prev_in_mem = False

            # In-place fusion, maximizes the use of queried buffer
            # esp. for long sequence where the buffer will be flushed
            if (closest_ti != self.t) and (closest_ti != -1):
                self.prob[:, ti] = self.fuse_one_frame(
                    closest_ti, idx, ti, self.prob[:, ti], out_mask, key_k,
                    quary_key)
            else:
                self.prob[:, ti] = out_mask

            # Callback function for the GUI
            if step_cb is not None:
                step_cb()

        return closest_ti

        Do a complete pass that includes propagation and fusion
        key_k/key_v -  memory feature of the starting frame
        idx - Frame index of the starting frame
        forward - forward/backward propagation
        step_cb - Callback function used for GUI (progress bar) only
        
EISeg/eiseg/plugin/video/inference_core.py