AddressSpaceStream
bug: 140112486
Change-Id: Ida19e194b7160975fb6b4bdaaf4982291456e05c
diff --git a/BUILD.gn b/BUILD.gn
index 0f4d394..09463d1 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -36,6 +36,7 @@
"shared/OpenglCodecCommon/goldfish_address_space.h",
"shared/OpenglCodecCommon/goldfish_dma.cpp",
"shared/OpenglCodecCommon/goldfish_dma.h",
+ "system/OpenglSystemCommon/AddressSpaceStream.cpp",
"system/OpenglSystemCommon/HostConnection.cpp",
"system/OpenglSystemCommon/HostConnection.h",
"system/OpenglSystemCommon/ProcessPipe.cpp",
diff --git a/system/OpenglSystemCommon/AddressSpaceStream.cpp b/system/OpenglSystemCommon/AddressSpaceStream.cpp
new file mode 100644
index 0000000..00d654c
--- /dev/null
+++ b/system/OpenglSystemCommon/AddressSpaceStream.cpp
@@ -0,0 +1,545 @@
+/*
+* Copyright (C) 2011 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 "AddressSpaceStream.h"
+
+#if PLATFORM_SDK_VERSION < 26
+#include <cutils/log.h>
+#else
+#include <log/log.h>
+#endif
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+static const uint32_t kDeviceId = 0;
+
+static const size_t kReadSize = 512 * 1024;
+static const size_t kWriteOffset = kReadSize;
+
+AddressSpaceStream* createAddressSpaceStream(size_t ignored_bufSize) {
+ // Ignore incoming ignored_bufSize
+ (void)ignored_bufSize;
+
+ auto handle = goldfish_address_space_open();
+
+ struct goldfish_address_space_ping request;
+ request.metadata = kDeviceId;
+
+ if (!goldfish_address_space_ping(handle, &request)) {
+ ALOGE("AddressSpaceStream::create failed (initial device create)\n");
+ goldfish_address_space_close(handle);
+ return nullptr;
+ }
+
+ request.metadata = ASG_GET_RING;
+ if (!goldfish_address_space_ping(handle, &request)) {
+ ALOGE("AddressSpaceStream::create failed (get ring)\n");
+ goldfish_address_space_close(handle);
+ return nullptr;
+ }
+
+ uint64_t ringOffset = request.metadata;
+
+ request.metadata = ASG_GET_BUFFER;
+ if (!goldfish_address_space_ping(handle, &request)) {
+ ALOGE("AddressSpaceStream::create failed (get buffer)\n");
+ goldfish_address_space_close(handle);
+ return nullptr;
+ }
+
+ uint64_t bufferOffset = request.metadata;
+ uint64_t bufferSize = request.size;
+
+ if (!goldfish_address_space_claim_shared(
+ handle, ringOffset, sizeof(asg_ring_storage))) {
+ ALOGE("AddressSpaceStream::create failed (claim ring storage)\n");
+ goldfish_address_space_close(handle);
+ return nullptr;
+ }
+
+ if (!goldfish_address_space_claim_shared(
+ handle, bufferOffset, bufferSize)) {
+ ALOGE("AddressSpaceStream::create failed (claim buffer storage)\n");
+ goldfish_address_space_unclaim_shared(handle, ringOffset);
+ goldfish_address_space_close(handle);
+ return nullptr;
+ }
+
+ char* ringPtr = (char*)goldfish_address_space_map(
+ handle, ringOffset, sizeof(struct asg_ring_storage));
+
+ if (!ringPtr) {
+ ALOGE("AddressSpaceStream::create failed (map ring storage)\n");
+ goldfish_address_space_unclaim_shared(handle, bufferOffset);
+ goldfish_address_space_unclaim_shared(handle, ringOffset);
+ goldfish_address_space_close(handle);
+ return nullptr;
+ }
+
+ char* bufferPtr = (char*)goldfish_address_space_map(
+ handle, bufferOffset, bufferSize);
+
+ if (!bufferPtr) {
+ ALOGE("AddressSpaceStream::create failed (map buffer storage)\n");
+ goldfish_address_space_unmap(ringPtr, sizeof(struct asg_ring_storage));
+ goldfish_address_space_unclaim_shared(handle, bufferOffset);
+ goldfish_address_space_unclaim_shared(handle, ringOffset);
+ goldfish_address_space_close(handle);
+ return nullptr;
+ }
+
+ struct asg_context context =
+ asg_context_create(
+ ringPtr, bufferPtr, bufferSize);
+
+ request.metadata = ASG_SET_VERSION;
+ request.size = 1; // version 1
+
+ if (!goldfish_address_space_ping(handle, &request)) {
+ ALOGE("AddressSpaceStream::create failed (get buffer)\n");
+ goldfish_address_space_unmap(bufferPtr, bufferSize);
+ goldfish_address_space_unmap(ringPtr, sizeof(struct asg_ring_storage));
+ goldfish_address_space_unclaim_shared(handle, bufferOffset);
+ goldfish_address_space_unclaim_shared(handle, ringOffset);
+ goldfish_address_space_close(handle);
+ return nullptr;
+ }
+
+ uint32_t version = request.size;
+
+ context.ring_config->transfer_mode = 1;
+ context.ring_config->host_consumed_pos = 0;
+ context.ring_config->guest_write_pos = 0;
+
+ AddressSpaceStream* res =
+ new AddressSpaceStream(
+ handle, version, context,
+ ringOffset, bufferOffset);
+
+ return res;
+}
+
+AddressSpaceStream::AddressSpaceStream(
+ address_space_handle_t handle,
+ uint32_t version,
+ struct asg_context context,
+ uint64_t ringOffset,
+ uint64_t writeBufferOffset) :
+ IOStream(context.ring_config->flush_interval),
+ m_tmpBuf(0),
+ m_tmpBufSize(0),
+ m_tmpBufXferSize(0),
+ m_usingTmpBuf(0),
+ m_readBuf(0),
+ m_read(0),
+ m_readLeft(0),
+ m_handle(handle),
+ m_version(version),
+ m_context(context),
+ m_ringOffset(ringOffset),
+ m_writeBufferOffset(writeBufferOffset),
+ m_writeBufferSize(context.ring_config->buffer_size),
+ m_writeBufferMask(m_writeBufferSize - 1),
+ m_buf((unsigned char*)context.buffer),
+ m_writeStart(m_buf),
+ m_writeStep(context.ring_config->flush_interval) {
+ // We'll use this in the future, but at the moment,
+ // it's a potential compile Werror.
+ (void)m_version;
+}
+
+AddressSpaceStream::~AddressSpaceStream() {
+ goldfish_address_space_unmap(m_context.to_host, sizeof(struct asg_ring_storage));
+ goldfish_address_space_unmap(m_context.buffer, m_writeBufferSize);
+ goldfish_address_space_unclaim_shared(m_handle, m_ringOffset);
+ goldfish_address_space_unclaim_shared(m_handle, m_writeBufferOffset);
+ goldfish_address_space_close(m_handle);
+ if (m_readBuf) free(m_readBuf);
+ if (m_tmpBuf) free(m_tmpBuf);
+}
+
+size_t AddressSpaceStream::idealAllocSize(size_t len) {
+ if (len > m_writeStep) return len;
+ return m_writeStep;
+}
+
+void *AddressSpaceStream::allocBuffer(size_t minSize) {
+ if (!m_readBuf) {
+ m_readBuf = (unsigned char*)malloc(kReadSize);
+ }
+
+ size_t allocSize =
+ (m_writeStep < minSize ? minSize : m_writeStep);
+
+ if (m_writeStep < allocSize) {
+ ALOGD("%s: oops. wanted %zu have %zu\n", __func__,
+ (size_t)allocSize, (size_t)m_writeStep);
+ if (!m_tmpBuf) {
+ m_tmpBufSize = allocSize * 2;
+ m_tmpBuf = (unsigned char*)malloc(m_tmpBufSize);
+ }
+
+ if (m_tmpBufSize < allocSize) {
+ m_tmpBufSize = allocSize * 2;
+ m_tmpBuf = (unsigned char*)realloc(m_tmpBuf, m_tmpBufSize);
+ }
+
+ if (!m_usingTmpBuf) {
+ ensureType1Finished();
+ }
+
+ m_usingTmpBuf = true;
+ m_tmpBufXferSize = allocSize;
+ return m_tmpBuf;
+ } else {
+ if (m_usingTmpBuf) {
+ writeFully(m_tmpBuf, m_tmpBufXferSize);
+ m_usingTmpBuf = false;
+ m_tmpBufXferSize = 0;
+ }
+
+ return m_writeStart;
+ }
+};
+
+int AddressSpaceStream::commitBuffer(size_t size)
+{
+ if (size == 0) return 0;
+
+ if (m_usingTmpBuf) {
+ writeFully(m_tmpBuf, m_tmpBufXferSize);
+ m_tmpBufXferSize = 0;
+ m_usingTmpBuf = false;
+ return 0;
+ } else {
+ int res = type1Write(m_writeStart - m_buf, size);
+ advanceWrite();
+ return res;
+ }
+}
+
+const unsigned char *AddressSpaceStream::readFully(void *ptr, size_t totalReadSize)
+{
+ ensureConsumerFinishing();
+
+ unsigned char* userReadBuf = static_cast<unsigned char*>(ptr);
+
+ if (!userReadBuf) {
+ if (totalReadSize > 0) {
+ ALOGE("AddressSpaceStream::commitBufferAndReadFully failed, userReadBuf=NULL, totalReadSize %zu, lethal"
+ " error, exiting.", totalReadSize);
+ abort();
+ }
+ return nullptr;
+ }
+
+ // Advance buffered read if not yet consumed.
+ size_t remaining = totalReadSize;
+ size_t bufferedReadSize =
+ m_readLeft < remaining ? m_readLeft : remaining;
+
+ if (bufferedReadSize) {
+ memcpy(userReadBuf,
+ m_readBuf + (m_read - m_readLeft),
+ bufferedReadSize);
+ remaining -= bufferedReadSize;
+ m_readLeft -= bufferedReadSize;
+ }
+
+ if (!remaining) return userReadBuf;
+
+ // Read up to kReadSize bytes if all buffered read has been consumed.
+ size_t maxRead = m_readLeft ? 0 : kReadSize;
+ ssize_t actual = 0;
+
+ if (maxRead) {
+ actual = speculativeRead(m_readBuf, maxRead);
+
+ // Updated buffered read size.
+ if (actual > 0) {
+ m_read = m_readLeft = actual;
+ }
+
+ if (actual == 0) {
+ ALOGD("%s: end of pipe", __FUNCTION__);
+ return NULL;
+ }
+ }
+
+ // Consume buffered read and read more if necessary.
+ while (remaining) {
+ bufferedReadSize = m_readLeft < remaining ? m_readLeft : remaining;
+ if (bufferedReadSize) {
+ memcpy(userReadBuf + (totalReadSize - remaining),
+ m_readBuf + (m_read - m_readLeft),
+ bufferedReadSize);
+ remaining -= bufferedReadSize;
+ m_readLeft -= bufferedReadSize;
+ continue;
+ }
+
+ actual = speculativeRead(m_readBuf, kReadSize);
+
+ if (actual == 0) {
+ ALOGD("%s: Failed reading from pipe: %d", __FUNCTION__, errno);
+ return NULL;
+ }
+
+ if (actual > 0) {
+ m_read = m_readLeft = actual;
+ continue;
+ }
+ }
+
+ return userReadBuf;
+}
+
+const unsigned char *AddressSpaceStream::read(void *buf, size_t *inout_len) {
+ unsigned char* dst = (unsigned char*)buf;
+ size_t wanted = *inout_len;
+ ssize_t actual = speculativeRead(dst, wanted);
+
+ if (actual >= 0) {
+ *inout_len = actual;
+ } else {
+ return nullptr;
+ }
+
+ return (const unsigned char*)dst;
+}
+
+int AddressSpaceStream::writeFully(const void *buf, size_t size)
+{
+ ensureType1Finished();
+ m_context.ring_config->transfer_size = size;
+ m_context.ring_config->transfer_mode = 3;
+
+ size_t sent = 0;
+ size_t quarterRingSize = m_writeBufferSize / 4;
+ size_t chunkSize = size < quarterRingSize ? size : quarterRingSize;
+ const uint8_t* bufferBytes = (const uint8_t*)buf;
+
+ while (sent < size) {
+ size_t remaining = size - sent;
+ size_t sendThisTime = remaining < chunkSize ? remaining : chunkSize;
+
+ long sentChunks =
+ ring_buffer_view_write(
+ m_context.to_host_large_xfer.ring,
+ &m_context.to_host_large_xfer.view,
+ bufferBytes + sent, sendThisTime, 1);
+
+ if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME) {
+ notifyAvailable();
+ }
+
+ if (sentChunks == 0) {
+ ring_buffer_yield();
+ }
+
+ sent += sentChunks * sendThisTime;
+
+ if (isInError()) {
+ return -1;
+ }
+ }
+
+ ensureType3Finished();
+ m_context.ring_config->transfer_mode = 1;
+ return 0;
+}
+
+const unsigned char *AddressSpaceStream::commitBufferAndReadFully(
+ size_t writeSize, void *userReadBufPtr, size_t totalReadSize) {
+
+ if (m_usingTmpBuf) {
+ writeFully(m_tmpBuf, m_tmpBufXferSize);
+ m_usingTmpBuf = false;
+ m_tmpBufXferSize = 0;
+ }
+
+ commitBuffer(writeSize);
+ return readFully(userReadBufPtr, totalReadSize);
+}
+
+bool AddressSpaceStream::isInError() const {
+ return 1 == m_context.ring_config->in_error;
+}
+
+ssize_t AddressSpaceStream::speculativeRead(unsigned char* readBuffer, size_t trySize) {
+ flush();
+ ensureConsumerFinishing();
+
+ size_t actuallyRead = 0;
+ while (!actuallyRead) {
+ uint32_t readAvail =
+ ring_buffer_available_read(
+ m_context.from_host_large_xfer.ring,
+ &m_context.from_host_large_xfer.view);
+
+ if (!readAvail) {
+ ring_buffer_yield();
+ continue;
+ }
+
+ uint32_t toRead = readAvail > trySize ? trySize : readAvail;
+
+ long stepsRead = ring_buffer_view_read(
+ m_context.from_host_large_xfer.ring,
+ &m_context.from_host_large_xfer.view,
+ readBuffer, toRead, 1);
+
+ actuallyRead += stepsRead * toRead;
+
+ if (isInError()) {
+ return -1;
+ }
+ }
+
+ return actuallyRead;
+}
+
+void AddressSpaceStream::notifyAvailable() {
+ struct goldfish_address_space_ping request;
+ request.metadata = ASG_NOTIFY_AVAILABLE;
+ goldfish_address_space_ping(m_handle, &request);
+}
+
+uint32_t AddressSpaceStream::getRelativeBufferPos(uint32_t pos) {
+ return pos & m_writeBufferMask;
+}
+
+uint32_t AddressSpaceStream::getAvailableForWrite() {
+ uint32_t host_consumed_view;
+ __atomic_load(&m_context.ring_config->host_consumed_pos,
+ &host_consumed_view,
+ __ATOMIC_SEQ_CST);
+ uint32_t availableForWrite =
+ getRelativeBufferPos(
+ host_consumed_view -
+ m_context.ring_config->guest_write_pos - 1);
+ return availableForWrite;
+}
+
+void AddressSpaceStream::advanceWrite() {
+ uint32_t avail = getAvailableForWrite();
+
+ while (avail < m_context.ring_config->flush_interval) {
+ ensureConsumerFinishing();
+ avail = getAvailableForWrite();
+ }
+
+ __atomic_add_fetch(
+ &m_context.ring_config->guest_write_pos,
+ m_context.ring_config->flush_interval,
+ __ATOMIC_SEQ_CST);
+
+ unsigned char* newBuffer =
+ m_buf +
+ getRelativeBufferPos(
+ m_context.ring_config->guest_write_pos);
+
+ m_writeStart = newBuffer;
+}
+
+void AddressSpaceStream::ensureConsumerFinishing() {
+ uint32_t currAvailRead =
+ ring_buffer_available_read(m_context.to_host, 0);
+
+ while (currAvailRead) {
+ ring_buffer_yield();
+ uint32_t nextAvailRead = ring_buffer_available_read(m_context.to_host, 0);
+
+ if (nextAvailRead != currAvailRead) {
+ break;
+ }
+
+ if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME) {
+ notifyAvailable();
+ break;
+ }
+ }
+}
+
+void AddressSpaceStream::ensureType1Finished() {
+ ensureConsumerFinishing();
+
+ uint32_t currAvailRead =
+ ring_buffer_available_read(m_context.to_host, 0);
+
+ while (currAvailRead) {
+ ring_buffer_yield();
+ currAvailRead = ring_buffer_available_read(m_context.to_host, 0);
+ if (isInError()) {
+ return;
+ }
+ }
+}
+
+void AddressSpaceStream::ensureType3Finished() {
+ uint32_t availReadLarge =
+ ring_buffer_available_read(
+ m_context.to_host_large_xfer.ring,
+ &m_context.to_host_large_xfer.view);
+ while (availReadLarge) {
+ ring_buffer_yield();
+ availReadLarge =
+ ring_buffer_available_read(
+ m_context.to_host_large_xfer.ring,
+ &m_context.to_host_large_xfer.view);
+ if (isInError()) {
+ return;
+ }
+ }
+}
+
+int AddressSpaceStream::type1Write(uint32_t bufferOffset, size_t size) {
+ size_t sent = 0;
+ size_t sizeForRing = sizeof(struct asg_type1_xfer);
+
+ struct asg_type1_xfer xfer = {
+ bufferOffset,
+ (uint32_t)size,
+ };
+
+ uint8_t* writeBufferBytes = (uint8_t*)(&xfer);
+
+ while (sent < sizeForRing) {
+
+ long sentChunks = ring_buffer_write(
+ m_context.to_host,
+ writeBufferBytes + sent,
+ sizeForRing - sent, 1);
+
+ if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME) {
+ notifyAvailable();
+ }
+
+ if (sentChunks == 0) {
+ ring_buffer_yield();
+ }
+
+ sent += sentChunks * (sizeForRing - sent);
+
+ if (isInError()) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/system/OpenglSystemCommon/AddressSpaceStream.h b/system/OpenglSystemCommon/AddressSpaceStream.h
new file mode 100644
index 0000000..9a06991
--- /dev/null
+++ b/system/OpenglSystemCommon/AddressSpaceStream.h
@@ -0,0 +1,81 @@
+/*
+* Copyright (C) 2011 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.
+*/
+#ifndef __ADDRESS_SPACE_STREAM_H
+#define __ADDRESS_SPACE_STREAM_H
+
+#include "IOStream.h"
+
+#include "address_space_graphics_types.h"
+#include "goldfish_address_space.h"
+
+class AddressSpaceStream;
+
+AddressSpaceStream* createAddressSpaceStream(size_t bufSize);
+
+class AddressSpaceStream : public IOStream {
+public:
+ explicit AddressSpaceStream(
+ address_space_handle_t handle,
+ uint32_t version,
+ struct asg_context context,
+ uint64_t ringOffset,
+ uint64_t writeBufferOffset);
+ ~AddressSpaceStream();
+
+ virtual size_t idealAllocSize(size_t len);
+ virtual void *allocBuffer(size_t minSize);
+ virtual int commitBuffer(size_t size);
+ virtual const unsigned char *readFully( void *buf, size_t len);
+ virtual const unsigned char *read( void *buf, size_t *inout_len);
+ virtual int writeFully(const void *buf, size_t len);
+ virtual const unsigned char *commitBufferAndReadFully(size_t size, void *buf, size_t len);
+
+private:
+ bool isInError() const;
+ ssize_t speculativeRead(unsigned char* readBuffer, size_t trySize);
+ void notifyAvailable();
+ uint32_t getRelativeBufferPos(uint32_t pos);
+ uint32_t getAvailableForWrite();
+ void advanceWrite();
+ void ensureConsumerFinishing();
+ void ensureType1Finished();
+ void ensureType3Finished();
+ int type1Write(uint32_t offset, size_t size);
+
+ unsigned char* m_tmpBuf;
+ size_t m_tmpBufSize;
+ size_t m_tmpBufXferSize;
+ bool m_usingTmpBuf;
+
+ unsigned char* m_readBuf;
+ size_t m_read;
+ size_t m_readLeft;
+
+ address_space_handle_t m_handle;
+ uint32_t m_version;
+ struct asg_context m_context;
+
+ uint64_t m_ringOffset;
+ uint64_t m_writeBufferOffset;
+
+ uint32_t m_writeBufferSize;
+ uint32_t m_writeBufferMask;
+ unsigned char* m_buf;
+ unsigned char* m_writeStart;
+ uint32_t m_writeStep;
+};
+
+#endif
diff --git a/system/OpenglSystemCommon/Android.mk b/system/OpenglSystemCommon/Android.mk
index e8af35b..98dc5d3 100644
--- a/system/OpenglSystemCommon/Android.mk
+++ b/system/OpenglSystemCommon/Android.mk
@@ -11,6 +11,7 @@
FormatConversions.cpp \
HostConnection.cpp \
QemuPipeStream.cpp \
+ AddressSpaceStream.cpp \
ProcessPipe.cpp \
LOCAL_CFLAGS += -Wno-unused-variable -Wno-unused-parameter
diff --git a/system/OpenglSystemCommon/CMakeLists.txt b/system/OpenglSystemCommon/CMakeLists.txt
index 25d2b6c..fb8fc56 100644
--- a/system/OpenglSystemCommon/CMakeLists.txt
+++ b/system/OpenglSystemCommon/CMakeLists.txt
@@ -1,8 +1,8 @@
# This is an autogenerated file! Do not edit!
# instead run make from .../device/generic/goldfish-opengl
# which will re-generate this file.
-android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/Android.mk" "e8484748e692aa93eff0414a8e15ca19c75c3e333427c74a392d774872e56050")
-set(OpenglSystemCommon_src FormatConversions.cpp HostConnection.cpp QemuPipeStream.cpp ProcessPipe.cpp ThreadInfo_host.cpp)
+android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/Android.mk" "0fe7f681af374ebed00868184abdad57ad606f31f12c91a2485d98feb282b62d")
+set(OpenglSystemCommon_src FormatConversions.cpp HostConnection.cpp QemuPipeStream.cpp AddressSpaceStream.cpp ProcessPipe.cpp ThreadInfo_host.cpp)
android_add_shared_library(OpenglSystemCommon)
target_include_directories(OpenglSystemCommon PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
target_compile_definitions(OpenglSystemCommon PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGOLDFISH_VULKAN")
diff --git a/system/OpenglSystemCommon/HostConnection.cpp b/system/OpenglSystemCommon/HostConnection.cpp
index 2cd1d38..6e604df 100644
--- a/system/OpenglSystemCommon/HostConnection.cpp
+++ b/system/OpenglSystemCommon/HostConnection.cpp
@@ -44,6 +44,7 @@
#ifdef GOLDFISH_VULKAN
#include "VkEncoder.h"
+#include "AddressSpaceStream.h"
#else
namespace goldfish_vk {
struct VkEncoder {
@@ -51,6 +52,11 @@
int placeholder;
};
} // namespace goldfish_vk
+using AddressSpaceStream = QemuPipeStream;
+AddressSpaceStream* createAddressSpaceStream(size_t bufSize) {
+ ALOGE("%s: FATAL: Trying to create ASG stream in unsupported build\n", __func__);
+ abort();
+}
#endif
using goldfish_vk::VkEncoder;
@@ -162,11 +168,22 @@
if (!con) return con;
const enum HostConnectionType connType = getConnectionTypeFromProperty();
+ // const enum HostConnectionType connType = HOST_CONNECTION_VIRTIO_GPU;
switch (connType) {
- default:
- case HOST_CONNECTION_ADDRESS_SPACE: // Not implemented yet
- ALOGE("Trying to use address space graphics device, not implemented yet\n");
+ case HOST_CONNECTION_ADDRESS_SPACE: {
+ AddressSpaceStream *stream = createAddressSpaceStream(STREAM_BUFFER_SIZE);
+ if (!stream) {
+ ALOGE("Failed to create AddressSpaceStream for host connection!!!\n");
+ delete con;
+ return NULL;
+ }
+ con->m_connectionType = HOST_CONNECTION_ADDRESS_SPACE;
+ con->m_stream = stream;
+ con->m_grallocHelper = &m_goldfishGralloc;
+ con->m_processPipe = &m_goldfishProcessPipe;
+ break;
+ }
case HOST_CONNECTION_QEMU_PIPE: {
QemuPipeStream *stream = new QemuPipeStream(STREAM_BUFFER_SIZE);
if (!stream) {
@@ -233,6 +250,9 @@
con->m_processPipe = stream->getProcessPipe();
break;
}
+#else
+ default:
+ break;
#endif
}
@@ -244,6 +264,8 @@
ALOGD("HostConnection::get() New Host Connection established %p, tid %d\n",
con, getCurrentThreadId());
+
+ // ALOGD("Address space echo latency check done\n");
return con;
}
diff --git a/system/OpenglSystemCommon/address_space_graphics_types.h b/system/OpenglSystemCommon/address_space_graphics_types.h
new file mode 100644
index 0000000..1ebad34
--- /dev/null
+++ b/system/OpenglSystemCommon/address_space_graphics_types.h
@@ -0,0 +1,354 @@
+// Copyright 2019 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.
+#pragma once
+
+#include "android/base/ring_buffer.h"
+
+#include <functional>
+
+// This file defines common types for address space graphics and provides
+// documentation.
+
+// Address space graphics======================================================
+//
+// Basic idea
+//
+// Address space graphics (ASG) is a subdevice of the address space device that
+// provides a way to run graphics commands and data with fewer VM exits by
+// leveraging shared memory ring buffers.
+//
+// Each GL/Vk thread in the guest is associated with a context (asg_context).
+// asg_context consists of pointers into the shared memory that view it as a
+// collection of ring buffers and a common write buffer.
+//
+// Consumer concept
+//
+// ASG does not assume a particular rendering backend (though we will use
+// RenderThread's). This is for ease of coding/testing and flexibility; the
+// implementation is not coupled to emugl/libOpenglRender.
+//
+// Instead, there is the concept of a "Consumer" of ASG that will do something
+// with the data arriving from the shared memory region, and possibly reply
+// back to the guest. We register functions to construct and deconstruct
+// Consumers as part of emulator init (setConsumer).
+//
+// Guest workflow
+//
+// 1. Open address space device
+//
+// 2. Create the graphics context as the subdevice
+//
+// 3. ping(ASG_GET_RING) to get the offset/size of the ring buffer admin. info
+//
+// 4. ping(ASG_GET_BUFFER) to get the offset/size of the shared transfer buffer.
+//
+// 5. ioctl(CLAIM_SHARED) and mmap on those two offset/size pairs to get a
+// guest-side mapping.
+//
+// 6. call asg_context_create on the ring and buffer pointers to create the asg_context.
+//
+// 7. Now the guest and host share asg_context pts and can communicate.
+//
+// 8. But usually the guest will sometimes need to ping(ASG_NOTIFY_AVAILABLE)
+// so that the host side (which is usually a separate thread that we don't want
+// to spin too much) wakes up and processes data.
+
+namespace android {
+namespace base {
+
+class Stream;
+
+} // namespace base
+} // namespace android
+
+#define ADDRESS_SPACE_GRAPHICS_DEVICE_ID 0
+#define ADDRESS_SPACE_GRAPHICS_PAGE_SIZE 4096
+#define ADDRESS_SPACE_GRAPHICS_BLOCK_SIZE (16ULL * 1048576ULL)
+
+// AddressSpaceGraphicsContext shares memory with
+// the guest via the following layout:
+extern "C" {
+
+struct asg_ring_storage { // directly shared with guest
+ char to_host[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
+ char to_host_large_xfer[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
+ char from_host_large_xfer[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
+};
+
+// Set by the address space graphics device to notify the guest that the host
+// has slept or is able to consume something, or we are exiting, or there is an
+// error.
+enum asg_host_state {
+ // The host renderthread is asleep and needs to be woken up.
+ ASG_HOST_STATE_NEED_NOTIFY = 0,
+
+ // The host renderthread is active and can consume new data
+ // without notification.
+ ASG_HOST_STATE_CAN_CONSUME = 1,
+
+ // Normal exit
+ ASG_HOST_STATE_EXIT = 2,
+
+ // Error: Something weird happened and we need to exit.
+ ASG_HOST_STATE_ERROR = 3,
+};
+
+struct asg_ring_config;
+
+// Each context has a pair of ring buffers for communication
+// to and from the host. There is another ring buffer for large xfers
+// to the host (all xfers from the host are already considered "large").
+//
+// Each context also comes with _one_ auxiliary buffer to hold both its own
+// commands and to perform private DMA transfers.
+struct asg_context { // ptrs into RingStorage
+ struct ring_buffer* to_host;
+ char* buffer;
+ asg_host_state* host_state;
+ asg_ring_config* ring_config;
+ struct ring_buffer_with_view to_host_large_xfer;
+ struct ring_buffer_with_view from_host_large_xfer;
+};
+
+// Helper function that will be common between guest and host:
+// Given ring storage and a write buffer, returns asg_context that
+// is the correct view into it.
+static struct asg_context asg_context_create(
+ char* ring_storage,
+ char* buffer,
+ uint32_t buffer_size) {
+
+ struct asg_context res;
+
+ res.to_host =
+ reinterpret_cast<struct ring_buffer*>(
+ ring_storage +
+ offsetof(struct asg_ring_storage, to_host));
+ res.to_host_large_xfer.ring =
+ reinterpret_cast<struct ring_buffer*>(
+ ring_storage +
+ offsetof(struct asg_ring_storage, to_host_large_xfer));
+ res.from_host_large_xfer.ring =
+ reinterpret_cast<struct ring_buffer*>(
+ ring_storage +
+ offsetof(struct asg_ring_storage, from_host_large_xfer));
+
+ ring_buffer_init(res.to_host);
+
+ res.buffer = buffer;
+ res.host_state =
+ reinterpret_cast<asg_host_state*>(
+ &res.to_host->state);
+ res.ring_config =
+ reinterpret_cast<asg_ring_config*>(
+ res.to_host->config);
+
+ ring_buffer_view_init(
+ res.to_host_large_xfer.ring,
+ &res.to_host_large_xfer.view,
+ (uint8_t*)res.buffer, buffer_size);
+
+ ring_buffer_view_init(
+ res.from_host_large_xfer.ring,
+ &res.from_host_large_xfer.view,
+ (uint8_t*)res.buffer, buffer_size);
+
+ return res;
+}
+
+// During operation, the guest sends commands and data over the auxiliary
+// buffer while using the |to_host| ring to communicate what parts of the auxiliary
+// buffer is outstanding traffic needing to be consumed by the host.
+// After a transfer completes to the host, the host may write back data.
+// The guest then reads the results on the same auxiliary buffer
+// while being notified of which parts to read via the |from_host| ring.
+//
+// The size of the auxiliary buffer and flush interval is defined by
+// the following config.ini android_hw setting:
+//
+// 1) android_hw->hw_gltransport_asg_writeBufferSize
+// 2) android_hw->hw_gltransport_asg_writeStepSize
+//
+// 1) the size for the auxiliary buffer
+// 2) the step size over which commands are flushed to the host
+//
+// When transferring commands, command data is built up in writeStepSize
+// chunks and flushed to the host when either writeStepSize is reached or
+// the guest flushes explicitly.
+//
+// Command vs. Data Modes
+//
+// For command data larger than writeStepSize or when transferring data, we
+// fall back to using a different mode where the entire auxiliary buffer is
+// used to perform the transfer, |asg_writeBufferSize| steps at a time. The
+// host is also notified of the total transport size.
+//
+// When writing back to the guest, it is assumed that the write buffer will
+// be completely empty as the guest has already flushed and the host has
+// already consumed all commands/data, and is writing back. In this case,
+// the full auxiliary buffer is used at the same time for writing back to
+// the guest.
+//
+// Larger / Shared transfers
+//
+// Each of |to_host| and |from_host| can contain elements of type 1, 2, or 3:
+// Type 1: 8 bytes: 4 bytes offset, 4 bytes size. Relative to write buffer.
+struct __attribute__((__packed__)) asg_type1_xfer {
+ uint32_t offset;
+ uint32_t size;
+};
+// Type 2: 16 bytes: 16 bytes offset into address space PCI space, 8 bytes
+// size.
+struct __attribute__((__packed__)) asg_type2_xfer {
+ uint64_t physAddr;
+ uint64_t size;
+};
+// Type 3: There is a large transfer of known size and the entire write buffer
+// will be used to send it over.
+//
+// For type 1 transfers, we get the corresponding host virtual address by
+// adding the offset to the beginning of the write buffer. For type 2
+// transfers, we need to calculate the guest physical address and then call
+// addressspacecontrolops.gethostptr, which is slower since it goes through
+// a data structure map of existing mappings.
+//
+// The rings never contain a mix of type 1 and 2 elements. For to_host,
+// the guest initiates changes between type 1 and 2.
+//
+// The config fields:
+//
+struct asg_ring_config {
+ // config[0]: size of the auxiliary buffer
+ uint32_t buffer_size;
+
+ // config[1]: flush interval for the auxiliary buffer
+ uint32_t flush_interval;
+
+ // the position of the interval in the auxiliary buffer
+ // that the host has read so far
+ uint32_t host_consumed_pos;
+
+ // the start of the places the guest might write to next
+ uint32_t guest_write_pos;
+
+ // 1 if transfers are of type 1, 2 if transfers of type 2,
+ // 3 if the overall transfer size is known and we are sending something large.
+ uint32_t transfer_mode;
+
+ // the size of the transfer, used if transfer size is known.
+ // Set before setting config[2] to 3.
+ uint32_t transfer_size;
+
+ // error state
+ uint32_t in_error;
+};
+
+// State/config changes may only occur if the ring is empty, or the state
+// is transitioning to Error. That way, the host and guest have a chance to
+// synchronize on the same state.
+//
+// Thus far we've established how commands and data are transferred
+// to and from the host. Next, let's discuss how AddressSpaceGraphicsContext
+// talks to the code that actually does something with the commands
+// and sends data back.
+
+} // extern "C"
+
+namespace android {
+namespace emulation {
+namespace asg {
+
+// Consumer Concept
+//
+// AddressSpaceGraphicsContext's are each associated with a consumer that
+// takes data off the auxiliary buffer and to_host, while sending back data
+// over the auxiliary buffer / from_host.
+//
+// will read the commands and write back data.
+//
+// The consumer type is fixed at startup. The interface is as follows:
+
+// Called by the consumer, implemented in AddressSpaceGraphicsContext:
+//
+// Called when the consumer doesn't find anything to
+// read in to_host. Will make the consumer sleep
+// until another Ping(NotifyAvailable).
+using OnUnavailableReadCallback =
+ std::function<int()>;
+
+// Unpacks a type 2 transfer into host pointer and size.
+using GetPtrCallback =
+ std::function<char*(uint64_t)>;
+
+struct ConsumerCallbacks {
+ OnUnavailableReadCallback onUnavailableRead;
+ GetPtrCallback getPtr;
+};
+
+using ConsumerCreateCallback =
+ std::function<void* (struct asg_context, ConsumerCallbacks)>;
+using ConsumerDestroyCallback =
+ std::function<void(void*)>;
+using ConsumerSaveCallback =
+ std::function<void(void*, base::Stream*)>;
+using ConsumerLoadCallback =
+ std::function<void(void*, base::Stream*)>;
+
+struct ConsumerInterface {
+ ConsumerCreateCallback create;
+ ConsumerDestroyCallback destroy;
+ ConsumerSaveCallback save;
+ ConsumerLoadCallback load;
+};
+
+} // namespace asg
+} // namespace emulation
+} // namespace android
+
+// The interface for the guest:
+
+extern "C" {
+// Handled outside in address_space_device.cpp:
+//
+// Ping(device id): Create the device. On the host, the two rings and
+// auxiliary buffer are allocated. The two rings are allocated up front.
+// Both the auxiliary buffers and the rings are allocated from blocks of
+// rings and auxiliary buffers. New blocks are created if we run out either
+// way.
+enum asg_command {
+ // Ping(get_ring): Returns, in the fields:
+ // metadata: offset to give to claimShared and mmap() in the guest
+ // size: size to give to claimShared and mmap() in the guest
+ ASG_GET_RING = 0,
+
+ // Ping(get_buffer): Returns, in the fields:
+ // metadata: offset to give to claimShared and mmap() in the guest
+ // size: size to give to claimShared and mmap() in the guest
+ ASG_GET_BUFFER = 1,
+
+ // Ping(set_version): Run after the guest reads and negotiates its
+ // version of the device with the host. The host now knows the guest's
+ // version and can proceed with a protocol that works for both.
+ // size (in): the version of the guest
+ // size (out): the version of the host
+ // After this command runs, the consumer is
+ // implicitly created.
+ ASG_SET_VERSION = 2,
+
+ // Ping(notiy_available): Wakes up the consumer from sleep so it
+ // can read data via toHost
+ ASG_NOTIFY_AVAILABLE = 3,
+};
+
+} // extern "C"