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__anona2530b8d0111::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__anona2530b8d0111::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__anona2530b8d0111::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__anona2530b8d0111::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__anona2530b8d0111::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__anona2530b8d0111::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__anona2530b8d0111::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, ®->req))
245 return nullptr;
246 return reg;
247 }
248
~EventRegistration__anona2530b8d0111::EventRegistration249 ~EventRegistration() {
250 drmIoctl(fd, DRM_IOCTL_MSM_DEREGISTER_EVENT, &req);
251 }
252
253 private:
EventRegistration__anona2530b8d0111::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