1 // Copyright (C) 2016 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include "RenderChannelImpl.h"
15
16 #include "RenderThread.h"
17 #include "base/Lock.h"
18
19 #include <algorithm>
20 #include <utility>
21
22 #include <assert.h>
23 #include <string.h>
24
25 #define EMUGL_DEBUG_LEVEL 0
26 #include "host-common/debug.h"
27
28 namespace emugl {
29
30 using Buffer = RenderChannel::Buffer;
31 using IoResult = android::base::BufferQueueResult;
32 using State = RenderChannel::State;
33 using AutoLock = android::base::AutoLock;
34
35 // These constants correspond to the capacities of buffer queues
36 // used by each RenderChannelImpl instance. Benchmarking shows that
37 // it's important to have a large queue for guest -> host transfers,
38 // but a much smaller one works for host -> guest ones.
39 // Note: 32-bit Windows just doesn't have enough RAM to allocate optimal
40 // capacity.
41 #if defined(_WIN32) && !defined(_WIN64)
42 static constexpr size_t kGuestToHostQueueCapacity = 32U;
43 #else
44 static constexpr size_t kGuestToHostQueueCapacity = 1024U;
45 #endif
46 static constexpr size_t kHostToGuestQueueCapacity = 16U;
47
RenderChannelImpl(android::base::Stream * loadStream)48 RenderChannelImpl::RenderChannelImpl(android::base::Stream* loadStream)
49 : mFromGuest(kGuestToHostQueueCapacity, mLock),
50 mToGuest(kHostToGuestQueueCapacity, mLock) {
51 if (loadStream) {
52 mFromGuest.onLoadLocked(loadStream);
53 mToGuest.onLoadLocked(loadStream);
54 mState = (State)loadStream->getBe32();
55 mWantedEvents = (State)loadStream->getBe32();
56 #ifndef NDEBUG
57 // Make sure we're in a consistent state after loading.
58 const auto state = mState;
59 updateStateLocked();
60 assert(state == mState);
61 #endif
62 } else {
63 updateStateLocked();
64 }
65 mRenderThread.reset(new RenderThread(this, loadStream));
66 mRenderThread->start();
67 }
68
setEventCallback(EventCallback && callback)69 void RenderChannelImpl::setEventCallback(EventCallback&& callback) {
70 mEventCallback = std::move(callback);
71 notifyStateChangeLocked();
72 }
73
setWantedEvents(State state)74 void RenderChannelImpl::setWantedEvents(State state) {
75 D("state=%d", (int)state);
76 AutoLock lock(mLock);
77 mWantedEvents |= state;
78 notifyStateChangeLocked();
79 }
80
state() const81 RenderChannel::State RenderChannelImpl::state() const {
82 AutoLock lock(mLock);
83 return mState;
84 }
85
tryWrite(Buffer && buffer)86 IoResult RenderChannelImpl::tryWrite(Buffer&& buffer) {
87 D("buffer size=%d", (int)buffer.size());
88 AutoLock lock(mLock);
89 auto result = mFromGuest.tryPushLocked(std::move(buffer));
90 updateStateLocked();
91 DD("mFromGuest.tryPushLocked() returned %d, state %d", (int)result,
92 (int)mState);
93 return result;
94 }
95
tryRead(Buffer * buffer)96 IoResult RenderChannelImpl::tryRead(Buffer* buffer) {
97 D("enter");
98 AutoLock lock(mLock);
99 auto result = mToGuest.tryPopLocked(buffer);
100 updateStateLocked();
101 DD("mToGuest.tryPopLocked() returned %d, buffer size %d, state %d",
102 (int)result, (int)buffer->size(), (int)mState);
103 return result;
104 }
105
readBefore(Buffer * buffer,Duration waitUntilUs)106 IoResult RenderChannelImpl::readBefore(Buffer* buffer, Duration waitUntilUs) {
107 D("enter");
108 AutoLock lock(mLock);
109 auto result = mToGuest.popLockedBefore(buffer, waitUntilUs);
110 updateStateLocked();
111 DD("mToGuest.popLockedBefore() returned %d, buffer size %d, state %d",
112 (int)result, (int)buffer->size(), (int)mState);
113 return result;
114 }
115
stop()116 void RenderChannelImpl::stop() {
117 D("enter");
118 AutoLock lock(mLock);
119 mFromGuest.closeLocked();
120 mToGuest.closeLocked();
121 mEventCallback = [](State state) {};
122 }
123
writeToGuest(Buffer && buffer)124 bool RenderChannelImpl::writeToGuest(Buffer&& buffer) {
125 D("buffer size=%d", (int)buffer.size());
126 AutoLock lock(mLock);
127 IoResult result = mToGuest.pushLocked(std::move(buffer));
128 updateStateLocked();
129 D("mToGuest.pushLocked() returned %d, state %d", (int)result, (int)mState);
130 notifyStateChangeLocked();
131 return result == IoResult::Ok;
132 }
133
readFromGuest(Buffer * buffer,bool blocking)134 IoResult RenderChannelImpl::readFromGuest(Buffer* buffer, bool blocking) {
135 D("enter");
136 AutoLock lock(mLock);
137 IoResult result;
138 if (blocking) {
139 result = mFromGuest.popLocked(buffer);
140 } else {
141 result = mFromGuest.tryPopLocked(buffer);
142 }
143 updateStateLocked();
144 DD("mFromGuest.%s() return %d, buffer size %d, state %d",
145 blocking ? "popLocked" : "tryPopLocked", (int)result,
146 (int)buffer->size(), (int)mState);
147 notifyStateChangeLocked();
148 return result;
149 }
150
stopFromHost()151 void RenderChannelImpl::stopFromHost() {
152 D("enter");
153
154 AutoLock lock(mLock);
155 mFromGuest.closeLocked();
156 mToGuest.closeLocked();
157 mState |= State::Stopped;
158 notifyStateChangeLocked();
159 mEventCallback = [](State state) {};
160 }
161
isStopped() const162 bool RenderChannelImpl::isStopped() const {
163 AutoLock lock(mLock);
164 return (mState & State::Stopped) != 0;
165 }
166
renderThread() const167 RenderThread* RenderChannelImpl::renderThread() const {
168 return mRenderThread.get();
169 }
170
pausePreSnapshot()171 void RenderChannelImpl::pausePreSnapshot() {
172 AutoLock lock(mLock);
173 mFromGuest.setSnapshotModeLocked(true);
174 mToGuest.setSnapshotModeLocked(true);
175 }
176
resume()177 void RenderChannelImpl::resume() {
178 AutoLock lock(mLock);
179 mFromGuest.setSnapshotModeLocked(false);
180 mToGuest.setSnapshotModeLocked(false);
181 }
182
~RenderChannelImpl()183 RenderChannelImpl::~RenderChannelImpl() {
184 // Make sure the render thread is stopped before the channel is gone.
185 mRenderThread->wait();
186 }
187
updateStateLocked()188 void RenderChannelImpl::updateStateLocked() {
189 State state = RenderChannel::State::Empty;
190
191 if (mToGuest.canPopLocked()) {
192 state |= State::CanRead;
193 }
194 if (mFromGuest.canPushLocked()) {
195 state |= State::CanWrite;
196 }
197 if (mToGuest.isClosedLocked()) {
198 state |= State::Stopped;
199 }
200 mState = state;
201 }
202
notifyStateChangeLocked()203 void RenderChannelImpl::notifyStateChangeLocked() {
204 // Always report stop events, event if not explicitly asked for.
205 State available = mState & (mWantedEvents | State::Stopped);
206 if (available != 0) {
207 D("callback with %d", (int)available);
208 mWantedEvents &= ~mState;
209 mEventCallback(available);
210 }
211 }
212
onSave(android::base::Stream * stream)213 void RenderChannelImpl::onSave(android::base::Stream* stream) {
214 D("enter");
215 AutoLock lock(mLock);
216 mFromGuest.onSaveLocked(stream);
217 mToGuest.onSaveLocked(stream);
218 stream->putBe32(static_cast<uint32_t>(mState));
219 stream->putBe32(static_cast<uint32_t>(mWantedEvents));
220 lock.unlock();
221 mRenderThread->save(stream);
222 }
223
224 } // namespace emugl
225