Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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 | //#define LOG_NDEBUG 0 |
| 18 | #define LOG_TAG "StreamBufferCacheManager" |
| 19 | #define ATRACE_TAG ATRACE_TAG_CAMERA |
| 20 | |
| 21 | #include <cutils/native_handle.h> |
vincechiu | 2079123 | 2021-10-26 16:43:25 +0800 | [diff] [blame] | 22 | #include <cutils/properties.h> |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 23 | #include <log/log.h> |
| 24 | #include <sync/sync.h> |
vincechiu | 2079123 | 2021-10-26 16:43:25 +0800 | [diff] [blame] | 25 | #include <sys/resource.h> |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 26 | #include <utils/Trace.h> |
| 27 | |
| 28 | #include <chrono> |
| 29 | |
| 30 | #include "stream_buffer_cache_manager.h" |
vincechiu | b38617e | 2020-07-13 16:35:57 +0800 | [diff] [blame] | 31 | #include "utils.h" |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 32 | |
| 33 | using namespace std::chrono_literals; |
| 34 | |
| 35 | namespace android { |
| 36 | namespace google_camera_hal { |
| 37 | |
vincechiu | 2079123 | 2021-10-26 16:43:25 +0800 | [diff] [blame] | 38 | // setprop key for raising buffer allocation priority |
| 39 | inline constexpr char kRaiseBufAllocationPriority[] = |
| 40 | "persist.vendor.camera.raise_buf_allocation_priority"; |
| 41 | |
vincechiu | f15c29b | 2020-05-15 14:34:11 +0800 | [diff] [blame] | 42 | // For CTS testCameraDeviceCaptureFailure, it holds image buffers and hal hits |
| 43 | // refill buffer timeout. Large timeout time also results in close session time |
| 44 | // is larger than 5 second in this test case. Typical buffer request from |
| 45 | // provider(e.g. framework) usually takes 1~2 ms. Small timeout time here may |
| 46 | // cause more framedrop in certain cases. But large timeout time can lead to |
| 47 | // extra long delay of traffic(in both ways) between the framework and the layer |
| 48 | // below HWL. |
| 49 | static constexpr auto kBufferWaitingTimeOutSec = 400ms; |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 50 | |
| 51 | StreamBufferCacheManager::StreamBufferCacheManager() { |
| 52 | workload_thread_ = std::thread([this] { this->WorkloadThreadLoop(); }); |
vincechiu | b38617e | 2020-07-13 16:35:57 +0800 | [diff] [blame] | 53 | if (utils::SupportRealtimeThread()) { |
| 54 | status_t res = utils::SetRealtimeThread(workload_thread_.native_handle()); |
| 55 | if (res != OK) { |
| 56 | ALOGE("%s: SetRealtimeThread fail", __FUNCTION__); |
| 57 | } else { |
| 58 | ALOGI("%s: SetRealtimeThread OK", __FUNCTION__); |
| 59 | } |
| 60 | } |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | StreamBufferCacheManager::~StreamBufferCacheManager() { |
| 64 | ALOGI("%s: Destroying stream buffer cache manager.", __FUNCTION__); |
| 65 | { |
| 66 | std::lock_guard<std::mutex> lock(workload_mutex_); |
| 67 | workload_thread_exiting_ = true; |
| 68 | } |
| 69 | workload_cv_.notify_one(); |
| 70 | workload_thread_.join(); |
| 71 | } |
| 72 | |
| 73 | std::unique_ptr<StreamBufferCacheManager> StreamBufferCacheManager::Create() { |
| 74 | ATRACE_CALL(); |
| 75 | |
| 76 | auto manager = |
| 77 | std::unique_ptr<StreamBufferCacheManager>(new StreamBufferCacheManager()); |
| 78 | if (manager == nullptr) { |
| 79 | ALOGE("%s: Failed to create stream buffer cache manager.", __FUNCTION__); |
| 80 | return nullptr; |
| 81 | } |
| 82 | |
| 83 | manager->dummy_buffer_allocator_ = GrallocBufferAllocator::Create(); |
| 84 | if (manager->dummy_buffer_allocator_ == nullptr) { |
| 85 | ALOGE("%s: Failed to create gralloc buffer allocator", __FUNCTION__); |
| 86 | return nullptr; |
| 87 | } |
| 88 | |
| 89 | ALOGI("%s: Created StreamBufferCacheManager.", __FUNCTION__); |
| 90 | |
| 91 | return manager; |
| 92 | } |
| 93 | |
| 94 | status_t StreamBufferCacheManager::RegisterStream( |
| 95 | const StreamBufferCacheRegInfo& reg_info) { |
| 96 | ATRACE_CALL(); |
| 97 | if (reg_info.request_func == nullptr || reg_info.return_func == nullptr) { |
| 98 | ALOGE("%s: Can't register stream, request or return function is nullptr.", |
| 99 | __FUNCTION__); |
| 100 | return BAD_VALUE; |
| 101 | } |
| 102 | |
| 103 | if (reg_info.num_buffers_to_cache != 1) { |
| 104 | ALOGE("%s: Only support caching one buffer.", __FUNCTION__); |
| 105 | return BAD_VALUE; |
| 106 | } |
| 107 | |
| 108 | std::lock_guard<std::mutex> lock(caches_map_mutex_); |
| 109 | if (stream_buffer_caches_.find(reg_info.stream_id) != |
| 110 | stream_buffer_caches_.end()) { |
| 111 | ALOGE("%s: Stream %d has been registered.", __FUNCTION__, |
| 112 | reg_info.stream_id); |
| 113 | return INVALID_OPERATION; |
| 114 | } |
| 115 | |
| 116 | status_t res = AddStreamBufferCacheLocked(reg_info); |
| 117 | if (res != OK) { |
| 118 | ALOGE("%s: Failed to add stream buffer cache.", __FUNCTION__); |
| 119 | return UNKNOWN_ERROR; |
| 120 | } |
| 121 | return OK; |
| 122 | } |
| 123 | |
| 124 | status_t StreamBufferCacheManager::GetStreamBuffer( |
| 125 | int32_t stream_id, StreamBufferRequestResult* res) { |
| 126 | ATRACE_CALL(); |
| 127 | |
| 128 | StreamBufferCache* stream_buffer_cache = nullptr; |
| 129 | status_t result = GetStreamBufferCache(stream_id, &stream_buffer_cache); |
| 130 | if (result != OK) { |
| 131 | ALOGE("%s: Querying stream buffer cache failed.", __FUNCTION__); |
| 132 | return result; |
| 133 | } |
| 134 | |
| 135 | result = stream_buffer_cache->GetBuffer(res); |
| 136 | if (result != OK) { |
| 137 | ALOGE("%s: Get buffer for stream %d failed.", __FUNCTION__, stream_id); |
| 138 | return UNKNOWN_ERROR; |
| 139 | } |
| 140 | |
| 141 | { |
| 142 | int fence_status = 0; |
| 143 | if (res->buffer.acquire_fence != nullptr) { |
| 144 | native_handle_t* fence_handle = |
| 145 | const_cast<native_handle_t*>(res->buffer.acquire_fence); |
| 146 | if (fence_handle->numFds == 1) { |
| 147 | fence_status = sync_wait(fence_handle->data[0], kSyncWaitTimeMs); |
| 148 | } |
| 149 | if (0 != fence_status) { |
| 150 | ALOGE("%s: Fence check failed.", __FUNCTION__); |
| 151 | } |
| 152 | native_handle_close(fence_handle); |
| 153 | native_handle_delete(fence_handle); |
| 154 | res->buffer.acquire_fence = nullptr; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | NotifyThreadWorkload(); |
| 159 | return OK; |
| 160 | } |
| 161 | |
| 162 | status_t StreamBufferCacheManager::NotifyProviderReadiness(int32_t stream_id) { |
| 163 | StreamBufferCache* stream_buffer_cache = nullptr; |
| 164 | status_t res = GetStreamBufferCache(stream_id, &stream_buffer_cache); |
| 165 | if (res != OK) { |
| 166 | ALOGE("%s: Querying stream buffer cache failed.", __FUNCTION__); |
| 167 | return res; |
| 168 | } |
| 169 | |
Shuzhen Wang | 4a1bd0b | 2021-02-02 17:31:12 -0800 | [diff] [blame] | 170 | stream_buffer_cache->SetManagerState(/*active=*/true); |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 171 | |
| 172 | NotifyThreadWorkload(); |
| 173 | return OK; |
| 174 | } |
| 175 | |
| 176 | status_t StreamBufferCacheManager::NotifyFlushingAll() { |
| 177 | // Mark all StreamBufferCache as need to be flushed |
| 178 | std::vector<StreamBufferCache*> stream_buffer_caches; |
| 179 | { |
| 180 | std::lock_guard<std::mutex> map_lock(caches_map_mutex_); |
| 181 | for (auto& [stream_id, stream_buffer_cache] : stream_buffer_caches_) { |
| 182 | stream_buffer_caches.push_back(stream_buffer_cache.get()); |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | { |
| 187 | std::unique_lock<std::mutex> flush_lock(flush_mutex_); |
| 188 | for (auto& stream_buffer_cache : stream_buffer_caches) { |
Shuzhen Wang | 4a1bd0b | 2021-02-02 17:31:12 -0800 | [diff] [blame] | 189 | stream_buffer_cache->SetManagerState(/*active=*/false); |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 190 | } |
| 191 | } |
| 192 | |
| 193 | NotifyThreadWorkload(); |
| 194 | return OK; |
| 195 | } |
| 196 | |
| 197 | status_t StreamBufferCacheManager::IsStreamActive(int32_t stream_id, |
| 198 | bool* is_active) { |
| 199 | StreamBufferCache* stream_buffer_cache = nullptr; |
| 200 | status_t res = GetStreamBufferCache(stream_id, &stream_buffer_cache); |
| 201 | if (res != OK) { |
| 202 | ALOGE("%s: Querying stream buffer cache failed.", __FUNCTION__); |
| 203 | return res; |
| 204 | } |
| 205 | |
| 206 | *is_active = !stream_buffer_cache->IsStreamDeactivated(); |
| 207 | return OK; |
| 208 | } |
| 209 | |
| 210 | status_t StreamBufferCacheManager::AddStreamBufferCacheLocked( |
| 211 | const StreamBufferCacheRegInfo& reg_info) { |
| 212 | auto stream_buffer_cache = StreamBufferCacheManager::StreamBufferCache::Create( |
| 213 | reg_info, [this] { this->NotifyThreadWorkload(); }, |
| 214 | dummy_buffer_allocator_.get()); |
| 215 | if (stream_buffer_cache == nullptr) { |
| 216 | ALOGE("%s: Failed to create StreamBufferCache for stream %d", __FUNCTION__, |
| 217 | reg_info.stream_id); |
| 218 | return UNKNOWN_ERROR; |
| 219 | } |
| 220 | |
| 221 | stream_buffer_caches_[reg_info.stream_id] = std::move(stream_buffer_cache); |
| 222 | return OK; |
| 223 | } |
| 224 | |
| 225 | void StreamBufferCacheManager::WorkloadThreadLoop() { |
Xin Qi | fc623c5 | 2022-08-26 20:44:18 +0000 | [diff] [blame] | 226 | if (property_get_bool(kRaiseBufAllocationPriority, true)) { |
vincechiu | 2079123 | 2021-10-26 16:43:25 +0800 | [diff] [blame] | 227 | pid_t tid = gettid(); |
| 228 | setpriority(PRIO_PROCESS, tid, -20); |
| 229 | } |
Jay | 6b82f87 | 2021-10-13 20:21:00 +0000 | [diff] [blame] | 230 | // max thread name len = 16 |
| 231 | pthread_setname_np(pthread_self(), "StreamBufMgr"); |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 232 | while (1) { |
| 233 | bool exiting = false; |
| 234 | { |
| 235 | std::unique_lock<std::mutex> thread_lock(workload_mutex_); |
| 236 | workload_cv_.wait(thread_lock, [this] { |
| 237 | return has_new_workload_ || workload_thread_exiting_; |
| 238 | }); |
| 239 | has_new_workload_ = false; |
| 240 | exiting = workload_thread_exiting_; |
| 241 | } |
| 242 | |
| 243 | std::vector<StreamBufferCacheManager::StreamBufferCache*> stream_buffer_caches; |
| 244 | { |
| 245 | std::unique_lock<std::mutex> map_lock(caches_map_mutex_); |
| 246 | for (auto& [stream_id, cache] : stream_buffer_caches_) { |
| 247 | stream_buffer_caches.push_back(cache.get()); |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | { |
| 252 | std::unique_lock<std::mutex> flush_lock(flush_mutex_); |
| 253 | for (auto& stream_buffer_cache : stream_buffer_caches) { |
| 254 | status_t res = stream_buffer_cache->UpdateCache(exiting); |
| 255 | if (res != OK) { |
| 256 | ALOGE("%s: Updating(flush/refill) cache failed.", __FUNCTION__); |
| 257 | } |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | if (exiting) { |
| 262 | ALOGI("%s: Exiting stream buffer cache manager workload thread.", |
| 263 | __FUNCTION__); |
| 264 | return; |
| 265 | } |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | void StreamBufferCacheManager::NotifyThreadWorkload() { |
| 270 | { |
| 271 | std::lock_guard<std::mutex> lock(workload_mutex_); |
| 272 | has_new_workload_ = true; |
| 273 | } |
| 274 | workload_cv_.notify_one(); |
| 275 | } |
| 276 | |
| 277 | std::unique_ptr<StreamBufferCacheManager::StreamBufferCache> |
| 278 | StreamBufferCacheManager::StreamBufferCache::Create( |
| 279 | const StreamBufferCacheRegInfo& reg_info, |
| 280 | NotifyManagerThreadWorkloadFunc notify, |
| 281 | IHalBufferAllocator* dummy_buffer_allocator) { |
| 282 | if (notify == nullptr || dummy_buffer_allocator == nullptr) { |
| 283 | ALOGE("%s: notify is nullptr or dummy_buffer_allocator is nullptr.", |
| 284 | __FUNCTION__); |
| 285 | return nullptr; |
| 286 | } |
| 287 | |
| 288 | auto cache = std::unique_ptr<StreamBufferCacheManager::StreamBufferCache>( |
| 289 | new StreamBufferCacheManager::StreamBufferCache(reg_info, notify, |
| 290 | dummy_buffer_allocator)); |
| 291 | if (cache == nullptr) { |
| 292 | ALOGE("%s: Failed to create stream buffer cache.", __FUNCTION__); |
| 293 | return nullptr; |
| 294 | } |
| 295 | |
| 296 | return cache; |
| 297 | } |
| 298 | |
| 299 | StreamBufferCacheManager::StreamBufferCache::StreamBufferCache( |
| 300 | const StreamBufferCacheRegInfo& reg_info, |
| 301 | NotifyManagerThreadWorkloadFunc notify, |
| 302 | IHalBufferAllocator* dummy_buffer_allocator) |
| 303 | : cache_info_(reg_info) { |
| 304 | std::lock_guard<std::mutex> lock(cache_access_mutex_); |
| 305 | notify_for_workload_ = notify; |
| 306 | dummy_buffer_allocator_ = dummy_buffer_allocator; |
| 307 | } |
| 308 | |
| 309 | status_t StreamBufferCacheManager::StreamBufferCache::UpdateCache( |
| 310 | bool forced_flushing) { |
| 311 | status_t res = OK; |
| 312 | std::unique_lock<std::mutex> cache_lock(cache_access_mutex_); |
Shuzhen Wang | 4a1bd0b | 2021-02-02 17:31:12 -0800 | [diff] [blame] | 313 | if (forced_flushing || !is_active_) { |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 314 | res = FlushLocked(forced_flushing); |
| 315 | if (res != OK) { |
| 316 | ALOGE("%s: Failed to flush stream buffer cache for stream %d", |
| 317 | __FUNCTION__, cache_info_.stream_id); |
| 318 | return res; |
| 319 | } |
| 320 | } else if (RefillableLocked()) { |
| 321 | cache_lock.unlock(); |
| 322 | res = Refill(); |
| 323 | if (res != OK) { |
| 324 | ALOGE("%s: Failed to refill stream buffer cache for stream %d", |
| 325 | __FUNCTION__, cache_info_.stream_id); |
| 326 | return res; |
| 327 | } |
| 328 | } |
| 329 | return OK; |
| 330 | } |
| 331 | |
| 332 | status_t StreamBufferCacheManager::StreamBufferCache::GetBuffer( |
| 333 | StreamBufferRequestResult* res) { |
| 334 | std::unique_lock<std::mutex> cache_lock(cache_access_mutex_); |
| 335 | |
Shuzhen Wang | 4a1bd0b | 2021-02-02 17:31:12 -0800 | [diff] [blame] | 336 | // 0. the buffer cache must be active |
| 337 | if (!is_active_) { |
| 338 | ALOGW("%s: The buffer cache for stream %d is not active.", __FUNCTION__, |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 339 | cache_info_.stream_id); |
| 340 | return INVALID_OPERATION; |
| 341 | } |
| 342 | |
Shuzhen Wang | 4a1bd0b | 2021-02-02 17:31:12 -0800 | [diff] [blame] | 343 | // 1. check if the cache is deactived |
| 344 | if (stream_deactived_) { |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 345 | res->is_dummy_buffer = true; |
| 346 | res->buffer = dummy_buffer_; |
| 347 | return OK; |
| 348 | } |
| 349 | |
| 350 | // 2. check if there is any buffer available in the cache. If not, try |
| 351 | // to wait for a short period and check again. In case of timeout, use the |
| 352 | // dummy buffer instead. |
| 353 | if (cached_buffers_.empty()) { |
| 354 | // In case the GetStreamBufer is called after NotifyFlushingAll, this will |
| 355 | // be the first event that should trigger the dedicated thread to restart |
| 356 | // and refill the caches. An extra notification of thread workload is |
| 357 | // harmless and will be bypassed very quickly. |
| 358 | cache_lock.unlock(); |
| 359 | notify_for_workload_(); |
| 360 | cache_lock.lock(); |
| 361 | // Need to check this again since the state may change after the lock is |
| 362 | // acquired for the second time. |
| 363 | if (cached_buffers_.empty()) { |
| 364 | // Wait for a certain amount of time for the cache to be refilled |
| 365 | if (cache_access_cv_.wait_for(cache_lock, kBufferWaitingTimeOutSec) == |
| 366 | std::cv_status::timeout) { |
| 367 | ALOGW("%s: StreamBufferCache for stream %d waiting for refill timeout.", |
| 368 | __FUNCTION__, cache_info_.stream_id); |
| 369 | } |
| 370 | } |
| 371 | } |
| 372 | |
| 373 | // 3. use dummy buffer if the cache is still empty |
| 374 | if (cached_buffers_.empty()) { |
| 375 | // Only allocate dummy buffer for the first time |
| 376 | if (dummy_buffer_.buffer == nullptr) { |
| 377 | status_t result = AllocateDummyBufferLocked(); |
| 378 | if (result != OK) { |
| 379 | ALOGE("%s: Allocate dummy buffer failed.", __FUNCTION__); |
| 380 | return UNKNOWN_ERROR; |
| 381 | } |
| 382 | } |
| 383 | res->is_dummy_buffer = true; |
| 384 | res->buffer = dummy_buffer_; |
| 385 | return OK; |
| 386 | } else { |
| 387 | res->is_dummy_buffer = false; |
| 388 | res->buffer = cached_buffers_.back(); |
| 389 | cached_buffers_.pop_back(); |
| 390 | } |
| 391 | |
| 392 | return OK; |
| 393 | } |
| 394 | |
| 395 | bool StreamBufferCacheManager::StreamBufferCache::IsStreamDeactivated() { |
| 396 | std::unique_lock<std::mutex> lock(cache_access_mutex_); |
| 397 | return stream_deactived_; |
| 398 | } |
| 399 | |
Shuzhen Wang | 4a1bd0b | 2021-02-02 17:31:12 -0800 | [diff] [blame] | 400 | void StreamBufferCacheManager::StreamBufferCache::SetManagerState(bool active) { |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 401 | std::unique_lock<std::mutex> lock(cache_access_mutex_); |
Shuzhen Wang | 4a1bd0b | 2021-02-02 17:31:12 -0800 | [diff] [blame] | 402 | is_active_ = active; |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 403 | } |
| 404 | |
| 405 | status_t StreamBufferCacheManager::StreamBufferCache::FlushLocked( |
| 406 | bool forced_flushing) { |
Shuzhen Wang | 4a1bd0b | 2021-02-02 17:31:12 -0800 | [diff] [blame] | 407 | if (is_active_ && !forced_flushing) { |
| 408 | ALOGI("%s: Active stream buffer cache is not notified for forced flushing.", |
| 409 | __FUNCTION__); |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 410 | return INVALID_OPERATION; |
| 411 | } |
| 412 | |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 413 | if (cache_info_.return_func == nullptr) { |
| 414 | ALOGE("%s: return_func is nullptr.", __FUNCTION__); |
| 415 | return UNKNOWN_ERROR; |
| 416 | } |
| 417 | |
| 418 | if (cached_buffers_.empty()) { |
| 419 | ALOGV("%s: Stream buffer cache is already empty.", __FUNCTION__); |
| 420 | ReleaseDummyBufferLocked(); |
| 421 | return OK; |
| 422 | } |
| 423 | |
| 424 | status_t res = cache_info_.return_func(cached_buffers_); |
| 425 | if (res != OK) { |
| 426 | ALOGE("%s: Failed to return buffers.", __FUNCTION__); |
| 427 | return res; |
| 428 | } |
| 429 | |
| 430 | cached_buffers_.clear(); |
| 431 | ReleaseDummyBufferLocked(); |
| 432 | |
| 433 | return OK; |
| 434 | } |
| 435 | |
| 436 | status_t StreamBufferCacheManager::StreamBufferCache::Refill() { |
| 437 | int32_t num_buffers_to_acquire = 0; |
| 438 | { |
| 439 | std::unique_lock<std::mutex> cache_lock(cache_access_mutex_); |
| 440 | if (cache_info_.request_func == nullptr) { |
| 441 | ALOGE("%s: request_func is nullptr.", __FUNCTION__); |
| 442 | return UNKNOWN_ERROR; |
| 443 | } |
| 444 | |
Shuzhen Wang | 4a1bd0b | 2021-02-02 17:31:12 -0800 | [diff] [blame] | 445 | if (!is_active_) { |
| 446 | ALOGI("%s: Buffer cache is not active.", __FUNCTION__); |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 447 | return UNKNOWN_ERROR; |
| 448 | } |
| 449 | |
Shuzhen Wang | 4a1bd0b | 2021-02-02 17:31:12 -0800 | [diff] [blame] | 450 | if (stream_deactived_) { |
| 451 | ALOGI("%s: Stream already deactived.", __FUNCTION__); |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 452 | return OK; |
| 453 | } |
| 454 | |
| 455 | if (cached_buffers_.size() >= cache_info_.num_buffers_to_cache) { |
| 456 | ALOGV("%s: Stream buffer cache is already full.", __FUNCTION__); |
| 457 | return INVALID_OPERATION; |
| 458 | } |
| 459 | |
| 460 | num_buffers_to_acquire = |
| 461 | cache_info_.num_buffers_to_cache - cached_buffers_.size(); |
| 462 | } |
| 463 | |
| 464 | // Requesting buffer from the provider can take long(e.g. even > 1sec), |
| 465 | // consumer should not be blocked by this procedure and can get dummy buffer |
| 466 | // to unblock other pipelines. Thus, cache_access_mutex_ doesn't need to be |
| 467 | // locked here. |
| 468 | std::vector<StreamBuffer> buffers; |
| 469 | StreamBufferRequestError req_status = StreamBufferRequestError::kOk; |
| 470 | status_t res = |
| 471 | cache_info_.request_func(num_buffers_to_acquire, &buffers, &req_status); |
| 472 | |
| 473 | std::unique_lock<std::mutex> cache_lock(cache_access_mutex_); |
| 474 | if (res != OK) { |
| 475 | status_t result = AllocateDummyBufferLocked(); |
| 476 | if (result != OK) { |
| 477 | ALOGE("%s: Allocate dummy buffer failed.", __FUNCTION__); |
| 478 | return UNKNOWN_ERROR; |
| 479 | } |
| 480 | } |
| 481 | |
| 482 | if (buffers.empty() || res != OK) { |
| 483 | ALOGW("%s: Failed to acquire buffer for stream %d, error %d", __FUNCTION__, |
| 484 | cache_info_.stream_id, req_status); |
| 485 | switch (req_status) { |
| 486 | case StreamBufferRequestError::kNoBufferAvailable: |
| 487 | case StreamBufferRequestError::kMaxBufferExceeded: |
| 488 | ALOGI( |
| 489 | "%s: No buffer available or max buffer exceeded for stream %d. " |
| 490 | "Will retry for next request or when refilling other streams.", |
| 491 | __FUNCTION__, cache_info_.stream_id); |
| 492 | break; |
| 493 | case StreamBufferRequestError::kStreamDisconnected: |
| 494 | case StreamBufferRequestError::kUnknownError: |
| 495 | ALOGW( |
| 496 | "%s: Stream %d is disconnected or unknown error observed." |
| 497 | "This stream is marked as inactive.", |
| 498 | __FUNCTION__, cache_info_.stream_id); |
| 499 | ALOGI("%s: Stream %d begin to use dummy buffer.", __FUNCTION__, |
| 500 | cache_info_.stream_id); |
| 501 | stream_deactived_ = true; |
| 502 | break; |
| 503 | default: |
| 504 | ALOGE("%s: Unknown error code: %d", __FUNCTION__, req_status); |
| 505 | break; |
| 506 | } |
| 507 | } else { |
| 508 | for (auto& buffer : buffers) { |
| 509 | cached_buffers_.push_back(buffer); |
| 510 | } |
| 511 | } |
| 512 | |
| 513 | cache_access_cv_.notify_one(); |
| 514 | |
| 515 | return OK; |
| 516 | } |
| 517 | |
| 518 | bool StreamBufferCacheManager::StreamBufferCache::RefillableLocked() const { |
Shuzhen Wang | 4a1bd0b | 2021-02-02 17:31:12 -0800 | [diff] [blame] | 519 | // No need to refill if the buffer cache is not active |
| 520 | if (!is_active_) { |
Cheng Gu | 4609c5e | 2019-10-29 17:56:02 -0700 | [diff] [blame] | 521 | return false; |
| 522 | } |
| 523 | |
| 524 | // Need to refill if the cache is not full |
| 525 | return cached_buffers_.size() < cache_info_.num_buffers_to_cache; |
| 526 | } |
| 527 | |
| 528 | status_t StreamBufferCacheManager::StreamBufferCache::AllocateDummyBufferLocked() { |
| 529 | if (dummy_buffer_.buffer != nullptr) { |
| 530 | ALOGW("%s: Dummy buffer has already been allocated.", __FUNCTION__); |
| 531 | return OK; |
| 532 | } |
| 533 | |
| 534 | HalBufferDescriptor hal_buffer_descriptor{ |
| 535 | .stream_id = cache_info_.stream_id, |
| 536 | .width = cache_info_.width, |
| 537 | .height = cache_info_.height, |
| 538 | .format = cache_info_.format, |
| 539 | .producer_flags = cache_info_.producer_flags, |
| 540 | .consumer_flags = cache_info_.consumer_flags, |
| 541 | .immediate_num_buffers = 1, |
| 542 | .max_num_buffers = 1, |
| 543 | }; |
| 544 | std::vector<buffer_handle_t> buffers; |
| 545 | |
| 546 | status_t res = |
| 547 | dummy_buffer_allocator_->AllocateBuffers(hal_buffer_descriptor, &buffers); |
| 548 | if (res != OK) { |
| 549 | ALOGE("%s: Dummy buffer allocator AllocateBuffers failed.", __FUNCTION__); |
| 550 | return res; |
| 551 | } |
| 552 | |
| 553 | if (buffers.size() != hal_buffer_descriptor.immediate_num_buffers) { |
| 554 | ALOGE("%s: Not enough buffers allocated.", __FUNCTION__); |
| 555 | return NO_MEMORY; |
| 556 | } |
| 557 | dummy_buffer_.stream_id = cache_info_.stream_id; |
| 558 | dummy_buffer_.buffer = buffers[0]; |
| 559 | ALOGI("%s: [sbc] Dummy buffer allocated: strm %d buffer %p", __FUNCTION__, |
| 560 | dummy_buffer_.stream_id, dummy_buffer_.buffer); |
| 561 | |
| 562 | return OK; |
| 563 | } |
| 564 | |
| 565 | void StreamBufferCacheManager::StreamBufferCache::ReleaseDummyBufferLocked() { |
| 566 | // Release dummy buffer if ever acquired from the dummy_buffer_allocator_. |
| 567 | if (dummy_buffer_.buffer != nullptr) { |
| 568 | std::vector<buffer_handle_t> buffers(1, dummy_buffer_.buffer); |
| 569 | dummy_buffer_allocator_->FreeBuffers(&buffers); |
| 570 | dummy_buffer_.buffer = nullptr; |
| 571 | } |
| 572 | } |
| 573 | |
| 574 | status_t StreamBufferCacheManager::GetStreamBufferCache( |
| 575 | int32_t stream_id, StreamBufferCache** stream_buffer_cache) { |
| 576 | std::unique_lock<std::mutex> map_lock(caches_map_mutex_); |
| 577 | if (stream_buffer_caches_.find(stream_id) == stream_buffer_caches_.end()) { |
| 578 | ALOGE("%s: Sream %d can not be found.", __FUNCTION__, stream_id); |
| 579 | return BAD_VALUE; |
| 580 | } |
| 581 | |
| 582 | *stream_buffer_cache = stream_buffer_caches_[stream_id].get(); |
| 583 | if (*stream_buffer_cache == nullptr) { |
| 584 | ALOGE("%s: Get null cache pointer.", __FUNCTION__); |
| 585 | return UNKNOWN_ERROR; |
| 586 | } |
| 587 | return OK; |
| 588 | } |
| 589 | |
| 590 | } // namespace google_camera_hal |
| 591 | } // namespace android |