• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 #include <chrono>
18 #include <ctime>
19 #include <iomanip>
20 #include <fcntl.h>
21 #include <fstream>
22 #include <log/log.h>
23 #include <memory>
24 #include <sstream>
25 #include <sys/epoll.h>
26 #include <sys/prctl.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <tuple>
30 #include <unistd.h>
31 #include <unordered_map>
32 #include <vector>
33 
34 #include <drm/msm_drm.h>
35 #include <drm/msm_drm_pp.h>
36 #include <xf86drm.h>
37 #include <xf86drmMode.h>
38 
39 #include "histogram_collector.h"
40 #include "ringbuffer.h"
41 
42 namespace {
43 
44 class ManagedFd
45 {
46 public:
create(int fd)47     static std::unique_ptr<ManagedFd> create(int fd) {
48         if (fd < 0)
49             return nullptr;
50         return std::unique_ptr<ManagedFd>(new ManagedFd(fd));
51     }
52 
~ManagedFd()53     ~ManagedFd() {
54         close(drmfd_);
55     }
56 
operator int() const57     operator int() const {
58         return drmfd_;
59     }
60 
61 private:
62     ManagedFd(ManagedFd const&) = delete;
63     ManagedFd& operator=(ManagedFd const&) = delete;
64 
ManagedFd(int fd)65     ManagedFd(int fd) : drmfd_(fd) {
66     }
67     int const drmfd_ = -1;
68 };
69 
70 class DrmResources
71 {
72 public:
create(int drm_fd)73     static std::unique_ptr<DrmResources> create(int drm_fd) {
74         auto resources = drmModeGetResources(drm_fd);
75         if (!resources || !resources->connectors || !resources->crtcs || !resources->encoders) {
76             return nullptr;
77         }
78         return std::unique_ptr<DrmResources>(new DrmResources(drm_fd, resources));
79     }
80 
~DrmResources()81     ~DrmResources() {
82         for (auto encoder : encoders_)
83             drmModeFreeEncoder(encoder.second);
84         for (auto crtc : crtcs_ )
85             drmModeFreeCrtc(crtc.second);
86         for (auto connector : connectors_)
87             drmModeFreeConnector(connector.second);
88         drmModeFreeResources(resources_);
89     }
90 
find_first_connector_of_type(uint32_t type)91     drmModeConnectorPtr find_first_connector_of_type(uint32_t type) {
92         auto connector = std::find_if(connectors_.begin(), connectors_.end(),
93             [type] (auto const& c) { return c.second->connector_type == type; });
94         if (connector != connectors_.end()) {
95             return connector->second;
96         }
97         return nullptr;
98     }
99 
find_encoder_by_connector_and_type(drmModeConnectorPtr con,uint32_t type)100     drmModeEncoderPtr find_encoder_by_connector_and_type(drmModeConnectorPtr con, uint32_t type) {
101         for (auto i = 0; i < con->count_encoders; i++) {
102             auto enc = encoders_.find(con->encoders[i]);
103             if (enc != encoders_.end() && (enc->second->encoder_type == type)) {
104                 return enc->second;
105             }
106         }
107         return nullptr;
108     }
109 
find_histogram_supporting_crtc(int fd,drmModeEncoderPtr encoder,drmModeCrtcPtr * crtc,int * histogram_ctrl,int * histogram_irq)110     bool find_histogram_supporting_crtc(int fd, drmModeEncoderPtr encoder,
111         drmModeCrtcPtr* crtc, int* histogram_ctrl, int* histogram_irq) {
112 
113         for (auto i = 0; i < resources_->count_crtcs; i++) {
114             if (!(encoder->possible_crtcs & (1 << i)))
115                 continue;
116 
117             auto it = crtcs_.find(resources_->crtcs[i]);
118             if (it == crtcs_.end()) {
119                 ALOGW("Could not find CRTC %i reported as possible by encoder %i",
120                     resources_->crtcs[i], encoder->encoder_id);
121                 continue;
122             }
123             *crtc = it->second;
124 
125             int hist_ctl_found = -1;
126             int hist_irq_found = -1;
127             auto props = drmModeObjectGetProperties(fd, (*crtc)->crtc_id, DRM_MODE_OBJECT_CRTC);
128             for (auto j = 0u; j < props->count_props; j++) {
129                 auto info = drmModeGetProperty(fd, props->props[j]);
130                 if (std::string(info->name) == "SDE_DSPP_HIST_CTRL_V1") {
131                     hist_ctl_found = props->props[j];
132                 }
133                 if (std::string(info->name) == "SDE_DSPP_HIST_IRQ_V1") {
134                     hist_irq_found = props->props[j];
135                 }
136                 drmModeFreeProperty(info);
137             }
138             drmModeFreeObjectProperties(props);
139             if ((hist_ctl_found != -1 ) && (hist_irq_found != -1)) {
140                 *histogram_ctrl = hist_ctl_found;
141                 *histogram_irq = hist_irq_found;
142                 return true;
143             }
144         }
145         return false;
146     }
147 
148 private:
149     DrmResources(DrmResources const&) = delete;
150     DrmResources& operator=(DrmResources const&) = delete;
151 
DrmResources(int drm_fd,drmModeResPtr resources)152     DrmResources(int drm_fd, drmModeResPtr resources) :
153         resources_(resources),
154         crtcs_(resources_->count_crtcs),
155         connectors_(resources_->count_connectors),
156         encoders_(resources_->count_encoders) {
157 
158         for (auto i = 0; i < resources_->count_connectors; i++) {
159             auto connector = drmModeGetConnector(drm_fd, resources_->connectors[i]);
160             connectors_[connector->connector_id] = connector;
161         }
162 
163         for (auto i = 0; i < resources_->count_crtcs; i++) {
164             auto crtc = drmModeGetCrtc(drm_fd, resources_->crtcs[i]);
165             crtcs_[crtc->crtc_id] = crtc;
166         }
167 
168         for (auto i = 0; i < resources_->count_encoders; i++) {
169             auto encoder = drmModeGetEncoder(drm_fd, resources_->encoders[i]);
170             encoders_[encoder->encoder_id] = encoder;
171         }
172     }
173 
174     drmModeResPtr resources_;
175     std::unordered_map<int, drmModeCrtcPtr> crtcs_;
176     std::unordered_map<int, drmModeConnectorPtr> connectors_;
177     std::unordered_map<int, drmModeEncoderPtr> encoders_;
178 };
179 
180 // Registering DRM_EVENT_CRTC_POWER does not trigger a notification on the DRM fd.
181 struct PowerEventRegistration
182 {
create__anondd33d3f60111::PowerEventRegistration183     static std::unique_ptr<PowerEventRegistration> create(int drm_fd, int crtc_id) {
184         auto r = std::unique_ptr<PowerEventRegistration>(new PowerEventRegistration(drm_fd, crtc_id));
185         if (drmIoctl(drm_fd, DRM_IOCTL_MSM_REGISTER_EVENT, &r->req))
186            return nullptr;
187         return r;
188     }
189 
~PowerEventRegistration__anondd33d3f60111::PowerEventRegistration190     ~PowerEventRegistration() {
191         drmIoctl(fd, DRM_IOCTL_MSM_DEREGISTER_EVENT, &req);
192     }
193 private:
194     PowerEventRegistration(PowerEventRegistration const&) = delete;
195     PowerEventRegistration operator=(PowerEventRegistration const&) = delete;
196 
PowerEventRegistration__anondd33d3f60111::PowerEventRegistration197     PowerEventRegistration(int drm_fd, int crtc_id) :
198         fd(drm_fd) {
199        req.object_id = crtc_id;
200        req.object_type = DRM_MODE_OBJECT_CRTC;
201        req.event = DRM_EVENT_CRTC_POWER;
202     }
203 
204     int const fd; //non-owning.
205     struct drm_msm_event_req req = {};
206 };
207 
208 struct HistogramRAIIEnabler
209 {
create__anondd33d3f60111::HistogramRAIIEnabler210     static std::unique_ptr<HistogramRAIIEnabler> create(int fd, int crtc_id, int histogram_prop) {
211         auto hist = std::unique_ptr<HistogramRAIIEnabler>(
212             new HistogramRAIIEnabler(fd, crtc_id, histogram_prop));
213         if (drmModeObjectSetProperty(fd, crtc_id, DRM_MODE_OBJECT_CRTC, histogram_prop, 1))
214            return nullptr;
215         return hist;
216     }
217 
~HistogramRAIIEnabler__anondd33d3f60111::HistogramRAIIEnabler218     ~HistogramRAIIEnabler() {
219        drmModeObjectSetProperty(fd, crtc_id, DRM_MODE_OBJECT_CRTC, histogram_property, 0);
220     }
221 
222 private:
223     HistogramRAIIEnabler(HistogramRAIIEnabler const&) = delete;
224     HistogramRAIIEnabler& operator=(HistogramRAIIEnabler const&) = delete;
225 
HistogramRAIIEnabler__anondd33d3f60111::HistogramRAIIEnabler226     HistogramRAIIEnabler(int fd, int crtc_id, int histogram_property) :
227         fd(fd),
228         crtc_id(crtc_id),
229         histogram_property(histogram_property) {
230     }
231 
232     int fd;
233     int crtc_id;
234     int histogram_property;
235 };
236 
237 struct EventRegistration
238 {
create__anondd33d3f60111::EventRegistration239     static std::unique_ptr<EventRegistration> create(
240         int drm_fd, int crtc_id, int histogram_property) {
241         auto reg = std::unique_ptr<EventRegistration>(
242             new EventRegistration(drm_fd, crtc_id, histogram_property));
243         if (!reg->property_registration ||
244                 drmIoctl(drm_fd, DRM_IOCTL_MSM_REGISTER_EVENT, &reg->req))
245            return nullptr;
246         return reg;
247     }
248 
~EventRegistration__anondd33d3f60111::EventRegistration249     ~EventRegistration() {
250         drmIoctl(fd, DRM_IOCTL_MSM_DEREGISTER_EVENT, &req);
251     }
252 
253 private:
EventRegistration__anondd33d3f60111::EventRegistration254     EventRegistration(int drm_fd, int crtc_id, int histogram_property) :
255         property_registration(HistogramRAIIEnabler::create(drm_fd, crtc_id, histogram_property)),
256         fd(drm_fd) {
257        req.object_id = crtc_id;
258        req.object_type = DRM_MODE_OBJECT_CRTC;
259        req.event = DRM_EVENT_HISTOGRAM;
260     }
261     EventRegistration(EventRegistration const&) = delete;
262     EventRegistration operator&(EventRegistration const&) = delete;
263 
264     //SDE_DSPP_HIST_CTRL_V1 must be turned on before receiving events
265     std::unique_ptr<HistogramRAIIEnabler> property_registration;
266     int const fd; //non-owning.
267     struct drm_msm_event_req req = {};
268 };
269 
270 //These are not the DPMS enum encodings.
271 enum class CrtcPowerState
272 {
273     OFF,
274     ON,
275     UNKNOWN
276 };
277 
278 constexpr static auto implementation_defined_max_frame_ringbuffer = 300;
279 }
280 
HistogramCollector()281 histogram::HistogramCollector::HistogramCollector() :
282     histogram(histogram::Ringbuffer::create(
283         implementation_defined_max_frame_ringbuffer, std::make_unique<histogram::DefaultTimeKeeper>())) {
284 }
285 
~HistogramCollector()286 histogram::HistogramCollector::~HistogramCollector() {
287     stop();
288 }
289 
290 namespace {
291 static constexpr size_t numBuckets = 8;
292 static_assert((HIST_V_SIZE % numBuckets) == 0,
293            "histogram cannot be rebucketed to smaller number of buckets");
294 static constexpr int bucket_compression = HIST_V_SIZE / numBuckets;
295 
rebucketTo8Buckets(std::array<uint64_t,HIST_V_SIZE> const & frame)296 std::array<uint64_t, numBuckets> rebucketTo8Buckets(std::array<uint64_t, HIST_V_SIZE> const& frame) {
297     std::array<uint64_t, numBuckets> bins;
298     bins.fill(0);
299     for (auto i = 0u; i < HIST_V_SIZE; i++)
300         bins[i / bucket_compression] += frame[i];
301     return bins;
302 }
303 }
304 
Dump() const305 std::string histogram::HistogramCollector::Dump() const {
306     uint64_t num_frames;
307     std::array<uint64_t, HIST_V_SIZE> all_sample_buckets;
308     std::tie(num_frames, all_sample_buckets) = histogram->collect_cumulative();
309     std::array<uint64_t, numBuckets> samples = rebucketTo8Buckets(all_sample_buckets);
310 
311     std::stringstream ss;
312     ss << "Color Sampling, dark (0.0) to light (1.0): sampled frames: " << num_frames << '\n';
313     if (num_frames == 0) {
314         ss << "\tno color statistics collected\n";
315         return ss.str();
316     }
317 
318     ss << std::fixed << std::setprecision(3);
319     ss << "\tbucket\t\t: # of displayed pixels at bucket value\n";
320     for (auto i = 0u; i < samples.size(); i++) {
321         ss << "\t" << i / static_cast<float>(samples.size()) <<
322               " to " << ( i + 1 ) / static_cast<float>(samples.size()) << "\t: " <<
323               samples[i] << '\n';
324     }
325 
326     return ss.str();
327 }
328 
collect(uint64_t max_frames,uint64_t timestamp,int32_t out_samples_size[NUM_HISTOGRAM_COLOR_COMPONENTS],uint64_t * out_samples[NUM_HISTOGRAM_COLOR_COMPONENTS],uint64_t * out_num_frames) const329 HWC2::Error histogram::HistogramCollector::collect(
330     uint64_t max_frames,
331     uint64_t timestamp,
332     int32_t out_samples_size[NUM_HISTOGRAM_COLOR_COMPONENTS],
333     uint64_t* out_samples[NUM_HISTOGRAM_COLOR_COMPONENTS],
334     uint64_t* out_num_frames) const {
335 
336     if (!out_samples_size || !out_num_frames)
337         return HWC2::Error::BadParameter;
338 
339     out_samples_size[0] = 0;
340     out_samples_size[1] = 0;
341     out_samples_size[2] = numBuckets;
342     out_samples_size[3] = 0;
343 
344     uint64_t num_frames;
345     std::array<uint64_t, HIST_V_SIZE> samples;
346 
347     if (max_frames == 0 && timestamp == 0) {
348         std::tie(num_frames, samples) = histogram->collect_cumulative();
349     } else if (max_frames == 0) {
350         std::tie(num_frames, samples) = histogram->collect_after(timestamp);
351     } else if (timestamp == 0) {
352         std::tie(num_frames, samples) = histogram->collect_max(max_frames);
353     } else {
354         std::tie(num_frames, samples) = histogram->collect_max_after(timestamp, max_frames);
355     }
356 
357     auto samples_rebucketed = rebucketTo8Buckets(samples);
358     *out_num_frames = num_frames;
359     if (out_samples && out_samples[2])
360         memcpy(out_samples[2], samples_rebucketed.data(), sizeof(uint64_t) * samples_rebucketed.size());
361 
362     return HWC2::Error::None;
363 }
364 
getAttributes(int32_t * format,int32_t * dataspace,uint8_t * supported_components) const365 HWC2::Error histogram::HistogramCollector::getAttributes(int32_t* format,
366                                                          int32_t* dataspace,
367                                                          uint8_t* supported_components) const {
368     if (!format || !dataspace || !supported_components)
369         return HWC2::Error::BadParameter;
370 
371     *format = HAL_PIXEL_FORMAT_HSV_888;
372     *dataspace = HAL_DATASPACE_UNKNOWN;
373     *supported_components = HWC2_FORMAT_COMPONENT_2;
374     return HWC2::Error::None;
375 }
376 
start()377 void histogram::HistogramCollector::start() {
378     start(implementation_defined_max_frame_ringbuffer);
379 }
380 
start(uint64_t max_frames)381 void histogram::HistogramCollector::start(uint64_t max_frames) {
382     std::unique_lock<decltype(thread_control)> lk(thread_control);
383     if (started) {
384         return;
385     }
386 
387     if (pipe2(selfpipe, O_CLOEXEC | O_NONBLOCK )) {
388         ALOGE("histogram thread not started, could not create control pipe.");
389         return;
390     }
391     histogram = histogram::Ringbuffer::create(max_frames, std::make_unique<histogram::DefaultTimeKeeper>());
392     monitoring_thread = std::thread(&HistogramCollector::collecting_thread, this, selfpipe[0]);
393     started = true;
394 }
395 
stop()396 void histogram::HistogramCollector::stop() {
397     std::unique_lock<decltype(thread_control)> lk(thread_control);
398     if (!started) {
399         return;
400     }
401 
402     char dummy = 's';
403     write(selfpipe[1], &dummy, 1);
404     if (monitoring_thread.joinable())
405         monitoring_thread.join();
406     close(selfpipe[0]);
407     close(selfpipe[1]);
408     started = false;
409 }
410 
collecting_thread(int selfpipe)411 void histogram::HistogramCollector::collecting_thread(int selfpipe) {
412     if (prctl(PR_SET_NAME, "histogram-collector", 0, 0, 0))
413         ALOGW("could not set thread name for histogram collector.");
414 
415     int const control_minor_version { 64 };
416     auto drm = ManagedFd::create(drmOpenControl(control_minor_version));
417     if (!drm) {
418         ALOGW("could not find DRM control node. Histogram collection disabled.");
419         return;
420     }
421     auto drm_resources = DrmResources::create(*drm);
422     if (!drm_resources) {
423         ALOGW("could not get DRM resources. Histogram collection disabled.");
424         return;
425     }
426 
427     //Find the connector and encoder on the DSI. Check the possible CRTCs for support
428     //for the histogram property.
429     auto connector = drm_resources->find_first_connector_of_type(DRM_MODE_CONNECTOR_DSI);
430     if (!connector) {
431         ALOGE("Could not find connector. Histogram collection disabled.");
432         return;
433     }
434 
435     auto encoder = drm_resources->find_encoder_by_connector_and_type(
436         connector, DRM_MODE_ENCODER_DSI);
437     if (!encoder) {
438         ALOGE("Could not find encoder. Histogram collection disabled.");
439         return;
440     }
441 
442     auto histogram_property = -1;
443     auto histogram_irq = -1;
444     drmModeCrtcPtr crtc = nullptr;
445     if (!drm_resources->find_histogram_supporting_crtc(
446         *drm, encoder, &crtc, &histogram_property, &histogram_irq)) {
447         ALOGE("Could not find CRTC that supports color sampling. Histogram collection disabled.");
448         return;
449     }
450 
451     // Set up event loop.
452     // Event loop will listen to 1) FD that exposes color sampling events (1 per displayed frame),
453     // and 2) a self-pipe that will indicate when this thread should shut down.
454     enum class EventType
455     {
456         DRM,
457         CTL,
458         NUM_EVENT_TYPES
459     };
460 
461     struct epoll_event ev, events[static_cast<int>(EventType::NUM_EVENT_TYPES)];
462     auto epollfd = ManagedFd::create(epoll_create1(EPOLL_CLOEXEC));
463     if (!epollfd) {
464         ALOGE("Error creating epoll loop. Histogram collection disabled.");
465         return;
466     }
467 
468     ev.events = EPOLLIN;
469     ev.data.u32 = static_cast<uint32_t>(EventType::DRM);
470     if (epoll_ctl(*epollfd, EPOLL_CTL_ADD, *drm, &ev) == -1) {
471         ALOGE("Error adding drm fd to epoll. Histogram collection disabled.");
472         return;
473     }
474 
475     ev.events = EPOLLIN;
476     ev.data.u32 = static_cast<uint32_t>(EventType::CTL);
477     if (epoll_ctl(*epollfd, EPOLL_CTL_ADD, selfpipe, &ev) == -1) {
478         ALOGE("Error adding control fd to epoll. Histogram collection disabled.");
479         return;
480     }
481 
482     if (fcntl(*drm, F_SETFL, fcntl(*drm, F_GETFL) | O_NONBLOCK)) {
483         ALOGE("Error making drm read nonblocking. Histogram collection disabled.");
484         return;
485     }
486 
487     /* Attempting to set SDE_DSPP_HIST_CTRL_V1, SDE_DSPP_HIST_IRQ_V1, or DRM_EVENT_HISTOGRAM
488      * while the screen is off will result in an error.
489      *
490      * Since we have to wait on events (or poll the connector for power state), and then issue
491      * based on that info, there's no 100% certain way to know if enabling those histogram events
492      * are done when the screen is actually on. We work around this by retrying when those
493      * events fail, and not trying to enable those when we know the screen is off.
494      */
495     std::unique_ptr<EventRegistration> hist_registration = nullptr;
496     CrtcPowerState state = CrtcPowerState::UNKNOWN;
497     bool collecting = true;
498 
499     auto power_registration = PowerEventRegistration::create(*drm, crtc->crtc_id);
500     if (!power_registration) {
501         ALOGE("could not register event to monitor power events. Histogram collection disabled.");
502         return;
503     }
504 
505     while (collecting) {
506         if (state != CrtcPowerState::OFF) {
507             if (!hist_registration) {
508                 hist_registration = EventRegistration::create(
509                     *drm, crtc->crtc_id, histogram_property);
510             }
511 
512             if (drmModeObjectSetProperty(*drm,
513                     crtc->crtc_id, DRM_MODE_OBJECT_CRTC, histogram_irq, 1)) {
514                 ALOGI("Failed to enable histogram property on crtc, will retry");
515                 state = CrtcPowerState::OFF;
516                 hist_registration = nullptr;
517             }
518         }
519 
520         int nfds = epoll_wait(*epollfd, events, static_cast<int>(EventType::NUM_EVENT_TYPES), -1);
521         if (nfds == -1) {
522             if (errno != EINTR)
523                 collecting = false;
524             continue;
525         }
526 
527         for (auto i = 0; i < nfds; i++) {
528             if (events[i].data.u32 == static_cast<uint32_t>(EventType::CTL)) {
529                 collecting = false;
530             } else if (events[i].data.u32 == static_cast<uint32_t>(EventType::DRM)) {
531                 //VLA has a single int as blob id, or power mode
532                 char buffer[sizeof(drm_msm_event_resp) + sizeof(uint32_t)];
533                 auto size_read = read(*drm, buffer, sizeof(buffer));
534                 if (size_read != sizeof(buffer)) {
535                     ALOGW("Histogram event wrong size (%zu bytes, errno: %X). Skipping event.",
536                         size_read, errno);
537                     continue;
538                 }
539 
540                 struct drm_msm_event_resp* response =
541                     reinterpret_cast<struct drm_msm_event_resp*>(buffer);
542                 if (response->base.type == DRM_EVENT_HISTOGRAM) {
543                     uint32_t blob_id = *reinterpret_cast<uint32_t*>(response->data);
544                     drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(*drm, blob_id);
545                     histogram->insert(*static_cast<struct drm_msm_hist*>(blob->data));
546                     drmModeFreePropertyBlob(blob);
547                 }
548 
549                 if (response->base.type == DRM_EVENT_CRTC_POWER) {
550                     uint32_t state_raw = *reinterpret_cast<uint32_t*>(response->data);
551                     state = (state_raw) ? CrtcPowerState::ON : CrtcPowerState::OFF;
552                 }
553             }
554         }
555     }
556 }
557