Re: [PATCH v2] venus: vdec: Handle DRC after drain

From: Fritz Koenig
Date: Wed Dec 02 2020 - 12:41:32 EST


On Tue, Dec 1, 2020 at 9:58 PM Alexandre Courbot <acourbot@xxxxxxxxxxxx> wrote:
>
> On Wed, Dec 2, 2020 at 7:34 AM Stanimir Varbanov
> <stanimir.varbanov@xxxxxxxxxx> wrote:
> >
> > Hi Fritz,
> >
> > On 12/1/20 6:23 AM, Fritz Koenig wrote:
> > > If the DRC is near the end of the stream the client
> > > may send a V4L2_DEC_CMD_STOP before the DRC occurs.
> > > V4L2_DEC_CMD_STOP puts the driver into the
> > > VENUS_DEC_STATE_DRAIN state. DRC must be aware so
> > > that after the DRC event the state can be restored
> > > correctly.
> > >
> > > Signed-off-by: Fritz Koenig <frkoenig@xxxxxxxxxxxx>
> > > ---
> > >
> > > v2: remove TODO
> > >
> > > drivers/media/platform/qcom/venus/core.h | 1 +
> > > drivers/media/platform/qcom/venus/vdec.c | 17 +++++++++++++++--
> > > 2 files changed, 16 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
> > > index 2b0899bf5b05f..1680c936c06fb 100644
> > > --- a/drivers/media/platform/qcom/venus/core.h
> > > +++ b/drivers/media/platform/qcom/venus/core.h
> > > @@ -406,6 +406,7 @@ struct venus_inst {
> > > unsigned int core_acquired: 1;
> > > unsigned int bit_depth;
> > > bool next_buf_last;
> > > + bool drain_active;
> >
> > Could you introduce a new codec state instead of adding a flag for such
> > corner case.
> >
> > I think that we will need to handle at least one more corner case (DRC
> > during seek operation),
>
> Just happen to have posted a patch for that. :)
>
> https://lkml.org/lkml/2020/12/2/24
>
> > so then we have to introduce another flag, and
> > this is not a good solution to me. These additional flags will
> > complicate the state handling and will make the code readability worst
> >
> > I'd introduce: VENUS_DEC_STATE_DRC_DURING_DRAIN or something similar.
>
> I'm wondering what is the best approach here. As you see in my patch,
> I did not have to introduce a new state but relied instead on the
> state of next_buf_last (which may or may not be correct - maybe we can
> think of a way to merge both patches into one?). Flushes, either
> explicit or implicitly triggered by a DRC, are more than a state by
> themselves but rather an extra dimension from which other states can
> still apply. I'm afraid we already have many states as it is and
> adding more might add complexity.
>
> A lot of the recent issues we had came from that number of states,
> notably from the fact that not all states are always tested when they
> should (and fall back to the default: branch of a switch case that
> does nothing). I think we could improve the robustness of this driver
> if we mandate that each state check must be done using a switch
> statement without a default: branch. That would force us to ensure
> that each newly introduced state is considered in every situation
> where it might be relevant.
>

I'm finding it hard to just add an extra state.
The DRC nominally goes something like this:
VENUS_DEC_STATE_DECODING
received HFI_EVENT_DATA_SEQUENCE_CHANGED : transition to VENUS_DEC_STATE_DRAIN
received stop_capture: transition to VENUS_DEC_STATE_STOPPED
received start_capture: transition to VENUS_DEC_STATE_DECODING


The problematic one:
VENUS_DEC_STATE_DECODING
received V4L2_DEC_CMD_STOP : transition to VENUS_DEC_STATE_DRAIN
received HFI_EVENT_DATA_SEQUENCE_CHANGED : transition to
VENUS_DEC_STATE_DRC_DURING_DRAIN
received stop_capture: transition to VENUS_DEC_STATE_DRC_DURING_DRAIN
received start_capture: transition to VENUS_DEC_STATE_DECODING

