| /* |
| * Copyright (C) 2019 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. |
| */ |
| |
| #define LOG_TAG "Camera3-BufUtils" |
| #define ATRACE_TAG ATRACE_TAG_CAMERA |
| //#define LOG_NDEBUG 0 |
| //#define LOG_NNDEBUG 0 // Per-frame verbose logging |
| |
| #include <inttypes.h> |
| |
| #include <utils/Log.h> |
| |
| #include "device3/BufferUtils.h" |
| |
| namespace android { |
| namespace camera3 { |
| |
| void BufferRecords::takeInflightBufferMap(BufferRecords& other) { |
| std::lock_guard<std::mutex> oLock(other.mInflightLock); |
| std::lock_guard<std::mutex> lock(mInflightLock); |
| if (mInflightBufferMap.size() > 0) { |
| ALOGE("%s: inflight map is set in non-empty state!", __FUNCTION__); |
| } |
| mInflightBufferMap = std::move(other.mInflightBufferMap); |
| other.mInflightBufferMap.clear(); |
| } |
| |
| void BufferRecords::takeRequestedBufferMap(BufferRecords& other) { |
| std::lock_guard<std::mutex> oLock(other.mRequestedBuffersLock); |
| std::lock_guard<std::mutex> lock(mRequestedBuffersLock); |
| if (mRequestedBufferMap.size() > 0) { |
| ALOGE("%s: requested buffer map is set in non-empty state!", __FUNCTION__); |
| } |
| mRequestedBufferMap = std::move(other.mRequestedBufferMap); |
| other.mRequestedBufferMap.clear(); |
| } |
| |
| void BufferRecords::takeBufferCaches(BufferRecords& other, const std::vector<int32_t>& streams) { |
| std::lock_guard<std::mutex> oLock(other.mBufferIdMapLock); |
| std::lock_guard<std::mutex> lock(mBufferIdMapLock); |
| if (mBufferIdMaps.size() > 0) { |
| ALOGE("%s: buffer ID map is set in non-empty state!", __FUNCTION__); |
| } |
| for (auto streamId : streams) { |
| mBufferIdMaps.insert({streamId, std::move(other.mBufferIdMaps.at(streamId))}); |
| } |
| other.mBufferIdMaps.clear(); |
| } |
| |
| std::pair<bool, uint64_t> BufferRecords::getBufferId( |
| const buffer_handle_t& buf, int streamId) { |
| std::lock_guard<std::mutex> lock(mBufferIdMapLock); |
| |
| BufferIdMap& bIdMap = mBufferIdMaps.at(streamId); |
| auto it = bIdMap.find(buf); |
| if (it == bIdMap.end()) { |
| bIdMap[buf] = mNextBufferId++; |
| ALOGV("stream %d now have %zu buffer caches, buf %p", |
| streamId, bIdMap.size(), buf); |
| return std::make_pair(true, mNextBufferId - 1); |
| } else { |
| return std::make_pair(false, it->second); |
| } |
| } |
| |
| void BufferRecords::tryCreateBufferCache(int streamId) { |
| std::lock_guard<std::mutex> lock(mBufferIdMapLock); |
| if (mBufferIdMaps.count(streamId) == 0) { |
| mBufferIdMaps.emplace(streamId, BufferIdMap{}); |
| } |
| } |
| |
| void BufferRecords::removeInactiveBufferCaches(const std::set<int32_t>& activeStreams) { |
| std::lock_guard<std::mutex> lock(mBufferIdMapLock); |
| for(auto it = mBufferIdMaps.begin(); it != mBufferIdMaps.end();) { |
| int streamId = it->first; |
| bool active = activeStreams.count(streamId) > 0; |
| if (!active) { |
| it = mBufferIdMaps.erase(it); |
| } else { |
| ++it; |
| } |
| } |
| } |
| |
| uint64_t BufferRecords::removeOneBufferCache(int streamId, const native_handle_t* handle) { |
| std::lock_guard<std::mutex> lock(mBufferIdMapLock); |
| uint64_t bufferId = BUFFER_ID_NO_BUFFER; |
| auto mapIt = mBufferIdMaps.find(streamId); |
| if (mapIt == mBufferIdMaps.end()) { |
| // streamId might be from a deleted stream here |
| ALOGI("%s: stream %d has been removed", |
| __FUNCTION__, streamId); |
| return BUFFER_ID_NO_BUFFER; |
| } |
| BufferIdMap& bIdMap = mapIt->second; |
| auto it = bIdMap.find(handle); |
| if (it == bIdMap.end()) { |
| ALOGW("%s: cannot find buffer %p in stream %d", |
| __FUNCTION__, handle, streamId); |
| return BUFFER_ID_NO_BUFFER; |
| } else { |
| bufferId = it->second; |
| bIdMap.erase(it); |
| ALOGV("%s: stream %d now have %zu buffer caches after removing buf %p", |
| __FUNCTION__, streamId, bIdMap.size(), handle); |
| } |
| return bufferId; |
| } |
| |
| std::vector<uint64_t> BufferRecords::clearBufferCaches(int streamId) { |
| std::lock_guard<std::mutex> lock(mBufferIdMapLock); |
| std::vector<uint64_t> ret; |
| auto mapIt = mBufferIdMaps.find(streamId); |
| if (mapIt == mBufferIdMaps.end()) { |
| ALOGE("%s: streamId %d not found!", __FUNCTION__, streamId); |
| return ret; |
| } |
| BufferIdMap& bIdMap = mapIt->second; |
| ret.reserve(bIdMap.size()); |
| for (const auto& it : bIdMap) { |
| ret.push_back(it.second); |
| } |
| bIdMap.clear(); |
| return ret; |
| } |
| |
| bool BufferRecords::isStreamCached(int streamId) { |
| std::lock_guard<std::mutex> lock(mBufferIdMapLock); |
| return mBufferIdMaps.find(streamId) != mBufferIdMaps.end(); |
| } |
| |
| bool BufferRecords::verifyBufferIds( |
| int32_t streamId, std::vector<uint64_t>& bufIds) { |
| std::lock_guard<std::mutex> lock(mBufferIdMapLock); |
| camera3::BufferIdMap& bIdMap = mBufferIdMaps.at(streamId); |
| if (bIdMap.size() != bufIds.size()) { |
| ALOGE("%s: stream ID %d buffer cache number mismatch: %zu/%zu (service/HAL)", |
| __FUNCTION__, streamId, bIdMap.size(), bufIds.size()); |
| return false; |
| } |
| std::vector<uint64_t> internalBufIds; |
| internalBufIds.reserve(bIdMap.size()); |
| for (const auto& pair : bIdMap) { |
| internalBufIds.push_back(pair.second); |
| } |
| std::sort(bufIds.begin(), bufIds.end()); |
| std::sort(internalBufIds.begin(), internalBufIds.end()); |
| for (size_t i = 0; i < bufIds.size(); i++) { |
| if (bufIds[i] != internalBufIds[i]) { |
| ALOGE("%s: buffer cache mismatch! Service %" PRIu64 ", HAL %" PRIu64, |
| __FUNCTION__, internalBufIds[i], bufIds[i]); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void BufferRecords::getInflightBufferKeys( |
| std::vector<std::pair<int32_t, int32_t>>* out) { |
| std::lock_guard<std::mutex> lock(mInflightLock); |
| out->clear(); |
| out->reserve(mInflightBufferMap.size()); |
| for (auto& pair : mInflightBufferMap) { |
| uint64_t key = pair.first; |
| int32_t streamId = key & 0xFFFFFFFF; |
| int32_t frameNumber = (key >> 32) & 0xFFFFFFFF; |
| out->push_back(std::make_pair(frameNumber, streamId)); |
| } |
| return; |
| } |
| |
| status_t BufferRecords::pushInflightBuffer( |
| int32_t frameNumber, int32_t streamId, buffer_handle_t *buffer) { |
| std::lock_guard<std::mutex> lock(mInflightLock); |
| uint64_t key = static_cast<uint64_t>(frameNumber) << 32 | static_cast<uint64_t>(streamId); |
| mInflightBufferMap[key] = buffer; |
| return OK; |
| } |
| |
| status_t BufferRecords::popInflightBuffer( |
| int32_t frameNumber, int32_t streamId, |
| /*out*/ buffer_handle_t **buffer) { |
| std::lock_guard<std::mutex> lock(mInflightLock); |
| |
| uint64_t key = static_cast<uint64_t>(frameNumber) << 32 | static_cast<uint64_t>(streamId); |
| auto it = mInflightBufferMap.find(key); |
| if (it == mInflightBufferMap.end()) return NAME_NOT_FOUND; |
| if (buffer != nullptr) { |
| *buffer = it->second; |
| } |
| mInflightBufferMap.erase(it); |
| return OK; |
| } |
| |
| void BufferRecords::popInflightBuffers( |
| const std::vector<std::pair<int32_t, int32_t>>& buffers) { |
| for (const auto& pair : buffers) { |
| int32_t frameNumber = pair.first; |
| int32_t streamId = pair.second; |
| popInflightBuffer(frameNumber, streamId, nullptr); |
| } |
| } |
| |
| status_t BufferRecords::pushInflightRequestBuffer( |
| uint64_t bufferId, buffer_handle_t* buf, int32_t streamId) { |
| std::lock_guard<std::mutex> lock(mRequestedBuffersLock); |
| auto pair = mRequestedBufferMap.insert({bufferId, {streamId, buf}}); |
| if (!pair.second) { |
| ALOGE("%s: bufId %" PRIu64 " is already inflight!", |
| __FUNCTION__, bufferId); |
| return BAD_VALUE; |
| } |
| return OK; |
| } |
| |
| // Find and pop a buffer_handle_t based on bufferId |
| status_t BufferRecords::popInflightRequestBuffer( |
| uint64_t bufferId, |
| /*out*/ buffer_handle_t** buffer, |
| /*optional out*/ int32_t* streamId) { |
| if (buffer == nullptr) { |
| ALOGE("%s: buffer (%p) must not be null", __FUNCTION__, buffer); |
| return BAD_VALUE; |
| } |
| std::lock_guard<std::mutex> lock(mRequestedBuffersLock); |
| auto it = mRequestedBufferMap.find(bufferId); |
| if (it == mRequestedBufferMap.end()) { |
| ALOGE("%s: bufId %" PRIu64 " is not inflight!", |
| __FUNCTION__, bufferId); |
| return BAD_VALUE; |
| } |
| *buffer = it->second.second; |
| if (streamId != nullptr) { |
| *streamId = it->second.first; |
| } |
| mRequestedBufferMap.erase(it); |
| return OK; |
| } |
| |
| void BufferRecords::getInflightRequestBufferKeys( |
| std::vector<uint64_t>* out) { |
| std::lock_guard<std::mutex> lock(mRequestedBuffersLock); |
| out->clear(); |
| out->reserve(mRequestedBufferMap.size()); |
| for (auto& pair : mRequestedBufferMap) { |
| out->push_back(pair.first); |
| } |
| return; |
| } |
| |
| |
| } // camera3 |
| } // namespace android |