OSDN Git Service

drm_hwcomposer: implement the safe handling of layers
[android-x86/external-drm_hwcomposer.git] / drmdisplaycompositor.cpp
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 #include "drmcrtc.h"
22 #include "drmplane.h"
23 #include "drmresources.h"
24 #include "glworker.h"
25
26 #include <algorithm>
27 #include <pthread.h>
28 #include <sched.h>
29 #include <sstream>
30 #include <stdlib.h>
31 #include <time.h>
32 #include <vector>
33
34 #include <drm/drm_mode.h>
35 #include <cutils/log.h>
36 #include <sync/sync.h>
37 #include <utils/Trace.h>
38
39 #define DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH 3
40
41 namespace android {
42
43 DrmDisplayCompositor::DrmDisplayCompositor()
44     : drm_(NULL),
45       display_(-1),
46       worker_(this),
47       initialized_(false),
48       active_(false),
49       framebuffer_index_(0),
50       dump_frames_composited_(0),
51       dump_last_timestamp_ns_(0) {
52   struct timespec ts;
53   if (clock_gettime(CLOCK_MONOTONIC, &ts))
54     return;
55   dump_last_timestamp_ns_ = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
56 }
57
58 DrmDisplayCompositor::~DrmDisplayCompositor() {
59   if (!initialized_)
60     return;
61
62   worker_.Exit();
63
64   int ret = pthread_mutex_lock(&lock_);
65   if (ret)
66     ALOGE("Failed to acquire compositor lock %d", ret);
67
68   while (!composite_queue_.empty()) {
69     composite_queue_.front().reset();
70     composite_queue_.pop();
71   }
72   active_composition_.reset();
73
74   ret = pthread_mutex_unlock(&lock_);
75   if (ret)
76     ALOGE("Failed to acquire compositor lock %d", ret);
77
78   pthread_mutex_destroy(&lock_);
79 }
80
81 int DrmDisplayCompositor::Init(DrmResources *drm, int display) {
82   drm_ = drm;
83   display_ = display;
84
85   int ret = pthread_mutex_init(&lock_, NULL);
86   if (ret) {
87     ALOGE("Failed to initialize drm compositor lock %d\n", ret);
88     return ret;
89   }
90   ret = worker_.Init();
91   if (ret) {
92     pthread_mutex_destroy(&lock_);
93     ALOGE("Failed to initialize compositor worker %d\n", ret);
94     return ret;
95   }
96
97   initialized_ = true;
98   return 0;
99 }
100
101 int DrmDisplayCompositor::QueueComposition(
102     std::unique_ptr<DrmDisplayComposition> composition) {
103   switch (composition->type()) {
104     case DRM_COMPOSITION_TYPE_FRAME:
105       if (!active_)
106         return -ENODEV;
107       break;
108     case DRM_COMPOSITION_TYPE_DPMS:
109       /*
110        * Update the state as soon as we get it so we can start/stop queuing
111        * frames asap.
112        */
113       active_ = (composition->dpms_mode() == DRM_MODE_DPMS_ON);
114       break;
115     case DRM_COMPOSITION_TYPE_EMPTY:
116       return 0;
117     default:
118       ALOGE("Unknown composition type %d/%d", composition->type(), display_);
119       return -ENOENT;
120   }
121
122   int ret = pthread_mutex_lock(&lock_);
123   if (ret) {
124     ALOGE("Failed to acquire compositor lock %d", ret);
125     return ret;
126   }
127
128   // Block the queue if it gets too large. Otherwise, SurfaceFlinger will start
129   // to eat our buffer handles when we get about 1 second behind.
130   while (composite_queue_.size() >= DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH) {
131     pthread_mutex_unlock(&lock_);
132     sched_yield();
133     pthread_mutex_lock(&lock_);
134   }
135
136   composite_queue_.push(std::move(composition));
137
138   ret = pthread_mutex_unlock(&lock_);
139   if (ret) {
140     ALOGE("Failed to release compositor lock %d", ret);
141     return ret;
142   }
143
144   worker_.Signal();
145   return 0;
146 }
147
148 static bool drm_composition_layer_has_plane(
149     const DrmCompositionLayer &comp_layer) {
150   if (comp_layer.plane != NULL)
151     if (comp_layer.plane->type() == DRM_PLANE_TYPE_OVERLAY ||
152         comp_layer.plane->type() == DRM_PLANE_TYPE_PRIMARY)
153       return true;
154   return false;
155 }
156
157 static bool drm_composition_layer_has_no_plane(
158     const DrmCompositionLayer &comp_layer) {
159   return comp_layer.plane == NULL;
160 }
161
162 int DrmDisplayCompositor::ApplyPreComposite(
163     DrmDisplayComposition *display_comp) {
164   int ret = 0;
165   std::vector<DrmCompositionLayer> *layers =
166       display_comp->GetCompositionLayers();
167
168   DrmConnector *connector = drm_->GetConnectorForDisplay(display_);
169   if (connector == NULL) {
170     ALOGE("Failed to determine display mode: no connector for display %d",
171           display_);
172     return -ENODEV;
173   }
174
175   const DrmMode &mode = connector->active_mode();
176   DrmFramebuffer &fb = framebuffers_[framebuffer_index_];
177   ret = fb.WaitReleased(-1);
178   if (ret) {
179     ALOGE("Failed to wait for framebuffer release %d", ret);
180     return ret;
181   }
182   fb.set_release_fence_fd(-1);
183   if (!fb.Allocate(mode.h_display(), mode.v_display())) {
184     ALOGE("Failed to allocate framebuffer with size %dx%d", mode.h_display(),
185           mode.v_display());
186     return -ENOMEM;
187   }
188
189   std::vector<DrmCompositionLayer> pre_comp_layers;
190   for (auto &comp_layer : *layers) {
191     if (comp_layer.plane == NULL) {
192       pre_comp_layers.emplace_back(std::move(comp_layer));
193     }
194   }
195
196   ret = pre_compositor_->Composite(pre_comp_layers.data(),
197                                    pre_comp_layers.size(), fb.buffer());
198   pre_compositor_->Finish();
199
200   if (ret) {
201     ALOGE("Failed to composite layers");
202     return ret;
203   }
204
205   DrmCompositionLayer &pre_comp_layer =
206       layers->at(display_comp->pre_composition_layer_index());
207   ret = pre_comp_layer.buffer.ImportBuffer(fb.buffer()->handle,
208                                            display_comp->importer());
209   if (ret) {
210     ALOGE("Failed to import handle of layer %d", ret);
211     return ret;
212   }
213   pre_comp_layer.source_crop = DrmHwcRect<float>(0, 0, fb.buffer()->getWidth(),
214                                                  fb.buffer()->getHeight());
215   pre_comp_layer.display_frame =
216       DrmHwcRect<int>(0, 0, fb.buffer()->getWidth(), fb.buffer()->getHeight());
217
218   // TODO(zachr) get a release fence
219   // fb.set_release_fence_fd(pre_comp_layer.release_fence.Release());
220   framebuffer_index_ = (framebuffer_index_ + 1) % DRM_DISPLAY_BUFFERS;
221
222   display_comp->RemoveNoPlaneLayers();
223   display_comp->SignalPreCompositionDone();
224   return ret;
225 }
226
227 int DrmDisplayCompositor::ApplyFrame(DrmDisplayComposition *display_comp) {
228   int ret = 0;
229
230   if (display_comp->pre_composition_layer_index() >= 0) {
231     ret = ApplyPreComposite(display_comp);
232     if (ret)
233       return ret;
234   }
235
236   drmModePropertySetPtr pset = drmModePropertySetAlloc();
237   if (!pset) {
238     ALOGE("Failed to allocate property set");
239     return -ENOMEM;
240   }
241
242   std::vector<DrmCompositionLayer> *layers =
243       display_comp->GetCompositionLayers();
244   for (DrmCompositionLayer &layer : *layers) {
245     int acquire_fence = layer.acquire_fence.get();
246     if (acquire_fence >= 0) {
247       ret = sync_wait(acquire_fence, -1);
248       if (ret) {
249         ALOGE("Failed to wait for acquire %d/%d", acquire_fence, ret);
250         drmModePropertySetFree(pset);
251         return ret;
252       }
253       layer.acquire_fence.Close();
254     }
255
256     DrmPlane *plane = layer.plane;
257     DrmCrtc *crtc = layer.crtc;
258
259     // Disable the plane if there's no crtc
260     if (!crtc) {
261       ret = drmModePropertySetAdd(pset, plane->id(),
262                                   plane->crtc_property().id(), 0) ||
263             drmModePropertySetAdd(pset, plane->id(), plane->fb_property().id(),
264                                   0);
265       if (ret) {
266         ALOGE("Failed to add plane %d disable to pset", plane->id());
267         break;
268       }
269       continue;
270     }
271
272     if (!layer.buffer) {
273       ALOGE("Expected a valid framebuffer for pset");
274       ret = -EINVAL;
275       break;
276     }
277
278     uint64_t rotation;
279     switch (layer.transform) {
280       case DrmHwcTransform::kFlipH:
281         rotation = 1 << DRM_REFLECT_X;
282         break;
283       case DrmHwcTransform::kFlipV:
284         rotation = 1 << DRM_REFLECT_Y;
285         break;
286       case DrmHwcTransform::kRotate90:
287         rotation = 1 << DRM_ROTATE_90;
288         break;
289       case DrmHwcTransform::kRotate180:
290         rotation = 1 << DRM_ROTATE_180;
291         break;
292       case DrmHwcTransform::kRotate270:
293         rotation = 1 << DRM_ROTATE_270;
294         break;
295       case DrmHwcTransform::kIdentity:
296         rotation = 0;
297         break;
298       default:
299         ALOGE("Invalid transform value 0x%x given", layer.transform);
300         ret = -EINVAL;
301         break;
302     }
303     if (ret)
304       break;
305
306     // TODO: Once we have atomic test, this should fall back to GL
307     if (rotation && plane->rotation_property().id() == 0) {
308       ALOGE("Rotation is not supported on plane %d", plane->id());
309       ret = -EINVAL;
310       break;
311     }
312
313     ret =
314         drmModePropertySetAdd(pset, plane->id(), plane->crtc_property().id(),
315                               crtc->id()) ||
316         drmModePropertySetAdd(pset, plane->id(), plane->fb_property().id(),
317                               layer.buffer->fb_id) ||
318         drmModePropertySetAdd(pset, plane->id(), plane->crtc_x_property().id(),
319                               layer.display_frame.left) ||
320         drmModePropertySetAdd(pset, plane->id(), plane->crtc_y_property().id(),
321                               layer.display_frame.top) ||
322         drmModePropertySetAdd(
323             pset, plane->id(), plane->crtc_w_property().id(),
324             layer.display_frame.right - layer.display_frame.left) ||
325         drmModePropertySetAdd(
326             pset, plane->id(), plane->crtc_h_property().id(),
327             layer.display_frame.bottom - layer.display_frame.top) ||
328         drmModePropertySetAdd(pset, plane->id(), plane->src_x_property().id(),
329                               (int)(layer.source_crop.left) << 16) ||
330         drmModePropertySetAdd(pset, plane->id(), plane->src_y_property().id(),
331                               (int)(layer.source_crop.top) << 16) ||
332         drmModePropertySetAdd(
333             pset, plane->id(), plane->src_w_property().id(),
334             (int)(layer.source_crop.right - layer.source_crop.left) << 16) ||
335         drmModePropertySetAdd(
336             pset, plane->id(), plane->src_h_property().id(),
337             (int)(layer.source_crop.bottom - layer.source_crop.top) << 16);
338     if (ret) {
339       ALOGE("Failed to add plane %d to set", plane->id());
340       break;
341     }
342
343     if (plane->rotation_property().id()) {
344       ret = drmModePropertySetAdd(pset, plane->id(),
345                                   plane->rotation_property().id(), rotation);
346       if (ret) {
347         ALOGE("Failed to add rotation property %d to plane %d",
348               plane->rotation_property().id(), plane->id());
349         break;
350       }
351     }
352   }
353
354   if (!ret) {
355     ret = drmModePropertySetCommit(drm_->fd(), 0, drm_, pset);
356     if (ret)
357       ALOGE("Failed to commit pset ret=%d\n", ret);
358   }
359   if (pset)
360     drmModePropertySetFree(pset);
361
362   return ret;
363 }
364
365 int DrmDisplayCompositor::ApplyDpms(DrmDisplayComposition *display_comp) {
366   DrmConnector *conn = drm_->GetConnectorForDisplay(display_);
367   if (!conn) {
368     ALOGE("Failed to get DrmConnector for display %d", display_);
369     return -ENODEV;
370   }
371
372   const DrmProperty &prop = conn->dpms_property();
373   int ret = drmModeConnectorSetProperty(drm_->fd(), conn->id(), prop.id(),
374                                         display_comp->dpms_mode());
375   if (ret) {
376     ALOGE("Failed to set DPMS property for connector %d", conn->id());
377     return ret;
378   }
379   return 0;
380 }
381
382 int DrmDisplayCompositor::Composite() {
383   ATRACE_CALL();
384
385   if (!pre_compositor_) {
386     pre_compositor_.reset(new GLWorkerCompositor());
387     int ret = pre_compositor_->Init();
388     if (ret) {
389       ALOGE("Failed to initialize OpenGL compositor %d", ret);
390       return ret;
391     }
392   }
393
394   int ret = pthread_mutex_lock(&lock_);
395   if (ret) {
396     ALOGE("Failed to acquire compositor lock %d", ret);
397     return ret;
398   }
399   if (composite_queue_.empty()) {
400     ret = pthread_mutex_unlock(&lock_);
401     if (ret)
402       ALOGE("Failed to release compositor lock %d", ret);
403     return ret;
404   }
405
406   std::unique_ptr<DrmDisplayComposition> composition(
407       std::move(composite_queue_.front()));
408
409   composite_queue_.pop();
410
411   ret = pthread_mutex_unlock(&lock_);
412   if (ret) {
413     ALOGE("Failed to release compositor lock %d", ret);
414     return ret;
415   }
416
417   switch (composition->type()) {
418     case DRM_COMPOSITION_TYPE_FRAME:
419       ret = ApplyFrame(composition.get());
420       if (ret) {
421         ALOGE("Composite failed for display %d", display_);
422         return ret;
423       }
424       ++dump_frames_composited_;
425       break;
426     case DRM_COMPOSITION_TYPE_DPMS:
427       ret = ApplyDpms(composition.get());
428       if (ret)
429         ALOGE("Failed to apply dpms for display %d", display_);
430       return ret;
431     default:
432       ALOGE("Unknown composition type %d", composition->type());
433       return -EINVAL;
434   }
435
436   if (active_composition_)
437     active_composition_->FinishComposition();
438
439   ret = pthread_mutex_lock(&lock_);
440   if (ret)
441     ALOGE("Failed to acquire lock for active_composition swap");
442
443   active_composition_.swap(composition);
444
445   if (!ret)
446     ret = pthread_mutex_unlock(&lock_);
447   if (ret)
448     ALOGE("Failed to release lock for active_composition swap");
449
450   return ret;
451 }
452
453 bool DrmDisplayCompositor::HaveQueuedComposites() const {
454   int ret = pthread_mutex_lock(&lock_);
455   if (ret) {
456     ALOGE("Failed to acquire compositor lock %d", ret);
457     return false;
458   }
459
460   bool empty_ret = !composite_queue_.empty();
461
462   ret = pthread_mutex_unlock(&lock_);
463   if (ret) {
464     ALOGE("Failed to release compositor lock %d", ret);
465     return false;
466   }
467
468   return empty_ret;
469 }
470
471 struct DrmDumpLayer {
472   int plane_id;
473   int crtc_id;
474   DrmHwcTransform transform;
475   DrmHwcRect<float> source_crop;
476   DrmHwcRect<int> display_frame;
477
478   DrmDumpLayer(DrmCompositionLayer &rhs)
479       : plane_id(rhs.plane->id()),
480         crtc_id(rhs.crtc ? rhs.crtc->id() : -1),
481         transform(rhs.transform),
482         source_crop(rhs.source_crop),
483         display_frame(rhs.display_frame) {
484   }
485 };
486
487 void DrmDisplayCompositor::Dump(std::ostringstream *out) const {
488   uint64_t cur_ts;
489
490   int ret = pthread_mutex_lock(&lock_);
491   if (ret)
492     return;
493
494   uint64_t num_frames = dump_frames_composited_;
495   dump_frames_composited_ = 0;
496
497   struct timespec ts;
498   ret = clock_gettime(CLOCK_MONOTONIC, &ts);
499
500   std::vector<DrmCompositionLayer> *input_layers =
501       active_composition_->GetCompositionLayers();
502   std::vector<DrmDumpLayer> layers;
503   if (active_composition_)
504     layers.insert(layers.begin(), input_layers->begin(), input_layers->end());
505   else
506     ret = -EAGAIN;
507
508   ret |= pthread_mutex_unlock(&lock_);
509   if (ret)
510     return;
511
512   cur_ts = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
513   uint64_t num_ms = (cur_ts - dump_last_timestamp_ns_) / (1000 * 1000);
514   float fps = num_ms ? (num_frames * 1000.0f) / (num_ms) : 0.0f;
515
516   *out << "--DrmDisplayCompositor[" << display_
517        << "]: num_frames=" << num_frames << " num_ms=" << num_ms
518        << " fps=" << fps << "\n";
519
520   dump_last_timestamp_ns_ = cur_ts;
521
522   *out << "---- DrmDisplayCompositor Layers: num=" << layers.size() << "\n";
523   for (std::vector<DrmDumpLayer>::iterator iter = layers.begin();
524        iter != layers.end(); ++iter) {
525     *out << "------ DrmDisplayCompositor Layer: plane=" << iter->plane_id
526          << " ";
527
528     if (iter->crtc_id < 0) {
529       *out << "disabled\n";
530       continue;
531     }
532
533     *out << "crtc=" << iter->crtc_id
534          << " crtc[x/y/w/h]=" << iter->display_frame.left << "/"
535          << iter->display_frame.top << "/"
536          << iter->display_frame.right - iter->display_frame.left << "/"
537          << iter->display_frame.bottom - iter->display_frame.top << " "
538          << " src[x/y/w/h]=" << iter->source_crop.left << "/"
539          << iter->source_crop.top << "/"
540          << iter->source_crop.right - iter->source_crop.left << "/"
541          << iter->source_crop.bottom - iter->source_crop.top
542          << " transform=" << (uint32_t)iter->transform << "\n";
543   }
544 }
545 }