So it looks like I would need to add another state
VENUS_DEC_STATE_STOPPED_DURING_DRC_DURING_DRAIN so that transitioning
back to VENUS_DEC_STATE_DECODING would be smooth. Otherwise
VENUS_DEC_STATE_DRC_DURING_DRAIN and VENUS_DEC_STATE_STOPPED will mean
the same thing.

This is why I had originally added the flag instead of states. I'm
still working on getting the states to work. My first implementation
only added VENUS_DEC_STATE_DRC_DURING_DRAIN state and I haven't
totally gotten it working yet because of trying to work out the logic
around VENUS_DEC_STATE_STOPPED.

Please let me know if I have overlooked anything. I'm going to try
adding two states and see if the logic is clearer.

-Fritz

> >
> > > };
> > >
> > > #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX)
> > > diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
> > > index 5671cf3458a68..1d551b4d651a8 100644
> > > --- a/drivers/media/platform/qcom/venus/vdec.c
> > > +++ b/drivers/media/platform/qcom/venus/vdec.c
> > > @@ -523,8 +523,10 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
> > >
> > > ret = hfi_session_process_buf(inst, &fdata);
> > >
> > > - if (!ret && inst->codec_state == VENUS_DEC_STATE_DECODING)
> > > + if (!ret && inst->codec_state == VENUS_DEC_STATE_DECODING) {
> > > inst->codec_state = VENUS_DEC_STATE_DRAIN;
> > > + inst->drain_active = true;
> > > + }
> > > }
> > >
> > > unlock:
> > > @@ -976,10 +978,14 @@ static int vdec_start_capture(struct venus_inst *inst)
> > >
> > > inst->codec_state = VENUS_DEC_STATE_DECODING;
> > >
> > > + if (inst->drain_active)
> > > + inst->codec_state = VENUS_DEC_STATE_DRAIN;
> > > +
> > > inst->streamon_cap = 1;
> > > inst->sequence_cap = 0;
> > > inst->reconfig = false;
> > > inst->next_buf_last = false;
> > > + inst->drain_active = false;
> > >
> > > return 0;
> > >
> > > @@ -1105,6 +1111,7 @@ static int vdec_stop_capture(struct venus_inst *inst)
> > > /* fallthrough */
> > > case VENUS_DEC_STATE_DRAIN:
> > > inst->codec_state = VENUS_DEC_STATE_STOPPED;
> > > + inst->drain_active = false;
> > > /* fallthrough */
> > > case VENUS_DEC_STATE_SEEK:
> > > vdec_cancel_dst_buffers(inst);
> > > @@ -1304,8 +1311,10 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type,
> > >
> > > v4l2_event_queue_fh(&inst->fh, &ev);
> > >
> > > - if (inst->codec_state == VENUS_DEC_STATE_DRAIN)
> > > + if (inst->codec_state == VENUS_DEC_STATE_DRAIN) {
> > > + inst->drain_active = false;
> > > inst->codec_state = VENUS_DEC_STATE_STOPPED;
> > > + }
> > > }
> > >
> > > if (!bytesused)
> > > @@ -1429,9 +1438,13 @@ static void vdec_event_notify(struct venus_inst *inst, u32 event,
> > > case EVT_SYS_EVENT_CHANGE:
> > > switch (data->event_type) {
> > > case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES:
> > > + if (inst->codec_state == VENUS_DEC_STATE_DRAIN)
> > > + inst->codec_state = VENUS_DEC_STATE_DECODING;
> >
> > Could you move this state transition into vdec_event_change(). That way
> > we will do it only once.
> >
> > > vdec_event_change(inst, data, true);
> > > break;
> > > case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES:
> > > + if (inst->codec_state == VENUS_DEC_STATE_DRAIN)
> > > + inst->codec_state = VENUS_DEC_STATE_DECODING;
> >
> > ditto
> >
> > > vdec_event_change(inst, data, false);
> > > break;
> > > case HFI_EVENT_RELEASE_BUFFER_REFERENCE:
> > >
> >
> > --
> > regards,
> > Stan