diff mbox

[hwc,v2,17/18] drm_hwcomposer: Flatten scene synchronously

Message ID 1523460149-1740-18-git-send-email-alexandru-cosmin.gheorghe@arm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Alexandru-Cosmin Gheorghe April 11, 2018, 3:22 p.m. UTC
Flatten scene on the same CRTC as the one driving the display.
The active composition is played back to the display with a buffer
attached to the writeback connector.
Then we build a composition that has only one plane enabled and that
uses the result of the writeback as the input.

Signed-off-by: Alexandru Gheorghe <alexandru-cosmin.gheorghe@arm.com>
---
 drmdisplaycompositor.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++++--
 drmdisplaycompositor.h   |   7 +-
 2 files changed, 204 insertions(+), 6 deletions(-)

Comments

Sean Paul April 17, 2018, 5:47 p.m. UTC | #1
On Wed, Apr 11, 2018 at 04:22:28PM +0100, Alexandru Gheorghe wrote:
> Flatten scene on the same CRTC as the one driving the display.
> The active composition is played back to the display with a buffer
> attached to the writeback connector.
> Then we build a composition that has only one plane enabled and that
> uses the result of the writeback as the input.
> 
> Signed-off-by: Alexandru Gheorghe <alexandru-cosmin.gheorghe@arm.com>
> ---
>  drmdisplaycompositor.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++++--
>  drmdisplaycompositor.h   |   7 +-
>  2 files changed, 204 insertions(+), 6 deletions(-)
> 
> diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
> index e535e8a..cb670e6 100644
> --- a/drmdisplaycompositor.cpp
> +++ b/drmdisplaycompositor.cpp
> @@ -36,6 +36,7 @@
>  #include "drmplane.h"
>  #include "drmresources.h"
>  #include "glworker.h"
> +static const uint32_t kWaitWritebackFence = 100;  // ms
>  
>  namespace android {
>  
> @@ -523,7 +524,9 @@ int DrmDisplayCompositor::PrepareFrame(DrmDisplayComposition *display_comp) {
>  }
>  
>  int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> -                                      bool test_only) {
> +                                      bool test_only,
> +                                      DrmDisplayComposition *writeback_comp,
> +                                      DrmConnector *writeback_conn) {
>    ATRACE_CALL();
>  
>    int ret = 0;
> @@ -532,6 +535,7 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
>    std::vector<DrmCompositionPlane> &comp_planes =
>        display_comp->composition_planes();
>    uint64_t out_fences[drm_->crtcs().size()];
> +  int writeback_fence = -1;
>  
>    DrmConnector *connector = drm_->GetConnectorForDisplay(display_);
>    if (!connector) {
> @@ -550,9 +554,37 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
>      return -ENOMEM;
>    }
>  
> +  if (writeback_comp != NULL) {
> +    if (writeback_conn == NULL)
> +      return -EINVAL;
> +    if (writeback_conn->writeback_fb_id().id() == 0 ||
> +        writeback_conn->writeback_out_fence().id() == 0) {
> +      ALOGE("Writeback properties don't exit");
> +      return -EINVAL;
> +    }
> +    if (writeback_comp->layers().size() != 1) {
> +      ALOGE("Invalid number of layers for writeback composition");
> +      return -EINVAL;
> +    }
> +    ret = drmModeAtomicAddProperty(
> +        pset, writeback_conn->id(), writeback_conn->writeback_fb_id().id(),
> +        writeback_comp->layers().back().buffer->fb_id);
> +    if (ret < 0) {
> +      ALOGE("Failed to add writeback_fb_id");
> +      return ret;
> +    }
> +    ret = drmModeAtomicAddProperty(pset, writeback_conn->id(),
> +                                   writeback_conn->writeback_out_fence().id(),
> +                                   (uint64_t)&writeback_fence);

Upcasting int to u64 isn't a great idea, please go the other way (as we do with
out_fences).

> +    if (ret < 0) {
> +      ALOGE("Failed to add writeback_out_fence");
> +      return ret;
> +    }
> +  }

This would be more readable if it was split off into a function.

>    if (crtc->out_fence_ptr_property().id() != 0) {
> -    ret = drmModeAtomicAddProperty(pset, crtc->id(), crtc->out_fence_ptr_property().id(),
> -                                   (uint64_t) &out_fences[crtc->pipe()]);
> +    ret = drmModeAtomicAddProperty(pset, crtc->id(),
> +                                   crtc->out_fence_ptr_property().id(),
> +                                   (uint64_t)&out_fences[crtc->pipe()]);
>      if (ret < 0) {
>        ALOGE("Failed to add OUT_FENCE_PTR property to pset: %d", ret);
>        drmModeAtomicFree(pset);
> @@ -580,6 +612,15 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
>      }
>    }
>  
> +  if (writeback_conn != NULL) {
> +    ret = drmModeAtomicAddProperty(pset, writeback_conn->id(),
> +                                   writeback_conn->crtc_id_property().id(),
> +                                   crtc->id());
> +    if (ret < 0) {
> +      ALOGE("Failed to  attach writeback");
> +    }
> +  }

Can you do this above with the rest of the writeback properties?

