1 /*
2 * Copyright (C) 2023 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 "HistogramController.h"
18
HistogramController(ExynosDisplay * display)19 HistogramController::HistogramController(ExynosDisplay* display)
20 : HistogramDevice(display, 4, {3}) {
21 for (int i = 0; i < mConfigInfos.size(); ++i)
22 mConfigInfos[i] = std::make_shared<ConfigInfo>(mPreemptConfigs[i].config);
23 }
24
initPlatformHistogramCapability()25 void HistogramController::initPlatformHistogramCapability() {
26 mHistogramCapability.supportSamplePosList.push_back(HistogramSamplePos::PRE_POSTPROC);
27 mHistogramCapability.supportBlockingRoi = true;
28
29 #if defined(EXYNOS_CONTEXT_HISTOGRAM_EVENT_REQUEST)
30 SCOPED_HIST_LOCK(mHistogramMutex);
31 if ((int)mFreeChannels.size() + (int)mUsedChannels.size() >= (int)mPreemptConfigs.size())
32 mHistogramCapability.supportQueryOpr = true;
33 else
34 mHistogramCapability.supportQueryOpr = false;
35 #else
36 mHistogramCapability.supportQueryOpr = false;
37 #endif
38 }
39
queryOPR(std::array<double,kOPRConfigsCount> & oprVals)40 ndk::ScopedAStatus HistogramController::queryOPR(std::array<double, kOPRConfigsCount>& oprVals) {
41 ATRACE_CALL();
42
43 if (waitInitDrmDone() == false) {
44 HIST_LOG(E, "initDrm is not completed yet");
45 // TODO: b/323158344 - add retry error in HistogramErrorCode and return here.
46 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
47 }
48
49 {
50 std::shared_lock lock(mHistogramCapabilityMutex);
51 if (!mHistogramCapability.supportQueryOpr) {
52 HIST_LOG(W, "supportQueryOpr is false");
53 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
54 }
55 }
56
57 HistogramErrorCode histogramErrorCode = HistogramErrorCode::NONE;
58
59 const auto [displayActiveH, displayActiveV] = snapDisplayActiveSize();
60
61 std::array<uint32_t, kOPRConfigsCount> blobIds;
62 std::array<int, kOPRConfigsCount> channelIds;
63
64 {
65 SCOPED_HIST_LOCK(mHistogramMutex);
66 if ((histogramErrorCode = getOPRBlobs(blobIds, displayActiveH, displayActiveV)) !=
67 HistogramErrorCode::NONE)
68 return errorToStatus(histogramErrorCode);
69
70 if ((histogramErrorCode = preemptOPRChannels()) != HistogramErrorCode::NONE)
71 return errorToStatus(histogramErrorCode);
72
73 scheduler();
74 getOPRChannels(channelIds);
75 }
76
77 {
78 ATRACE_NAME("HistogramOnRefresh");
79 mDisplay->mDevice->onRefresh(mDisplay->mDisplayId);
80 }
81
82 histogramErrorCode = getOPRVals(blobIds, channelIds, oprVals);
83
84 bool needRefresh = false;
85 {
86 // Clear configInfo for OPR request
87 SCOPED_HIST_LOCK(mHistogramMutex);
88 for (auto& configInfo : mConfigInfos) resetConfigInfoStatus(configInfo);
89 needRefresh = scheduler();
90 }
91
92 if (needRefresh) {
93 ATRACE_NAME("HistogramOnRefresh");
94 mDisplay->mDevice->onRefresh(mDisplay->mDisplayId);
95 }
96
97 if (histogramErrorCode != HistogramErrorCode::NONE) return errorToStatus(histogramErrorCode);
98
99 return ndk::ScopedAStatus::ok();
100 }
101
getOPRVals(const std::array<uint32_t,kOPRConfigsCount> & blobIds,const std::array<int,kOPRConfigsCount> & channelIds,std::array<double,kOPRConfigsCount> & oprVals)102 HistogramController::HistogramErrorCode HistogramController::getOPRVals(
103 const std::array<uint32_t, kOPRConfigsCount>& blobIds,
104 const std::array<int, kOPRConfigsCount>& channelIds,
105 std::array<double, kOPRConfigsCount>& oprVals) {
106 ATRACE_CALL();
107
108 std::array<std::vector<char16_t>, kOPRConfigsCount> histogramBuffer;
109 std::array<std::cv_status, kOPRConfigsCount> cv_status;
110 std::array<HistogramErrorCode, kOPRConfigsCount> errorCodes;
111
112 // Get the moduleDisplayInterface pointer
113 ExynosDisplayDrmInterface* moduleDisplayInterface =
114 static_cast<ExynosDisplayDrmInterface*>(mDisplay->mDisplayInterface.get());
115 if (!moduleDisplayInterface) {
116 HIST_LOG(E, "ENABLE_HIST_ERROR, moduleDisplayInterface is NULL");
117 return HistogramErrorCode::ENABLE_HIST_ERROR;
118 }
119
120 // Use shared_ptr to keep the blobIdData which will be used by receiveBlobIdData and
121 // receiveBlobIdData.
122 std::array<std::shared_ptr<BlobIdData>, kOPRConfigsCount> blobIdDatas;
123 for (int i = 0; i < kOPRConfigsCount; ++i)
124 searchOrCreateBlobIdData(blobIds[i], true, blobIdDatas[i]);
125
126 // Request the drmEvent of the blobId, hold only one uniqueLock at a time.
127 for (int i = 0; i < kOPRConfigsCount; ++i) {
128 std::shared_ptr<BlobIdData>& blobIdData = blobIdDatas[i];
129 uint32_t blobId = blobIds[i];
130 int channelId = channelIds[i];
131
132 std::unique_lock<std::mutex> lock(blobIdData->mDataCollectingMutex);
133 ::android::base::ScopedLockAssertion lock_assertion(blobIdData->mDataCollectingMutex);
134 ATRACE_NAME(String8::format("mDataCollectingMutex(blob#%u)", blobId));
135
136 errorCodes[i] = HistogramErrorCode::NONE;
137 requestBlobIdData(moduleDisplayInterface, &errorCodes[i], channelId, blobId, blobIdData);
138 }
139
140 // Receive the drmEvent of the blobId, hold only one uniqueLock at a time.
141 for (int i = 0; i < kOPRConfigsCount; ++i) {
142 // If the requestBlobIdData has some error, not need to receive blobIdData.
143 if (errorCodes[i] != HistogramErrorCode::NONE) continue;
144
145 std::shared_ptr<BlobIdData>& blobIdData = blobIdDatas[i];
146
147 std::unique_lock<std::mutex> lock(blobIdData->mDataCollectingMutex);
148 ::android::base::ScopedLockAssertion lock_assertion(blobIdData->mDataCollectingMutex);
149 ATRACE_NAME(String8::format("mDataCollectingMutex(blob#%u)", blobIds[i]));
150
151 cv_status[i] =
152 receiveBlobIdData(moduleDisplayInterface, &histogramBuffer[i], &errorCodes[i],
153 channelIds[i], blobIds[i], blobIdData, lock);
154 }
155
156 // Check the query result and clear the buffer if needed (no lock is held now)
157 for (int i = 0; i < blobIdDatas.size(); ++i) {
158 checkQueryResult(&histogramBuffer[i], &errorCodes[i], channelIds[i], blobIds[i],
159 cv_status[i]);
160 }
161
162 // Check the histogram error code
163 for (auto& error : errorCodes)
164 if (error != HistogramErrorCode::NONE) return error;
165
166 for (int i = 0; i < kOPRConfigsCount; ++i) {
167 errorCodes[i] = calculateOPRVal(mPreemptConfigs[i].name, histogramBuffer[i], blobIds[i],
168 channelIds[i], oprVals[i]);
169 if (errorCodes[i] != HistogramErrorCode::NONE) return errorCodes[i];
170 }
171
172 return HistogramErrorCode::NONE;
173 }
174
calculateOPRVal(const char * configName,const std::vector<char16_t> & histogramBuffer,const uint32_t blobId,const int channelId,double & oprVal) const175 HistogramController::HistogramErrorCode HistogramController::calculateOPRVal(
176 const char* configName, const std::vector<char16_t>& histogramBuffer, const uint32_t blobId,
177 const int channelId, double& oprVal) const {
178 if (histogramBuffer.size() != HISTOGRAM_BINS_SIZE) {
179 HIST_LOG(E, "BAD_HIST_DATA for %s, buffer size is not %lu", configName,
180 HISTOGRAM_BINS_SIZE);
181 return HistogramErrorCode::BAD_HIST_DATA;
182 }
183
184 int totalPixels = 0;
185 double totalLuma = 0;
186
187 for (int i = 0; i < HISTOGRAM_BINS_SIZE; ++i) {
188 // TODO: b/340403552 - More efficient way to convert gamma into linear space.
189 totalPixels += histogramBuffer[i];
190 totalLuma += std::pow((i / (double)(HISTOGRAM_BINS_SIZE - 1)), 2.2) * histogramBuffer[i];
191 }
192
193 if (!totalPixels) {
194 HIST_BLOB_CH_LOG(E, blobId, channelId, "BAD_HIST_DATA for %s, pixel count is zero",
195 configName);
196 return HistogramErrorCode::BAD_HIST_DATA;
197 }
198
199 oprVal = totalLuma / totalPixels;
200 HIST_BLOB_CH_LOG(V, blobId, channelId, "%s channel opr: %lf, pixels count: %d", configName,
201 oprVal, totalPixels);
202 return HistogramErrorCode::NONE;
203 }
204
getOPRBlobs(std::array<uint32_t,kOPRConfigsCount> & blobIds,const int displayActiveH,const int displayActiveV)205 HistogramController::HistogramErrorCode HistogramController::getOPRBlobs(
206 std::array<uint32_t, kOPRConfigsCount>& blobIds, const int displayActiveH,
207 const int displayActiveV) {
208 ATRACE_CALL();
209
210 // Get blobIds for all mPreemptConfigs
211 for (int i = 0; i < kOPRConfigsCount; ++i) {
212 auto& configInfo = mConfigInfos[i];
213 bool isBlobIdChanged = false;
214
215 uint32_t blobId = getMatchBlobId(configInfo->mBlobsList, displayActiveH, displayActiveV,
216 isBlobIdChanged);
217
218 // Create the blobId for not matched case (first time queryOPR or RRS detected)
219 int ret;
220 if (!blobId) {
221 std::shared_ptr<PropertyBlob> drmConfigBlob;
222 ret = createDrmConfigBlob(configInfo->mRequestedConfig, displayActiveH, displayActiveV,
223 drmConfigBlob);
224 if (ret || !drmConfigBlob) {
225 HIST_LOG(E, "createDrmConfigBlob failed, ret(%d)", ret);
226 return HistogramErrorCode::CONFIG_HIST_ERROR;
227 }
228
229 // Attach the histogram drmConfigBlob to the configInfo
230 configInfo->mBlobsList.emplace_front(displayActiveH, displayActiveV, drmConfigBlob);
231 blobId = drmConfigBlob->getId();
232 }
233
234 blobIds[i] = blobId;
235 }
236
237 return HistogramErrorCode::NONE;
238 }
239
getOPRChannels(std::array<int,kOPRConfigsCount> & channelIds) const240 void HistogramController::getOPRChannels(std::array<int, kOPRConfigsCount>& channelIds) const {
241 // Get channelIds
242 for (int i = 0; i < kOPRConfigsCount; ++i) channelIds[i] = mConfigInfos[i]->mChannelId;
243 }
244
preemptOPRChannels()245 HistogramController::HistogramErrorCode HistogramController::preemptOPRChannels() {
246 ATRACE_CALL();
247
248 for (auto& configInfo : mConfigInfos) resetConfigInfoStatus(configInfo);
249
250 int preemptCount = (int)mConfigInfos.size() - (int)mFreeChannels.size();
251 if (preemptCount > (int)mUsedChannels.size()) {
252 HIST_LOG(E, "no channel available for preemption");
253 return HistogramErrorCode::NO_CHANNEL_AVAILABLE;
254 }
255
256 for (int i = 0; i < preemptCount; ++i) swapOutConfigInfo(*mUsedChannels.begin());
257
258 for (auto& configInfo : mConfigInfos) addConfigToInactiveList(configInfo, true);
259
260 return HistogramErrorCode::NONE;
261 }
262
postAtomicCommitCleanup()263 void HistogramController::postAtomicCommitCleanup() {
264 ATRACE_CALL();
265
266 bool needRefresh = false;
267
268 {
269 bool needSchedule = false;
270
271 SCOPED_HIST_LOCK(mHistogramMutex);
272 for (int i = 0; i < kOPRConfigsCount; ++i) {
273 auto& configInfo = mConfigInfos[i];
274 if (configInfo->mStatus == ConfigInfo::Status_t::HAS_CHANNEL_ASSIGNED &&
275 mChannels[configInfo->mChannelId].mStatus != ChannelStatus_t::CONFIG_PENDING) {
276 needSchedule = true;
277 resetConfigInfoStatus(configInfo);
278 }
279 }
280
281 if (needSchedule) needRefresh = scheduler();
282 }
283
284 if (needRefresh) {
285 ATRACE_NAME("HistogramOnRefresh");
286 mDisplay->mDevice->onRefresh(mDisplay->mDisplayId);
287 }
288 }
289
dumpInternalConfigs(String8 & result) const290 void HistogramController::dumpInternalConfigs(String8& result) const {
291 for (int i = 0; i < kOPRConfigsCount; ++i) {
292 result.appendFormat("Histogram internal config \"%s\":\n", mPreemptConfigs[i].name);
293 if (mConfigInfos[i]) {
294 mConfigInfos[i]->dump(result, "\t");
295 } else {
296 result.appendFormat("\tconfigInfo: %p\n", mConfigInfos[i].get());
297 }
298 }
299 }
300