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