1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
18 #define LOG_TAG "hwc-drm-display-compositor"
19 
20 #include "drmdisplaycompositor.h"
21 
22 #include <pthread.h>
23 #include <sched.h>
24 #include <stdlib.h>
25 #include <time.h>
26 #include <sstream>
27 #include <vector>
28 
29 #include <drm/drm_mode.h>
30 #include <log/log.h>
31 #include <sync/sync.h>
32 #include <utils/Trace.h>
33 
34 #include "autolock.h"
35 #include "drmcrtc.h"
36 #include "drmdevice.h"
37 #include "drmplane.h"
38 
39 static const uint32_t kWaitWritebackFence = 100;  // ms
40 
41 namespace android {
42 
43 class CompositorVsyncCallback : public VsyncCallback {
44  public:
CompositorVsyncCallback(DrmDisplayCompositor * compositor)45   CompositorVsyncCallback(DrmDisplayCompositor *compositor)
46       : compositor_(compositor) {
47   }
48 
Callback(int display,int64_t timestamp)49   void Callback(int display, int64_t timestamp) {
50     compositor_->Vsync(display, timestamp);
51   }
52 
53  private:
54   DrmDisplayCompositor *compositor_;
55 };
56 
DrmDisplayCompositor()57 DrmDisplayCompositor::DrmDisplayCompositor()
58     : resource_manager_(NULL),
59       display_(-1),
60       initialized_(false),
61       active_(false),
62       use_hw_overlays_(true),
63       dump_frames_composited_(0),
64       dump_last_timestamp_ns_(0),
65       flatten_countdown_(FLATTEN_COUNTDOWN_INIT),
66       writeback_fence_(-1) {
67   struct timespec ts;
68   if (clock_gettime(CLOCK_MONOTONIC, &ts))
69     return;
70   dump_last_timestamp_ns_ = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
71 }
72 
~DrmDisplayCompositor()73 DrmDisplayCompositor::~DrmDisplayCompositor() {
74   if (!initialized_)
75     return;
76 
77   vsync_worker_.Exit();
78   int ret = pthread_mutex_lock(&lock_);
79   if (ret)
80     ALOGE("Failed to acquire compositor lock %d", ret);
81   DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
82   if (mode_.blob_id)
83     drm->DestroyPropertyBlob(mode_.blob_id);
84   if (mode_.old_blob_id)
85     drm->DestroyPropertyBlob(mode_.old_blob_id);
86 
87   active_composition_.reset();
88 
89   ret = pthread_mutex_unlock(&lock_);
90   if (ret)
91     ALOGE("Failed to acquire compositor lock %d", ret);
92 
93   pthread_mutex_destroy(&lock_);
94 }
95 
Init(ResourceManager * resource_manager,int display)96 int DrmDisplayCompositor::Init(ResourceManager *resource_manager, int display) {
97   resource_manager_ = resource_manager;
98   display_ = display;
99   DrmDevice *drm = resource_manager_->GetDrmDevice(display);
100   if (!drm) {
101     ALOGE("Could not find drmdevice for display");
102     return -EINVAL;
103   }
104   int ret = pthread_mutex_init(&lock_, NULL);
105   if (ret) {
106     ALOGE("Failed to initialize drm compositor lock %d\n", ret);
107     return ret;
108   }
109   planner_ = Planner::CreateInstance(drm);
110 
111   vsync_worker_.Init(drm, display_);
112   auto callback = std::make_shared<CompositorVsyncCallback>(this);
113   vsync_worker_.RegisterCallback(callback);
114 
115   initialized_ = true;
116   return 0;
117 }
118 
CreateComposition() const119 std::unique_ptr<DrmDisplayComposition> DrmDisplayCompositor::CreateComposition()
120     const {
121   return std::unique_ptr<DrmDisplayComposition>(new DrmDisplayComposition());
122 }
123 
124 std::unique_ptr<DrmDisplayComposition>
CreateInitializedComposition() const125 DrmDisplayCompositor::CreateInitializedComposition() const {
126   DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
127   DrmCrtc *crtc = drm->GetCrtcForDisplay(display_);
128   if (!crtc) {
129     ALOGE("Failed to find crtc for display = %d", display_);
130     return std::unique_ptr<DrmDisplayComposition>();
131   }
132   std::unique_ptr<DrmDisplayComposition> comp = CreateComposition();
133   std::shared_ptr<Importer> importer = resource_manager_->GetImporter(display_);
134   if (!importer) {
135     ALOGE("Failed to find resources for display = %d", display_);
136     return std::unique_ptr<DrmDisplayComposition>();
137   }
138   int ret = comp->Init(drm, crtc, importer.get(), planner_.get(), 0);
139   if (ret) {
140     ALOGE("Failed to init composition for display = %d", display_);
141     return std::unique_ptr<DrmDisplayComposition>();
142   }
143   return comp;
144 }
145 
146 std::tuple<uint32_t, uint32_t, int>
GetActiveModeResolution()147 DrmDisplayCompositor::GetActiveModeResolution() {
148   DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
149   DrmConnector *connector = drm->GetConnectorForDisplay(display_);
150   if (connector == NULL) {
151     ALOGE("Failed to determine display mode: no connector for display %d",
152           display_);
153     return std::make_tuple(0, 0, -ENODEV);
154   }
155 
156   const DrmMode &mode = connector->active_mode();
157   return std::make_tuple(mode.h_display(), mode.v_display(), 0);
158 }
159 
DisablePlanes(DrmDisplayComposition * display_comp)160 int DrmDisplayCompositor::DisablePlanes(DrmDisplayComposition *display_comp) {
161   drmModeAtomicReqPtr pset = drmModeAtomicAlloc();
162   if (!pset) {
163     ALOGE("Failed to allocate property set");
164     return -ENOMEM;
165   }
166 
167   int ret;
168   std::vector<DrmCompositionPlane> &comp_planes = display_comp
169                                                       ->composition_planes();
170   for (DrmCompositionPlane &comp_plane : comp_planes) {
171     DrmPlane *plane = comp_plane.plane();
172     ret = drmModeAtomicAddProperty(pset, plane->id(),
173                                    plane->crtc_property().id(), 0) < 0 ||
174           drmModeAtomicAddProperty(pset, plane->id(), plane->fb_property().id(),
175                                    0) < 0;
176     if (ret) {
177       ALOGE("Failed to add plane %d disable to pset", plane->id());
178       drmModeAtomicFree(pset);
179       return ret;
180     }
181   }
182   DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
183   ret = drmModeAtomicCommit(drm->fd(), pset, 0, drm);
184   if (ret) {
185     ALOGE("Failed to commit pset ret=%d\n", ret);
186     drmModeAtomicFree(pset);
187     return ret;
188   }
189 
190   drmModeAtomicFree(pset);
191   return 0;
192 }
193 
SetupWritebackCommit(drmModeAtomicReqPtr pset,uint32_t crtc_id,DrmConnector * writeback_conn,DrmHwcBuffer * writeback_buffer)194 int DrmDisplayCompositor::SetupWritebackCommit(drmModeAtomicReqPtr pset,
195                                                uint32_t crtc_id,
196                                                DrmConnector *writeback_conn,
197                                                DrmHwcBuffer *writeback_buffer) {
198   int ret = 0;
199   if (writeback_conn->writeback_fb_id().id() == 0 ||
200       writeback_conn->writeback_out_fence().id() == 0) {
201     ALOGE("Writeback properties don't exit");
202     return -EINVAL;
203   }
204   if ((*writeback_buffer)->fb_id == 0) {
205     ALOGE("Invalid writeback buffer");
206     return -EINVAL;
207   }
208   ret = drmModeAtomicAddProperty(pset, writeback_conn->id(),
209                                  writeback_conn->writeback_fb_id().id(),
210                                  (*writeback_buffer)->fb_id);
211   if (ret < 0) {
212     ALOGE("Failed to add writeback_fb_id");
213     return ret;
214   }
215   ret = drmModeAtomicAddProperty(pset, writeback_conn->id(),
216                                  writeback_conn->writeback_out_fence().id(),
217                                  (uint64_t)&writeback_fence_);
218   if (ret < 0) {
219     ALOGE("Failed to add writeback_out_fence");
220     return ret;
221   }
222 
223   ret = drmModeAtomicAddProperty(pset, writeback_conn->id(),
224                                  writeback_conn->crtc_id_property().id(),
225                                  crtc_id);
226   if (ret < 0) {
227     ALOGE("Failed to  attach writeback");
228     return ret;
229   }
230   return 0;
231 }
232 
CommitFrame(DrmDisplayComposition * display_comp,bool test_only,DrmConnector * writeback_conn,DrmHwcBuffer * writeback_buffer)233 int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
234                                       bool test_only,
235                                       DrmConnector *writeback_conn,
236                                       DrmHwcBuffer *writeback_buffer) {
237   ATRACE_CALL();
238 
239   int ret = 0;
240 
241   std::vector<DrmHwcLayer> &layers = display_comp->layers();
242   std::vector<DrmCompositionPlane> &comp_planes = display_comp
243                                                       ->composition_planes();
244   DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
245   uint64_t out_fences[drm->crtcs().size()];
246 
247   DrmConnector *connector = drm->GetConnectorForDisplay(display_);
248   if (!connector) {
249     ALOGE("Could not locate connector for display %d", display_);
250     return -ENODEV;
251   }
252   DrmCrtc *crtc = drm->GetCrtcForDisplay(display_);
253   if (!crtc) {
254     ALOGE("Could not locate crtc for display %d", display_);
255     return -ENODEV;
256   }
257 
258   drmModeAtomicReqPtr pset = drmModeAtomicAlloc();
259   if (!pset) {
260     ALOGE("Failed to allocate property set");
261     return -ENOMEM;
262   }
263 
264   if (writeback_buffer != NULL) {
265     if (writeback_conn == NULL) {
266       ALOGE("Invalid arguments requested writeback without writeback conn");
267       return -EINVAL;
268     }
269     ret = SetupWritebackCommit(pset, crtc->id(), writeback_conn,
270                                writeback_buffer);
271     if (ret < 0) {
272       ALOGE("Failed to Setup Writeback Commit ret = %d", ret);
273       return ret;
274     }
275   }
276   if (crtc->out_fence_ptr_property().id() != 0) {
277     ret = drmModeAtomicAddProperty(pset, crtc->id(),
278                                    crtc->out_fence_ptr_property().id(),
279                                    (uint64_t)&out_fences[crtc->pipe()]);
280     if (ret < 0) {
281       ALOGE("Failed to add OUT_FENCE_PTR property to pset: %d", ret);
282       drmModeAtomicFree(pset);
283       return ret;
284     }
285   }
286 
287   if (mode_.needs_modeset) {
288     ret = drmModeAtomicAddProperty(pset, crtc->id(),
289                                    crtc->active_property().id(), 1);
290     if (ret < 0) {
291       ALOGE("Failed to add crtc active to pset\n");
292       drmModeAtomicFree(pset);
293       return ret;
294     }
295 
296     ret = drmModeAtomicAddProperty(pset, crtc->id(), crtc->mode_property().id(),
297                                    mode_.blob_id) < 0 ||
298           drmModeAtomicAddProperty(pset, connector->id(),
299                                    connector->crtc_id_property().id(),
300                                    crtc->id()) < 0;
301     if (ret) {
302       ALOGE("Failed to add blob %d to pset", mode_.blob_id);
303       drmModeAtomicFree(pset);
304       return ret;
305     }
306   }
307 
308   for (DrmCompositionPlane &comp_plane : comp_planes) {
309     DrmPlane *plane = comp_plane.plane();
310     DrmCrtc *crtc = comp_plane.crtc();
311     std::vector<size_t> &source_layers = comp_plane.source_layers();
312 
313     int fb_id = -1;
314     int fence_fd = -1;
315     hwc_rect_t display_frame;
316     hwc_frect_t source_crop;
317     uint64_t rotation = 0;
318     uint64_t alpha = 0xFFFF;
319     uint64_t blend;
320 
321     if (comp_plane.type() != DrmCompositionPlane::Type::kDisable) {
322       if (source_layers.size() > 1) {
323         ALOGE("Can't handle more than one source layer sz=%zu type=%d",
324               source_layers.size(), comp_plane.type());
325         continue;
326       }
327 
328       if (source_layers.empty() || source_layers.front() >= layers.size()) {
329         ALOGE("Source layer index %zu out of bounds %zu type=%d",
330               source_layers.front(), layers.size(), comp_plane.type());
331         break;
332       }
333       DrmHwcLayer &layer = layers[source_layers.front()];
334       if (!layer.buffer) {
335         ALOGE("Expected a valid framebuffer for pset");
336         break;
337       }
338       fb_id = layer.buffer->fb_id;
339       fence_fd = layer.acquire_fence.get();
340       display_frame = layer.display_frame;
341       source_crop = layer.source_crop;
342       alpha = layer.alpha;
343 
344       if (plane->blend_property().id()) {
345         switch (layer.blending) {
346           case DrmHwcBlending::kPreMult:
347             std::tie(blend, ret) = plane->blend_property().GetEnumValueWithName(
348                 "Pre-multiplied");
349             break;
350           case DrmHwcBlending::kCoverage:
351             std::tie(blend, ret) = plane->blend_property().GetEnumValueWithName(
352                 "Coverage");
353             break;
354           case DrmHwcBlending::kNone:
355           default:
356             std::tie(blend, ret) = plane->blend_property().GetEnumValueWithName(
357                 "None");
358             break;
359         }
360       }
361 
362       if (plane->zpos_property().id() &&
363           !plane->zpos_property().is_immutable()) {
364         uint64_t min_zpos = 0;
365 
366         // Ignore ret and use min_zpos as 0 by default
367         std::tie(std::ignore, min_zpos) = plane->zpos_property().range_min();
368 
369         ret = drmModeAtomicAddProperty(pset, plane->id(),
370                                        plane->zpos_property().id(),
371                                        source_layers.front() + min_zpos) < 0;
372         if (ret) {
373           ALOGE("Failed to add zpos property %d to plane %d",
374                 plane->zpos_property().id(), plane->id());
375           break;
376         }
377       }
378 
379       rotation = 0;
380       if (layer.transform & DrmHwcTransform::kFlipH)
381         rotation |= DRM_MODE_REFLECT_X;
382       if (layer.transform & DrmHwcTransform::kFlipV)
383         rotation |= DRM_MODE_REFLECT_Y;
384       if (layer.transform & DrmHwcTransform::kRotate90)
385         rotation |= DRM_MODE_ROTATE_90;
386       else if (layer.transform & DrmHwcTransform::kRotate180)
387         rotation |= DRM_MODE_ROTATE_180;
388       else if (layer.transform & DrmHwcTransform::kRotate270)
389         rotation |= DRM_MODE_ROTATE_270;
390       else
391         rotation |= DRM_MODE_ROTATE_0;
392 
393       if (fence_fd >= 0) {
394         int prop_id = plane->in_fence_fd_property().id();
395         if (prop_id == 0) {
396           ALOGE("Failed to get IN_FENCE_FD property id");
397           break;
398         }
399         ret = drmModeAtomicAddProperty(pset, plane->id(), prop_id, fence_fd);
400         if (ret < 0) {
401           ALOGE("Failed to add IN_FENCE_FD property to pset: %d", ret);
402           break;
403         }
404       }
405     }
406 
407     // Disable the plane if there's no framebuffer
408     if (fb_id < 0) {
409       ret = drmModeAtomicAddProperty(pset, plane->id(),
410                                      plane->crtc_property().id(), 0) < 0 ||
411             drmModeAtomicAddProperty(pset, plane->id(),
412                                      plane->fb_property().id(), 0) < 0;
413       if (ret) {
414         ALOGE("Failed to add plane %d disable to pset", plane->id());
415         break;
416       }
417       continue;
418     }
419 
420     ret = drmModeAtomicAddProperty(pset, plane->id(),
421                                    plane->crtc_property().id(), crtc->id()) < 0;
422     ret |= drmModeAtomicAddProperty(pset, plane->id(),
423                                     plane->fb_property().id(), fb_id) < 0;
424     ret |= drmModeAtomicAddProperty(pset, plane->id(),
425                                     plane->crtc_x_property().id(),
426                                     display_frame.left) < 0;
427     ret |= drmModeAtomicAddProperty(pset, plane->id(),
428                                     plane->crtc_y_property().id(),
429                                     display_frame.top) < 0;
430     ret |= drmModeAtomicAddProperty(pset, plane->id(),
431                                     plane->crtc_w_property().id(),
432                                     display_frame.right - display_frame.left) <
433            0;
434     ret |= drmModeAtomicAddProperty(pset, plane->id(),
435                                     plane->crtc_h_property().id(),
436                                     display_frame.bottom - display_frame.top) <
437            0;
438     ret |= drmModeAtomicAddProperty(pset, plane->id(),
439                                     plane->src_x_property().id(),
440                                     (int)(source_crop.left) << 16) < 0;
441     ret |= drmModeAtomicAddProperty(pset, plane->id(),
442                                     plane->src_y_property().id(),
443                                     (int)(source_crop.top) << 16) < 0;
444     ret |= drmModeAtomicAddProperty(pset, plane->id(),
445                                     plane->src_w_property().id(),
446                                     (int)(source_crop.right - source_crop.left)
447                                         << 16) < 0;
448     ret |= drmModeAtomicAddProperty(pset, plane->id(),
449                                     plane->src_h_property().id(),
450                                     (int)(source_crop.bottom - source_crop.top)
451                                         << 16) < 0;
452     if (ret) {
453       ALOGE("Failed to add plane %d to set", plane->id());
454       break;
455     }
456 
457     if (plane->rotation_property().id()) {
458       ret = drmModeAtomicAddProperty(pset, plane->id(),
459                                      plane->rotation_property().id(),
460                                      rotation) < 0;
461       if (ret) {
462         ALOGE("Failed to add rotation property %d to plane %d",
463               plane->rotation_property().id(), plane->id());
464         break;
465       }
466     }
467 
468     if (plane->alpha_property().id()) {
469       ret = drmModeAtomicAddProperty(pset, plane->id(),
470                                      plane->alpha_property().id(), alpha) < 0;
471       if (ret) {
472         ALOGE("Failed to add alpha property %d to plane %d",
473               plane->alpha_property().id(), plane->id());
474         break;
475       }
476     }
477 
478     if (plane->blend_property().id()) {
479       ret = drmModeAtomicAddProperty(pset, plane->id(),
480                                      plane->blend_property().id(), blend) < 0;
481       if (ret) {
482         ALOGE("Failed to add pixel blend mode property %d to plane %d",
483               plane->blend_property().id(), plane->id());
484         break;
485       }
486     }
487   }
488 
489   if (!ret) {
490     uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
491     if (test_only)
492       flags |= DRM_MODE_ATOMIC_TEST_ONLY;
493 
494     ret = drmModeAtomicCommit(drm->fd(), pset, flags, drm);
495     if (ret) {
496       if (!test_only)
497         ALOGE("Failed to commit pset ret=%d\n", ret);
498       drmModeAtomicFree(pset);
499       return ret;
500     }
501   }
502   if (pset)
503     drmModeAtomicFree(pset);
504 
505   if (!test_only && mode_.needs_modeset) {
506     ret = drm->DestroyPropertyBlob(mode_.old_blob_id);
507     if (ret) {
508       ALOGE("Failed to destroy old mode property blob %" PRIu32 "/%d",
509             mode_.old_blob_id, ret);
510       return ret;
511     }
512 
513     /* TODO: Add dpms to the pset when the kernel supports it */
514     ret = ApplyDpms(display_comp);
515     if (ret) {
516       ALOGE("Failed to apply DPMS after modeset %d\n", ret);
517       return ret;
518     }
519 
520     connector->set_active_mode(mode_.mode);
521     mode_.old_blob_id = mode_.blob_id;
522     mode_.blob_id = 0;
523     mode_.needs_modeset = false;
524   }
525 
526   if (crtc->out_fence_ptr_property().id()) {
527     display_comp->set_out_fence((int)out_fences[crtc->pipe()]);
528   }
529 
530   return ret;
531 }
532 
ApplyDpms(DrmDisplayComposition * display_comp)533 int DrmDisplayCompositor::ApplyDpms(DrmDisplayComposition *display_comp) {
534   DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
535   DrmConnector *conn = drm->GetConnectorForDisplay(display_);
536   if (!conn) {
537     ALOGE("Failed to get DrmConnector for display %d", display_);
538     return -ENODEV;
539   }
540 
541   const DrmProperty &prop = conn->dpms_property();
542   int ret = drmModeConnectorSetProperty(drm->fd(), conn->id(), prop.id(),
543                                         display_comp->dpms_mode());
544   if (ret) {
545     ALOGE("Failed to set DPMS property for connector %d", conn->id());
546     return ret;
547   }
548   return 0;
549 }
550 
CreateModeBlob(const DrmMode & mode)551 std::tuple<int, uint32_t> DrmDisplayCompositor::CreateModeBlob(
552     const DrmMode &mode) {
553   struct drm_mode_modeinfo drm_mode;
554   memset(&drm_mode, 0, sizeof(drm_mode));
555   mode.ToDrmModeModeInfo(&drm_mode);
556 
557   uint32_t id = 0;
558   DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
559   int ret = drm->CreatePropertyBlob(&drm_mode, sizeof(struct drm_mode_modeinfo),
560                                     &id);
561   if (ret) {
562     ALOGE("Failed to create mode property blob %d", ret);
563     return std::make_tuple(ret, 0);
564   }
565   ALOGE("Create blob_id %" PRIu32 "\n", id);
566   return std::make_tuple(ret, id);
567 }
568 
ClearDisplay()569 void DrmDisplayCompositor::ClearDisplay() {
570   if (!active_composition_)
571     return;
572 
573   if (DisablePlanes(active_composition_.get()))
574     return;
575 
576   active_composition_.reset(NULL);
577   vsync_worker_.VSyncControl(false);
578 }
579 
ApplyFrame(std::unique_ptr<DrmDisplayComposition> composition,int status,bool writeback)580 void DrmDisplayCompositor::ApplyFrame(
581     std::unique_ptr<DrmDisplayComposition> composition, int status,
582     bool writeback) {
583   AutoLock lock(&lock_, __func__);
584   if (lock.Lock())
585     return;
586   int ret = status;
587 
588   if (!ret) {
589     if (writeback && !CountdownExpired()) {
590       ALOGE("Abort playing back scene");
591       return;
592     }
593     ret = CommitFrame(composition.get(), false);
594   }
595 
596   if (ret) {
597     ALOGE("Composite failed for display %d", display_);
598     // Disable the hw used by the last active composition. This allows us to
599     // signal the release fences from that composition to avoid hanging.
600     ClearDisplay();
601     return;
602   }
603   ++dump_frames_composited_;
604 
605   active_composition_.swap(composition);
606 
607   flatten_countdown_ = FLATTEN_COUNTDOWN_INIT;
608   vsync_worker_.VSyncControl(!writeback);
609 }
610 
ApplyComposition(std::unique_ptr<DrmDisplayComposition> composition)611 int DrmDisplayCompositor::ApplyComposition(
612     std::unique_ptr<DrmDisplayComposition> composition) {
613   int ret = 0;
614   switch (composition->type()) {
615     case DRM_COMPOSITION_TYPE_FRAME:
616       if (composition->geometry_changed()) {
617         // Send the composition to the kernel to ensure we can commit it. This
618         // is just a test, it won't actually commit the frame.
619         ret = CommitFrame(composition.get(), true);
620         if (ret) {
621           ALOGE("Commit test failed for display %d, FIXME", display_);
622           return ret;
623         }
624       }
625 
626       ApplyFrame(std::move(composition), ret);
627       break;
628     case DRM_COMPOSITION_TYPE_DPMS:
629       active_ = (composition->dpms_mode() == DRM_MODE_DPMS_ON);
630       ret = ApplyDpms(composition.get());
631       if (ret)
632         ALOGE("Failed to apply dpms for display %d", display_);
633       return ret;
634     case DRM_COMPOSITION_TYPE_MODESET:
635       mode_.mode = composition->display_mode();
636       if (mode_.blob_id)
637         resource_manager_->GetDrmDevice(display_)->DestroyPropertyBlob(
638             mode_.blob_id);
639       std::tie(ret, mode_.blob_id) = CreateModeBlob(mode_.mode);
640       if (ret) {
641         ALOGE("Failed to create mode blob for display %d", display_);
642         return ret;
643       }
644       mode_.needs_modeset = true;
645       return 0;
646     default:
647       ALOGE("Unknown composition type %d", composition->type());
648       return -EINVAL;
649   }
650 
651   return ret;
652 }
653 
TestComposition(DrmDisplayComposition * composition)654 int DrmDisplayCompositor::TestComposition(DrmDisplayComposition *composition) {
655   return CommitFrame(composition, true);
656 }
657 
658 // Flatten a scene on the display by using a writeback connector
659 // and returns the composition result as a DrmHwcLayer.
FlattenOnDisplay(std::unique_ptr<DrmDisplayComposition> & src,DrmConnector * writeback_conn,DrmMode & src_mode,DrmHwcLayer * writeback_layer)660 int DrmDisplayCompositor::FlattenOnDisplay(
661     std::unique_ptr<DrmDisplayComposition> &src, DrmConnector *writeback_conn,
662     DrmMode &src_mode, DrmHwcLayer *writeback_layer) {
663   int ret = 0;
664   DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
665   ret = writeback_conn->UpdateModes();
666   if (ret) {
667     ALOGE("Failed to update modes %d", ret);
668     return ret;
669   }
670   for (const DrmMode &mode : writeback_conn->modes()) {
671     if (mode.h_display() == src_mode.h_display() &&
672         mode.v_display() == src_mode.v_display()) {
673       mode_.mode = mode;
674       if (mode_.blob_id)
675         drm->DestroyPropertyBlob(mode_.blob_id);
676       std::tie(ret, mode_.blob_id) = CreateModeBlob(mode_.mode);
677       if (ret) {
678         ALOGE("Failed to create mode blob for display %d", display_);
679         return ret;
680       }
681       mode_.needs_modeset = true;
682       break;
683     }
684   }
685   if (mode_.blob_id <= 0) {
686     ALOGE("Failed to find similar mode");
687     return -EINVAL;
688   }
689 
690   DrmCrtc *crtc = drm->GetCrtcForDisplay(display_);
691   if (!crtc) {
692     ALOGE("Failed to find crtc for display %d", display_);
693     return -EINVAL;
694   }
695   // TODO what happens if planes could go to both CRTCs, I don't think it's
696   // handled anywhere
697   std::vector<DrmPlane *> primary_planes;
698   std::vector<DrmPlane *> overlay_planes;
699   for (auto &plane : drm->planes()) {
700     if (!plane->GetCrtcSupported(*crtc))
701       continue;
702     if (plane->type() == DRM_PLANE_TYPE_PRIMARY)
703       primary_planes.push_back(plane.get());
704     else if (plane->type() == DRM_PLANE_TYPE_OVERLAY)
705       overlay_planes.push_back(plane.get());
706   }
707 
708   ret = src->Plan(&primary_planes, &overlay_planes);
709   if (ret) {
710     ALOGE("Failed to plan the composition ret = %d", ret);
711     return ret;
712   }
713 
714   // Disable the planes we're not using
715   for (auto i = primary_planes.begin(); i != primary_planes.end();) {
716     src->AddPlaneDisable(*i);
717     i = primary_planes.erase(i);
718   }
719   for (auto i = overlay_planes.begin(); i != overlay_planes.end();) {
720     src->AddPlaneDisable(*i);
721     i = overlay_planes.erase(i);
722   }
723 
724   AutoLock lock(&lock_, __func__);
725   ret = lock.Lock();
726   if (ret)
727     return ret;
728   DrmFramebuffer *writeback_fb = &framebuffers_[framebuffer_index_];
729   framebuffer_index_ = (framebuffer_index_ + 1) % DRM_DISPLAY_BUFFERS;
730   if (!writeback_fb->Allocate(mode_.mode.h_display(), mode_.mode.v_display())) {
731     ALOGE("Failed to allocate writeback buffer");
732     return -ENOMEM;
733   }
734   DrmHwcBuffer *writeback_buffer = &writeback_layer->buffer;
735   writeback_layer->sf_handle = writeback_fb->buffer()->handle;
736   ret = writeback_layer->ImportBuffer(
737       resource_manager_->GetImporter(display_).get());
738   if (ret) {
739     ALOGE("Failed to import writeback buffer");
740     return ret;
741   }
742 
743   ret = CommitFrame(src.get(), true, writeback_conn, writeback_buffer);
744   if (ret) {
745     ALOGE("Atomic check failed");
746     return ret;
747   }
748   ret = CommitFrame(src.get(), false, writeback_conn, writeback_buffer);
749   if (ret) {
750     ALOGE("Atomic commit failed");
751     return ret;
752   }
753 
754   ret = sync_wait(writeback_fence_, kWaitWritebackFence);
755   writeback_layer->acquire_fence.Set(writeback_fence_);
756   writeback_fence_ = -1;
757   if (ret) {
758     ALOGE("Failed to wait on writeback fence");
759     return ret;
760   }
761   return 0;
762 }
763 
764 // Flatten a scene by enabling the writeback connector attached
765 // to the same CRTC as the one driving the display.
FlattenSerial(DrmConnector * writeback_conn)766 int DrmDisplayCompositor::FlattenSerial(DrmConnector *writeback_conn) {
767   ALOGV("FlattenSerial by enabling writeback connector to the same crtc");
768   // Flattened composition with only one layer that is obtained
769   // using the writeback connector
770   std::unique_ptr<DrmDisplayComposition>
771       writeback_comp = CreateInitializedComposition();
772   if (!writeback_comp)
773     return -EINVAL;
774 
775   AutoLock lock(&lock_, __func__);
776   int ret = lock.Lock();
777   if (ret)
778     return ret;
779   if (!CountdownExpired() || active_composition_->layers().size() < 2) {
780     ALOGV("Flattening is not needed");
781     return -EALREADY;
782   }
783 
784   DrmFramebuffer *writeback_fb = &framebuffers_[framebuffer_index_];
785   framebuffer_index_ = (framebuffer_index_ + 1) % DRM_DISPLAY_BUFFERS;
786   lock.Unlock();
787 
788   if (!writeback_fb->Allocate(mode_.mode.h_display(), mode_.mode.v_display())) {
789     ALOGE("Failed to allocate writeback buffer");
790     return -ENOMEM;
791   }
792   writeback_comp->layers().emplace_back();
793 
794   DrmHwcLayer &writeback_layer = writeback_comp->layers().back();
795   writeback_layer.sf_handle = writeback_fb->buffer()->handle;
796   writeback_layer.source_crop = {0, 0, (float)mode_.mode.h_display(),
797                                  (float)mode_.mode.v_display()};
798   writeback_layer.display_frame = {0, 0, (int)mode_.mode.h_display(),
799                                    (int)mode_.mode.v_display()};
800   ret = writeback_layer.ImportBuffer(
801       resource_manager_->GetImporter(display_).get());
802   if (ret || writeback_comp->layers().size() != 1) {
803     ALOGE("Failed to import writeback buffer");
804     return ret;
805   }
806 
807   drmModeAtomicReqPtr pset = drmModeAtomicAlloc();
808   if (!pset) {
809     ALOGE("Failed to allocate property set");
810     return -ENOMEM;
811   }
812   DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
813   DrmCrtc *crtc = drm->GetCrtcForDisplay(display_);
814   if (!crtc) {
815     ALOGE("Failed to find crtc for display %d", display_);
816     return -EINVAL;
817   }
818   ret = SetupWritebackCommit(pset, crtc->id(), writeback_conn,
819                              &writeback_layer.buffer);
820   if (ret < 0) {
821     ALOGE("Failed to Setup Writeback Commit");
822     return ret;
823   }
824   ret = drmModeAtomicCommit(drm->fd(), pset, 0, drm);
825   if (ret) {
826     ALOGE("Failed to enable writeback %d", ret);
827     return ret;
828   }
829   ret = sync_wait(writeback_fence_, kWaitWritebackFence);
830   writeback_layer.acquire_fence.Set(writeback_fence_);
831   writeback_fence_ = -1;
832   if (ret) {
833     ALOGE("Failed to wait on writeback fence");
834     return ret;
835   }
836 
837   DrmCompositionPlane squashed_comp(DrmCompositionPlane::Type::kLayer, NULL,
838                                     crtc);
839   for (auto &drmplane : drm->planes()) {
840     if (!drmplane->GetCrtcSupported(*crtc))
841       continue;
842     if (!squashed_comp.plane() && drmplane->type() == DRM_PLANE_TYPE_PRIMARY)
843       squashed_comp.set_plane(drmplane.get());
844     else
845       writeback_comp->AddPlaneDisable(drmplane.get());
846   }
847   squashed_comp.source_layers().push_back(0);
848   ret = writeback_comp->AddPlaneComposition(std::move(squashed_comp));
849   if (ret) {
850     ALOGE("Failed to add flatten scene");
851     return ret;
852   }
853 
854   ApplyFrame(std::move(writeback_comp), 0, true);
855   return 0;
856 }
857 
858 // Flatten a scene by using a crtc which works concurrent with
859 // the one driving the display.
FlattenConcurrent(DrmConnector * writeback_conn)860 int DrmDisplayCompositor::FlattenConcurrent(DrmConnector *writeback_conn) {
861   ALOGV("FlattenConcurrent by using an unused crtc/display");
862   int ret = 0;
863   DrmDisplayCompositor drmdisplaycompositor;
864   ret = drmdisplaycompositor.Init(resource_manager_, writeback_conn->display());
865   if (ret) {
866     ALOGE("Failed to init  drmdisplaycompositor = %d", ret);
867     return ret;
868   }
869   // Copy of the active_composition, needed because of two things:
870   // 1) Not to hold the lock for the whole time we are accessing
871   //    active_composition
872   // 2) It will be committed on a crtc that might not be on the same
873   //     dri node, so buffers need to be imported on the right node.
874   std::unique_ptr<DrmDisplayComposition>
875       copy_comp = drmdisplaycompositor.CreateInitializedComposition();
876 
877   // Writeback composition that will be committed to the display.
878   std::unique_ptr<DrmDisplayComposition>
879       writeback_comp = CreateInitializedComposition();
880 
881   if (!copy_comp || !writeback_comp)
882     return -EINVAL;
883   AutoLock lock(&lock_, __func__);
884   ret = lock.Lock();
885   if (ret)
886     return ret;
887   if (!CountdownExpired() || active_composition_->layers().size() < 2) {
888     ALOGV("Flattening is not needed");
889     return -EALREADY;
890   }
891   DrmCrtc *crtc = active_composition_->crtc();
892 
893   std::vector<DrmHwcLayer> copy_layers;
894   for (DrmHwcLayer &src_layer : active_composition_->layers()) {
895     DrmHwcLayer copy;
896     ret = copy.InitFromDrmHwcLayer(&src_layer,
897                                    resource_manager_
898                                        ->GetImporter(writeback_conn->display())
899                                        .get());
900     if (ret) {
901       ALOGE("Failed to import buffer ret = %d", ret);
902       return -EINVAL;
903     }
904     copy_layers.emplace_back(std::move(copy));
905   }
906   ret = copy_comp->SetLayers(copy_layers.data(), copy_layers.size(), true);
907   if (ret) {
908     ALOGE("Failed to set copy_comp layers");
909     return ret;
910   }
911 
912   lock.Unlock();
913   DrmHwcLayer writeback_layer;
914   ret = drmdisplaycompositor.FlattenOnDisplay(copy_comp, writeback_conn,
915                                               mode_.mode, &writeback_layer);
916   if (ret) {
917     ALOGE("Failed to flatten on display ret = %d", ret);
918     return ret;
919   }
920 
921   DrmCompositionPlane squashed_comp(DrmCompositionPlane::Type::kLayer, NULL,
922                                     crtc);
923   for (auto &drmplane : resource_manager_->GetDrmDevice(display_)->planes()) {
924     if (!drmplane->GetCrtcSupported(*crtc))
925       continue;
926     if (drmplane->type() == DRM_PLANE_TYPE_PRIMARY)
927       squashed_comp.set_plane(drmplane.get());
928     else
929       writeback_comp->AddPlaneDisable(drmplane.get());
930   }
931   writeback_comp->layers().emplace_back();
932   DrmHwcLayer &next_layer = writeback_comp->layers().back();
933   next_layer.sf_handle = writeback_layer.get_usable_handle();
934   next_layer.blending = DrmHwcBlending::kPreMult;
935   next_layer.source_crop = {0, 0, (float)mode_.mode.h_display(),
936                             (float)mode_.mode.v_display()};
937   next_layer.display_frame = {0, 0, (int)mode_.mode.h_display(),
938                               (int)mode_.mode.v_display()};
939   ret = next_layer.ImportBuffer(resource_manager_->GetImporter(display_).get());
940   if (ret) {
941     ALOGE("Failed to import framebuffer for display %d", ret);
942     return ret;
943   }
944   squashed_comp.source_layers().push_back(0);
945   ret = writeback_comp->AddPlaneComposition(std::move(squashed_comp));
946   if (ret) {
947     ALOGE("Failed to add plane composition %d", ret);
948     return ret;
949   }
950   ApplyFrame(std::move(writeback_comp), 0, true);
951   return ret;
952 }
953 
FlattenActiveComposition()954 int DrmDisplayCompositor::FlattenActiveComposition() {
955   DrmConnector *writeback_conn = resource_manager_->AvailableWritebackConnector(
956       display_);
957   if (!active_composition_ || !writeback_conn) {
958     ALOGV("No writeback connector available");
959     return -EINVAL;
960   }
961 
962   if (writeback_conn->display() != display_) {
963     return FlattenConcurrent(writeback_conn);
964   } else {
965     return FlattenSerial(writeback_conn);
966   }
967 
968   return 0;
969 }
970 
CountdownExpired() const971 bool DrmDisplayCompositor::CountdownExpired() const {
972   return flatten_countdown_ <= 0;
973 }
974 
Vsync(int display,int64_t timestamp)975 void DrmDisplayCompositor::Vsync(int display, int64_t timestamp) {
976   AutoLock lock(&lock_, __func__);
977   if (lock.Lock())
978     return;
979   flatten_countdown_--;
980   if (!CountdownExpired())
981     return;
982   lock.Unlock();
983   int ret = FlattenActiveComposition();
984   ALOGV("scene flattening triggered for display %d at timestamp %" PRIu64
985         " result = %d \n",
986         display, timestamp, ret);
987 }
988 
Dump(std::ostringstream * out) const989 void DrmDisplayCompositor::Dump(std::ostringstream *out) const {
990   int ret = pthread_mutex_lock(&lock_);
991   if (ret)
992     return;
993 
994   uint64_t num_frames = dump_frames_composited_;
995   dump_frames_composited_ = 0;
996 
997   struct timespec ts;
998   ret = clock_gettime(CLOCK_MONOTONIC, &ts);
999   if (ret) {
1000     pthread_mutex_unlock(&lock_);
1001     return;
1002   }
1003 
1004   uint64_t cur_ts = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
1005   uint64_t num_ms = (cur_ts - dump_last_timestamp_ns_) / (1000 * 1000);
1006   float fps = num_ms ? (num_frames * 1000.0f) / (num_ms) : 0.0f;
1007 
1008   *out << "--DrmDisplayCompositor[" << display_
1009        << "]: num_frames=" << num_frames << " num_ms=" << num_ms
1010        << " fps=" << fps << "\n";
1011 
1012   dump_last_timestamp_ns_ = cur_ts;
1013 
1014   pthread_mutex_unlock(&lock_);
1015 }
1016 }  // namespace android
1017