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> |
Corey Tabaka | 2b99ee5 | 2017-05-04 10:56:05 -0700 | [diff] [blame] | 5 | #include <poll.h> |
Hendrik Wagenaar | 4d3590f | 2017-05-06 22:36:04 -0700 | [diff] [blame] | 6 | #include <sys/epoll.h> |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 7 | |
| 8 | #include <array> |
| 9 | |
| 10 | #include <pdx/default_transport/client_channel.h> |
| 11 | #include <pdx/default_transport/client_channel_factory.h> |
| 12 | #include <pdx/file_handle.h> |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 13 | #include <pdx/trace.h> |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 14 | |
Corey Tabaka | 2b99ee5 | 2017-05-04 10:56:05 -0700 | [diff] [blame] | 15 | #define RETRY_EINTR(fnc_call) \ |
| 16 | ([&]() -> decltype(fnc_call) { \ |
| 17 | decltype(fnc_call) result; \ |
| 18 | do { \ |
| 19 | result = (fnc_call); \ |
| 20 | } while (result == -1 && errno == EINTR); \ |
| 21 | return result; \ |
| 22 | })() |
| 23 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 24 | using android::pdx::ErrorStatus; |
| 25 | using android::pdx::LocalChannelHandle; |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 26 | using android::pdx::LocalHandle; |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 27 | using android::pdx::Status; |
| 28 | |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 29 | namespace android { |
| 30 | namespace dvr { |
| 31 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 32 | namespace { |
| 33 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 34 | std::pair<int32_t, int32_t> Unstuff(uint64_t value) { |
| 35 | return {static_cast<int32_t>(value >> 32), |
| 36 | static_cast<int32_t>(value & ((1ull << 32) - 1))}; |
| 37 | } |
| 38 | |
| 39 | uint64_t Stuff(int32_t a, int32_t b) { |
| 40 | const uint32_t ua = static_cast<uint32_t>(a); |
| 41 | const uint32_t ub = static_cast<uint32_t>(b); |
| 42 | return (static_cast<uint64_t>(ua) << 32) | static_cast<uint64_t>(ub); |
| 43 | } |
| 44 | |
| 45 | } // anonymous namespace |
| 46 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 47 | BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle) |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 48 | : Client{pdx::default_transport::ClientChannel::Create( |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 49 | std::move(channel_handle))} { |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 50 | Initialize(); |
| 51 | } |
| 52 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 53 | BufferHubQueue::BufferHubQueue(const std::string& endpoint_path) |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 54 | : Client{ |
| 55 | pdx::default_transport::ClientChannelFactory::Create(endpoint_path)} { |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 56 | Initialize(); |
| 57 | } |
| 58 | |
| 59 | void BufferHubQueue::Initialize() { |
| 60 | int ret = epoll_fd_.Create(); |
| 61 | if (ret < 0) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 62 | ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s", |
| 63 | strerror(-ret)); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 64 | return; |
| 65 | } |
| 66 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 67 | epoll_event event = { |
| 68 | .events = EPOLLIN | EPOLLET, |
| 69 | .data = {.u64 = Stuff(-1, BufferHubQueue::kEpollQueueEventIndex)}}; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 70 | ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event); |
| 71 | if (ret < 0) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 72 | ALOGE("BufferHubQueue::Initialize: Failed to add event fd to epoll set: %s", |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 73 | strerror(-ret)); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 74 | } |
| 75 | } |
| 76 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 77 | Status<void> BufferHubQueue::ImportQueue() { |
| 78 | auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>(); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 79 | if (!status) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 80 | ALOGE("BufferHubQueue::ImportQueue: Failed to import queue: %s", |
| 81 | status.GetErrorMessage().c_str()); |
| 82 | return ErrorStatus(status.error()); |
| 83 | } else { |
Jiwen 'Steve' Cai | 6bffc67 | 2017-05-18 23:05:05 -0700 | [diff] [blame] | 84 | SetupQueue(status.get()); |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 85 | return {}; |
| 86 | } |
| 87 | } |
| 88 | |
Jiwen 'Steve' Cai | 6bffc67 | 2017-05-18 23:05:05 -0700 | [diff] [blame] | 89 | void BufferHubQueue::SetupQueue(const QueueInfo& queue_info) { |
| 90 | is_async_ = queue_info.producer_config.is_async; |
| 91 | default_width_ = queue_info.producer_config.default_width; |
| 92 | default_height_ = queue_info.producer_config.default_height; |
| 93 | default_format_ = queue_info.producer_config.default_format; |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 94 | user_metadata_size_ = queue_info.producer_config.user_metadata_size; |
Jiwen 'Steve' Cai | 6bffc67 | 2017-05-18 23:05:05 -0700 | [diff] [blame] | 95 | id_ = queue_info.id; |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 96 | } |
| 97 | |
| 98 | std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() { |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 99 | if (auto status = CreateConsumerQueueHandle(/*silent*/ false)) |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 100 | return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); |
| 101 | else |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 102 | return nullptr; |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 103 | } |
| 104 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame] | 105 | std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateSilentConsumerQueue() { |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 106 | if (auto status = CreateConsumerQueueHandle(/*silent*/ true)) |
| 107 | return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame] | 108 | else |
| 109 | return nullptr; |
| 110 | } |
| 111 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 112 | Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle( |
| 113 | bool silent) { |
| 114 | auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>(silent); |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 115 | if (!status) { |
| 116 | ALOGE( |
| 117 | "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: " |
| 118 | "%s", |
| 119 | status.GetErrorMessage().c_str()); |
| 120 | return ErrorStatus(status.error()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 121 | } |
| 122 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 123 | return status; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 124 | } |
| 125 | |
Jiwen 'Steve' Cai | a88e3ee | 2017-11-03 17:33:33 -0700 | [diff] [blame] | 126 | pdx::Status<ConsumerQueueParcelable> |
| 127 | BufferHubQueue::CreateConsumerQueueParcelable(bool silent) { |
| 128 | auto status = CreateConsumerQueueHandle(silent); |
| 129 | if (!status) |
| 130 | return status.error_status(); |
| 131 | |
| 132 | // A temporary consumer queue client to pull its channel parcelable. |
| 133 | auto consumer_queue = |
| 134 | std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); |
| 135 | ConsumerQueueParcelable queue_parcelable( |
| 136 | consumer_queue->GetChannel()->TakeChannelParcelable()); |
| 137 | |
| 138 | if (!queue_parcelable.IsValid()) { |
| 139 | ALOGE( |
| 140 | "BufferHubQueue::CreateConsumerQueueParcelable: Failed to create " |
| 141 | "consumer queue parcelable."); |
| 142 | return ErrorStatus(EINVAL); |
| 143 | } |
| 144 | |
| 145 | return {std::move(queue_parcelable)}; |
| 146 | } |
| 147 | |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 148 | bool BufferHubQueue::WaitForBuffers(int timeout) { |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 149 | ATRACE_NAME("BufferHubQueue::WaitForBuffers"); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 150 | std::array<epoll_event, kMaxEvents> events; |
| 151 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame] | 152 | // Loop at least once to check for hangups. |
| 153 | do { |
Corey Tabaka | 2b99ee5 | 2017-05-04 10:56:05 -0700 | [diff] [blame] | 154 | ALOGD_IF( |
| 155 | TRACE, |
| 156 | "BufferHubQueue::WaitForBuffers: queue_id=%d count=%zu capacity=%zu", |
| 157 | id(), count(), capacity()); |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame] | 158 | |
| 159 | // If there is already a buffer then just check for hangup without waiting. |
| 160 | const int ret = epoll_fd_.Wait(events.data(), events.size(), |
| 161 | count() == 0 ? timeout : 0); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 162 | |
| 163 | if (ret == 0) { |
Corey Tabaka | 9d8bd09 | 2017-04-25 16:47:44 -0700 | [diff] [blame] | 164 | ALOGI_IF(TRACE, |
Corey Tabaka | 2b99ee5 | 2017-05-04 10:56:05 -0700 | [diff] [blame] | 165 | "BufferHubQueue::WaitForBuffers: No events before timeout: " |
| 166 | "queue_id=%d", |
| 167 | id()); |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame] | 168 | return count() != 0; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 169 | } |
| 170 | |
| 171 | if (ret < 0 && ret != -EINTR) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 172 | ALOGE("BufferHubQueue::WaitForBuffers: Failed to wait for buffers: %s", |
| 173 | strerror(-ret)); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 174 | return false; |
| 175 | } |
| 176 | |
| 177 | const int num_events = ret; |
| 178 | |
| 179 | // A BufferQueue's epoll fd tracks N+1 events, where there are N events, |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 180 | // one for each buffer in the queue, and one extra event for the queue |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 181 | // client itself. |
| 182 | for (int i = 0; i < num_events; i++) { |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 183 | int32_t event_fd; |
| 184 | int32_t index; |
| 185 | std::tie(event_fd, index) = Unstuff(events[i].data.u64); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 186 | |
Corey Tabaka | 4a05cbf | 2017-10-11 10:52:54 -0700 | [diff] [blame] | 187 | PDX_TRACE_FORMAT( |
| 188 | "epoll_event|queue_id=%d;num_events=%d;event_index=%d;event_fd=%d;" |
| 189 | "slot=%d|", |
| 190 | id(), num_events, i, event_fd, index); |
| 191 | |
Corey Tabaka | 9d8bd09 | 2017-04-25 16:47:44 -0700 | [diff] [blame] | 192 | ALOGD_IF(TRACE, |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 193 | "BufferHubQueue::WaitForBuffers: event %d: event_fd=%d index=%d", |
| 194 | i, event_fd, index); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 195 | |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 196 | if (is_buffer_event_index(index)) { |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 197 | HandleBufferEvent(static_cast<size_t>(index), event_fd, |
| 198 | events[i].events); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 199 | } else if (is_queue_event_index(index)) { |
Corey Tabaka | 2b99ee5 | 2017-05-04 10:56:05 -0700 | [diff] [blame] | 200 | HandleQueueEvent(events[i].events); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 201 | } else { |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 202 | ALOGW( |
| 203 | "BufferHubQueue::WaitForBuffers: Unknown event type event_fd=%d " |
| 204 | "index=%d", |
| 205 | event_fd, index); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 206 | } |
| 207 | } |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame] | 208 | } while (count() == 0 && capacity() > 0 && !hung_up()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 209 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame] | 210 | return count() != 0; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 211 | } |
| 212 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 213 | Status<void> BufferHubQueue::HandleBufferEvent(size_t slot, int event_fd, |
| 214 | int poll_events) { |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 215 | ATRACE_NAME("BufferHubQueue::HandleBufferEvent"); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 216 | if (!buffers_[slot]) { |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 217 | ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 218 | return ErrorStatus(ENOENT); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 219 | } |
| 220 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 221 | auto status = buffers_[slot]->GetEventMask(poll_events); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 222 | if (!status) { |
| 223 | ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s", |
| 224 | status.GetErrorMessage().c_str()); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 225 | return status.error_status(); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 226 | } |
| 227 | |
Corey Tabaka | 2b99ee5 | 2017-05-04 10:56:05 -0700 | [diff] [blame] | 228 | const int events = status.get(); |
Corey Tabaka | 4a05cbf | 2017-10-11 10:52:54 -0700 | [diff] [blame] | 229 | PDX_TRACE_FORMAT( |
| 230 | "buffer|queue_id=%d;buffer_id=%d;slot=%zu;event_fd=%d;poll_events=%x;" |
| 231 | "events=%d|", |
| 232 | id(), buffers_[slot]->id(), slot, event_fd, poll_events, events); |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 233 | |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 234 | if (events & EPOLLIN) { |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 235 | return Enqueue({buffers_[slot], slot, buffers_[slot]->GetQueueIndex()}); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 236 | } else if (events & EPOLLHUP) { |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 237 | ALOGW( |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 238 | "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP event: slot=%zu " |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 239 | "event_fd=%d buffer_id=%d", |
| 240 | slot, buffers_[slot]->event_fd(), buffers_[slot]->id()); |
| 241 | return RemoveBuffer(slot); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 242 | } else { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 243 | ALOGW( |
| 244 | "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll " |
| 245 | "events=%d", |
| 246 | slot, events); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 247 | } |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 248 | |
| 249 | return {}; |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 250 | } |
| 251 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 252 | Status<void> BufferHubQueue::HandleQueueEvent(int poll_event) { |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 253 | ATRACE_NAME("BufferHubQueue::HandleQueueEvent"); |
Corey Tabaka | 2b99ee5 | 2017-05-04 10:56:05 -0700 | [diff] [blame] | 254 | auto status = GetEventMask(poll_event); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 255 | if (!status) { |
| 256 | ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s", |
| 257 | status.GetErrorMessage().c_str()); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 258 | return status.error_status(); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 259 | } |
| 260 | |
Corey Tabaka | 2b99ee5 | 2017-05-04 10:56:05 -0700 | [diff] [blame] | 261 | const int events = status.get(); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 262 | if (events & EPOLLIN) { |
| 263 | // Note that after buffer imports, if |count()| still returns 0, epoll |
| 264 | // wait will be tried again to acquire the newly imported buffer. |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 265 | auto buffer_status = OnBufferAllocated(); |
| 266 | if (!buffer_status) { |
| 267 | ALOGE("BufferHubQueue::HandleQueueEvent: Failed to import buffer: %s", |
| 268 | buffer_status.GetErrorMessage().c_str()); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 269 | } |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame] | 270 | } else if (events & EPOLLHUP) { |
| 271 | ALOGD_IF(TRACE, "BufferHubQueue::HandleQueueEvent: hang up event!"); |
| 272 | hung_up_ = true; |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 273 | } else { |
Corey Tabaka | 2b99ee5 | 2017-05-04 10:56:05 -0700 | [diff] [blame] | 274 | ALOGW("BufferHubQueue::HandleQueueEvent: Unknown epoll events=%x", events); |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 275 | } |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 276 | |
| 277 | return {}; |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 278 | } |
| 279 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 280 | Status<void> BufferHubQueue::AddBuffer( |
Jiwen 'Steve' Cai | c6fcf2f | 2018-09-27 23:34:45 -0700 | [diff] [blame] | 281 | const std::shared_ptr<BufferHubBase>& buffer, size_t slot) { |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 282 | ALOGD_IF(TRACE, "BufferHubQueue::AddBuffer: buffer_id=%d slot=%zu", |
| 283 | buffer->id(), slot); |
| 284 | |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 285 | if (is_full()) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 286 | ALOGE("BufferHubQueue::AddBuffer queue is at maximum capacity: %zu", |
| 287 | capacity_); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 288 | return ErrorStatus(E2BIG); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 289 | } |
| 290 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 291 | if (buffers_[slot]) { |
| 292 | // Replace the buffer if the slot is occupied. This could happen when the |
Jiwen 'Steve' Cai | bb701db | 2017-05-23 11:15:33 -0700 | [diff] [blame] | 293 | // producer side replaced the slot with a newly allocated buffer. Remove the |
Jiwen 'Steve' Cai | dc14e5b | 2017-01-24 17:05:12 -0800 | [diff] [blame] | 294 | // buffer before setting up with the new one. |
Jiwen 'Steve' Cai | bb701db | 2017-05-23 11:15:33 -0700 | [diff] [blame] | 295 | auto remove_status = RemoveBuffer(slot); |
| 296 | if (!remove_status) |
| 297 | return remove_status.error_status(); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 298 | } |
| 299 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 300 | for (const auto& event_source : buffer->GetEventSources()) { |
| 301 | epoll_event event = {.events = event_source.event_mask | EPOLLET, |
| 302 | .data = {.u64 = Stuff(buffer->event_fd(), slot)}}; |
| 303 | const int ret = |
| 304 | epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event); |
| 305 | if (ret < 0) { |
| 306 | ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s", |
| 307 | strerror(-ret)); |
| 308 | return ErrorStatus(-ret); |
| 309 | } |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 310 | } |
| 311 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 312 | buffers_[slot] = buffer; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 313 | capacity_++; |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 314 | return {}; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 315 | } |
| 316 | |
Jiwen 'Steve' Cai | bb701db | 2017-05-23 11:15:33 -0700 | [diff] [blame] | 317 | Status<void> BufferHubQueue::RemoveBuffer(size_t slot) { |
| 318 | ALOGD_IF(TRACE, "BufferHubQueue::RemoveBuffer: slot=%zu", slot); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 319 | |
| 320 | if (buffers_[slot]) { |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 321 | for (const auto& event_source : buffers_[slot]->GetEventSources()) { |
| 322 | const int ret = |
| 323 | epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr); |
| 324 | if (ret < 0) { |
| 325 | ALOGE( |
| 326 | "BufferHubQueue::RemoveBuffer: Failed to remove buffer from epoll " |
| 327 | "set: %s", |
| 328 | strerror(-ret)); |
| 329 | return ErrorStatus(-ret); |
| 330 | } |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 331 | } |
| 332 | |
Jiwen 'Steve' Cai | 0b80155 | 2017-05-24 11:50:11 -0700 | [diff] [blame] | 333 | // Trigger OnBufferRemoved callback if registered. |
| 334 | if (on_buffer_removed_) |
| 335 | on_buffer_removed_(buffers_[slot]); |
| 336 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 337 | buffers_[slot] = nullptr; |
| 338 | capacity_--; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 339 | } |
| 340 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 341 | return {}; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 342 | } |
| 343 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 344 | Status<void> BufferHubQueue::Enqueue(Entry entry) { |
| 345 | if (!is_full()) { |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 346 | available_buffers_.push(std::move(entry)); |
Jiwen 'Steve' Cai | 0b80155 | 2017-05-24 11:50:11 -0700 | [diff] [blame] | 347 | |
| 348 | // Trigger OnBufferAvailable callback if registered. |
| 349 | if (on_buffer_available_) |
| 350 | on_buffer_available_(); |
| 351 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 352 | return {}; |
| 353 | } else { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 354 | ALOGE("BufferHubQueue::Enqueue: Buffer queue is full!"); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 355 | return ErrorStatus(E2BIG); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 356 | } |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 357 | } |
| 358 | |
Jiwen 'Steve' Cai | c6fcf2f | 2018-09-27 23:34:45 -0700 | [diff] [blame] | 359 | Status<std::shared_ptr<BufferHubBase>> BufferHubQueue::Dequeue(int timeout, |
| 360 | size_t* slot) { |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 361 | ALOGD_IF(TRACE, "BufferHubQueue::Dequeue: count=%zu, timeout=%d", count(), |
| 362 | timeout); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 363 | |
Corey Tabaka | 4a05cbf | 2017-10-11 10:52:54 -0700 | [diff] [blame] | 364 | PDX_TRACE_FORMAT("BufferHubQueue::Dequeue|count=%zu|", count()); |
| 365 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 366 | if (count() == 0) { |
| 367 | if (!WaitForBuffers(timeout)) |
| 368 | return ErrorStatus(ETIMEDOUT); |
| 369 | } |
| 370 | |
| 371 | auto& entry = available_buffers_.top(); |
Corey Tabaka | 4a05cbf | 2017-10-11 10:52:54 -0700 | [diff] [blame] | 372 | PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(), |
| 373 | entry.slot); |
| 374 | |
Jiwen 'Steve' Cai | c6fcf2f | 2018-09-27 23:34:45 -0700 | [diff] [blame] | 375 | std::shared_ptr<BufferHubBase> buffer = std::move(entry.buffer); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 376 | *slot = entry.slot; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 377 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 378 | available_buffers_.pop(); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 379 | |
| 380 | return {std::move(buffer)}; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 381 | } |
| 382 | |
Jiwen 'Steve' Cai | 0b80155 | 2017-05-24 11:50:11 -0700 | [diff] [blame] | 383 | void BufferHubQueue::SetBufferAvailableCallback( |
| 384 | BufferAvailableCallback callback) { |
| 385 | on_buffer_available_ = callback; |
| 386 | } |
| 387 | |
| 388 | void BufferHubQueue::SetBufferRemovedCallback(BufferRemovedCallback callback) { |
| 389 | on_buffer_removed_ = callback; |
| 390 | } |
| 391 | |
Jiwen 'Steve' Cai | 005f45d | 2017-08-04 17:34:37 -0700 | [diff] [blame] | 392 | pdx::Status<void> BufferHubQueue::FreeAllBuffers() { |
| 393 | // Clear all available buffers. |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 394 | while (!available_buffers_.empty()) |
| 395 | available_buffers_.pop(); |
Jiwen 'Steve' Cai | 005f45d | 2017-08-04 17:34:37 -0700 | [diff] [blame] | 396 | |
| 397 | pdx::Status<void> last_error; // No error. |
| 398 | // Clear all buffers this producer queue is tracking. |
| 399 | for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) { |
| 400 | if (buffers_[slot] != nullptr) { |
| 401 | auto status = RemoveBuffer(slot); |
| 402 | if (!status) { |
| 403 | ALOGE( |
| 404 | "ProducerQueue::FreeAllBuffers: Failed to remove buffer at " |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 405 | "slot=%zu.", |
Jiwen 'Steve' Cai | 005f45d | 2017-08-04 17:34:37 -0700 | [diff] [blame] | 406 | slot); |
| 407 | last_error = status.error_status(); |
| 408 | } |
| 409 | } |
| 410 | } |
| 411 | |
| 412 | return last_error; |
| 413 | } |
| 414 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 415 | ProducerQueue::ProducerQueue(LocalChannelHandle handle) |
| 416 | : BASE(std::move(handle)) { |
| 417 | auto status = ImportQueue(); |
| 418 | if (!status) { |
| 419 | ALOGE("ProducerQueue::ProducerQueue: Failed to import queue: %s", |
| 420 | status.GetErrorMessage().c_str()); |
| 421 | Close(-status.error()); |
| 422 | } |
| 423 | } |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 424 | |
Jiwen 'Steve' Cai | 6bffc67 | 2017-05-18 23:05:05 -0700 | [diff] [blame] | 425 | ProducerQueue::ProducerQueue(const ProducerQueueConfig& config, |
| 426 | const UsagePolicy& usage) |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 427 | : BASE(BufferHubRPC::kClientPath) { |
Jiwen 'Steve' Cai | cbd32bf | 2017-05-18 17:03:20 -0700 | [diff] [blame] | 428 | auto status = |
Jiwen 'Steve' Cai | 6bffc67 | 2017-05-18 23:05:05 -0700 | [diff] [blame] | 429 | InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>(config, usage); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 430 | if (!status) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 431 | ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s", |
| 432 | status.GetErrorMessage().c_str()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 433 | Close(-status.error()); |
| 434 | return; |
| 435 | } |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 436 | |
Jiwen 'Steve' Cai | 6bffc67 | 2017-05-18 23:05:05 -0700 | [diff] [blame] | 437 | SetupQueue(status.get()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 438 | } |
| 439 | |
Jiwen 'Steve' Cai | 8fa4e10 | 2017-05-24 23:16:54 -0700 | [diff] [blame] | 440 | Status<std::vector<size_t>> ProducerQueue::AllocateBuffers( |
| 441 | uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, |
| 442 | uint64_t usage, size_t buffer_count) { |
Tianyu | a6e33a1 | 2018-09-20 15:57:17 -0700 | [diff] [blame] | 443 | if (buffer_count == 0) { |
| 444 | return {std::vector<size_t>()}; |
| 445 | } |
| 446 | |
Jiwen 'Steve' Cai | 8fa4e10 | 2017-05-24 23:16:54 -0700 | [diff] [blame] | 447 | if (capacity() + buffer_count > kMaxQueueCapacity) { |
| 448 | ALOGE( |
| 449 | "ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot " |
| 450 | "allocate %zu more buffer(s).", |
| 451 | capacity(), buffer_count); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 452 | return ErrorStatus(E2BIG); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 453 | } |
| 454 | |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 455 | Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status = |
| 456 | InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>( |
Jiwen 'Steve' Cai | 8fa4e10 | 2017-05-24 23:16:54 -0700 | [diff] [blame] | 457 | width, height, layer_count, format, usage, buffer_count); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 458 | if (!status) { |
Jiwen 'Steve' Cai | 8fa4e10 | 2017-05-24 23:16:54 -0700 | [diff] [blame] | 459 | ALOGE("ProducerQueue::AllocateBuffers: failed to allocate buffers: %s", |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame] | 460 | status.GetErrorMessage().c_str()); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 461 | return status.error_status(); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 462 | } |
| 463 | |
| 464 | auto buffer_handle_slots = status.take(); |
Jiwen 'Steve' Cai | 8fa4e10 | 2017-05-24 23:16:54 -0700 | [diff] [blame] | 465 | LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != buffer_count, |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 466 | "BufferHubRPC::ProducerQueueAllocateBuffers should " |
Jiwen 'Steve' Cai | 8fa4e10 | 2017-05-24 23:16:54 -0700 | [diff] [blame] | 467 | "return %zu buffer handle(s), but returned %zu instead.", |
| 468 | buffer_count, buffer_handle_slots.size()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 469 | |
Jiwen 'Steve' Cai | 8fa4e10 | 2017-05-24 23:16:54 -0700 | [diff] [blame] | 470 | std::vector<size_t> buffer_slots; |
| 471 | buffer_slots.reserve(buffer_count); |
| 472 | |
| 473 | // Bookkeeping for each buffer. |
| 474 | for (auto& hs : buffer_handle_slots) { |
| 475 | auto& buffer_handle = hs.first; |
| 476 | size_t buffer_slot = hs.second; |
| 477 | |
| 478 | // Note that import might (though very unlikely) fail. If so, buffer_handle |
| 479 | // will be closed and included in returned buffer_slots. |
| 480 | if (AddBuffer(BufferProducer::Import(std::move(buffer_handle)), |
| 481 | buffer_slot)) { |
| 482 | ALOGD_IF(TRACE, "ProducerQueue::AllocateBuffers: new buffer at slot: %zu", |
| 483 | buffer_slot); |
| 484 | buffer_slots.push_back(buffer_slot); |
| 485 | } |
| 486 | } |
| 487 | |
Tianyu | a6e33a1 | 2018-09-20 15:57:17 -0700 | [diff] [blame] | 488 | if (buffer_slots.size() != buffer_count) { |
| 489 | // Error out if the count of imported buffer(s) is not correct. |
| 490 | ALOGE( |
| 491 | "ProducerQueue::AllocateBuffers: requested to import %zu " |
| 492 | "buffers, but actually imported %zu buffers.", |
| 493 | buffer_count, buffer_slots.size()); |
| 494 | return ErrorStatus(ENOMEM); |
Jiwen 'Steve' Cai | 8fa4e10 | 2017-05-24 23:16:54 -0700 | [diff] [blame] | 495 | } |
| 496 | |
| 497 | return {std::move(buffer_slots)}; |
| 498 | } |
| 499 | |
| 500 | Status<size_t> ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height, |
| 501 | uint32_t layer_count, |
| 502 | uint32_t format, uint64_t usage) { |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 503 | // We only allocate one buffer at a time. |
Jiwen 'Steve' Cai | 8fa4e10 | 2017-05-24 23:16:54 -0700 | [diff] [blame] | 504 | constexpr size_t buffer_count = 1; |
| 505 | auto status = |
| 506 | AllocateBuffers(width, height, layer_count, format, usage, buffer_count); |
| 507 | if (!status) { |
| 508 | ALOGE("ProducerQueue::AllocateBuffer: Failed to allocate buffer: %s", |
| 509 | status.GetErrorMessage().c_str()); |
| 510 | return status.error_status(); |
| 511 | } |
Tianyu Jiang | d8c000d | 2018-09-14 23:40:23 +0000 | [diff] [blame] | 512 | |
Jiwen 'Steve' Cai | 8fa4e10 | 2017-05-24 23:16:54 -0700 | [diff] [blame] | 513 | return {status.get()[0]}; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 514 | } |
| 515 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 516 | Status<void> ProducerQueue::AddBuffer( |
| 517 | const std::shared_ptr<BufferProducer>& buffer, size_t slot) { |
Corey Tabaka | 2b99ee5 | 2017-05-04 10:56:05 -0700 | [diff] [blame] | 518 | ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu", |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 519 | id(), buffer->id(), slot); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 520 | // For producer buffer, we need to enqueue the newly added buffer |
| 521 | // immediately. Producer queue starts with all buffers in available state. |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 522 | auto status = BufferHubQueue::AddBuffer(buffer, slot); |
| 523 | if (!status) |
| 524 | return status; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 525 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 526 | return BufferHubQueue::Enqueue({buffer, slot, 0ULL}); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 527 | } |
| 528 | |
Jiwen 'Steve' Cai | 57ae3ee | 2018-05-03 17:51:52 -0700 | [diff] [blame] | 529 | Status<size_t> ProducerQueue::InsertBuffer( |
| 530 | const std::shared_ptr<BufferProducer>& buffer) { |
| 531 | if (buffer == nullptr || |
| 532 | !BufferHubDefs::IsBufferGained(buffer->buffer_state())) { |
| 533 | ALOGE( |
| 534 | "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in " |
| 535 | "gained state."); |
| 536 | return ErrorStatus(EINVAL); |
| 537 | } |
| 538 | |
| 539 | auto status_or_slot = |
| 540 | InvokeRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>( |
| 541 | buffer->cid()); |
| 542 | if (!status_or_slot) { |
| 543 | ALOGE( |
| 544 | "ProducerQueue::InsertBuffer: Failed to insert producer buffer: " |
| 545 | "buffer_cid=%d, error: %s.", |
| 546 | buffer->cid(), status_or_slot.GetErrorMessage().c_str()); |
| 547 | return status_or_slot.error_status(); |
| 548 | } |
| 549 | |
| 550 | size_t slot = status_or_slot.get(); |
| 551 | |
| 552 | // Note that we are calling AddBuffer() from the base class to explicitly |
| 553 | // avoid Enqueue() the BufferProducer. |
| 554 | auto status = BufferHubQueue::AddBuffer(buffer, slot); |
| 555 | if (!status) { |
| 556 | ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.", |
| 557 | status.GetErrorMessage().c_str()); |
| 558 | return status.error_status(); |
| 559 | } |
| 560 | return {slot}; |
| 561 | } |
| 562 | |
Jiwen 'Steve' Cai | bb701db | 2017-05-23 11:15:33 -0700 | [diff] [blame] | 563 | Status<void> ProducerQueue::RemoveBuffer(size_t slot) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 564 | auto status = |
Jiwen 'Steve' Cai | bb701db | 2017-05-23 11:15:33 -0700 | [diff] [blame] | 565 | InvokeRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(slot); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 566 | if (!status) { |
Jiwen 'Steve' Cai | bb701db | 2017-05-23 11:15:33 -0700 | [diff] [blame] | 567 | ALOGE("ProducerQueue::RemoveBuffer: Failed to remove producer buffer: %s", |
Corey Tabaka | 9d8bd09 | 2017-04-25 16:47:44 -0700 | [diff] [blame] | 568 | status.GetErrorMessage().c_str()); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 569 | return status.error_status(); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 570 | } |
| 571 | |
Jiwen 'Steve' Cai | bb701db | 2017-05-23 11:15:33 -0700 | [diff] [blame] | 572 | return BufferHubQueue::RemoveBuffer(slot); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 573 | } |
| 574 | |
Corey Tabaka | 9d8bd09 | 2017-04-25 16:47:44 -0700 | [diff] [blame] | 575 | Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue( |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 576 | int timeout, size_t* slot, LocalHandle* release_fence) { |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 577 | DvrNativeBufferMetadata canonical_meta; |
| 578 | return Dequeue(timeout, slot, &canonical_meta, release_fence); |
| 579 | } |
| 580 | |
| 581 | pdx::Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue( |
| 582 | int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, |
| 583 | pdx::LocalHandle* release_fence) { |
| 584 | ATRACE_NAME("ProducerQueue::Dequeue"); |
| 585 | if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) { |
| 586 | ALOGE("ProducerQueue::Dequeue: Invalid parameter."); |
Corey Tabaka | 9d8bd09 | 2017-04-25 16:47:44 -0700 | [diff] [blame] | 587 | return ErrorStatus(EINVAL); |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 588 | } |
| 589 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 590 | auto status = BufferHubQueue::Dequeue(timeout, slot); |
| 591 | if (!status) |
| 592 | return status.error_status(); |
Corey Tabaka | 9d8bd09 | 2017-04-25 16:47:44 -0700 | [diff] [blame] | 593 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 594 | auto buffer = std::static_pointer_cast<BufferProducer>(status.take()); |
| 595 | const int ret = buffer->GainAsync(out_meta, release_fence); |
| 596 | if (ret < 0 && ret != -EALREADY) |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 597 | return ErrorStatus(-ret); |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 598 | |
| 599 | return {std::move(buffer)}; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 600 | } |
| 601 | |
Jiwen 'Steve' Cai | a88e3ee | 2017-11-03 17:33:33 -0700 | [diff] [blame] | 602 | pdx::Status<ProducerQueueParcelable> ProducerQueue::TakeAsParcelable() { |
| 603 | if (capacity() != 0) { |
| 604 | ALOGE( |
| 605 | "ProducerQueue::TakeAsParcelable: producer queue can only be taken out" |
| 606 | " as a parcelable when empty. Current queue capacity: %zu", |
| 607 | capacity()); |
| 608 | return ErrorStatus(EINVAL); |
| 609 | } |
| 610 | |
| 611 | std::unique_ptr<pdx::ClientChannel> channel = TakeChannel(); |
| 612 | ProducerQueueParcelable queue_parcelable(channel->TakeChannelParcelable()); |
| 613 | |
| 614 | // Here the queue parcelable is returned and holds the underlying system |
| 615 | // resources backing the queue; while the original client channel of this |
| 616 | // producer queue is destroyed in place so that this client can no longer |
| 617 | // provide producer operations. |
| 618 | return {std::move(queue_parcelable)}; |
| 619 | } |
| 620 | |
Jiwen 'Steve' Cai | a2a27b2 | 2018-02-07 17:29:56 -0800 | [diff] [blame] | 621 | /*static */ |
| 622 | std::unique_ptr<ConsumerQueue> ConsumerQueue::Import( |
| 623 | LocalChannelHandle handle) { |
| 624 | return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(std::move(handle))); |
| 625 | } |
| 626 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 627 | ConsumerQueue::ConsumerQueue(LocalChannelHandle handle) |
| 628 | : BufferHubQueue(std::move(handle)) { |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 629 | auto status = ImportQueue(); |
| 630 | if (!status) { |
| 631 | ALOGE("ConsumerQueue::ConsumerQueue: Failed to import queue: %s", |
| 632 | status.GetErrorMessage().c_str()); |
| 633 | Close(-status.error()); |
| 634 | } |
| 635 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame] | 636 | auto import_status = ImportBuffers(); |
| 637 | if (import_status) { |
| 638 | ALOGI("ConsumerQueue::ConsumerQueue: Imported %zu buffers.", |
| 639 | import_status.get()); |
| 640 | } else { |
| 641 | ALOGE("ConsumerQueue::ConsumerQueue: Failed to import buffers: %s", |
| 642 | import_status.GetErrorMessage().c_str()); |
| 643 | } |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 644 | } |
| 645 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 646 | Status<size_t> ConsumerQueue::ImportBuffers() { |
| 647 | auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>(); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 648 | if (!status) { |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 649 | if (status.error() == EBADR) { |
| 650 | ALOGI( |
| 651 | "ConsumerQueue::ImportBuffers: Queue is silent, no buffers " |
| 652 | "imported."); |
| 653 | return {0}; |
| 654 | } else { |
| 655 | ALOGE( |
| 656 | "ConsumerQueue::ImportBuffers: Failed to import consumer buffer: %s", |
Corey Tabaka | 9d8bd09 | 2017-04-25 16:47:44 -0700 | [diff] [blame] | 657 | status.GetErrorMessage().c_str()); |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 658 | return status.error_status(); |
| 659 | } |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 660 | } |
| 661 | |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame] | 662 | int ret; |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 663 | Status<void> last_error; |
| 664 | size_t imported_buffers_count = 0; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 665 | |
| 666 | auto buffer_handle_slots = status.take(); |
| 667 | for (auto& buffer_handle_slot : buffer_handle_slots) { |
Corey Tabaka | 9d8bd09 | 2017-04-25 16:47:44 -0700 | [diff] [blame] | 668 | ALOGD_IF(TRACE, "ConsumerQueue::ImportBuffers: buffer_handle=%d", |
Jiwen 'Steve' Cai | 25fd3fa | 2017-03-20 15:30:21 -0700 | [diff] [blame] | 669 | buffer_handle_slot.first.value()); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 670 | |
| 671 | std::unique_ptr<BufferConsumer> buffer_consumer = |
| 672 | BufferConsumer::Import(std::move(buffer_handle_slot.first)); |
Corey Tabaka | cf02372 | 2017-07-28 19:46:30 -0700 | [diff] [blame] | 673 | if (!buffer_consumer) { |
| 674 | ALOGE("ConsumerQueue::ImportBuffers: Failed to import buffer: slot=%zu", |
| 675 | buffer_handle_slot.second); |
| 676 | last_error = ErrorStatus(EPIPE); |
| 677 | continue; |
| 678 | } |
Corey Tabaka | 8a4e6a9 | 2017-04-20 13:42:02 -0700 | [diff] [blame] | 679 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 680 | auto add_status = |
| 681 | AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second); |
| 682 | if (!add_status) { |
Corey Tabaka | 9d8bd09 | 2017-04-25 16:47:44 -0700 | [diff] [blame] | 683 | ALOGE("ConsumerQueue::ImportBuffers: Failed to add buffer: %s", |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 684 | add_status.GetErrorMessage().c_str()); |
| 685 | last_error = add_status; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 686 | } else { |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 687 | imported_buffers_count++; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 688 | } |
| 689 | } |
| 690 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 691 | if (imported_buffers_count > 0) |
| 692 | return {imported_buffers_count}; |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 693 | else |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 694 | return last_error.error_status(); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 695 | } |
| 696 | |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 697 | Status<void> ConsumerQueue::AddBuffer( |
| 698 | const std::shared_ptr<BufferConsumer>& buffer, size_t slot) { |
Corey Tabaka | 2b99ee5 | 2017-05-04 10:56:05 -0700 | [diff] [blame] | 699 | ALOGD_IF(TRACE, "ConsumerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu", |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 700 | id(), buffer->id(), slot); |
Jiwen 'Steve' Cai | 394de7e | 2017-12-07 19:24:02 -0800 | [diff] [blame] | 701 | return BufferHubQueue::AddBuffer(buffer, slot); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 702 | } |
| 703 | |
Corey Tabaka | 9d8bd09 | 2017-04-25 16:47:44 -0700 | [diff] [blame] | 704 | Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 705 | int timeout, size_t* slot, void* meta, size_t user_metadata_size, |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 706 | LocalHandle* acquire_fence) { |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 707 | if (user_metadata_size != user_metadata_size_) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame] | 708 | ALOGE( |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 709 | "ConsumerQueue::Dequeue: Metadata size (%zu) for the dequeuing buffer " |
| 710 | "does not match metadata size (%zu) for the queue.", |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 711 | user_metadata_size, user_metadata_size_); |
Corey Tabaka | 9d8bd09 | 2017-04-25 16:47:44 -0700 | [diff] [blame] | 712 | return ErrorStatus(EINVAL); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 713 | } |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 714 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 715 | DvrNativeBufferMetadata canonical_meta; |
| 716 | auto status = Dequeue(timeout, slot, &canonical_meta, acquire_fence); |
| 717 | if (!status) |
| 718 | return status.error_status(); |
| 719 | |
| 720 | if (meta && user_metadata_size) { |
| 721 | void* metadata_src = |
| 722 | reinterpret_cast<void*>(canonical_meta.user_metadata_ptr); |
| 723 | if (metadata_src) { |
| 724 | memcpy(meta, metadata_src, user_metadata_size); |
| 725 | } else { |
| 726 | ALOGW("ConsumerQueue::Dequeue: no user-defined metadata."); |
| 727 | } |
Jiwen 'Steve' Cai | ed65432 | 2017-03-13 17:04:43 -0700 | [diff] [blame] | 728 | } |
| 729 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 730 | return status; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 731 | } |
| 732 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 733 | Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( |
| 734 | int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, |
| 735 | pdx::LocalHandle* acquire_fence) { |
| 736 | ATRACE_NAME("ConsumerQueue::Dequeue"); |
| 737 | if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) { |
| 738 | ALOGE("ConsumerQueue::Dequeue: Invalid parameter."); |
| 739 | return ErrorStatus(EINVAL); |
| 740 | } |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 741 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 742 | auto status = BufferHubQueue::Dequeue(timeout, slot); |
| 743 | if (!status) |
| 744 | return status.error_status(); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 745 | |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 746 | auto buffer = std::static_pointer_cast<BufferConsumer>(status.take()); |
| 747 | const int ret = buffer->AcquireAsync(out_meta, acquire_fence); |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 748 | if (ret < 0) |
| 749 | return ErrorStatus(-ret); |
Corey Tabaka | 52ea25c | 2017-09-13 18:02:48 -0700 | [diff] [blame] | 750 | |
| 751 | return {std::move(buffer)}; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 752 | } |
| 753 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 754 | Status<void> ConsumerQueue::OnBufferAllocated() { |
Corey Tabaka | b7ca5de | 2017-05-08 18:55:02 -0700 | [diff] [blame] | 755 | ALOGD_IF(TRACE, "ConsumerQueue::OnBufferAllocated: queue_id=%d", id()); |
| 756 | |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 757 | auto status = ImportBuffers(); |
| 758 | if (!status) { |
| 759 | ALOGE("ConsumerQueue::OnBufferAllocated: Failed to import buffers: %s", |
| 760 | status.GetErrorMessage().c_str()); |
| 761 | return ErrorStatus(status.error()); |
| 762 | } else if (status.get() == 0) { |
| 763 | ALOGW("ConsumerQueue::OnBufferAllocated: No new buffers allocated!"); |
| 764 | return ErrorStatus(ENOBUFS); |
| 765 | } else { |
Corey Tabaka | 9d8bd09 | 2017-04-25 16:47:44 -0700 | [diff] [blame] | 766 | ALOGD_IF(TRACE, |
| 767 | "ConsumerQueue::OnBufferAllocated: Imported %zu consumer buffers.", |
| 768 | status.get()); |
Corey Tabaka | 1db8a5d | 2017-03-22 02:12:52 -0700 | [diff] [blame] | 769 | return {}; |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 770 | } |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 771 | } |
| 772 | |
| 773 | } // namespace dvr |
| 774 | } // namespace android |