[2/n] Improvements to AddressSpaceStream protocol

- Add a new state that represents the emulator rendering for sure.
- Avoid excessive notification spam.
- Add writeFullyAsync, writeFully that doesn't wait for finish.

This should be compatible with older hosts, but will notify old hosts
a bit more.

Bug: 177241396

Change-Id: Icfb00757a419f0b1328ff66eca39e51c566d8e11
diff --git a/system/OpenglSystemCommon/AddressSpaceStream.cpp b/system/OpenglSystemCommon/AddressSpaceStream.cpp
index 8c30afd..53b5285 100644
--- a/system/OpenglSystemCommon/AddressSpaceStream.cpp
+++ b/system/OpenglSystemCommon/AddressSpaceStream.cpp
@@ -15,6 +15,8 @@
 */
 #include "AddressSpaceStream.h"
 
+#include "android/base/Tracing.h"
+
 #if PLATFORM_SDK_VERSION < 26
 #include <cutils/log.h>
 #else
@@ -321,6 +323,9 @@
 }
 
 void *AddressSpaceStream::allocBuffer(size_t minSize) {
+    AEMU_SCOPED_TRACE("allocBuffer");
+    ensureType3Finished();
+
     if (!m_readBuf) {
         m_readBuf = (unsigned char*)malloc(kReadSize);
     }
@@ -465,7 +470,7 @@
 
 int AddressSpaceStream::writeFully(const void *buf, size_t size)
 {
-    ensureConsumerFinishing();
+    AEMU_SCOPED_TRACE("writeFully");
     ensureType3Finished();
     ensureType1Finished();
 
@@ -473,10 +478,77 @@
     m_context.ring_config->transfer_mode = 3;
 
     size_t sent = 0;
-    size_t quarterRingSize = m_writeBufferSize / 4;
-    size_t chunkSize = size < quarterRingSize ? size : quarterRingSize;
+    size_t preferredChunkSize = m_writeBufferSize / 4;
+    size_t chunkSize = size < preferredChunkSize ? size : preferredChunkSize;
     const uint8_t* bufferBytes = (const uint8_t*)buf;
 
+    bool hostPinged = false;
+    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 (!hostPinged && *(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME &&
+            *(m_context.host_state) != ASG_HOST_STATE_RENDERING) {
+            notifyAvailable();
+            hostPinged = true;
+        }
+
+        if (sentChunks == 0) {
+            ring_buffer_yield();
+            backoff();
+        }
+
+        sent += sentChunks * sendThisTime;
+
+        if (isInError()) {
+            return -1;
+        }
+    }
+
+    bool isRenderingAfter = ASG_HOST_STATE_RENDERING == __atomic_load_n(m_context.host_state, __ATOMIC_ACQUIRE);
+
+    if (!isRenderingAfter) {
+        notifyAvailable();
+    }
+
+    ensureType3Finished();
+
+    resetBackoff();
+    m_context.ring_config->transfer_mode = 1;
+    m_written += size;
+
+    float mb = (float)m_written / 1048576.0f;
+    if (mb > 100.0f) {
+        ALOGD("%s: %f mb in %d notifs. %f mb/notif\n", __func__,
+              mb, m_notifs, m_notifs ? mb / (float)m_notifs : 0.0f);
+        m_notifs = 0;
+        m_written = 0;
+    }
+    return 0;
+}
+
+int AddressSpaceStream::writeFullyAsync(const void *buf, size_t size)
+{
+    AEMU_SCOPED_TRACE("writeFullyAsync");
+    ensureType3Finished();
+    ensureType1Finished();
+
+    __atomic_store_n(&m_context.ring_config->transfer_size, size, __ATOMIC_RELEASE);
+    m_context.ring_config->transfer_mode = 3;
+
+    size_t sent = 0;
+    size_t preferredChunkSize = m_writeBufferSize / 2;
+    size_t chunkSize = size < preferredChunkSize ? size : preferredChunkSize;
+    const uint8_t* bufferBytes = (const uint8_t*)buf;
+
+    bool pingedHost = false;
+
     while (sent < size) {
         size_t remaining = size - sent;
         size_t sendThisTime = remaining < chunkSize ? remaining : chunkSize;
@@ -487,7 +559,12 @@
                 &m_context.to_host_large_xfer.view,
                 bufferBytes + sent, sendThisTime, 1);
 
-        if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME) {
+        uint32_t hostState = __atomic_load_n(m_context.host_state, __ATOMIC_ACQUIRE);
+
+        if (!pingedHost &&
+            hostState != ASG_HOST_STATE_CAN_CONSUME &&
+            hostState != ASG_HOST_STATE_RENDERING) {
+            pingedHost = true;
             notifyAvailable();
         }
 
@@ -503,10 +580,24 @@
         }
     }
 
-    ensureType3Finished();
+
+    bool isRenderingAfter = ASG_HOST_STATE_RENDERING == __atomic_load_n(m_context.host_state, __ATOMIC_ACQUIRE);
+
+    if (!isRenderingAfter) {
+        notifyAvailable();
+    }
+
     resetBackoff();
     m_context.ring_config->transfer_mode = 1;
     m_written += size;
+
+    float mb = (float)m_written / 1048576.0f;
+    if (mb > 100.0f) {
+        ALOGD("%s: %f mb in %d notifs. %f mb/notif\n", __func__,
+              mb, m_notifs, m_notifs ? mb / (float)m_notifs : 0.0f);
+        m_notifs = 0;
+        m_written = 0;
+    }
     return 0;
 }
 
