| // Copyright (C) 2016 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| #include "RenderChannelImpl.h" |
| |
| #include "RenderThread.h" |
| #include "aemu/base/synchronization/Lock.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include <assert.h> |
| #include <string.h> |
| |
| #define EMUGL_DEBUG_LEVEL 0 |
| #include "host-common/debug.h" |
| |
| namespace gfxstream { |
| |
| using Buffer = RenderChannel::Buffer; |
| using IoResult = android::base::BufferQueueResult; |
| using State = RenderChannel::State; |
| using AutoLock = android::base::AutoLock; |
| |
| // These constants correspond to the capacities of buffer queues |
| // used by each RenderChannelImpl instance. Benchmarking shows that |
| // it's important to have a large queue for guest -> host transfers, |
| // but a much smaller one works for host -> guest ones. |
| // Note: 32-bit Windows just doesn't have enough RAM to allocate optimal |
| // capacity. |
| #if defined(_WIN32) && !defined(_WIN64) |
| static constexpr size_t kGuestToHostQueueCapacity = 32U; |
| #else |
| static constexpr size_t kGuestToHostQueueCapacity = 1024U; |
| #endif |
| static constexpr size_t kHostToGuestQueueCapacity = 16U; |
| |
| RenderChannelImpl::RenderChannelImpl(android::base::Stream* loadStream, uint32_t contextId) |
| : mFromGuest(kGuestToHostQueueCapacity, mLock), |
| mToGuest(kHostToGuestQueueCapacity, mLock) { |
| if (loadStream) { |
| mFromGuest.onLoadLocked(loadStream); |
| mToGuest.onLoadLocked(loadStream); |
| mState = (State)loadStream->getBe32(); |
| mWantedEvents = (State)loadStream->getBe32(); |
| #ifndef NDEBUG |
| // Make sure we're in a consistent state after loading. |
| const auto state = mState; |
| updateStateLocked(); |
| assert(state == mState); |
| #endif |
| } else { |
| updateStateLocked(); |
| } |
| mRenderThread.reset(new RenderThread(this, loadStream, contextId)); |
| mRenderThread->start(); |
| } |
| |
| void RenderChannelImpl::setEventCallback(EventCallback&& callback) { |
| mEventCallback = std::move(callback); |
| notifyStateChangeLocked(); |
| } |
| |
| void RenderChannelImpl::setWantedEvents(State state) { |
| D("state=%d", (int)state); |
| AutoLock lock(mLock); |
| mWantedEvents |= state; |
| notifyStateChangeLocked(); |
| } |
| |
| RenderChannel::State RenderChannelImpl::state() const { |
| AutoLock lock(mLock); |
| return mState; |
| } |
| |
| IoResult RenderChannelImpl::tryWrite(Buffer&& buffer) { |
| D("buffer size=%d", (int)buffer.size()); |
| AutoLock lock(mLock); |
| auto result = mFromGuest.tryPushLocked(std::move(buffer)); |
| updateStateLocked(); |
| DD("mFromGuest.tryPushLocked() returned %d, state %d", (int)result, |
| (int)mState); |
| return result; |
| } |
| |
| void RenderChannelImpl::waitUntilWritable() { |
| AutoLock lock(mLock); |
| mFromGuest.waitUntilPushableLocked(); |
| } |
| |
| IoResult RenderChannelImpl::tryRead(Buffer* buffer) { |
| D("enter"); |
| AutoLock lock(mLock); |
| auto result = mToGuest.tryPopLocked(buffer); |
| updateStateLocked(); |
| DD("mToGuest.tryPopLocked() returned %d, buffer size %d, state %d", |
| (int)result, (int)buffer->size(), (int)mState); |
| return result; |
| } |
| |
| IoResult RenderChannelImpl::readBefore(Buffer* buffer, Duration waitUntilUs) { |
| D("enter"); |
| AutoLock lock(mLock); |
| auto result = mToGuest.popLockedBefore(buffer, waitUntilUs); |
| updateStateLocked(); |
| DD("mToGuest.popLockedBefore() returned %d, buffer size %d, state %d", |
| (int)result, (int)buffer->size(), (int)mState); |
| return result; |
| } |
| |
| void RenderChannelImpl::waitUntilReadable() { |
| AutoLock lock(mLock); |
| mToGuest.waitUntilPopableLocked(); |
| } |
| |
| void RenderChannelImpl::stop() { |
| D("enter"); |
| AutoLock lock(mLock); |
| mFromGuest.closeLocked(); |
| mToGuest.closeLocked(); |
| mEventCallback = [](State state) {}; |
| } |
| |
| bool RenderChannelImpl::writeToGuest(Buffer&& buffer) { |
| D("buffer size=%d", (int)buffer.size()); |
| AutoLock lock(mLock); |
| IoResult result = mToGuest.pushLocked(std::move(buffer)); |
| updateStateLocked(); |
| D("mToGuest.pushLocked() returned %d, state %d", (int)result, (int)mState); |
| notifyStateChangeLocked(); |
| return result == IoResult::Ok; |
| } |
| |
| IoResult RenderChannelImpl::readFromGuest(Buffer* buffer, bool blocking) { |
| D("enter"); |
| AutoLock lock(mLock); |
| IoResult result; |
| if (blocking) { |
| result = mFromGuest.popLocked(buffer); |
| } else { |
| result = mFromGuest.tryPopLocked(buffer); |
| } |
| updateStateLocked(); |
| DD("mFromGuest.%s() return %d, buffer size %d, state %d", |
| blocking ? "popLocked" : "tryPopLocked", (int)result, |
| (int)buffer->size(), (int)mState); |
| notifyStateChangeLocked(); |
| return result; |
| } |
| |
| void RenderChannelImpl::stopFromHost() { |
| D("enter"); |
| |
| AutoLock lock(mLock); |
| mFromGuest.closeLocked(); |
| mToGuest.closeLocked(); |
| mState |= State::Stopped; |
| notifyStateChangeLocked(); |
| mEventCallback = [](State state) {}; |
| } |
| |
| bool RenderChannelImpl::isStopped() const { |
| AutoLock lock(mLock); |
| return (mState & State::Stopped) != 0; |
| } |
| |
| RenderThread* RenderChannelImpl::renderThread() const { |
| return mRenderThread.get(); |
| } |
| |
| void RenderChannelImpl::pausePreSnapshot() { |
| AutoLock lock(mLock); |
| mFromGuest.setSnapshotModeLocked(true); |
| mToGuest.setSnapshotModeLocked(true); |
| } |
| |
| void RenderChannelImpl::resume() { |
| AutoLock lock(mLock); |
| mFromGuest.setSnapshotModeLocked(false); |
| mToGuest.setSnapshotModeLocked(false); |
| } |
| |
| RenderChannelImpl::~RenderChannelImpl() { |
| // Make sure the render thread is stopped before the channel is gone. |
| mRenderThread->wait(); |
| } |
| |
| void RenderChannelImpl::updateStateLocked() { |
| State state = RenderChannel::State::Empty; |
| |
| if (mToGuest.canPopLocked()) { |
| state |= State::CanRead; |
| } |
| if (mFromGuest.canPushLocked()) { |
| state |= State::CanWrite; |
| } |
| if (mToGuest.isClosedLocked()) { |
| state |= State::Stopped; |
| } |
| mState = state; |
| } |
| |
| void RenderChannelImpl::notifyStateChangeLocked() { |
| // Always report stop events, event if not explicitly asked for. |
| State available = mState & (mWantedEvents | State::Stopped); |
| if (available != 0) { |
| D("callback with %d", (int)available); |
| mWantedEvents &= ~mState; |
| mEventCallback(available); |
| } |
| } |
| |
| void RenderChannelImpl::onSave(android::base::Stream* stream) { |
| D("enter"); |
| AutoLock lock(mLock); |
| mFromGuest.onSaveLocked(stream); |
| mToGuest.onSaveLocked(stream); |
| stream->putBe32(static_cast<uint32_t>(mState)); |
| stream->putBe32(static_cast<uint32_t>(mWantedEvents)); |
| lock.unlock(); |
| mRenderThread->save(stream); |
| } |
| |
| } // namespace gfxstream |