blob: f52e2418091bfc9c0e9ace94636dd31cc4936a95 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fcntl.h>
#include <log/log.h>
#include <pthread.h>
#include <sys/epoll.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <memory>
#include <sstream>
#include <tuple>
#include <unordered_map>
#include <vector>
#include <drm/msm_drm.h>
#include <drm/msm_drm_pp.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "histogram_collector.h"
#include "ringbuffer.h"
constexpr static auto implementation_defined_max_frame_ringbuffer = 300;
histogram::HistogramCollector::HistogramCollector()
: histogram(histogram::Ringbuffer::create(implementation_defined_max_frame_ringbuffer,
std::make_unique<histogram::DefaultTimeKeeper>())) {}
histogram::HistogramCollector::~HistogramCollector() {
stop();
}
namespace {
static constexpr size_t numBuckets = 8;
static_assert((HIST_V_SIZE % numBuckets) == 0,
"histogram cannot be rebucketed to smaller number of buckets");
static constexpr int bucket_compression = HIST_V_SIZE / numBuckets;
std::array<uint64_t, numBuckets> rebucketTo8Buckets(
std::array<uint64_t, HIST_V_SIZE> const &frame) {
std::array<uint64_t, numBuckets> bins;
bins.fill(0);
for (auto i = 0u; i < HIST_V_SIZE; i++)
bins[i / bucket_compression] += frame[i];
return bins;
}
} // namespace
std::string histogram::HistogramCollector::Dump() const {
uint64_t num_frames = 0;
std::array<uint64_t, HIST_V_SIZE> all_sample_buckets;
std::tie(num_frames, all_sample_buckets) = histogram->collect_cumulative();
std::array<uint64_t, numBuckets> samples = rebucketTo8Buckets(all_sample_buckets);
std::stringstream ss;
ss << "Color Sampling, dark (0.0) to light (1.0): sampled frames: " << num_frames << '\n';
if (num_frames == 0) {
ss << "\tno color statistics collected\n";
return ss.str();
}
ss << std::fixed << std::setprecision(3);
ss << "\tbucket\t\t: # of displayed pixels at bucket value\n";
for (auto i = 0u; i < samples.size(); i++) {
ss << "\t" << i / static_cast<float>(samples.size()) << " to "
<< (i + 1) / static_cast<float>(samples.size()) << "\t: " << samples[i] << '\n';
}
return ss.str();
}
HWC2::Error histogram::HistogramCollector::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) const {
if (!out_samples_size || !out_num_frames)
return HWC2::Error::BadParameter;
out_samples_size[0] = 0;
out_samples_size[1] = 0;
out_samples_size[2] = numBuckets;
out_samples_size[3] = 0;
uint64_t num_frames = 0;
std::array<uint64_t, HIST_V_SIZE> samples;
if (max_frames == 0 && timestamp == 0) {
std::tie(num_frames, samples) = histogram->collect_cumulative();
} else if (max_frames == 0) {
std::tie(num_frames, samples) = histogram->collect_after(timestamp);
} else if (timestamp == 0) {
std::tie(num_frames, samples) = histogram->collect_max(max_frames);
} else {
std::tie(num_frames, samples) = histogram->collect_max_after(timestamp, max_frames);
}
auto samples_rebucketed = rebucketTo8Buckets(samples);
*out_num_frames = num_frames;
if (out_samples && out_samples[2])
memcpy(out_samples[2], samples_rebucketed.data(), sizeof(uint64_t) * samples_rebucketed.size());
return HWC2::Error::None;
}
HWC2::Error histogram::HistogramCollector::getAttributes(int32_t *format, int32_t *dataspace,
uint8_t *supported_components) const {
if (!format || !dataspace || !supported_components)
return HWC2::Error::BadParameter;
*format = HAL_PIXEL_FORMAT_HSV_888;
*dataspace = HAL_DATASPACE_UNKNOWN;
*supported_components = HWC2_FORMAT_COMPONENT_2;
return HWC2::Error::None;
}
void histogram::HistogramCollector::start() {
start(implementation_defined_max_frame_ringbuffer);
}
void histogram::HistogramCollector::start(uint64_t max_frames) {
std::unique_lock<decltype(mutex)> lk(mutex);
if (started) {
return;
}
started = true;
histogram =
histogram::Ringbuffer::create(max_frames, std::make_unique<histogram::DefaultTimeKeeper>());
monitoring_thread = std::thread(&HistogramCollector::blob_processing_thread, this);
}
void histogram::HistogramCollector::stop() {
std::unique_lock<decltype(mutex)> lk(mutex);
if (!started) {
return;
}
started = false;
cv.notify_all();
lk.unlock();
if (monitoring_thread.joinable())
monitoring_thread.join();
}
void histogram::HistogramCollector::notify_histogram_event(int blob_source_fd, BlobId id) {
std::unique_lock<decltype(mutex)> lk(mutex);
if (!started) {
ALOGW("Discarding event blob-id: %X", id);
return;
}
if (work_available) {
ALOGI("notified of histogram event before consuming last one. prior event discarded");
}
work_available = true;
blobwork = HistogramCollector::BlobWork{blob_source_fd, id};
cv.notify_all();
}
void histogram::HistogramCollector::blob_processing_thread() {
pthread_setname_np(pthread_self(), "histogram_blob");
std::unique_lock<decltype(mutex)> lk(mutex);
while (true) {
cv.wait(lk, [this] { return !started || work_available; });
if (!started) {
return;
}
auto work = blobwork;
work_available = false;
lk.unlock();
drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(work.fd, work.id);
if (!blob || !blob->data) {
lk.lock();
continue;
}
histogram->insert(*static_cast<struct drm_msm_hist *>(blob->data));
drmModeFreePropertyBlob(blob);
lk.lock();
}
}