@@ -529,7 +620,6 @@
 }
 
 ssize_t AddressSpaceStream::speculativeRead(unsigned char* readBuffer, size_t trySize) {
-    ensureConsumerFinishing();
     ensureType3Finished();
     ensureType1Finished();
 
@@ -568,6 +658,7 @@
 }
 
 void AddressSpaceStream::notifyAvailable() {
+    AEMU_SCOPED_TRACE("PING");
     struct address_space_ping request;
     request.metadata = ASG_NOTIFY_AVAILABLE;
     m_ops.ping(m_handle, &request);
@@ -597,7 +688,8 @@
             break;
         }
 
-        if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME) {
+        if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME &&
+            *(m_context.host_state) != ASG_HOST_STATE_RENDERING) {
             notifyAvailable();
             break;
         }
@@ -607,7 +699,7 @@
 }
 
 void AddressSpaceStream::ensureType1Finished() {
-    ensureConsumerFinishing();
+    AEMU_SCOPED_TRACE("ensureType1Finished");
 
     uint32_t currAvailRead =
         ring_buffer_available_read(m_context.to_host, 0);
@@ -623,6 +715,7 @@
 }
 
 void AddressSpaceStream::ensureType3Finished() {
+    AEMU_SCOPED_TRACE("ensureType3Finished");
     uint32_t availReadLarge =
         ring_buffer_available_read(
             m_context.to_host_large_xfer.ring,
@@ -634,7 +727,8 @@
             ring_buffer_available_read(
                 m_context.to_host_large_xfer.ring,
                 &m_context.to_host_large_xfer.view);
-        if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME) {
+        if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME &&
+            *(m_context.host_state) != ASG_HOST_STATE_RENDERING) {
             notifyAvailable();
         }
         if (isInError()) {
@@ -644,6 +738,11 @@
 }
 
 int AddressSpaceStream::type1Write(uint32_t bufferOffset, size_t size) {
+
+    AEMU_SCOPED_TRACE("type1Write");
+
+    ensureType3Finished();
+
     size_t sent = 0;
     size_t sizeForRing = sizeof(struct asg_type1_xfer);
 
@@ -663,10 +762,10 @@
     uint32_t ringAvailReadNow = ring_buffer_available_read(m_context.to_host, 0);
 
     while (ringAvailReadNow >= maxOutstanding * sizeForRing) {
-        ensureConsumerFinishing();
         ringAvailReadNow = ring_buffer_available_read(m_context.to_host, 0);
     }
 
+    bool hostPinged = false;
     while (sent < sizeForRing) {
 
         long sentChunks = ring_buffer_write(
@@ -674,8 +773,11 @@
             writeBufferBytes + sent,
             sizeForRing - sent, 1);
 
-        if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME) {
+        if (!hostPinged &&
+            *(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME &&
+            *(m_context.host_state) != ASG_HOST_STATE_RENDERING) {
             notifyAvailable();
+            hostPinged = true;
         }
 
         if (sentChunks == 0) {
@@ -690,7 +792,12 @@
         }
     }
 
-    ensureConsumerFinishing();
+    bool isRenderingAfter = ASG_HOST_STATE_RENDERING == __atomic_load_n(m_context.host_state, __ATOMIC_ACQUIRE);
+
+    if (!isRenderingAfter) {
+        notifyAvailable();
+    }
+
     m_written += size;
 
     float mb = (float)m_written / 1048576.0f;
@@ -706,7 +813,7 @@
 }
 
 void AddressSpaceStream::backoff() {
-#if defined(__APPLE__) || defined(__MACOSX) || defined(__Fuchsia__)
+#if defined(HOST_BUILD) || defined(__APPLE__) || defined(__MACOSX) || defined(__Fuchsia__)
     static const uint32_t kBackoffItersThreshold = 50000000;
     static const uint32_t kBackoffFactorDoublingIncrement = 50000000;
 #else
diff --git a/system/OpenglSystemCommon/AddressSpaceStream.h b/system/OpenglSystemCommon/AddressSpaceStream.h
index 9e81120..62f235e 100644
--- a/system/OpenglSystemCommon/AddressSpaceStream.h
+++ b/system/OpenglSystemCommon/AddressSpaceStream.h
@@ -44,6 +44,7 @@
     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 int writeFullyAsync(const void *buf, size_t len);
     virtual const unsigned char *commitBufferAndReadFully(size_t size, void *buf, size_t len);
 
     int getRendernodeFd() const {
@@ -97,8 +98,8 @@
     uint32_t m_notifs;
     uint32_t m_written;
 
-    uint32_t m_backoffIters;
-    uint32_t m_backoffFactor;
+    uint64_t m_backoffIters;
+    uint64_t m_backoffFactor;
 };
 
 #endif
diff --git a/system/OpenglSystemCommon/address_space_graphics_types.h b/system/OpenglSystemCommon/address_space_graphics_types.h
index 753644b..42c00f6 100644
--- a/system/OpenglSystemCommon/address_space_graphics_types.h
+++ b/system/OpenglSystemCommon/address_space_graphics_types.h
@@ -102,6 +102,9 @@
 
     // Error: Something weird happened and we need to exit.
     ASG_HOST_STATE_ERROR = 3,
+
+    // Host is rendering
+    ASG_HOST_STATE_RENDERING = 4,
 };
 
 struct asg_ring_config;