blob: 35aafffbb3ff07b0ec8a4e935f409d5e47c45a48 [file] [log] [blame]
// 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 1
#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 emugl::ChannelBuffer;
using emugl::RenderChannel;
using emugl::RenderChannelPtr;
using ChannelState = emugl::RenderChannel::State;
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](ChannelState state,
RenderChannel::EventSource source) {
this->onChannelIoEvent(state);
});
}
//////////////////////////////////////////////////////////////////////////
// Overriden AndroidPipe methods
virtual void onGuestClose() override {
D("%s", __func__);
mIsWorking = false;
mChannel->stop();
// stop callback will call close() which deletes the pipe
}
virtual unsigned onGuestPoll() const override {
DD("%s", __func__);
unsigned ret = 0;
ChannelState state = mChannel->currentState();
if (canReadAny(state)) {
ret |= PIPE_POLL_IN;
}
if (canWrite(state)) {
ret |= PIPE_POLL_OUT;
}
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.
//
// Spin a little bit here: many GL calls are much faster than
// the whole host-to-guest-to-host transition.
int spinCount = 20;
while (!mChannel->read(&mDataForReading,
RenderChannel::CallType::Nonblocking)) {
if (mChannel->isStopped()) {
return PIPE_ERROR_IO;
} else if (len == 0) {
if (canRead(mChannel->currentState()) || --spinCount > 0) {
continue;
}
return PIPE_ERROR_AGAIN;
} else {
return len;
}
}
mDataForReadingLeft = mDataForReading.size();
}
const size_t curSize =
std::min(buff->size - buffOffset, mDataForReadingLeft.load());
memcpy(buff->data + buffOffset,
mDataForReading.data() +
(mDataForReading.size() - mDataForReadingLeft),
curSize);
len += curSize;
mDataForReadingLeft -= curSize;
buffOffset += curSize;
if (buffOffset == buff->size) {
++buff;
buffOffset = 0;
}
}
return len;
}
virtual int onGuestSend(const AndroidPipeBuffer* buffers,
int numBuffers) override {
DD("%s", __func__);
if (int ret = sendReadyStatus()) {
return ret;
}
// 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;
}
// Send it through the channel.
if (!mChannel->write(std::move(outBuffer))) {
return PIPE_ERROR_IO;
}
return count;
}
virtual void onGuestWantWakeOn(int flags) override {
DD("%s: flags=%d", __func__, flags);
// We reset these flags when we actually wake the pipe, so only
// add to them here.
mCareAboutRead |= (flags & PIPE_WAKE_READ) != 0;
mCareAboutWrite |= (flags & PIPE_WAKE_WRITE) != 0;
ChannelState state = mChannel->currentState();
processIoEvents(state);
}
private:
// Returns true iff there is data to read from the emugl channel.
static bool canRead(ChannelState state) {
return (state & ChannelState::CanRead) != ChannelState::Empty;
}
// Returns true iff the emugl channel can accept data.
static bool canWrite(ChannelState state) {
return (state & ChannelState::CanWrite) != ChannelState::Empty;
}
// Returns true iff there is data to read from the local cache or from
// the emugl channel.
bool canReadAny(ChannelState state) const {
return mDataForReadingLeft != 0 || canRead(state);
}
// Check that the pipe is working and that the render channel can be
// written to. Return 0 in case of success, and one PIPE_ERROR_XXX
// value on error.
int sendReadyStatus() const {
if (mIsWorking) {
ChannelState state = mChannel->currentState();
if (canWrite(state)) {
return 0;
}
return PIPE_ERROR_AGAIN;
} else if (!mHwPipe) {
return PIPE_ERROR_IO;
} else {
return PIPE_ERROR_INVAL;
}
}
// Check the read/write state of the render channel and signal the pipe
// if any condition meets mCareAboutRead or mCareAboutWrite.
void processIoEvents(ChannelState state) {
int wakeFlags = 0;
if (mCareAboutRead && canReadAny(state)) {
wakeFlags |= PIPE_WAKE_READ;
mCareAboutRead = false;
}
if (mCareAboutWrite && canWrite(state)) {
wakeFlags |= PIPE_WAKE_WRITE;
mCareAboutWrite = false;
}
// Send wake signal to the guest if needed.
if (wakeFlags != 0) {
signalWake(wakeFlags);
}
}
// Called when an i/o event occurs on the render channel
void onChannelIoEvent(ChannelState state) {
D("%s: %d", __func__, (int)state);
if ((state & ChannelState::Stopped) != ChannelState::Empty) {
close();
} else {
processIoEvents(state);
}
}
// Close the pipe, this may be called from the host or guest side.
void close() {
D("%s", __func__);
// If the pipe isn't in a working state, delete immediately.
if (!mIsWorking) {
mChannel->stop();
delete this;
return;
}
// Force the closure of the channel - if a guest is blocked
// waiting for a wake signal, it will receive an error.
if (mHwPipe) {
closeFromHost();
mHwPipe = nullptr;
}
mIsWorking = false;
}
// A RenderChannel pointer used for communication.
RenderChannelPtr mChannel;
// Guest state tracking - if it requested us to wake on read/write
// availability. If guest doesn't care about some operation type, we should
// not wake it when that operation becomes available.
// Note: we need an atomic or operation, and atomic<bool> doesn't have it -
// that's why it is atomic<char> here.
std::atomic<char> mCareAboutRead {false};
std::atomic<char> mCareAboutWrite {false};
// 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;
std::atomic<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();
}