> +
>    for (DrmCompositionPlane &comp_plane : comp_planes) {
>      DrmPlane *plane = comp_plane.plane();
>      DrmCrtc *crtc = comp_plane.crtc();
> @@ -729,8 +770,18 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
>  
>    if (!ret) {
>      uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
> -    if (test_only)
> +    if (test_only) {
>        flags |= DRM_MODE_ATOMIC_TEST_ONLY;
> +    } else {
> +      if (writeback_comp != NULL) {
> +        if (!CountdownExpired() && active_composition_) {

Given that we're holding the lock throughout this function, can't you just
abort at the start?

> +          ALOGE("Writeback composition not needed, abort commit");
> +          drmModeAtomicFree(pset);
> +          return -EINVAL;
> +        };
> +        flags |= DRM_MODE_ATOMIC_NONBLOCK;

I'm guessing this is the cause of the active_composition race you're fixing
earlier in the series? Could you please pull this out and squash it into that
patch as a "Use non-blocking commits" standalone? It might be useful for
testing, and this is something that's substantial enough to warrant the
additional visibility of its own patch.

> +      }
> +    }
>  
>      ret = drmModeAtomicCommit(drm_->fd(), pset, flags, drm_);
>      if (ret) {
> @@ -769,6 +820,13 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
>    if (crtc->out_fence_ptr_property().id()) {
>      display_comp->set_out_fence((int) out_fences[crtc->pipe()]);
>    }
> +  if (writeback_fence >= 0) {
> +    if (writeback_comp->layers().size() != 1) {
> +      ALOGE("Invalid numbers of layer for writeback_comp");
> +      return -EINVAL;
> +    }

You already test this above?

> +    writeback_comp->layers()[0].acquire_fence.Set(writeback_fence);
> +  }
>  
>    return ret;
>  }
> @@ -837,6 +895,8 @@ void DrmDisplayCompositor::ApplyFrame(
>        if (active_composition_)
>          active_composition_->SignalCompositionDone();
>        active_composition_.swap(composition);
> +      flatten_countdown_ = FLATTEN_COUNTDOWN_INIT;
> +      vsync_worker_.VSyncControl(!writeback);
>      }
>    }
>  
> @@ -913,8 +973,141 @@ int DrmDisplayCompositor::ApplyComposition(
>    return ret;
>  }
>  
> +int DrmDisplayCompositor::WritebackComposite(DrmDisplayComposition *src,
> +                                             DrmDisplayComposition *dst,
> +                                             DrmConnector *writeback_conn) {
> +  int ret = 0;
> +  if (src == NULL || dst == NULL)
> +    return -EINVAL;
> +  std::vector<DrmCompositionPlane> &src_planes = src->composition_planes();
> +  DrmCompositionPlane squashed_comp(DrmCompositionPlane::Type::kPrecomp, NULL,
> +                                    src->crtc());
> +  for (DrmCompositionPlane &comp_plane : src_planes) {
> +    if (comp_plane.plane() == NULL) {
> +      ALOGE("Skipping squash all because of NULL plane");
> +      ret = -EINVAL;
> +    }
> +    if (!squashed_comp.plane() &&
> +        comp_plane.plane()->type() == DRM_PLANE_TYPE_PRIMARY)
> +      squashed_comp.set_plane(comp_plane.plane());
> +    else
> +      dst->AddPlaneDisable(comp_plane.plane());
> +  }
> +
> +  DrmFramebuffer *writeback_fb = NULL;
> +  AutoLock lock(&lock_, __FUNCTION__);
> +  if ((ret = lock.Lock()))

Same comments regarding assignment in a conditional.

> +    return ret;
> +  writeback_fb = &framebuffers_[framebuffer_index_];
> +  framebuffer_index_ = (framebuffer_index_ + 1) % DRM_DISPLAY_BUFFERS;
> +  ret = PrepareFramebuffer(*writeback_fb, dst, mode_.mode.h_display(),
> +                           mode_.mode.v_display());
> +  if (ret) {
> +    ALOGE("Failed to prepare destination buffer");
> +    return ret;
> +  }
> +  lock.Unlock();
> +  ret = CommitFrame(src, true, dst, writeback_conn);
> +  if (ret) {
> +    ALOGE("Atomic check failed");
> +    return ret;
> +  }
> +  if ((ret = lock.Lock()))

All of these locks and unlocks are going to cause races, as mentioned below,
please re-evaluate.

> +    return ret;
> +  if (!CountdownExpired() && active_composition_) {
> +    ALOGE("Writeback composition not needed abort");
> +    return -EINVAL;
> +  }
> +  ret = CommitFrame(src, false, dst, writeback_conn);
> +  lock.Unlock();
> +  if (ret || dst->layers().size() != 1) {
> +    ALOGE("Failed to flatten scene using writeback");
> +    return -EINVAL;
> +  }
> +  squashed_comp.source_layers().push_back(0);
> +  ret =
> +      sync_wait(dst->layers()[0].acquire_fence.Release(), kWaitWritebackFence);

line break

> +  if (ret) {
> +    ALOGE("Failed to wait on writeback fence");
> +    return ret;
> +  }
> +  ret = dst->AddPlaneComposition(std::move(squashed_comp));
> +  if (ret) {
> +    ALOGE("Failed to add flatten scene");
> +    return ret;
> +  }
> +  ret = dst->FinalizeComposition();
> +  if (ret) {
> +    ALOGE("Failed to finalize composition");
> +    return ret;
> +  }
> +  return 0;
> +}
> +
> +int DrmDisplayCompositor::FlattenSynchronously(DrmConnector *writeback_conn) {
> +  if (writeback_conn->display() != display_) {
> +    ALOGE("Cannot flatten synchronously on different display");
> +    return -EINVAL;
> +  }

You check this right before calling, so this isn't needed.

> +  ALOGI("FlattenSynchronously using the same display");

I think you should downgrade this log level.

> +  int ret = 0;
> +  /* Flattened composition with only one layer that is built
> +   * using the writeback connector
> +   */
> +  std::unique_ptr<DrmDisplayComposition> writeback_comp =
> +      CreateInitializedComposition();
> +  /* Copy of the active_composition_, we need a copy because
> +   * if we use the active composition we have to hold the lock
> +   * for the entire sequence of flattening.
> +   */
> +  std::unique_ptr<DrmDisplayComposition> copy_comp =
> +      CreateInitializedComposition();
> +
> +  if (!copy_comp || !writeback_comp)
> +    return -EINVAL;
> +  AutoLock lock(&lock_, __FUNCTION__);
> +  if ((ret = lock.Lock()))

Assignments in if statements make me nervous. Since you don't need the
declaration above, just do:

  int ret = lock.Lock();
  if (ret)

> +    return ret;
> +  if (CountdownExpired()) {
> +    ret = copy_comp->CopyLayers(active_composition_.get());
> +    if (ret)
> +      return ret;
> +    copy_comp->CopyCompPlanes(active_composition_.get());
> +  } else {
> +    return -EINVAL;
> +  }

  if (!CountdownExpired())
    return -EINVAL;

  ret = copy_comp->CopyLayers(active_composition_.get());
  if (ret)
    return ret;
  copy_comp->CopyCompPlanes(active_composition_.get());


> +  lock.Unlock();

Just hold the lock through WritebackComposite(), and consider creating a
ApplyFrameLocked() to hold it through ApplyFrame(). You should be able to reduce
the number of times you have to check CountdownExpired() (or remove it)
significantly by just consistently locking things.

> +  ret =
> +      WritebackComposite(copy_comp.get(), writeback_comp.get(), writeback_conn);
> +  if (ret) {
> +    ALOGE("Failed to prepare writebackScene");
> +    return ret;
> +  }
> +
> +  ApplyFrame(std::move(writeback_comp), 0, true);
> +  return 0;
> +}
> +
>  int DrmDisplayCompositor::FlattenScene() {
> -  return -EINVAL;
> +  DrmConnector *writeback_conn =
> +      drm_->resource_manager()->AvailableWritebackConnector(display_);
> +  if (!active_composition_ || !writeback_conn)
> +    return -EINVAL;
> +  std::vector<DrmCompositionPlane> &src_planes =
> +      active_composition_->composition_planes();
> +  size_t src_planes_with_layer = std::count_if(
> +      src_planes.begin(), src_planes.end(), [](DrmCompositionPlane &p) {
> +        return p.type() != DrmCompositionPlane::Type::kDisable;
> +      });
> +
> +  if (src_planes_with_layer <= 1)
> +    return -EALREADY;
> +
> +  if (writeback_conn->display() == display_) {
> +    return FlattenSynchronously(writeback_conn);
> +  }
> +
> +  return 0;
>  }
>  
>  int DrmDisplayCompositor::SquashAll() {
> diff --git a/drmdisplaycompositor.h b/drmdisplaycompositor.h
> index 26201b9..4cc4a5e 100644
> --- a/drmdisplaycompositor.h
> +++ b/drmdisplaycompositor.h
> @@ -125,7 +125,9 @@ class DrmDisplayCompositor {
>    int ApplySquash(DrmDisplayComposition *display_comp);
>    int ApplyPreComposite(DrmDisplayComposition *display_comp);
>    int PrepareFrame(DrmDisplayComposition *display_comp);
> -  int CommitFrame(DrmDisplayComposition *display_comp, bool test_only);
> +  int CommitFrame(DrmDisplayComposition *display_comp, bool test_only,
> +                  DrmDisplayComposition *writeback_comp = NULL,
> +                  DrmConnector *writeback_conn = NULL);
>    int SquashFrame(DrmDisplayComposition *src, DrmDisplayComposition *dst);
>    int ApplyDpms(DrmDisplayComposition *display_comp);
>    int DisablePlanes(DrmDisplayComposition *display_comp);
> @@ -134,7 +136,10 @@ class DrmDisplayCompositor {
>    void ApplyFrame(std::unique_ptr<DrmDisplayComposition> composition,
>                    int status, bool writeback = false);
>    int FlattenScene();
> +  int FlattenSynchronously(DrmConnector *writeback_conn);

Can we come up with a better name than Synchonrous/Asynchronous? Or at the very
least comment on what that means?

>  
> +  int WritebackComposite(DrmDisplayComposition *src, DrmDisplayComposition *dst,
> +                         DrmConnector *writeback_conn);
>    bool CountdownExpired() const;
>  
>    std::tuple<int, uint32_t> CreateModeBlob(const DrmMode &mode);
> -- 
> 2.7.4
>
Alexandru-Cosmin Gheorghe April 18, 2018, 11:14 a.m. UTC | #2
On Tue, Apr 17, 2018 at 01:47:46PM -0400, Sean Paul wrote:
> On Wed, Apr 11, 2018 at 04:22:28PM +0100, Alexandru Gheorghe wrote:
> > Flatten scene on the same CRTC as the one driving the display.
> > The active composition is played back to the display with a buffer
> > attached to the writeback connector.
> > Then we build a composition that has only one plane enabled and that
> > uses the result of the writeback as the input.
> > 
> > Signed-off-by: Alexandru Gheorghe <alexandru-cosmin.gheorghe@arm.com>
> > ---
> >  drmdisplaycompositor.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++++--
> >  drmdisplaycompositor.h   |   7 +-
> >  2 files changed, 204 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
> > index e535e8a..cb670e6 100644
> > --- a/drmdisplaycompositor.cpp
> > +++ b/drmdisplaycompositor.cpp
> > @@ -36,6 +36,7 @@
> >  #include "drmplane.h"
> >  #include "drmresources.h"
> >  #include "glworker.h"
> > +static const uint32_t kWaitWritebackFence = 100;  // ms
> >  
> >  namespace android {
> >  
> > @@ -523,7 +524,9 @@ int DrmDisplayCompositor::PrepareFrame(DrmDisplayComposition *display_comp) {
> >  }
> >  
> >  int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> > -                                      bool test_only) {
> > +                                      bool test_only,
> > +                                      DrmDisplayComposition *writeback_comp,
> > +                                      DrmConnector *writeback_conn) {
> >    ATRACE_CALL();
> >  
> >    int ret = 0;
> > @@ -532,6 +535,7 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> >    std::vector<DrmCompositionPlane> &comp_planes =
> >        display_comp->composition_planes();
> >    uint64_t out_fences[drm_->crtcs().size()];
> > +  int writeback_fence = -1;
> >  
> >    DrmConnector *connector = drm_->GetConnectorForDisplay(display_);
> >    if (!connector) {
> > @@ -550,9 +554,37 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> >      return -ENOMEM;
> >    }
> >  
> > +  if (writeback_comp != NULL) {
> > +    if (writeback_conn == NULL)
> > +      return -EINVAL;
> > +    if (writeback_conn->writeback_fb_id().id() == 0 ||
> > +        writeback_conn->writeback_out_fence().id() == 0) {
> > +      ALOGE("Writeback properties don't exit");
> > +      return -EINVAL;
> > +    }
> > +    if (writeback_comp->layers().size() != 1) {
> > +      ALOGE("Invalid number of layers for writeback composition");
> > +      return -EINVAL;
> > +    }
> > +    ret = drmModeAtomicAddProperty(
> > +        pset, writeback_conn->id(), writeback_conn->writeback_fb_id().id(),
> > +        writeback_comp->layers().back().buffer->fb_id);
> > +    if (ret < 0) {
> > +      ALOGE("Failed to add writeback_fb_id");
> > +      return ret;
> > +    }
> > +    ret = drmModeAtomicAddProperty(pset, writeback_conn->id(),
> > +                                   writeback_conn->writeback_out_fence().id(),
> > +                                   (uint64_t)&writeback_fence);
> 
> Upcasting int to u64 isn't a great idea, please go the other way (as we do with
> out_fences).

Genuinely curious about this, why does it make a difference I'm
upcasting the address not the int itself.
More, the kernel does this s32 __user *fence_ptr = u64_to_user_ptr(val);
To be completly fair it should have been int32_t.


> 
> > +    if (ret < 0) {
> > +      ALOGE("Failed to add writeback_out_fence");
> > +      return ret;
> > +    }
> > +  }
> 
> This would be more readable if it was split off into a function.
> 
> >    if (crtc->out_fence_ptr_property().id() != 0) {
> > -    ret = drmModeAtomicAddProperty(pset, crtc->id(), crtc->out_fence_ptr_property().id(),
> > -                                   (uint64_t) &out_fences[crtc->pipe()]);
> > +    ret = drmModeAtomicAddProperty(pset, crtc->id(),
> > +                                   crtc->out_fence_ptr_property().id(),
> > +                                   (uint64_t)&out_fences[crtc->pipe()]);
> >      if (ret < 0) {
> >        ALOGE("Failed to add OUT_FENCE_PTR property to pset: %d", ret);
> >        drmModeAtomicFree(pset);
> > @@ -580,6 +612,15 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> >      }
> >    }
> >  
> > +  if (writeback_conn != NULL) {
> > +    ret = drmModeAtomicAddProperty(pset, writeback_conn->id(),
> > +                                   writeback_conn->crtc_id_property().id(),
> > +                                   crtc->id());
> > +    if (ret < 0) {
> > +      ALOGE("Failed to  attach writeback");
> > +    }
> > +  }
> 
> Can you do this above with the rest of the writeback properties?
> 
> > +
> >    for (DrmCompositionPlane &comp_plane : comp_planes) {
> >      DrmPlane *plane = comp_plane.plane();
> >      DrmCrtc *crtc = comp_plane.crtc();
> > @@ -729,8 +770,18 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> >  
> >    if (!ret) {
> >      uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
> > -    if (test_only)
> > +    if (test_only) {
> >        flags |= DRM_MODE_ATOMIC_TEST_ONLY;
> > +    } else {
> > +      if (writeback_comp != NULL) {
> > +        if (!CountdownExpired() && active_composition_) {
> 
> Given that we're holding the lock throughout this function, can't you just
> abort at the start?

Yes, we could/should.

> 
> > +          ALOGE("Writeback composition not needed, abort commit");
> > +          drmModeAtomicFree(pset);
> > +          return -EINVAL;
> > +        };
> > +        flags |= DRM_MODE_ATOMIC_NONBLOCK;
> 
> I'm guessing this is the cause of the active_composition race you're fixing
> earlier in the series? Could you please pull this out and squash it into that
> patch as a "Use non-blocking commits" standalone? It might be useful for
> testing, and this is something that's substantial enough to warrant the
> additional visibility of its own patch.

Not really, that race could happen  even without non blocking. The
current implementation is:
1. Commit
2. lock.
3. Set active_composition
4. Unlock.

With two threads there is no guarantee active_composition will contain
what it's been comitted.


> 
> > +      }
> > +    }
> >  
> >      ret = drmModeAtomicCommit(drm_->fd(), pset, flags, drm_);
> >      if (ret) {
> > @@ -769,6 +820,13 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> >    if (crtc->out_fence_ptr_property().id()) {
> >      display_comp->set_out_fence((int) out_fences[crtc->pipe()]);
> >    }
> > +  if (writeback_fence >= 0) {
> > +    if (writeback_comp->layers().size() != 1) {
> > +      ALOGE("Invalid numbers of layer for writeback_comp");
> > +      return -EINVAL;
> > +    }
> 
> You already test this above?
> 
> > +    writeback_comp->layers()[0].acquire_fence.Set(writeback_fence);
> > +  }
> >  
> >    return ret;
> >  }
> > @@ -837,6 +895,8 @@ void DrmDisplayCompositor::ApplyFrame(
> >        if (active_composition_)
> >          active_composition_->SignalCompositionDone();
> >        active_composition_.swap(composition);
> > +      flatten_countdown_ = FLATTEN_COUNTDOWN_INIT;
> > +      vsync_worker_.VSyncControl(!writeback);
> >      }
> >    }
> >  
> > @@ -913,8 +973,141 @@ int DrmDisplayCompositor::ApplyComposition(
> >    return ret;
> >  }
> >  
> > +int DrmDisplayCompositor::WritebackComposite(DrmDisplayComposition *src,
> > +                                             DrmDisplayComposition *dst,
> > +                                             DrmConnector *writeback_conn) {
> > +  int ret = 0;
> > +  if (src == NULL || dst == NULL)
> > +    return -EINVAL;
> > +  std::vector<DrmCompositionPlane> &src_planes = src->composition_planes();
> > +  DrmCompositionPlane squashed_comp(DrmCompositionPlane::Type::kPrecomp, NULL,
> > +                                    src->crtc());
> > +  for (DrmCompositionPlane &comp_plane : src_planes) {
> > +    if (comp_plane.plane() == NULL) {
> > +      ALOGE("Skipping squash all because of NULL plane");
> > +      ret = -EINVAL;
> > +    }
> > +    if (!squashed_comp.plane() &&
> > +        comp_plane.plane()->type() == DRM_PLANE_TYPE_PRIMARY)
> > +      squashed_comp.set_plane(comp_plane.plane());
> > +    else
> > +      dst->AddPlaneDisable(comp_plane.plane());
> > +  }
> > +
> > +  DrmFramebuffer *writeback_fb = NULL;
> > +  AutoLock lock(&lock_, __FUNCTION__);
> > +  if ((ret = lock.Lock()))
> 
> Same comments regarding assignment in a conditional.
> 
> > +    return ret;
> > +  writeback_fb = &framebuffers_[framebuffer_index_];
> > +  framebuffer_index_ = (framebuffer_index_ + 1) % DRM_DISPLAY_BUFFERS;
> > +  ret = PrepareFramebuffer(*writeback_fb, dst, mode_.mode.h_display(),
> > +                           mode_.mode.v_display());
> > +  if (ret) {
> > +    ALOGE("Failed to prepare destination buffer");
> > +    return ret;
> > +  }
> > +  lock.Unlock();
> > +  ret = CommitFrame(src, true, dst, writeback_conn);
> > +  if (ret) {
> > +    ALOGE("Atomic check failed");
> > +    return ret;
> > +  }
> > +  if ((ret = lock.Lock()))
> 
> All of these locks and unlocks are going to cause races, as mentioned below,
> please re-evaluate.
> 
> > +    return ret;
> > +  if (!CountdownExpired() && active_composition_) {
> > +    ALOGE("Writeback composition not needed abort");
> > +    return -EINVAL;
> > +  }
> > +  ret = CommitFrame(src, false, dst, writeback_conn);
> > +  lock.Unlock();
> > +  if (ret || dst->layers().size() != 1) {
> > +    ALOGE("Failed to flatten scene using writeback");
> > +    return -EINVAL;
> > +  }
> > +  squashed_comp.source_layers().push_back(0);
> > +  ret =
> > +      sync_wait(dst->layers()[0].acquire_fence.Release(), kWaitWritebackFence);
> 
> line break
> 
> > +  if (ret) {
> > +    ALOGE("Failed to wait on writeback fence");
> > +    return ret;
> > +  }
> > +  ret = dst->AddPlaneComposition(std::move(squashed_comp));
> > +  if (ret) {
> > +    ALOGE("Failed to add flatten scene");
> > +    return ret;
> > +  }
> > +  ret = dst->FinalizeComposition();
> > +  if (ret) {
> > +    ALOGE("Failed to finalize composition");
> > +    return ret;
> > +  }
> > +  return 0;
> > +}
> > +
> > +int DrmDisplayCompositor::FlattenSynchronously(DrmConnector *writeback_conn) {
> > +  if (writeback_conn->display() != display_) {
> > +    ALOGE("Cannot flatten synchronously on different display");
> > +    return -EINVAL;
> > +  }
> 
> You check this right before calling, so this isn't needed.
> 
> > +  ALOGI("FlattenSynchronously using the same display");
> 
> I think you should downgrade this log level.
> 
> > +  int ret = 0;
> > +  /* Flattened composition with only one layer that is built
> > +   * using the writeback connector
> > +   */
> > +  std::unique_ptr<DrmDisplayComposition> writeback_comp =
> > +      CreateInitializedComposition();
> > +  /* Copy of the active_composition_, we need a copy because
> > +   * if we use the active composition we have to hold the lock
> > +   * for the entire sequence of flattening.
> > +   */
> > +  std::unique_ptr<DrmDisplayComposition> copy_comp =
> > +      CreateInitializedComposition();
> > +
> > +  if (!copy_comp || !writeback_comp)
> > +    return -EINVAL;
> > +  AutoLock lock(&lock_, __FUNCTION__);
> > +  if ((ret = lock.Lock()))
> 
> Assignments in if statements make me nervous. Since you don't need the
> declaration above, just do:
> 
>   int ret = lock.Lock();
>   if (ret)
> 
> > +    return ret;
> > +  if (CountdownExpired()) {
> > +    ret = copy_comp->CopyLayers(active_composition_.get());
> > +    if (ret)
> > +      return ret;
> > +    copy_comp->CopyCompPlanes(active_composition_.get());
> > +  } else {
> > +    return -EINVAL;
> > +  }
> 
>   if (!CountdownExpired())
>     return -EINVAL;
> 
>   ret = copy_comp->CopyLayers(active_composition_.get());
>   if (ret)
>     return ret;
>   copy_comp->CopyCompPlanes(active_composition_.get());
> 
> 
> > +  lock.Unlock();
> 
> Just hold the lock through WritebackComposite(), and consider creating a
> ApplyFrameLocked() to hold it through ApplyFrame(). You should be able to reduce
> the number of times you have to check CountdownExpired() (or remove it)
> significantly by just consistently locking things.
> 
> > +  ret =
> > +      WritebackComposite(copy_comp.get(), writeback_comp.get(), writeback_conn);
> > +  if (ret) {
> > +    ALOGE("Failed to prepare writebackScene");
> > +    return ret;
> > +  }
> > +
> > +  ApplyFrame(std::move(writeback_comp), 0, true);
> > +  return 0;
> > +}
> > +
> >  int DrmDisplayCompositor::FlattenScene() {
> > -  return -EINVAL;
> > +  DrmConnector *writeback_conn =
> > +      drm_->resource_manager()->AvailableWritebackConnector(display_);
> > +  if (!active_composition_ || !writeback_conn)
> > +    return -EINVAL;
> > +  std::vector<DrmCompositionPlane> &src_planes =
> > +      active_composition_->composition_planes();
> > +  size_t src_planes_with_layer = std::count_if(
> > +      src_planes.begin(), src_planes.end(), [](DrmCompositionPlane &p) {
> > +        return p.type() != DrmCompositionPlane::Type::kDisable;
> > +      });
> > +
> > +  if (src_planes_with_layer <= 1)
> > +    return -EALREADY;
> > +
> > +  if (writeback_conn->display() == display_) {
> > +    return FlattenSynchronously(writeback_conn);
> > +  }
> > +
> > +  return 0;
> >  }
> >  
> >  int DrmDisplayCompositor::SquashAll() {
> > diff --git a/drmdisplaycompositor.h b/drmdisplaycompositor.h
> > index 26201b9..4cc4a5e 100644
> > --- a/drmdisplaycompositor.h
> > +++ b/drmdisplaycompositor.h
> > @@ -125,7 +125,9 @@ class DrmDisplayCompositor {
> >    int ApplySquash(DrmDisplayComposition *display_comp);
> >    int ApplyPreComposite(DrmDisplayComposition *display_comp);
> >    int PrepareFrame(DrmDisplayComposition *display_comp);
> > -  int CommitFrame(DrmDisplayComposition *display_comp, bool test_only);
> > +  int CommitFrame(DrmDisplayComposition *display_comp, bool test_only,
> > +                  DrmDisplayComposition *writeback_comp = NULL,
> > +                  DrmConnector *writeback_conn = NULL);
> >    int SquashFrame(DrmDisplayComposition *src, DrmDisplayComposition *dst);
> >    int ApplyDpms(DrmDisplayComposition *display_comp);
> >    int DisablePlanes(DrmDisplayComposition *display_comp);
> > @@ -134,7 +136,10 @@ class DrmDisplayCompositor {
> >    void ApplyFrame(std::unique_ptr<DrmDisplayComposition> composition,
> >                    int status, bool writeback = false);
> >    int FlattenScene();
> > +  int FlattenSynchronously(DrmConnector *writeback_conn);
> 
> Can we come up with a better name than Synchonrous/Asynchronous? Or at the very
> least comment on what that means?
> 
> >  
> > +  int WritebackComposite(DrmDisplayComposition *src, DrmDisplayComposition *dst,
> > +                         DrmConnector *writeback_conn);
> >    bool CountdownExpired() const;
> >  
> >    std::tuple<int, uint32_t> CreateModeBlob(const DrmMode &mode);
> > -- 
> > 2.7.4
> > 
> 
> -- 
> Sean Paul, Software Engineer, Google / Chromium OS
Sean Paul April 18, 2018, 2:49 p.m. UTC | #3
On Wed, Apr 18, 2018 at 12:14:03PM +0100, Alexandru-Cosmin Gheorghe wrote:
> On Tue, Apr 17, 2018 at 01:47:46PM -0400, Sean Paul wrote:
> > On Wed, Apr 11, 2018 at 04:22:28PM +0100, Alexandru Gheorghe wrote:
> > > Flatten scene on the same CRTC as the one driving the display.
> > > The active composition is played back to the display with a buffer
> > > attached to the writeback connector.
> > > Then we build a composition that has only one plane enabled and that
> > > uses the result of the writeback as the input.
> > > 
> > > Signed-off-by: Alexandru Gheorghe <alexandru-cosmin.gheorghe@arm.com>
> > > ---
> > >  drmdisplaycompositor.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++++--
> > >  drmdisplaycompositor.h   |   7 +-
> > >  2 files changed, 204 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
> > > index e535e8a..cb670e6 100644
> > > --- a/drmdisplaycompositor.cpp
> > > +++ b/drmdisplaycompositor.cpp
> > > @@ -36,6 +36,7 @@
> > >  #include "drmplane.h"
> > >  #include "drmresources.h"
> > >  #include "glworker.h"
> > > +static const uint32_t kWaitWritebackFence = 100;  // ms
> > >  
> > >  namespace android {
> > >  
> > > @@ -523,7 +524,9 @@ int DrmDisplayCompositor::PrepareFrame(DrmDisplayComposition *display_comp) {
> > >  }
> > >  
> > >  int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> > > -                                      bool test_only) {
> > > +                                      bool test_only,
> > > +                                      DrmDisplayComposition *writeback_comp,
> > > +                                      DrmConnector *writeback_conn) {
> > >    ATRACE_CALL();
> > >  
> > >    int ret = 0;
> > > @@ -532,6 +535,7 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> > >    std::vector<DrmCompositionPlane> &comp_planes =
> > >        display_comp->composition_planes();
> > >    uint64_t out_fences[drm_->crtcs().size()];
> > > +  int writeback_fence = -1;
> > >  
> > >    DrmConnector *connector = drm_->GetConnectorForDisplay(display_);
> > >    if (!connector) {
> > > @@ -550,9 +554,37 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> > >      return -ENOMEM;
> > >    }
> > >  
> > > +  if (writeback_comp != NULL) {
> > > +    if (writeback_conn == NULL)
> > > +      return -EINVAL;
> > > +    if (writeback_conn->writeback_fb_id().id() == 0 ||
> > > +        writeback_conn->writeback_out_fence().id() == 0) {
> > > +      ALOGE("Writeback properties don't exit");
> > > +      return -EINVAL;
> > > +    }
> > > +    if (writeback_comp->layers().size() != 1) {
> > > +      ALOGE("Invalid number of layers for writeback composition");
> > > +      return -EINVAL;
> > > +    }
> > > +    ret = drmModeAtomicAddProperty(
> > > +        pset, writeback_conn->id(), writeback_conn->writeback_fb_id().id(),
> > > +        writeback_comp->layers().back().buffer->fb_id);
> > > +    if (ret < 0) {
> > > +      ALOGE("Failed to add writeback_fb_id");
> > > +      return ret;
> > > +    }
> > > +    ret = drmModeAtomicAddProperty(pset, writeback_conn->id(),
> > > +                                   writeback_conn->writeback_out_fence().id(),
> > > +                                   (uint64_t)&writeback_fence);
> > 
> > Upcasting int to u64 isn't a great idea, please go the other way (as we do with
> > out_fences).
> 
> Genuinely curious about this, why does it make a difference I'm
> upcasting the address not the int itself.

Right, for some reason I had inserted a * inside the parens with my head. Please
ignore :)

> More, the kernel does this s32 __user *fence_ptr = u64_to_user_ptr(val);
> To be completly fair it should have been int32_t.
> 
> 
> > 
> > > +    if (ret < 0) {
> > > +      ALOGE("Failed to add writeback_out_fence");
> > > +      return ret;
> > > +    }
> > > +  }
> > 
> > This would be more readable if it was split off into a function.
> > 
> > >    if (crtc->out_fence_ptr_property().id() != 0) {
> > > -    ret = drmModeAtomicAddProperty(pset, crtc->id(), crtc->out_fence_ptr_property().id(),
> > > -                                   (uint64_t) &out_fences[crtc->pipe()]);
> > > +    ret = drmModeAtomicAddProperty(pset, crtc->id(),
> > > +                                   crtc->out_fence_ptr_property().id(),
> > > +                                   (uint64_t)&out_fences[crtc->pipe()]);
> > >      if (ret < 0) {
> > >        ALOGE("Failed to add OUT_FENCE_PTR property to pset: %d", ret);
> > >        drmModeAtomicFree(pset);
> > > @@ -580,6 +612,15 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> > >      }
> > >    }
> > >  
> > > +  if (writeback_conn != NULL) {
> > > +    ret = drmModeAtomicAddProperty(pset, writeback_conn->id(),
> > > +                                   writeback_conn->crtc_id_property().id(),
> > > +                                   crtc->id());
> > > +    if (ret < 0) {
> > > +      ALOGE("Failed to  attach writeback");
> > > +    }
> > > +  }
> > 
> > Can you do this above with the rest of the writeback properties?
> > 
> > > +
> > >    for (DrmCompositionPlane &comp_plane : comp_planes) {
> > >      DrmPlane *plane = comp_plane.plane();
> > >      DrmCrtc *crtc = comp_plane.crtc();
> > > @@ -729,8 +770,18 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> > >  
> > >    if (!ret) {
> > >      uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
> > > -    if (test_only)
> > > +    if (test_only) {
> > >        flags |= DRM_MODE_ATOMIC_TEST_ONLY;
> > > +    } else {
> > > +      if (writeback_comp != NULL) {
> > > +        if (!CountdownExpired() && active_composition_) {
> > 
> > Given that we're holding the lock throughout this function, can't you just
> > abort at the start?
> 
> Yes, we could/should.
> 
> > 
> > > +          ALOGE("Writeback composition not needed, abort commit");
> > > +          drmModeAtomicFree(pset);
> > > +          return -EINVAL;
> > > +        };
> > > +        flags |= DRM_MODE_ATOMIC_NONBLOCK;
> > 
> > I'm guessing this is the cause of the active_composition race you're fixing
> > earlier in the series? Could you please pull this out and squash it into that
> > patch as a "Use non-blocking commits" standalone? It might be useful for
> > testing, and this is something that's substantial enough to warrant the
> > additional visibility of its own patch.
> 
> Not really, that race could happen  even without non blocking. The
> current implementation is:
> 1. Commit
> 2. lock.
> 3. Set active_composition
> 4. Unlock.
> 
> With two threads there is no guarantee active_composition will contain
> what it's been comitted.
> 

Makes sense, thanks for laying it out.

Sean
> 
> > 
> > > +      }
> > > +    }
> > >  
> > >      ret = drmModeAtomicCommit(drm_->fd(), pset, flags, drm_);
> > >      if (ret) {
> > > @@ -769,6 +820,13 @@ int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
> > >    if (crtc->out_fence_ptr_property().id()) {
> > >      display_comp->set_out_fence((int) out_fences[crtc->pipe()]);
> > >    }
> > > +  if (writeback_fence >= 0) {
> > > +    if (writeback_comp->layers().size() != 1) {
> > > +      ALOGE("Invalid numbers of layer for writeback_comp");
> > > +      return -EINVAL;
> > > +    }
> > 
> > You already test this above?
> > 
> > > +    writeback_comp->layers()[0].acquire_fence.Set(writeback_fence);
> > > +  }
> > >  
> > >    return ret;
> > >  }
> > > @@ -837,6 +895,8 @@ void DrmDisplayCompositor::ApplyFrame(
> > >        if (active_composition_)
> > >          active_composition_->SignalCompositionDone();
> > >        active_composition_.swap(composition);
> > > +      flatten_countdown_ = FLATTEN_COUNTDOWN_INIT;
> > > +      vsync_worker_.VSyncControl(!writeback);
> > >      }
> > >    }
> > >  
> > > @@ -913,8 +973,141 @@ int DrmDisplayCompositor::ApplyComposition(
> > >    return ret;
> > >  }
> > >  
> > > +int DrmDisplayCompositor::WritebackComposite(DrmDisplayComposition *src,
> > > +                                             DrmDisplayComposition *dst,
> > > +                                             DrmConnector *writeback_conn) {
> > > +  int ret = 0;
> > > +  if (src == NULL || dst == NULL)
> > > +    return -EINVAL;
> > > +  std::vector<DrmCompositionPlane> &src_planes = src->composition_planes();
> > > +  DrmCompositionPlane squashed_comp(DrmCompositionPlane::Type::kPrecomp, NULL,
> > > +                                    src->crtc());
> > > +  for (DrmCompositionPlane &comp_plane : src_planes) {
> > > +    if (comp_plane.plane() == NULL) {
> > > +      ALOGE("Skipping squash all because of NULL plane");
> > > +      ret = -EINVAL;
> > > +    }
> > > +    if (!squashed_comp.plane() &&
> > > +        comp_plane.plane()->type() == DRM_PLANE_TYPE_PRIMARY)
> > > +      squashed_comp.set_plane(comp_plane.plane());
> > > +    else
> > > +      dst->AddPlaneDisable(comp_plane.plane());
> > > +  }
> > > +
> > > +  DrmFramebuffer *writeback_fb = NULL;
> > > +  AutoLock lock(&lock_, __FUNCTION__);
> > > +  if ((ret = lock.Lock()))
> > 
> > Same comments regarding assignment in a conditional.
> > 
> > > +    return ret;
> > > +  writeback_fb = &framebuffers_[framebuffer_index_];
> > > +  framebuffer_index_ = (framebuffer_index_ + 1) % DRM_DISPLAY_BUFFERS;
> > > +  ret = PrepareFramebuffer(*writeback_fb, dst, mode_.mode.h_display(),
> > > +                           mode_.mode.v_display());
> > > +  if (ret) {
> > > +    ALOGE("Failed to prepare destination buffer");
> > > +    return ret;
> > > +  }
> > > +  lock.Unlock();
> > > +  ret = CommitFrame(src, true, dst, writeback_conn);
> > > +  if (ret) {
> > > +    ALOGE("Atomic check failed");
> > > +    return ret;
> > > +  }
> > > +  if ((ret = lock.Lock()))
> > 
> > All of these locks and unlocks are going to cause races, as mentioned below,
> > please re-evaluate.
> > 
> > > +    return ret;
> > > +  if (!CountdownExpired() && active_composition_) {
> > > +    ALOGE("Writeback composition not needed abort");
> > > +    return -EINVAL;
> > > +  }
> > > +  ret = CommitFrame(src, false, dst, writeback_conn);
> > > +  lock.Unlock();
> > > +  if (ret || dst->layers().size() != 1) {
> > > +    ALOGE("Failed to flatten scene using writeback");
> > > +    return -EINVAL;
> > > +  }
> > > +  squashed_comp.source_layers().push_back(0);
> > > +  ret =
> > > +      sync_wait(dst->layers()[0].acquire_fence.Release(), kWaitWritebackFence);
> > 
> > line break
> > 
> > > +  if (ret) {
> > > +    ALOGE("Failed to wait on writeback fence");
> > > +    return ret;
> > > +  }
> > > +  ret = dst->AddPlaneComposition(std::move(squashed_comp));
> > > +  if (ret) {
> > > +    ALOGE("Failed to add flatten scene");
> > > +    return ret;
> > > +  }
> > > +  ret = dst->FinalizeComposition();
> > > +  if (ret) {
> > > +    ALOGE("Failed to finalize composition");
> > > +    return ret;
> > > +  }
> > > +  return 0;
> > > +}
> > > +
> > > +int DrmDisplayCompositor::FlattenSynchronously(DrmConnector *writeback_conn) {
> > > +  if (writeback_conn->display() != display_) {
> > > +    ALOGE("Cannot flatten synchronously on different display");
> > > +    return -EINVAL;
> > > +  }
> > 
> > You check this right before calling, so this isn't needed.
> > 
> > > +  ALOGI("FlattenSynchronously using the same display");
> > 
> > I think you should downgrade this log level.
> > 
> > > +  int ret = 0;
> > > +  /* Flattened composition with only one layer that is built
> > > +   * using the writeback connector
> > > +   */
> > > +  std::unique_ptr<DrmDisplayComposition> writeback_comp =
> > > +      CreateInitializedComposition();
> > > +  /* Copy of the active_composition_, we need a copy because
> > > +   * if we use the active composition we have to hold the lock
> > > +   * for the entire sequence of flattening.
> > > +   */
> > > +  std::unique_ptr<DrmDisplayComposition> copy_comp =
> > > +      CreateInitializedComposition();
> > > +
> > > +  if (!copy_comp || !writeback_comp)
> > > +    return -EINVAL;
> > > +  AutoLock lock(&lock_, __FUNCTION__);
> > > +  if ((ret = lock.Lock()))
> > 
> > Assignments in if statements make me nervous. Since you don't need the
> > declaration above, just do:
> > 
> >   int ret = lock.Lock();
> >   if (ret)
> > 
> > > +    return ret;
> > > +  if (CountdownExpired()) {
> > > +    ret = copy_comp->CopyLayers(active_composition_.get());
> > > +    if (ret)
> > > +      return ret;
> > > +    copy_comp->CopyCompPlanes(active_composition_.get());
> > > +  } else {
> > > +    return -EINVAL;
> > > +  }
> > 
> >   if (!CountdownExpired())
> >     return -EINVAL;
> > 
> >   ret = copy_comp->CopyLayers(active_composition_.get());
> >   if (ret)
> >     return ret;
> >   copy_comp->CopyCompPlanes(active_composition_.get());
> > 
> > 
> > > +  lock.Unlock();
> > 
> > Just hold the lock through WritebackComposite(), and consider creating a
> > ApplyFrameLocked() to hold it through ApplyFrame(). You should be able to reduce
> > the number of times you have to check CountdownExpired() (or remove it)
> > significantly by just consistently locking things.
> > 
> > > +  ret =
> > > +      WritebackComposite(copy_comp.get(), writeback_comp.get(), writeback_conn);
> > > +  if (ret) {
> > > +    ALOGE("Failed to prepare writebackScene");
> > > +    return ret;
> > > +  }
> > > +
> > > +  ApplyFrame(std::move(writeback_comp), 0, true);
> > > +  return 0;
> > > +}
> > > +
> > >  int DrmDisplayCompositor::FlattenScene() {
> > > -  return -EINVAL;
> > > +  DrmConnector *writeback_conn =
> > > +      drm_->resource_manager()->AvailableWritebackConnector(display_);
> > > +  if (!active_composition_ || !writeback_conn)
> > > +    return -EINVAL;
> > > +  std::vector<DrmCompositionPlane> &src_planes =
> > > +      active_composition_->composition_planes();
> > > +  size_t src_planes_with_layer = std::count_if(
> > > +      src_planes.begin(), src_planes.end(), [](DrmCompositionPlane &p) {
> > > +        return p.type() != DrmCompositionPlane::Type::kDisable;
> > > +      });
> > > +
> > > +  if (src_planes_with_layer <= 1)
> > > +    return -EALREADY;
> > > +
> > > +  if (writeback_conn->display() == display_) {
> > > +    return FlattenSynchronously(writeback_conn);
> > > +  }
> > > +
> > > +  return 0;
> > >  }
> > >  
> > >  int DrmDisplayCompositor::SquashAll() {
> > > diff --git a/drmdisplaycompositor.h b/drmdisplaycompositor.h
> > > index 26201b9..4cc4a5e 100644
> > > --- a/drmdisplaycompositor.h
> > > +++ b/drmdisplaycompositor.h
> > > @@ -125,7 +125,9 @@ class DrmDisplayCompositor {
> > >    int ApplySquash(DrmDisplayComposition *display_comp);
> > >    int ApplyPreComposite(DrmDisplayComposition *display_comp);
> > >    int PrepareFrame(DrmDisplayComposition *display_comp);
> > > -  int CommitFrame(DrmDisplayComposition *display_comp, bool test_only);
> > > +  int CommitFrame(DrmDisplayComposition *display_comp, bool test_only,
> > > +                  DrmDisplayComposition *writeback_comp = NULL,
> > > +                  DrmConnector *writeback_conn = NULL);
> > >    int SquashFrame(DrmDisplayComposition *src, DrmDisplayComposition *dst);
> > >    int ApplyDpms(DrmDisplayComposition *display_comp);
> > >    int DisablePlanes(DrmDisplayComposition *display_comp);
> > > @@ -134,7 +136,10 @@ class DrmDisplayCompositor {
> > >    void ApplyFrame(std::unique_ptr<DrmDisplayComposition> composition,
> > >                    int status, bool writeback = false);
> > >    int FlattenScene();
> > > +  int FlattenSynchronously(DrmConnector *writeback_conn);
> > 
> > Can we come up with a better name than Synchonrous/Asynchronous? Or at the very
> > least comment on what that means?
> > 
> > >  
> > > +  int WritebackComposite(DrmDisplayComposition *src, DrmDisplayComposition *dst,
> > > +                         DrmConnector *writeback_conn);
> > >    bool CountdownExpired() const;
> > >  
> > >    std::tuple<int, uint32_t> CreateModeBlob(const DrmMode &mode);
> > > -- 
> > > 2.7.4
> > > 
> > 
> > -- 
> > Sean Paul, Software Engineer, Google / Chromium OS
> 
> -- 
> Cheers,
> Alex G
diff mbox

Patch

diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
index e535e8a..cb670e6 100644
--- a/drmdisplaycompositor.cpp
+++ b/drmdisplaycompositor.cpp
@@ -36,6 +36,7 @@ 
 #include "drmplane.h"
 #include "drmresources.h"
 #include "glworker.h"
+static const uint32_t kWaitWritebackFence = 100;  // ms
 
 namespace android {
 
@@ -523,7 +524,9 @@  int DrmDisplayCompositor::PrepareFrame(DrmDisplayComposition *display_comp) {
 }
 
 int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
-                                      bool test_only) {
+                                      bool test_only,
+                                      DrmDisplayComposition *writeback_comp,
+                                      DrmConnector *writeback_conn) {
   ATRACE_CALL();
 
   int ret = 0;
@@ -532,6 +535,7 @@  int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
   std::vector<DrmCompositionPlane> &comp_planes =
       display_comp->composition_planes();
   uint64_t out_fences[drm_->crtcs().size()];
+  int writeback_fence = -1;
 
   DrmConnector *connector = drm_->GetConnectorForDisplay(display_);
   if (!connector) {
@@ -550,9 +554,37 @@  int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
     return -ENOMEM;
   }
 
+  if (writeback_comp != NULL) {
+    if (writeback_conn == NULL)
+      return -EINVAL;
+    if (writeback_conn->writeback_fb_id().id() == 0 ||
+        writeback_conn->writeback_out_fence().id() == 0) {
+      ALOGE("Writeback properties don't exit");
+      return -EINVAL;
+    }
+    if (writeback_comp->layers().size() != 1) {
+      ALOGE("Invalid number of layers for writeback composition");
+      return -EINVAL;
+    }
+    ret = drmModeAtomicAddProperty(
+        pset, writeback_conn->id(), writeback_conn->writeback_fb_id().id(),
+        writeback_comp->layers().back().buffer->fb_id);
+    if (ret < 0) {
+      ALOGE("Failed to add writeback_fb_id");
+      return ret;
+    }
+    ret = drmModeAtomicAddProperty(pset, writeback_conn->id(),
+                                   writeback_conn->writeback_out_fence().id(),
+                                   (uint64_t)&writeback_fence);
+    if (ret < 0) {
+      ALOGE("Failed to add writeback_out_fence");
+      return ret;
+    }
+  }
   if (crtc->out_fence_ptr_property().id() != 0) {
-    ret = drmModeAtomicAddProperty(pset, crtc->id(), crtc->out_fence_ptr_property().id(),
-                                   (uint64_t) &out_fences[crtc->pipe()]);
+    ret = drmModeAtomicAddProperty(pset, crtc->id(),
+                                   crtc->out_fence_ptr_property().id(),
+                                   (uint64_t)&out_fences[crtc->pipe()]);
     if (ret < 0) {
       ALOGE("Failed to add OUT_FENCE_PTR property to pset: %d", ret);
       drmModeAtomicFree(pset);
@@ -580,6 +612,15 @@  int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
     }
   }
 
+  if (writeback_conn != NULL) {
+    ret = drmModeAtomicAddProperty(pset, writeback_conn->id(),
+                                   writeback_conn->crtc_id_property().id(),
+                                   crtc->id());
+    if (ret < 0) {
+      ALOGE("Failed to  attach writeback");
+    }
+  }
+
   for (DrmCompositionPlane &comp_plane : comp_planes) {
     DrmPlane *plane = comp_plane.plane();
     DrmCrtc *crtc = comp_plane.crtc();
@@ -729,8 +770,18 @@  int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
 
   if (!ret) {
     uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
-    if (test_only)
+    if (test_only) {
       flags |= DRM_MODE_ATOMIC_TEST_ONLY;
+    } else {
+      if (writeback_comp != NULL) {
+        if (!CountdownExpired() && active_composition_) {
+          ALOGE("Writeback composition not needed, abort commit");
+          drmModeAtomicFree(pset);
+          return -EINVAL;
+        };
+        flags |= DRM_MODE_ATOMIC_NONBLOCK;
+      }
+    }
 
     ret = drmModeAtomicCommit(drm_->fd(), pset, flags, drm_);
     if (ret) {
@@ -769,6 +820,13 @@  int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
   if (crtc->out_fence_ptr_property().id()) {
     display_comp->set_out_fence((int) out_fences[crtc->pipe()]);
   }
+  if (writeback_fence >= 0) {
+    if (writeback_comp->layers().size() != 1) {
+      ALOGE("Invalid numbers of layer for writeback_comp");
+      return -EINVAL;
+    }
+    writeback_comp->layers()[0].acquire_fence.Set(writeback_fence);
+  }
 
   return ret;
 }
@@ -837,6 +895,8 @@  void DrmDisplayCompositor::ApplyFrame(
       if (active_composition_)
         active_composition_->SignalCompositionDone();
       active_composition_.swap(composition);
+      flatten_countdown_ = FLATTEN_COUNTDOWN_INIT;
+      vsync_worker_.VSyncControl(!writeback);
     }
   }
 
@@ -913,8 +973,141 @@  int DrmDisplayCompositor::ApplyComposition(
   return ret;
 }
 
+int DrmDisplayCompositor::WritebackComposite(DrmDisplayComposition *src,
+                                             DrmDisplayComposition *dst,
+                                             DrmConnector *writeback_conn) {
+  int ret = 0;
+  if (src == NULL || dst == NULL)
+    return -EINVAL;
+  std::vector<DrmCompositionPlane> &src_planes = src->composition_planes();
+  DrmCompositionPlane squashed_comp(DrmCompositionPlane::Type::kPrecomp, NULL,
+                                    src->crtc());
+  for (DrmCompositionPlane &comp_plane : src_planes) {
+    if (comp_plane.plane() == NULL) {
+      ALOGE("Skipping squash all because of NULL plane");
+      ret = -EINVAL;
+    }
+    if (!squashed_comp.plane() &&
+        comp_plane.plane()->type() == DRM_PLANE_TYPE_PRIMARY)
+      squashed_comp.set_plane(comp_plane.plane());
+    else
+      dst->AddPlaneDisable(comp_plane.plane());
+  }
+
+  DrmFramebuffer *writeback_fb = NULL;
+  AutoLock lock(&lock_, __FUNCTION__);
+  if ((ret = lock.Lock()))
+    return ret;
+  writeback_fb = &framebuffers_[framebuffer_index_];
+  framebuffer_index_ = (framebuffer_index_ + 1) % DRM_DISPLAY_BUFFERS;
+  ret = PrepareFramebuffer(*writeback_fb, dst, mode_.mode.h_display(),
+                           mode_.mode.v_display());
+  if (ret) {
+    ALOGE("Failed to prepare destination buffer");
+    return ret;
+  }
+  lock.Unlock();
+  ret = CommitFrame(src, true, dst, writeback_conn);
+  if (ret) {
+    ALOGE("Atomic check failed");
+    return ret;
+  }
+  if ((ret = lock.Lock()))
+    return ret;
+  if (!CountdownExpired() && active_composition_) {
+    ALOGE("Writeback composition not needed abort");
+    return -EINVAL;
+  }
+  ret = CommitFrame(src, false, dst, writeback_conn);
+  lock.Unlock();
+  if (ret || dst->layers().size() != 1) {
+    ALOGE("Failed to flatten scene using writeback");
+    return -EINVAL;
+  }
+  squashed_comp.source_layers().push_back(0);
+  ret =
+      sync_wait(dst->layers()[0].acquire_fence.Release(), kWaitWritebackFence);
+  if (ret) {
+    ALOGE("Failed to wait on writeback fence");
+    return ret;
+  }
+  ret = dst->AddPlaneComposition(std::move(squashed_comp));
+  if (ret) {
+    ALOGE("Failed to add flatten scene");
+    return ret;
+  }
+  ret = dst->FinalizeComposition();
+  if (ret) {
+    ALOGE("Failed to finalize composition");
+    return ret;
+  }
+  return 0;
+}
+
+int DrmDisplayCompositor::FlattenSynchronously(DrmConnector *writeback_conn) {
+  if (writeback_conn->display() != display_) {
+    ALOGE("Cannot flatten synchronously on different display");
+    return -EINVAL;
+  }
+  ALOGI("FlattenSynchronously using the same display");
+  int ret = 0;
+  /* Flattened composition with only one layer that is built
+   * using the writeback connector
+   */
+  std::unique_ptr<DrmDisplayComposition> writeback_comp =
+      CreateInitializedComposition();
+  /* Copy of the active_composition_, we need a copy because
+   * if we use the active composition we have to hold the lock
+   * for the entire sequence of flattening.
+   */
+  std::unique_ptr<DrmDisplayComposition> copy_comp =
+      CreateInitializedComposition();
+
+  if (!copy_comp || !writeback_comp)
+    return -EINVAL;
+  AutoLock lock(&lock_, __FUNCTION__);
+  if ((ret = lock.Lock()))
+    return ret;
+  if (CountdownExpired()) {
+    ret = copy_comp->CopyLayers(active_composition_.get());
+    if (ret)
+      return ret;
+    copy_comp->CopyCompPlanes(active_composition_.get());
+  } else {
+    return -EINVAL;
+  }
+  lock.Unlock();
+  ret =
+      WritebackComposite(copy_comp.get(), writeback_comp.get(), writeback_conn);
+  if (ret) {
+    ALOGE("Failed to prepare writebackScene");
+    return ret;
+  }
+
+  ApplyFrame(std::move(writeback_comp), 0, true);
+  return 0;
+}
+
 int DrmDisplayCompositor::FlattenScene() {
-  return -EINVAL;
+  DrmConnector *writeback_conn =
+      drm_->resource_manager()->AvailableWritebackConnector(display_);
+  if (!active_composition_ || !writeback_conn)
+    return -EINVAL;
+  std::vector<DrmCompositionPlane> &src_planes =
+      active_composition_->composition_planes();
+  size_t src_planes_with_layer = std::count_if(
+      src_planes.begin(), src_planes.end(), [](DrmCompositionPlane &p) {
+        return p.type() != DrmCompositionPlane::Type::kDisable;
+      });
+
+  if (src_planes_with_layer <= 1)
+    return -EALREADY;
+
+  if (writeback_conn->display() == display_) {
+    return FlattenSynchronously(writeback_conn);
+  }
+
+  return 0;
 }
 
 int DrmDisplayCompositor::SquashAll() {
diff --git a/drmdisplaycompositor.h b/drmdisplaycompositor.h
index 26201b9..4cc4a5e 100644
--- a/drmdisplaycompositor.h
+++ b/drmdisplaycompositor.h
@@ -125,7 +125,9 @@  class DrmDisplayCompositor {
   int ApplySquash(DrmDisplayComposition *display_comp);
   int ApplyPreComposite(DrmDisplayComposition *display_comp);
   int PrepareFrame(DrmDisplayComposition *display_comp);
-  int CommitFrame(DrmDisplayComposition *display_comp, bool test_only);
+  int CommitFrame(DrmDisplayComposition *display_comp, bool test_only,
+                  DrmDisplayComposition *writeback_comp = NULL,
+                  DrmConnector *writeback_conn = NULL);
   int SquashFrame(DrmDisplayComposition *src, DrmDisplayComposition *dst);
   int ApplyDpms(DrmDisplayComposition *display_comp);
   int DisablePlanes(DrmDisplayComposition *display_comp);
@@ -134,7 +136,10 @@  class DrmDisplayCompositor {
   void ApplyFrame(std::unique_ptr<DrmDisplayComposition> composition,
                   int status, bool writeback = false);
   int FlattenScene();
+  int FlattenSynchronously(DrmConnector *writeback_conn);
 
+  int WritebackComposite(DrmDisplayComposition *src, DrmDisplayComposition *dst,
+                         DrmConnector *writeback_conn);
   bool CountdownExpired() const;
 
   std::tuple<int, uint32_t> CreateModeBlob(const DrmMode &mode);