| // Copyright 2016 The Android Open Source Project |
| // |
| // This software is licensed under the terms of the GNU General Public |
| // License version 2, as published by the Free Software Foundation, and |
| // may be copied, distributed, and modified under those terms. |
| // |
| // This program is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| #include "android/opengl/OpenglEsPipe.h" |
| |
| #include "android/base/async/Looper.h" |
| #include "android/opengles.h" |
| #include "android/opengles-pipe.h" |
| #include "android/opengl/GLProcessPipe.h" |
| |
| #include <atomic> |
| |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| // Set to 1 or 2 for debug traces |
| #define DEBUG 0 |
| |
| #if DEBUG >= 1 |
| #define D(...) printf(__VA_ARGS__), printf("\n"), fflush(stdout) |
| #else |
| #define D(...) ((void)0) |
| #endif |
| |
| #if DEBUG >= 2 |
| #define DD(...) printf(__VA_ARGS__), printf("\n"), fflush(stdout) |
| #else |
| #define DD(...) ((void)0) |
| #endif |
| |
| using ChannelBuffer = emugl::RenderChannel::Buffer; |
| using emugl::RenderChannel; |
| using emugl::RenderChannelPtr; |
| using ChannelState = emugl::RenderChannel::State; |
| using IoResult = emugl::RenderChannel::IoResult; |
| |
| namespace android { |
| namespace opengl { |
| |
| namespace { |
| |
| class EmuglPipe : public AndroidPipe { |
| public: |
| ////////////////////////////////////////////////////////////////////////// |
| // The pipe service class for this implementation. |
| class Service : public AndroidPipe::Service { |
| public: |
| Service() : AndroidPipe::Service("opengles") {} |
| |
| // Create a new EmuglPipe instance. |
| virtual AndroidPipe* create(void* mHwPipe, const char* args) override { |
| auto renderer = android_getOpenglesRenderer(); |
| if (!renderer) { |
| // This should never happen, unless there is a bug in the |
| // emulator's initialization, or the system image. |
| D("Trying to open the OpenGLES pipe without GPU emulation!"); |
| return nullptr; |
| } |
| |
| EmuglPipe* pipe = new EmuglPipe(mHwPipe, this, renderer); |
| if (!pipe->mIsWorking) { |
| delete pipe; |
| pipe = nullptr; |
| } |
| return pipe; |
| } |
| |
| // Really cannot save/load these pipes' state. |
| virtual bool canLoad() const override { return false; } |
| }; |
| |
| ///////////////////////////////////////////////////////////////////////// |
| // Constructor, check that |mIsWorking| is true after this call to verify |
| // that everything went well. |
| EmuglPipe(void* hwPipe, Service* service, |
| const emugl::RendererPtr& renderer) |
| : AndroidPipe(hwPipe, service) { |
| mChannel = renderer->createRenderChannel(); |
| if (!mChannel) { |
| D("Failed to create an OpenGLES pipe channel!"); |
| return; |
| } |
| |
| mIsWorking = true; |
| mChannel->setEventCallback( |
| [this](RenderChannel::State events) { |
| this->onChannelHostEvent(events); |
| }); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////// |
| // Overriden AndroidPipe methods |
| |
| virtual void onGuestClose() override { |
| D("%s", __func__); |
| mIsWorking = false; |
| mChannel->stop(); |
| delete this; |
| } |
| |
| virtual unsigned onGuestPoll() const override { |
| DD("%s", __func__); |
| |
| unsigned ret = 0; |
| if (mDataForReadingLeft > 0) { |
| ret |= PIPE_POLL_IN; |
| } |
| ChannelState state = mChannel->state(); |
| if ((state & ChannelState::CanRead) != 0) { |
| ret |= PIPE_POLL_IN; |
| } |
| if ((state & ChannelState::CanWrite) != 0) { |
| ret |= PIPE_POLL_OUT; |
| } |
| if ((state & ChannelState::Stopped) != 0) { |
| ret |= PIPE_POLL_HUP; |
| } |
| DD("%s: returning %d", __func__, ret); |
| return ret; |
| } |
| |
| virtual int onGuestRecv(AndroidPipeBuffer* buffers, int numBuffers) |
| override { |
| DD("%s", __func__); |
| |
| // Consume the pipe's dataForReading, then put the next received data |
| // piece there. Repeat until the buffers are full or we're out of data |
| // in the channel. |
| int len = 0; |
| size_t buffOffset = 0; |
| |
| auto buff = buffers; |
| const auto buffEnd = buff + numBuffers; |
| while (buff != buffEnd) { |
| if (mDataForReadingLeft == 0) { |
| // No data left, read a new chunk from the channel. |
| int spinCount = 20; |
| for (;;) { |
| auto result = mChannel->tryRead(&mDataForReading); |
| if (result == IoResult::Ok) { |
| mDataForReadingLeft = mDataForReading.size(); |
| break; |
| } |
| DD("%s: tryRead() failed with %d", __func__, (int)result); |
| // This failed either because the channel was stopped |
| // from the host, or if there was no data yet in the |
| // channel. |
| if (len > 0) { |
| DD("%s: returning %d bytes", __func__, (int)len); |
| return len; |
| } |
| if (result == IoResult::Error) { |
| return PIPE_ERROR_IO; |
| } |
| // Spin a little before declaring there is nothing |
| // to read. Many GL calls are much faster than the |
| // whole host-to-guest-to-host transition. |
| if (--spinCount > 0) { |
| continue; |
| } |
| DD("%s: returning PIPE_ERROR_AGAIN", __func__); |
| return PIPE_ERROR_AGAIN; |
| } |
| } |
| |
| const size_t curSize = |
| std::min(buff->size - buffOffset, mDataForReadingLeft); |
| memcpy(buff->data + buffOffset, |
| mDataForReading.data() + |
| (mDataForReading.size() - mDataForReadingLeft), |
| curSize); |
| |
| len += curSize; |
| mDataForReadingLeft -= curSize; |
| buffOffset += curSize; |
| if (buffOffset == buff->size) { |
| ++buff; |
| buffOffset = 0; |
| } |
| } |
| |
| DD("%s: received %d bytes", __func__, (int)len); |
| return len; |
| } |
| |
| virtual int onGuestSend(const AndroidPipeBuffer* buffers, |
| int numBuffers) override { |
| DD("%s", __func__); |
| |
| if (!mIsWorking) { |
| DD("%s: pipe already closed!", __func__); |
| return PIPE_ERROR_IO; |
| } |
| |
| // Count the total bytes to send. |
| int count = 0; |
| for (int n = 0; n < numBuffers; ++n) { |
| count += buffers[n].size; |
| } |
| |
| // Copy everything into a single ChannelBuffer. |
| ChannelBuffer outBuffer; |
| outBuffer.resize_noinit(count); |
| auto ptr = outBuffer.data(); |
| for (int n = 0; n < numBuffers; ++n) { |
| memcpy(ptr, buffers[n].data, buffers[n].size); |
| ptr += buffers[n].size; |
| } |
| |
| D("%s: sending %d bytes to host", __func__, count); |
| // Send it through the channel. |
| auto result = mChannel->tryWrite(std::move(outBuffer)); |
| if (result != IoResult::Ok) { |
| D("%s: tryWrite() failed with %d", __func__, (int)result); |
| return result == IoResult::Error ? PIPE_ERROR_IO : PIPE_ERROR_AGAIN; |
| } |
| |
| return count; |
| } |
| |
| virtual void onGuestWantWakeOn(int flags) override { |
| DD("%s: flags=%d", __func__, flags); |
| |
| // Translate |flags| into ChannelState flags. |
| ChannelState wanted = ChannelState::Empty; |
| if (flags & PIPE_WAKE_READ) { |
| wanted |= ChannelState::CanRead; |
| } |
| if (flags & PIPE_WAKE_WRITE) { |
| wanted |= ChannelState::CanWrite; |
| } |
| |
| // Signal events that are already available now. |
| ChannelState state = mChannel->state(); |
| ChannelState available = state & wanted; |
| DD("%s: state=%d wanted=%d available=%d", __func__, (int)state, |
| (int)wanted, (int)available); |
| if (available != ChannelState::Empty) { |
| DD("%s: signaling events %d", __func__, (int)available); |
| signalState(available); |
| wanted &= ~available; |
| } |
| |
| // Ask the channel to be notified of remaining events. |
| if (wanted != ChannelState::Empty) { |
| DD("%s: waiting for events %d", __func__, (int)wanted); |
| mChannel->setWantedEvents(wanted); |
| } |
| } |
| |
| private: |
| // Called to signal the guest that read/write wake events occured. |
| // Note: this can be called from either the guest or host render |
| // thread. |
| void signalState(ChannelState state) { |
| int wakeFlags = 0; |
| if ((state & ChannelState::CanRead) != 0) { |
| wakeFlags |= PIPE_WAKE_READ; |
| } |
| if ((state & ChannelState::CanWrite) != 0) { |
| wakeFlags |= PIPE_WAKE_WRITE; |
| } |
| if (wakeFlags != 0) { |
| this->signalWake(wakeFlags); |
| } |
| } |
| |
| // Called when an i/o event occurs on the render channel |
| void onChannelHostEvent(ChannelState state) { |
| D("%s: events %d", __func__, (int)state); |
| // NOTE: This is called from the host-side render thread. |
| // but closeFromHost() and signalWake() can be called from |
| // any thread. |
| if ((state & ChannelState::Stopped) != 0) { |
| this->closeFromHost(); |
| return; |
| } |
| signalState(state); |
| } |
| |
| // A RenderChannel pointer used for communication. |
| RenderChannelPtr mChannel; |
| |
| // Set to |true| if the pipe is in working state, |false| means we're not |
| // initialized or the pipe is closed. |
| bool mIsWorking = false; |
| |
| // These two variables serve as a reading buffer for the guest. |
| // Each time we get a read request, first we extract a single chunk from |
| // the |mChannel| into here, and then copy its content into the |
| // guest-supplied memory. |
| // If guest didn't have enough room for the whole buffer, we track the |
| // number of remaining bytes in |mDataForReadingLeft| for the next read(). |
| ChannelBuffer mDataForReading; |
| size_t mDataForReadingLeft = 0; |
| |
| DISALLOW_COPY_ASSIGN_AND_MOVE(EmuglPipe); |
| }; |
| |
| } // namespace |
| |
| void registerPipeService() { |
| android::AndroidPipe::Service::add(new EmuglPipe::Service()); |
| registerGLProcessPipeService(); |
| } |
| |
| } // namespace opengl |
| } // namespace android |
| |
| // Declared in android/opengles-pipe.h |
| void android_init_opengles_pipe() { |
| android::opengl::registerPipeService(); |
| } |