Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 1 | #include "include/private/dvr/buffer_hub_queue_client.h" |
| 2 | |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 3 | #include <inttypes.h> |
| 4 | #include <log/log.h> |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 5 | #include <sys/epoll.h> |
| 6 | |
| 7 | #include <array> |
| 8 | |
| 9 | #include <pdx/default_transport/client_channel.h> |
| 10 | #include <pdx/default_transport/client_channel_factory.h> |
| 11 | #include <pdx/file_handle.h> |
| 12 | #include <private/dvr/bufferhub_rpc.h> |
| 13 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 14 | using android::pdx::ErrorStatus; |
| 15 | using android::pdx::LocalChannelHandle; |
| 16 | using android::pdx::Status; |
| 17 | |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 18 | namespace android { |
| 19 | namespace dvr { |
| 20 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 21 | BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle) |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 22 | : Client{pdx::default_transport::ClientChannel::Create( |
| 23 | std::move(channel_handle))}, |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 24 | meta_size_(0), |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 25 | buffers_(BufferHubQueue::kMaxQueueCapacity), |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 26 | epollhup_pending_(BufferHubQueue::kMaxQueueCapacity, false), |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 27 | available_buffers_(BufferHubQueue::kMaxQueueCapacity), |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 28 | fences_(BufferHubQueue::kMaxQueueCapacity), |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 29 | capacity_(0), |
| 30 | id_(-1) { |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 31 | Initialize(); |
| 32 | } |
| 33 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 34 | BufferHubQueue::BufferHubQueue(const std::string& endpoint_path) |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 35 | : Client{pdx::default_transport::ClientChannelFactory::Create( |
| 36 | endpoint_path)}, |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 37 | meta_size_(0), |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 38 | buffers_(BufferHubQueue::kMaxQueueCapacity), |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 39 | epollhup_pending_(BufferHubQueue::kMaxQueueCapacity, false), |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 40 | available_buffers_(BufferHubQueue::kMaxQueueCapacity), |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 41 | fences_(BufferHubQueue::kMaxQueueCapacity), |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 42 | capacity_(0), |
| 43 | id_(-1) { |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 44 | Initialize(); |
| 45 | } |
| 46 | |
| 47 | void BufferHubQueue::Initialize() { |
| 48 | int ret = epoll_fd_.Create(); |
| 49 | if (ret < 0) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 50 | ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s", |
| 51 | strerror(-ret)); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 52 | return; |
| 53 | } |
| 54 | |
| 55 | epoll_event event = {.events = EPOLLIN | EPOLLET, |
| 56 | .data = {.u64 = static_cast<uint64_t>( |
| 57 | BufferHubQueue::kEpollQueueEventIndex)}}; |
| 58 | ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event); |
| 59 | if (ret < 0) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 60 | ALOGE("BufferHubQueue::Initialize: Failed to add event fd to epoll set: %s", |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 61 | strerror(-ret)); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 62 | } |
| 63 | } |
| 64 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 65 | Status<void> BufferHubQueue::ImportQueue() { |
| 66 | auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>(); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 67 | if (!status) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 68 | ALOGE("BufferHubQueue::ImportQueue: Failed to import queue: %s", |
| 69 | status.GetErrorMessage().c_str()); |
| 70 | return ErrorStatus(status.error()); |
| 71 | } else { |
| 72 | SetupQueue(status.get().meta_size_bytes, status.get().id); |
| 73 | return {}; |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | void BufferHubQueue::SetupQueue(size_t meta_size_bytes, int id) { |
| 78 | meta_size_ = meta_size_bytes; |
| 79 | id_ = id; |
| 80 | meta_buffer_tmp_.reset(meta_size_ > 0 ? new uint8_t[meta_size_] : nullptr); |
| 81 | } |
| 82 | |
| 83 | std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() { |
| 84 | if (auto status = CreateConsumerQueueHandle()) |
| 85 | return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); |
| 86 | else |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 87 | return nullptr; |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 88 | } |
| 89 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 90 | std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateSilentConsumerQueue() { |
| 91 | if (auto status = CreateConsumerQueueHandle()) |
| 92 | return std::unique_ptr<ConsumerQueue>( |
| 93 | new ConsumerQueue(status.take(), true)); |
| 94 | else |
| 95 | return nullptr; |
| 96 | } |
| 97 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 98 | Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle() { |
| 99 | auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>(); |
| 100 | if (!status) { |
| 101 | ALOGE( |
| 102 | "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: " |
| 103 | "%s", |
| 104 | status.GetErrorMessage().c_str()); |
| 105 | return ErrorStatus(status.error()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 106 | } |
| 107 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 108 | return status; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 109 | } |
| 110 | |
| 111 | bool BufferHubQueue::WaitForBuffers(int timeout) { |
| 112 | std::array<epoll_event, kMaxEvents> events; |
| 113 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 114 | // Loop at least once to check for hangups. |
| 115 | do { |
| 116 | ALOGD_IF(TRACE, "BufferHubQueue::WaitForBuffers: count=%zu capacity=%zu", |
| 117 | count(), capacity()); |
| 118 | |
| 119 | // If there is already a buffer then just check for hangup without waiting. |
| 120 | const int ret = epoll_fd_.Wait(events.data(), events.size(), |
| 121 | count() == 0 ? timeout : 0); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 122 | |
| 123 | if (ret == 0) { |
Jiwen 'Steve' Cai | 25fd3fa | 2017-03-20 15:30:21 -0700 | [diff] [blame] | 124 | ALOGD_IF(TRACE, "Wait on epoll returns nothing before timeout."); |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 125 | return count() != 0; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 126 | } |
| 127 | |
| 128 | if (ret < 0 && ret != -EINTR) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 129 | ALOGE("BufferHubQueue::WaitForBuffers: Failed to wait for buffers: %s", |
| 130 | strerror(-ret)); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 131 | return false; |
| 132 | } |
| 133 | |
| 134 | const int num_events = ret; |
| 135 | |
| 136 | // A BufferQueue's epoll fd tracks N+1 events, where there are N events, |
| 137 | // one for each buffer, in the queue and one extra event for the queue |
| 138 | // client itself. |
| 139 | for (int i = 0; i < num_events; i++) { |
| 140 | int64_t index = static_cast<int64_t>(events[i].data.u64); |
| 141 | |
Jiwen 'Steve' Cai | 25fd3fa | 2017-03-20 15:30:21 -0700 | [diff] [blame] | 142 | ALOGD_IF(TRACE, "New BufferHubQueue event %d: index=%" PRId64, i, index); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 143 | |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 144 | if (is_buffer_event_index(index)) { |
| 145 | HandleBufferEvent(static_cast<size_t>(index), events[i]); |
| 146 | } else if (is_queue_event_index(index)) { |
| 147 | HandleQueueEvent(events[i]); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 148 | } else { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 149 | ALOGW("BufferHubQueue::WaitForBuffers: Unknown event index: %" PRId64, |
| 150 | index); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 151 | } |
| 152 | } |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 153 | } while (count() == 0 && capacity() > 0 && !hung_up()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 154 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 155 | return count() != 0; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 156 | } |
| 157 | |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 158 | void BufferHubQueue::HandleBufferEvent(size_t slot, const epoll_event& event) { |
| 159 | auto buffer = buffers_[slot]; |
| 160 | if (!buffer) { |
| 161 | ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot); |
| 162 | return; |
| 163 | } |
| 164 | |
| 165 | auto status = buffer->GetEventMask(event.events); |
| 166 | if (!status) { |
| 167 | ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s", |
| 168 | status.GetErrorMessage().c_str()); |
| 169 | return; |
| 170 | } |
| 171 | |
| 172 | int events = status.get(); |
| 173 | if (events & EPOLLIN) { |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 174 | int ret = OnBufferReady(buffer, &fences_[slot]); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 175 | if (ret < 0) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 176 | ALOGE("BufferHubQueue::HandleBufferEvent: Failed to set buffer ready: %s", |
| 177 | strerror(-ret)); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 178 | return; |
| 179 | } |
| 180 | Enqueue(buffer, slot); |
| 181 | } else if (events & EPOLLHUP) { |
| 182 | // This might be caused by producer replacing an existing buffer slot, or |
| 183 | // when BufferHubQueue is shutting down. For the first case, currently the |
| 184 | // epoll FD is cleaned up when the replacement consumer client is imported, |
| 185 | // we shouldn't detach again if |epollhub_pending_[slot]| is set. |
| 186 | ALOGW( |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 187 | "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP at slot: %zu, " |
| 188 | "buffer event fd: %d, EPOLLHUP pending: %d", |
Alex Vakulenko | a1336cf | 2017-03-31 08:29:28 -0700 | [diff] [blame] | 189 | slot, buffer->event_fd(), int{epollhup_pending_[slot]}); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 190 | if (epollhup_pending_[slot]) { |
| 191 | epollhup_pending_[slot] = false; |
| 192 | } else { |
| 193 | DetachBuffer(slot); |
| 194 | } |
| 195 | } else { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 196 | ALOGW( |
| 197 | "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll " |
| 198 | "events=%d", |
| 199 | slot, events); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 200 | } |
| 201 | } |
| 202 | |
| 203 | void BufferHubQueue::HandleQueueEvent(const epoll_event& event) { |
| 204 | auto status = GetEventMask(event.events); |
| 205 | if (!status) { |
| 206 | ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s", |
| 207 | status.GetErrorMessage().c_str()); |
| 208 | return; |
| 209 | } |
| 210 | |
| 211 | int events = status.get(); |
| 212 | if (events & EPOLLIN) { |
| 213 | // Note that after buffer imports, if |count()| still returns 0, epoll |
| 214 | // wait will be tried again to acquire the newly imported buffer. |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 215 | auto buffer_status = OnBufferAllocated(); |
| 216 | if (!buffer_status) { |
| 217 | ALOGE("BufferHubQueue::HandleQueueEvent: Failed to import buffer: %s", |
| 218 | buffer_status.GetErrorMessage().c_str()); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 219 | } |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 220 | } else if (events & EPOLLHUP) { |
| 221 | ALOGD_IF(TRACE, "BufferHubQueue::HandleQueueEvent: hang up event!"); |
| 222 | hung_up_ = true; |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 223 | } else { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 224 | ALOGW("BufferHubQueue::HandleQueueEvent: Unknown epoll events=%d", events); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 225 | } |
| 226 | } |
| 227 | |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 228 | int BufferHubQueue::AddBuffer(const std::shared_ptr<BufferHubBuffer>& buf, |
| 229 | size_t slot) { |
| 230 | if (is_full()) { |
| 231 | // TODO(jwcai) Move the check into Producer's AllocateBuffer and consumer's |
| 232 | // import buffer. |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 233 | ALOGE("BufferHubQueue::AddBuffer queue is at maximum capacity: %zu", |
| 234 | capacity_); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 235 | return -E2BIG; |
| 236 | } |
| 237 | |
| 238 | if (buffers_[slot] != nullptr) { |
| 239 | // Replace the buffer if the slot is preoccupied. This could happen when the |
| 240 | // producer side replaced the slot with a newly allocated buffer. Detach the |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 241 | // buffer before setting up with the new one. |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 242 | DetachBuffer(slot); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 243 | epollhup_pending_[slot] = true; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 244 | } |
| 245 | |
| 246 | epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u64 = slot}}; |
| 247 | const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, buf->event_fd(), &event); |
| 248 | if (ret < 0) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 249 | ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s", |
| 250 | strerror(-ret)); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 251 | return ret; |
| 252 | } |
| 253 | |
| 254 | buffers_[slot] = buf; |
| 255 | capacity_++; |
| 256 | return 0; |
| 257 | } |
| 258 | |
| 259 | int BufferHubQueue::DetachBuffer(size_t slot) { |
| 260 | auto& buf = buffers_[slot]; |
| 261 | if (buf == nullptr) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 262 | ALOGE("BufferHubQueue::DetachBuffer: Invalid slot: %zu", slot); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 263 | return -EINVAL; |
| 264 | } |
| 265 | |
| 266 | const int ret = epoll_fd_.Control(EPOLL_CTL_DEL, buf->event_fd(), nullptr); |
| 267 | if (ret < 0) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 268 | ALOGE( |
| 269 | "BufferHubQueue::DetachBuffer: Failed to detach buffer from epoll set: " |
| 270 | "%s", |
| 271 | strerror(-ret)); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 272 | return ret; |
| 273 | } |
| 274 | |
| 275 | buffers_[slot] = nullptr; |
| 276 | capacity_--; |
| 277 | return 0; |
| 278 | } |
| 279 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 280 | void BufferHubQueue::Enqueue(const std::shared_ptr<BufferHubBuffer>& buf, |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 281 | size_t slot) { |
| 282 | if (count() == capacity_) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 283 | ALOGE("BufferHubQueue::Enqueue: Buffer queue is full!"); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 284 | return; |
| 285 | } |
| 286 | |
| 287 | // Set slot buffer back to vector. |
| 288 | // TODO(jwcai) Here have to dynamically allocate BufferInfo::metadata due to |
| 289 | // the limitation of the RingBuffer we are using. Would be better to refactor |
| 290 | // that. |
| 291 | BufferInfo buffer_info(slot, meta_size_); |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 292 | buffer_info.buffer = buf; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 293 | // Swap metadata loaded during onBufferReady into vector. |
| 294 | std::swap(buffer_info.metadata, meta_buffer_tmp_); |
| 295 | |
| 296 | available_buffers_.Append(std::move(buffer_info)); |
| 297 | } |
| 298 | |
| 299 | std::shared_ptr<BufferHubBuffer> BufferHubQueue::Dequeue(int timeout, |
| 300 | size_t* slot, |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 301 | void* meta, |
| 302 | LocalHandle* fence) { |
Jiwen 'Steve' Cai | 25fd3fa | 2017-03-20 15:30:21 -0700 | [diff] [blame] | 303 | ALOGD_IF(TRACE, "Dequeue: count=%zu, timeout=%d", count(), timeout); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 304 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 305 | if (!WaitForBuffers(timeout)) |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 306 | return nullptr; |
| 307 | |
| 308 | std::shared_ptr<BufferHubBuffer> buf; |
| 309 | BufferInfo& buffer_info = available_buffers_.Front(); |
| 310 | |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 311 | *fence = std::move(fences_[buffer_info.slot]); |
| 312 | |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 313 | // Report current pos as the output slot. |
| 314 | std::swap(buffer_info.slot, *slot); |
| 315 | // Swap buffer from vector to be returned later. |
| 316 | std::swap(buffer_info.buffer, buf); |
| 317 | // Swap metadata from vector into tmp so that we can write out to |meta|. |
| 318 | std::swap(buffer_info.metadata, meta_buffer_tmp_); |
| 319 | |
| 320 | available_buffers_.PopFront(); |
| 321 | |
| 322 | if (!buf) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 323 | ALOGE("BufferHubQueue::Dequeue: Buffer to be dequeued is nullptr"); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 324 | return nullptr; |
| 325 | } |
| 326 | |
| 327 | if (meta) { |
| 328 | std::copy(meta_buffer_tmp_.get(), meta_buffer_tmp_.get() + meta_size_, |
| 329 | reinterpret_cast<uint8_t*>(meta)); |
| 330 | } |
| 331 | |
| 332 | return buf; |
| 333 | } |
| 334 | |
| 335 | ProducerQueue::ProducerQueue(size_t meta_size) |
Jiwen 'Steve' Cai | 0057fdd | 2017-05-02 11:21:18 -0700 | [diff] [blame] | 336 | : ProducerQueue(meta_size, 0, 0, 0, 0) {} |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 337 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 338 | ProducerQueue::ProducerQueue(LocalChannelHandle handle) |
| 339 | : BASE(std::move(handle)) { |
| 340 | auto status = ImportQueue(); |
| 341 | if (!status) { |
| 342 | ALOGE("ProducerQueue::ProducerQueue: Failed to import queue: %s", |
| 343 | status.GetErrorMessage().c_str()); |
| 344 | Close(-status.error()); |
| 345 | } |
| 346 | } |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 347 | |
Jiwen 'Steve' Cai | 0057fdd | 2017-05-02 11:21:18 -0700 | [diff] [blame] | 348 | ProducerQueue::ProducerQueue(size_t meta_size, uint64_t usage_set_mask, |
| 349 | uint64_t usage_clear_mask, |
| 350 | uint64_t usage_deny_set_mask, |
| 351 | uint64_t usage_deny_clear_mask) |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 352 | : BASE(BufferHubRPC::kClientPath) { |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 353 | auto status = InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>( |
Jiwen 'Steve' Cai | 0057fdd | 2017-05-02 11:21:18 -0700 | [diff] [blame] | 354 | meta_size, UsagePolicy{usage_set_mask, usage_clear_mask, |
| 355 | usage_deny_set_mask, usage_deny_clear_mask}); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 356 | if (!status) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 357 | ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s", |
| 358 | status.GetErrorMessage().c_str()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 359 | Close(-status.error()); |
| 360 | return; |
| 361 | } |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 362 | |
| 363 | SetupQueue(status.get().meta_size_bytes, status.get().id); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 364 | } |
| 365 | |
Corey Tabaka | cd52dd9 | 2017-04-07 18:03:57 -0700 | [diff] [blame] | 366 | int ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height, |
Jiwen 'Steve' Cai | 0057fdd | 2017-05-02 11:21:18 -0700 | [diff] [blame] | 367 | uint32_t format, uint64_t usage, |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 368 | size_t slice_count, size_t* out_slot) { |
| 369 | if (out_slot == nullptr) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 370 | ALOGE("ProducerQueue::AllocateBuffer: Parameter out_slot cannot be null."); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 371 | return -EINVAL; |
| 372 | } |
| 373 | |
| 374 | if (is_full()) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 375 | ALOGE("ProducerQueue::AllocateBuffer queue is at maximum capacity: %zu", |
| 376 | capacity()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 377 | return -E2BIG; |
| 378 | } |
| 379 | |
| 380 | const size_t kBufferCount = 1U; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 381 | Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status = |
| 382 | InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>( |
Jiwen 'Steve' Cai | 0057fdd | 2017-05-02 11:21:18 -0700 | [diff] [blame] | 383 | width, height, format, usage, slice_count, kBufferCount); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 384 | if (!status) { |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 385 | ALOGE("ProducerQueue::AllocateBuffer failed to create producer buffer: %s", |
| 386 | status.GetErrorMessage().c_str()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 387 | return -status.error(); |
| 388 | } |
| 389 | |
| 390 | auto buffer_handle_slots = status.take(); |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 391 | LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != kBufferCount, |
| 392 | "BufferHubRPC::ProducerQueueAllocateBuffers should " |
| 393 | "return one and only one buffer handle."); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 394 | |
| 395 | // We only allocate one buffer at a time. |
| 396 | auto& buffer_handle = buffer_handle_slots[0].first; |
| 397 | size_t buffer_slot = buffer_handle_slots[0].second; |
Jiwen 'Steve' Cai | 25fd3fa | 2017-03-20 15:30:21 -0700 | [diff] [blame] | 398 | ALOGD_IF(TRACE, |
| 399 | "ProducerQueue::AllocateBuffer, new buffer, channel_handle: %d", |
| 400 | buffer_handle.value()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 401 | |
| 402 | *out_slot = buffer_slot; |
| 403 | return AddBuffer(BufferProducer::Import(std::move(buffer_handle)), |
| 404 | buffer_slot); |
| 405 | } |
| 406 | |
| 407 | int ProducerQueue::AddBuffer(const std::shared_ptr<BufferProducer>& buf, |
| 408 | size_t slot) { |
| 409 | // For producer buffer, we need to enqueue the newly added buffer |
| 410 | // immediately. Producer queue starts with all buffers in available state. |
| 411 | const int ret = BufferHubQueue::AddBuffer(buf, slot); |
| 412 | if (ret < 0) |
| 413 | return ret; |
| 414 | |
| 415 | Enqueue(buf, slot); |
| 416 | return 0; |
| 417 | } |
| 418 | |
| 419 | int ProducerQueue::DetachBuffer(size_t slot) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 420 | auto status = |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 421 | InvokeRemoteMethod<BufferHubRPC::ProducerQueueDetachBuffer>(slot); |
| 422 | if (!status) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 423 | ALOGE( |
| 424 | "ProducerQueue::DetachBuffer failed to detach producer buffer through " |
| 425 | "BufferHub, error: %s", |
| 426 | status.GetErrorMessage().c_str()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 427 | return -status.error(); |
| 428 | } |
| 429 | |
| 430 | return BufferHubQueue::DetachBuffer(slot); |
| 431 | } |
| 432 | |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 433 | std::shared_ptr<BufferProducer> ProducerQueue::Dequeue( |
| 434 | int timeout, size_t* slot, LocalHandle* release_fence) { |
| 435 | if (slot == nullptr || release_fence == nullptr) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 436 | ALOGE( |
| 437 | "ProducerQueue::Dequeue: invalid parameter, slot=%p, release_fence=%p", |
| 438 | slot, release_fence); |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 439 | return nullptr; |
| 440 | } |
| 441 | |
| 442 | auto buf = BufferHubQueue::Dequeue(timeout, slot, nullptr, release_fence); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 443 | return std::static_pointer_cast<BufferProducer>(buf); |
| 444 | } |
| 445 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 446 | int ProducerQueue::OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf, |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 447 | LocalHandle* release_fence) { |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 448 | auto buffer = std::static_pointer_cast<BufferProducer>(buf); |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 449 | return buffer->Gain(release_fence); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 450 | } |
| 451 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 452 | ConsumerQueue::ConsumerQueue(LocalChannelHandle handle, bool ignore_on_import) |
| 453 | : BufferHubQueue(std::move(handle)), ignore_on_import_(ignore_on_import) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 454 | auto status = ImportQueue(); |
| 455 | if (!status) { |
| 456 | ALOGE("ConsumerQueue::ConsumerQueue: Failed to import queue: %s", |
| 457 | status.GetErrorMessage().c_str()); |
| 458 | Close(-status.error()); |
| 459 | } |
| 460 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 461 | auto import_status = ImportBuffers(); |
| 462 | if (import_status) { |
| 463 | ALOGI("ConsumerQueue::ConsumerQueue: Imported %zu buffers.", |
| 464 | import_status.get()); |
| 465 | } else { |
| 466 | ALOGE("ConsumerQueue::ConsumerQueue: Failed to import buffers: %s", |
| 467 | import_status.GetErrorMessage().c_str()); |
| 468 | } |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 469 | } |
| 470 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 471 | Status<size_t> ConsumerQueue::ImportBuffers() { |
| 472 | auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>(); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 473 | if (!status) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 474 | ALOGE( |
| 475 | "ConsumerQueue::ImportBuffers failed to import consumer buffer through " |
| 476 | "BufferBub, error: %s", |
| 477 | status.GetErrorMessage().c_str()); |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 478 | return ErrorStatus(status.error()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 479 | } |
| 480 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 481 | int ret; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 482 | int last_error = 0; |
| 483 | int imported_buffers = 0; |
| 484 | |
| 485 | auto buffer_handle_slots = status.take(); |
| 486 | for (auto& buffer_handle_slot : buffer_handle_slots) { |
Jiwen 'Steve' Cai | 25fd3fa | 2017-03-20 15:30:21 -0700 | [diff] [blame] | 487 | ALOGD_IF(TRACE, |
| 488 | "ConsumerQueue::ImportBuffers, new buffer, buffer_handle: %d", |
| 489 | buffer_handle_slot.first.value()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 490 | |
| 491 | std::unique_ptr<BufferConsumer> buffer_consumer = |
| 492 | BufferConsumer::Import(std::move(buffer_handle_slot.first)); |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 493 | |
| 494 | // Setup ignore state before adding buffer to the queue. |
| 495 | if (ignore_on_import_) { |
| 496 | ALOGD_IF(TRACE, |
| 497 | "ConsumerQueue::ImportBuffers: Setting buffer to ignored state: " |
| 498 | "buffer_id=%d", |
| 499 | buffer_consumer->id()); |
| 500 | ret = buffer_consumer->SetIgnore(true); |
| 501 | if (ret < 0) { |
| 502 | ALOGE( |
| 503 | "ConsumerQueue::ImportBuffers: Failed to set ignored state on " |
| 504 | "imported buffer buffer_id=%d: %s", |
| 505 | buffer_consumer->id(), strerror(-ret)); |
| 506 | last_error = ret; |
| 507 | } |
| 508 | } |
| 509 | |
| 510 | ret = AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 511 | if (ret < 0) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 512 | ALOGE("ConsumerQueue::ImportBuffers failed to add buffer, ret: %s", |
| 513 | strerror(-ret)); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 514 | last_error = ret; |
| 515 | continue; |
| 516 | } else { |
| 517 | imported_buffers++; |
| 518 | } |
| 519 | } |
| 520 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 521 | if (imported_buffers > 0) |
| 522 | return {imported_buffers}; |
| 523 | else |
| 524 | return ErrorStatus(-last_error); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 525 | } |
| 526 | |
| 527 | int ConsumerQueue::AddBuffer(const std::shared_ptr<BufferConsumer>& buf, |
| 528 | size_t slot) { |
| 529 | // Consumer queue starts with all buffers in unavailable state. |
| 530 | return BufferHubQueue::AddBuffer(buf, slot); |
| 531 | } |
| 532 | |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 533 | std::shared_ptr<BufferConsumer> ConsumerQueue::Dequeue( |
| 534 | int timeout, size_t* slot, void* meta, size_t meta_size, |
| 535 | LocalHandle* acquire_fence) { |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 536 | if (meta_size != meta_size_) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 537 | ALOGE( |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 538 | "ConsumerQueue::Dequeue: Metadata size (%zu) for the dequeuing buffer " |
| 539 | "does not match metadata size (%zu) for the queue.", |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 540 | meta_size, meta_size_); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 541 | return nullptr; |
| 542 | } |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 543 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 544 | if (slot == nullptr || acquire_fence == nullptr) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 545 | ALOGE( |
| 546 | "ConsumerQueue::Dequeue: Invalid parameter, slot=%p, meta=%p, " |
| 547 | "acquire_fence=%p", |
| 548 | slot, meta, acquire_fence); |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 549 | return nullptr; |
| 550 | } |
| 551 | |
| 552 | auto buf = BufferHubQueue::Dequeue(timeout, slot, meta, acquire_fence); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 553 | return std::static_pointer_cast<BufferConsumer>(buf); |
| 554 | } |
| 555 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame^] | 556 | int ConsumerQueue::OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf, |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 557 | LocalHandle* acquire_fence) { |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 558 | auto buffer = std::static_pointer_cast<BufferConsumer>(buf); |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 559 | return buffer->Acquire(acquire_fence, meta_buffer_tmp_.get(), meta_size_); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 560 | } |
| 561 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 562 | Status<void> ConsumerQueue::OnBufferAllocated() { |
| 563 | auto status = ImportBuffers(); |
| 564 | if (!status) { |
| 565 | ALOGE("ConsumerQueue::OnBufferAllocated: Failed to import buffers: %s", |
| 566 | status.GetErrorMessage().c_str()); |
| 567 | return ErrorStatus(status.error()); |
| 568 | } else if (status.get() == 0) { |
| 569 | ALOGW("ConsumerQueue::OnBufferAllocated: No new buffers allocated!"); |
| 570 | return ErrorStatus(ENOBUFS); |
| 571 | } else { |
| 572 | ALOGD_IF(TRACE, "Imported %zu consumer buffers.", status.get()); |
| 573 | return {}; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 574 | } |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 575 | } |
| 576 | |
| 577 | } // namespace dvr |
| 578 | } // namespace android |