diff --git a/BUILD.gn b/BUILD.gn
index 1f50018866ae99cddb52da32b177aa238d71ff17..348ab87c46af21a98e704da82ad1a25b67b92f33 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -87,7 +87,11 @@ shared_library("vulkan_goldfish") {
   ldflags = [ "-static-libstdc++" ]
 
   if (target_os == "fuchsia") {
-    sources += [ "fuchsia/port.cc" ]
+    sources -= [ "system/OpenglSystemCommon/QemuPipeStream.cpp" ]
+    sources += [
+      "fuchsia/port.cc",
+      "system/OpenglSystemCommon/QemuPipeStreamFuchsia.cpp",
+    ]
 
     include_dirs += [
       "//third_party/vulkan_loader_and_validation_layers/include",
@@ -101,6 +105,7 @@ shared_library("vulkan_goldfish") {
     deps = [
       "//zircon/public/fidl/fuchsia-hardware-goldfish-address-space:fuchsia-hardware-goldfish-address-space_c",
       "//zircon/public/fidl/fuchsia-hardware-goldfish-control:fuchsia-hardware-goldfish-control_c",
+      "//zircon/public/fidl/fuchsia-hardware-goldfish-pipe:fuchsia-hardware-goldfish-pipe_c",
       "//zircon/public/fidl/fuchsia-sysmem",
       "//zircon/public/lib/fdio",
       "//zircon/public/lib/trace",
diff --git a/system/OpenglSystemCommon/QemuPipeStream.h b/system/OpenglSystemCommon/QemuPipeStream.h
index 0884d5740eff21f5a633ee305df52e6f3efe0ad6..8d64ab8419aa90c7845b9cfefe7342add9715ca7 100644
--- a/system/OpenglSystemCommon/QemuPipeStream.h
+++ b/system/OpenglSystemCommon/QemuPipeStream.h
@@ -25,6 +25,12 @@
 
 #include "qemu_pipe.h"
 
+#ifdef __Fuchsia__
+#include <lib/zx/channel.h>
+#include <lib/zx/event.h>
+#include <lib/zx/vmo.h>
+#endif
+
 class QemuPipeStream : public IOStream {
 public:
     typedef enum { ERR_INVALID_SOCKET = -1000 } QemuPipeStreamError;
@@ -48,6 +54,11 @@ private:
     QEMU_PIPE_HANDLE m_sock;
     size_t m_bufsize;
     unsigned char *m_buf;
+#ifdef __Fuchsia__
+    zx::channel m_channel;
+    zx::event m_event;
+    zx::vmo m_vmo;
+#endif
     QemuPipeStream(QEMU_PIPE_HANDLE sock, size_t bufSize);
 };
 
diff --git a/system/OpenglSystemCommon/QemuPipeStreamFuchsia.cpp b/system/OpenglSystemCommon/QemuPipeStreamFuchsia.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f4ee61c5e332b1fc04e4e342560ecaa707c802ed
--- /dev/null
+++ b/system/OpenglSystemCommon/QemuPipeStreamFuchsia.cpp
@@ -0,0 +1,296 @@
+/*
+* Copyright (C) 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.
+*/
+#include "QemuPipeStream.h"
+
+#include <cutils/log.h>
+#include <errno.h>
+#include <fuchsia/hardware/goldfish/pipe/c/fidl.h>
+#include <lib/fdio/fdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <zircon/process.h>
+
+#include <utility>
+
+QemuPipeStream::QemuPipeStream(size_t bufSize) :
+    IOStream(bufSize),
+    m_sock(-1),
+    m_bufsize(bufSize),
+    m_buf(nullptr)
+{
+}
+
+QemuPipeStream::QemuPipeStream(QEMU_PIPE_HANDLE sock, size_t bufSize) :
+    IOStream(bufSize),
+    m_sock(sock),
+    m_bufsize(bufSize),
+    m_buf(nullptr)
+{
+}
+
+QemuPipeStream::~QemuPipeStream()
+{
+    if (m_channel.is_valid()) {
+        flush();
+    }
+    if (m_buf) {
+        zx_status_t status = zx_vmar_unmap(zx_vmar_root_self(),
+                                           reinterpret_cast<zx_vaddr_t>(m_buf),
+                                           m_bufsize);
+        if (status != ZX_OK) {
+            ALOGE("zx_vmar_unmap failed: %d\n", status);
+            abort();
+        }
+    }
+}
+
+int QemuPipeStream::connect(void)
+{
+    int fd = TEMP_FAILURE_RETRY(open(QEMU_PIPE_PATH, O_RDWR));
+    if (fd < 0) {
+        ALOGE("%s: failed to open " QEMU_PIPE_PATH ": %s",
+              __FUNCTION__, strerror(errno));
+        return -1;
+    }
+
+    zx::channel channel;
+    zx_status_t status = fdio_get_service_handle(
+        fd, channel.reset_and_get_address());
+    if (status != ZX_OK) {
+        ALOGE("%s: failed to get service handle for " QEMU_PIPE_PATH ": %d",
+              __FUNCTION__, status);
+        close(fd);
+        return -1;
+    }
+
+    zx::event event;
+    status = zx::event::create(0, &event);
+    if (status != ZX_OK) {
+        ALOGE("%s: failed to create event: %d", __FUNCTION__, status);
+        return -1;
+    }
+    zx::event event_copy;
+    status = event.duplicate(ZX_RIGHT_SAME_RIGHTS, &event_copy);
+    if (status != ZX_OK) {
+        ALOGE("%s: failed to duplicate event: %d", __FUNCTION__, status);
+        return -1;
+    }
+
+    status = fuchsia_hardware_goldfish_pipe_DeviceSetEvent(
+        channel.get(), event_copy.release());
+    if (status != ZX_OK) {
+        ALOGE("%s: failed to set event: %d:%d", __FUNCTION__, status);
+        return -1;
+    }
+
+    zx_status_t status2 = ZX_OK;
+    zx::vmo vmo;
+    status = fuchsia_hardware_goldfish_pipe_DeviceGetBuffer(
+        channel.get(), &status2, vmo.reset_and_get_address());
+    if (status != ZX_OK || status2 != ZX_OK) {
+        ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__, status, status2);
+        return -1;
+    }
+
+    size_t len = strlen("pipe:opengles");
+    status = vmo.write("pipe:opengles", 0, len + 1);
+    if (status != ZX_OK) {
+        ALOGE("%s: failed write pipe name", __FUNCTION__);
+        return -1;
+    }
+
+    uint64_t actual;
+    status = fuchsia_hardware_goldfish_pipe_DeviceWrite(
+        channel.get(), len + 1, 0, &status2, &actual);
+    if (status != ZX_OK || status2 != ZX_OK) {
+        ALOGD("%s: connecting to pipe service failed: %d:%d", __FUNCTION__,
+              status, status2);
+        return -1;
+    }
+
+    m_channel = std::move(channel);
+    m_event = std::move(event);
+    m_vmo = std::move(vmo);
+    return 0;
+}
+
+void *QemuPipeStream::allocBuffer(size_t minSize)
+{
+    zx_status_t status;
+    if (m_buf) {
+        if (minSize <= m_bufsize) {
+            return m_buf;
+        }
+        status = zx_vmar_unmap(zx_vmar_root_self(),
+                               reinterpret_cast<zx_vaddr_t>(m_buf),
+                               m_bufsize);
+        if (status != ZX_OK) {
+          ALOGE("zx_vmar_unmap failed: %d\n", status);
+          abort();
+        }
+        m_buf = nullptr;
+    }
+
+    size_t allocSize = m_bufsize < minSize ? minSize : m_bufsize;
+
+    zx_status_t status2 = ZX_OK;
+    status = fuchsia_hardware_goldfish_pipe_DeviceSetBufferSize(
+        m_channel.get(), allocSize, &status2);
+    if (status != ZX_OK || status2 != ZX_OK) {
+        ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__, status, status2);
+        return nullptr;
+    }
+
+    zx::vmo vmo;
+    status = fuchsia_hardware_goldfish_pipe_DeviceGetBuffer(
+        m_channel.get(), &status2, vmo.reset_and_get_address());
+    if (status != ZX_OK || status2 != ZX_OK) {
+        ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__, status, status2);
+        return nullptr;
+    }
+
+    zx_vaddr_t mapped_addr;
+    status = zx_vmar_map(zx_vmar_root_self(),
+                         ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
+                         0, vmo.get(), 0, allocSize, &mapped_addr);
+    if (status != ZX_OK) {
+        ALOGE("%s: failed to map buffer: %d:%d", __FUNCTION__, status);
+        return nullptr;
+    }
+
+    m_buf = reinterpret_cast<unsigned char*>(mapped_addr);
+    m_bufsize = allocSize;
+    m_vmo = std::move(vmo);
+    return m_buf;
+}
+
+int QemuPipeStream::commitBuffer(size_t size)
+{
+    if (size == 0) return 0;
+
+    size_t remaining = size;
+    while (remaining) {
+        zx_status_t status2 = ZX_OK;
+        uint64_t actual = 0;
+        zx_status_t status = fuchsia_hardware_goldfish_pipe_DeviceWrite(
+            m_channel.get(), remaining, size - remaining, &status2, &actual);
+        if (status != ZX_OK) {
+            ALOGD("%s: Failed writing to pipe: %d", __FUNCTION__, status);
+            return -1;
+        }
+        if (actual) {
+            remaining -= actual;
+            continue;
+        }
+        if (status2 != ZX_ERR_SHOULD_WAIT) {
+            ALOGD("%s: Error writing to pipe: %d", __FUNCTION__, status2);
+            return -1;
+        }
+        zx_signals_t observed = ZX_SIGNAL_NONE;
+        status = m_event.wait_one(
+            fuchsia_hardware_goldfish_pipe_SIGNAL_WRITABLE |
+            fuchsia_hardware_goldfish_pipe_SIGNAL_HANGUP,
+            zx::time::infinite(), &observed);
+        if (status != ZX_OK) {
+            ALOGD("%s: wait_one failed: %d", __FUNCTION__, status);
+            return -1;
+        }
+        if (observed & fuchsia_hardware_goldfish_pipe_SIGNAL_HANGUP) {
+            ALOGD("%s: Remote end hungup", __FUNCTION__);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+int QemuPipeStream::writeFully(const void *buf, size_t len)
+{
+    ALOGE("%s: unsupported", __FUNCTION__);
+    abort();
+    return -1;
+}
+
+QEMU_PIPE_HANDLE QemuPipeStream::getSocket() const {
+    return m_sock;
+}
+
+const unsigned char *QemuPipeStream::readFully(void *buf, size_t len)
+{
+    if (!m_channel.is_valid()) return nullptr;
+
+    if (!buf) {
+        if (len > 0) {
+            ALOGE("QemuPipeStream::readFully failed, buf=NULL, len %zu, lethal"
+                    " error, exiting.", len);
+            abort();
+        }
+        return nullptr;
+    }
+
+    size_t remaining = len;
+    while (remaining) {
+        size_t readSize = m_bufsize < remaining ? m_bufsize : remaining;
+        zx_status_t status2 = ZX_OK;
+        uint64_t actual = 0;
+        zx_status_t status = fuchsia_hardware_goldfish_pipe_DeviceRead(
+            m_channel.get(), readSize, 0, &status2, &actual);
+        if (status != ZX_OK) {
+            ALOGD("%s: Failed reading from pipe: %d", __FUNCTION__, status);
+            return nullptr;
+        }
+        if (actual) {
+            m_vmo.read(static_cast<char *>(buf) + (len - remaining), 0, actual);
+            remaining -= actual;
+            continue;
+        }
+        if (status2 != ZX_ERR_SHOULD_WAIT) {
+            ALOGD("%s: Error reading from pipe: %d", __FUNCTION__, status2);
+            return nullptr;
+        }
+        zx_signals_t observed = ZX_SIGNAL_NONE;
+        status = m_event.wait_one(
+            fuchsia_hardware_goldfish_pipe_SIGNAL_READABLE |
+            fuchsia_hardware_goldfish_pipe_SIGNAL_HANGUP,
+            zx::time::infinite(), &observed);
+        if (status != ZX_OK) {
+            ALOGD("%s: wait_one failed: %d", __FUNCTION__, status);
+            return nullptr;
+        }
+        if (observed & fuchsia_hardware_goldfish_pipe_SIGNAL_HANGUP) {
+            ALOGD("%s: Remote end hungup", __FUNCTION__);
+            return nullptr;
+        }
+    }
+
+    return static_cast<const unsigned char *>(buf);
+}
+
+const unsigned char *QemuPipeStream::read(void *buf, size_t *inout_len)
+{
+    ALOGE("%s: unsupported", __FUNCTION__);
+    abort();
+    return nullptr;
+}
+
+int QemuPipeStream::recv(void *buf, size_t len)
+{
+    ALOGE("%s: unsupported", __FUNCTION__);
+    abort();
+    return -1;
+}
diff --git a/system/vulkan_enc/AndroidHardwareBuffer.cpp b/system/vulkan_enc/AndroidHardwareBuffer.cpp
index 05f14f1f3a3f9246592492a7dd94cd8c73c7f0d1..516791bd60e9c0c9ecdd1cb1a344bc2fb0e5abc2 100644
--- a/system/vulkan_enc/AndroidHardwareBuffer.cpp
+++ b/system/vulkan_enc/AndroidHardwareBuffer.cpp
@@ -59,8 +59,8 @@ VkResult getAndroidHardwareBufferPropertiesANDROID(
     VkAndroidHardwareBufferPropertiesANDROID* pProperties) {
 
     VkAndroidHardwareBufferFormatPropertiesANDROID* ahbFormatProps =
-        (VkAndroidHardwareBufferFormatPropertiesANDROID*)vk_find_struct(
-            (vk_struct_common*)pProperties->pNext,
+        vk_find_struct<VkAndroidHardwareBufferFormatPropertiesANDROID>(
+            pProperties,
             VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID);
 
     if (ahbFormatProps) {
@@ -76,7 +76,7 @@ VkResult getAndroidHardwareBufferPropertiesANDROID(
             return VK_ERROR_INVALID_EXTERNAL_HANDLE;
         }
 
-        ahbFormatProps->format = VK_FORMAT_UNDEFINED;
+        ahbFormatProps->format = vk_format_from_android(desc.format);
         ahbFormatProps->externalFormat = desc.format;
 
         // The formatFeatures member must include
diff --git a/system/vulkan_enc/ResourceTracker.cpp b/system/vulkan_enc/ResourceTracker.cpp
index 631f24d1a1cceeed1420f6e1adf620e5cb88b9cc..3b5bdb85f79c486d85d43e4e635013bd598fa6e8 100644
--- a/system/vulkan_enc/ResourceTracker.cpp
+++ b/system/vulkan_enc/ResourceTracker.cpp
@@ -1536,7 +1536,9 @@ public:
 
         VkEncoder* enc = (VkEncoder*)context;
 
-        VkMemoryAllocateInfo finalAllocInfo = *pAllocateInfo;
+        VkMemoryAllocateInfo finalAllocInfo = vk_make_orphan_copy(*pAllocateInfo);
+        vk_struct_chain_iterator structChainIter = vk_make_chain_iterator(&finalAllocInfo);
+
         VkMemoryDedicatedAllocateInfo dedicatedAllocInfo;
         VkImportColorBufferGOOGLE importCbInfo = {
             VK_STRUCTURE_TYPE_IMPORT_COLOR_BUFFER_GOOGLE, 0,
@@ -1545,29 +1547,24 @@ public:
         //     VK_STRUCTURE_TYPE_IMPORT_PHYSICAL_ADDRESS_GOOGLE, 0,
         // };
 
-        vk_struct_common* structChain =
-        structChain = vk_init_struct_chain(
-            (vk_struct_common*)(&finalAllocInfo));
-        structChain->pNext = nullptr;
-
-        VkExportMemoryAllocateInfo* exportAllocateInfoPtr =
-            (VkExportMemoryAllocateInfo*)vk_find_struct((vk_struct_common*)pAllocateInfo,
+        const VkExportMemoryAllocateInfo* exportAllocateInfoPtr =
+            vk_find_struct<VkExportMemoryAllocateInfo>(pAllocateInfo,
                 VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO);
 
-        VkImportAndroidHardwareBufferInfoANDROID* importAhbInfoPtr =
-            (VkImportAndroidHardwareBufferInfoANDROID*)vk_find_struct((vk_struct_common*)pAllocateInfo,
+        const VkImportAndroidHardwareBufferInfoANDROID* importAhbInfoPtr =
+            vk_find_struct<VkImportAndroidHardwareBufferInfoANDROID>(pAllocateInfo,
                 VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID);
 
-        VkImportMemoryBufferCollectionFUCHSIA* importBufferCollectionInfoPtr =
-            (VkImportMemoryBufferCollectionFUCHSIA*)vk_find_struct((vk_struct_common*)pAllocateInfo,
+        const VkImportMemoryBufferCollectionFUCHSIA* importBufferCollectionInfoPtr =
+            vk_find_struct<VkImportMemoryBufferCollectionFUCHSIA>(pAllocateInfo,
                 VK_STRUCTURE_TYPE_IMPORT_MEMORY_BUFFER_COLLECTION_FUCHSIA);
 
-        VkImportMemoryZirconHandleInfoFUCHSIA* importVmoInfoPtr =
-            (VkImportMemoryZirconHandleInfoFUCHSIA*)vk_find_struct((vk_struct_common*)pAllocateInfo,
+        const VkImportMemoryZirconHandleInfoFUCHSIA* importVmoInfoPtr =
+            vk_find_struct<VkImportMemoryZirconHandleInfoFUCHSIA>(pAllocateInfo,
                 VK_STRUCTURE_TYPE_TEMP_IMPORT_MEMORY_ZIRCON_HANDLE_INFO_FUCHSIA);
 
-        VkMemoryDedicatedAllocateInfo* dedicatedAllocInfoPtr =
-            (VkMemoryDedicatedAllocateInfo*)vk_find_struct((vk_struct_common*)pAllocateInfo,
+        const VkMemoryDedicatedAllocateInfo* dedicatedAllocInfoPtr =
+            vk_find_struct<VkMemoryDedicatedAllocateInfo>(pAllocateInfo,
                 VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
 
         bool shouldPassThroughDedicatedAllocInfo =
@@ -1592,12 +1589,8 @@ public:
 
         if (shouldPassThroughDedicatedAllocInfo &&
             dedicatedAllocInfoPtr) {
-            dedicatedAllocInfo = *dedicatedAllocInfoPtr;
-            structChain->pNext =
-                (vk_struct_common*)(&dedicatedAllocInfo);
-            structChain =
-                (vk_struct_common*)(&dedicatedAllocInfo);
-            structChain->pNext = nullptr;
+            dedicatedAllocInfo = vk_make_orphan_copy(*dedicatedAllocInfoPtr);
+            vk_append_struct(&structChainIter, &dedicatedAllocInfo);
         }
 
         // State needed for import/export.
@@ -1708,8 +1701,7 @@ public:
             const cb_handle_t* cb_handle =
                 reinterpret_cast<const cb_handle_t*>(handle);
             importCbInfo.colorBuffer = cb_handle->hostHandle;
-            structChain =
-                vk_append_struct(structChain, (vk_struct_common*)(&importCbInfo));
+            vk_append_struct(&structChainIter, &importCbInfo);
         }
 
         zx_handle_t vmo_handle = ZX_HANDLE_INVALID;
@@ -1829,8 +1821,7 @@ public:
             if (status != ZX_OK || status2 != ZX_OK) {
                 ALOGE("GetColorBuffer failed: %d:%d", status, status2);
             }
-            structChain =
-                vk_append_struct(structChain, (vk_struct_common*)(&importCbInfo));
+            vk_append_struct(&structChainIter, &importCbInfo);
         }
 #endif
 
@@ -2125,10 +2116,8 @@ public:
         transformExternalResourceMemoryRequirementsForGuest(&reqs2->memoryRequirements);
 
         VkMemoryDedicatedRequirements* dedicatedReqs =
-            (VkMemoryDedicatedRequirements*)
-            vk_find_struct(
-                (vk_struct_common*)reqs2,
-                VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS);
+            vk_find_struct<VkMemoryDedicatedRequirements>(
+                reqs2, VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS);
 
         if (!dedicatedReqs) return;
 
@@ -2157,10 +2146,8 @@ public:
         transformExternalResourceMemoryRequirementsForGuest(&reqs2->memoryRequirements);
 
         VkMemoryDedicatedRequirements* dedicatedReqs =
-            (VkMemoryDedicatedRequirements*)
-            vk_find_struct(
-                (vk_struct_common*)reqs2,
-                VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS);
+            vk_find_struct<VkMemoryDedicatedRequirements>(
+                reqs2, VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS);
 
         if (!dedicatedReqs) return;
 
@@ -2175,82 +2162,55 @@ public:
         VkImage *pImage) {
         VkEncoder* enc = (VkEncoder*)context;
 
-        VkImageCreateInfo localCreateInfo = *pCreateInfo;
-        VkNativeBufferANDROID localAnb;
+        VkImageCreateInfo localCreateInfo = vk_make_orphan_copy(*pCreateInfo);
+        vk_struct_chain_iterator structChainIter = vk_make_chain_iterator(&localCreateInfo);
         VkExternalMemoryImageCreateInfo localExtImgCi;
 
-        VkImageCreateInfo* pCreateInfo_mut = &localCreateInfo;
-
-        VkNativeBufferANDROID* anbInfoPtr =
-            (VkNativeBufferANDROID*)
-            vk_find_struct(
-                (vk_struct_common*)pCreateInfo_mut,
-                VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID);
-
-        if (anbInfoPtr) {
-            localAnb = *anbInfoPtr;
-        }
-
-        VkExternalMemoryImageCreateInfo* extImgCiPtr =
-            (VkExternalMemoryImageCreateInfo*)
-            vk_find_struct(
-                (vk_struct_common*)pCreateInfo_mut,
+        const VkExternalMemoryImageCreateInfo* extImgCiPtr =
+            vk_find_struct<VkExternalMemoryImageCreateInfo>(
+                pCreateInfo,
                 VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
-
-        if (extImgCiPtr) {
-            localExtImgCi = *extImgCiPtr;
-        }
-
-#ifdef VK_USE_PLATFORM_ANDROID_KHR
-        VkExternalFormatANDROID localExtFormatAndroid;
-        VkExternalFormatANDROID* extFormatAndroidPtr =
-        (VkExternalFormatANDROID*)
-        vk_find_struct(
-            (vk_struct_common*)pCreateInfo_mut,
-            VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID);
-        if (extFormatAndroidPtr) {
-            localExtFormatAndroid = *extFormatAndroidPtr;
-        }
-#endif
-
-#ifdef VK_USE_PLATFORM_FUCHSIA
-        VkBufferCollectionImageCreateInfoFUCHSIA* extBufferCollectionPtr =
-        (VkBufferCollectionImageCreateInfoFUCHSIA*)
-        vk_find_struct(
-            (vk_struct_common*)pCreateInfo_mut,
-            VK_STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA);
-#endif
-
-        vk_struct_common* structChain =
-            vk_init_struct_chain((vk_struct_common*)pCreateInfo_mut);
-
         if (extImgCiPtr) {
-            structChain =
-                vk_append_struct(
-                    structChain, (vk_struct_common*)(&localExtImgCi));
+            localExtImgCi = vk_make_orphan_copy(*extImgCiPtr);
+            vk_append_struct(&structChainIter, &localExtImgCi);
         }
 
 #ifdef VK_USE_PLATFORM_ANDROID_KHR
+        VkNativeBufferANDROID localAnb;
+        const VkNativeBufferANDROID* anbInfoPtr =
+            vk_find_struct<VkNativeBufferANDROID>(
+                pCreateInfo,
+                VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID);
         if (anbInfoPtr) {
-            structChain =
-                vk_append_struct(
-                    structChain, (vk_struct_common*)(&localAnb));
+            localAnb = vk_make_orphan_copy(*anbInfoPtr);
+            vk_append_struct(&structChainIter, &localAnb);
         }
 
+        VkExternalFormatANDROID localExtFormatAndroid;
+        const VkExternalFormatANDROID* extFormatAndroidPtr =
+            vk_find_struct<VkExternalFormatANDROID>(
+                pCreateInfo,
+                VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID);
         if (extFormatAndroidPtr) {
+            localExtFormatAndroid = vk_make_orphan_copy(*extFormatAndroidPtr);
+
             // Do not append external format android;
-            // instead, replace the local image pCreateInfo_mut format
+            // instead, replace the local image localCreateInfo format
             // with the corresponding Vulkan format
             if (extFormatAndroidPtr->externalFormat) {
-                pCreateInfo_mut->format =
+                localCreateInfo.format =
                     vk_format_from_android(extFormatAndroidPtr->externalFormat);
-                if (pCreateInfo_mut->format == VK_FORMAT_UNDEFINED)
+                if (localCreateInfo.format == VK_FORMAT_UNDEFINED)
                     return VK_ERROR_VALIDATION_FAILED_EXT;
             }
         }
 #endif
 
 #ifdef VK_USE_PLATFORM_FUCHSIA
+        const VkBufferCollectionImageCreateInfoFUCHSIA* extBufferCollectionPtr =
+            vk_find_struct<VkBufferCollectionImageCreateInfoFUCHSIA>(
+                pCreateInfo,
+                VK_STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA);
         if (extBufferCollectionPtr) {
             auto collection = reinterpret_cast<fuchsia::sysmem::BufferCollectionSyncPtr*>(
                 extBufferCollectionPtr->collection);
@@ -2273,8 +2233,8 @@ public:
                 status = fuchsia_hardware_goldfish_control_DeviceCreateColorBuffer(
                     mControlDevice,
                     vmo_handle,
-                    pCreateInfo_mut->extent.width,
-                    pCreateInfo_mut->extent.height,
+                    localCreateInfo.extent.width,
+                    localCreateInfo.extent.height,
                     fuchsia_hardware_goldfish_control_FormatType_BGRA,
                     &status2);
                 if (status != ZX_OK || status2 != ZX_OK) {
@@ -2284,17 +2244,17 @@ public:
         }
 
         // Allow external memory for all color attachments on fuchsia.
-        if (pCreateInfo_mut->usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) {
+        if (localCreateInfo.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) {
             if (!extImgCiPtr) {
                 localExtImgCi.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
                 localExtImgCi.pNext = nullptr;
                 localExtImgCi.handleTypes = ~0; // handle type just needs to be non-zero
-                extImgCiPtr = &localExtImgCi;
+                extImgCiPtr = &localExtImgCi;   // no vk_append_struct required
             }
         }
 #endif
 
-        VkResult res = enc->vkCreateImage(device, pCreateInfo_mut, pAllocator, pImage);
+        VkResult res = enc->vkCreateImage(device, &localCreateInfo, pAllocator, pImage);
 
         if (res != VK_SUCCESS) return res;
 
@@ -2306,7 +2266,7 @@ public:
         auto& info = it->second;
 
         info.device = device;
-        info.createInfo = *pCreateInfo_mut;
+        info.createInfo = *pCreateInfo;
         info.createInfo.pNext = nullptr;
 
         if (!extImgCiPtr) return res;
@@ -2324,28 +2284,16 @@ public:
         const VkAllocationCallbacks* pAllocator,
         VkSamplerYcbcrConversion* pYcbcrConversion) {
 
-        VkSamplerYcbcrConversionCreateInfo localCreateInfo = *pCreateInfo;
-        VkSamplerYcbcrConversionCreateInfo* pCreateInfo_mut = &localCreateInfo;
-
-#ifdef VK_USE_PLATFORM_ANDROID_KHR
-        VkExternalFormatANDROID localExtFormatAndroid;
-        VkExternalFormatANDROID* extFormatAndroidPtr =
-        (VkExternalFormatANDROID*)
-        vk_find_struct(
-            (vk_struct_common*)pCreateInfo_mut,
-            VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID);
-        if (extFormatAndroidPtr) {
-            localExtFormatAndroid = *extFormatAndroidPtr;
-        }
-#endif
-
-        vk_struct_common* structChain =
-            vk_init_struct_chain((vk_struct_common*)pCreateInfo_mut);
+        VkSamplerYcbcrConversionCreateInfo localCreateInfo = vk_make_orphan_copy(*pCreateInfo);
 
 #ifdef VK_USE_PLATFORM_ANDROID_KHR
+        const VkExternalFormatANDROID* extFormatAndroidPtr =
+            vk_find_struct<VkExternalFormatANDROID>(
+                pCreateInfo,
+                VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID);
         if (extFormatAndroidPtr) {
             if (extFormatAndroidPtr->externalFormat) {
-                pCreateInfo_mut->format =
+                localCreateInfo.format =
                     vk_format_from_android(extFormatAndroidPtr->externalFormat);
             }
         }
@@ -2353,7 +2301,7 @@ public:
 
         VkEncoder* enc = (VkEncoder*)context;
         return enc->vkCreateSamplerYcbcrConversion(
-            device, pCreateInfo, pAllocator, pYcbcrConversion);
+            device, &localCreateInfo, pAllocator, pYcbcrConversion);
     }
 
     VkResult on_vkCreateSamplerYcbcrConversionKHR(
@@ -2363,34 +2311,24 @@ public:
         const VkAllocationCallbacks* pAllocator,
         VkSamplerYcbcrConversion* pYcbcrConversion) {
 
-        VkSamplerYcbcrConversionCreateInfo localCreateInfo = *pCreateInfo;
-        VkSamplerYcbcrConversionCreateInfo* pCreateInfo_mut = &localCreateInfo;
+        VkSamplerYcbcrConversionCreateInfo localCreateInfo = vk_make_orphan_copy(*pCreateInfo);
 
 #ifdef VK_USE_PLATFORM_ANDROID_KHR
-        VkExternalFormatANDROID localExtFormatAndroid;
-        VkExternalFormatANDROID* extFormatAndroidPtr =
-        (VkExternalFormatANDROID*)
-        vk_find_struct(
-            (vk_struct_common*)pCreateInfo_mut,
-            VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID);
+        const VkExternalFormatANDROID* extFormatAndroidPtr =
+            vk_find_struct<VkExternalFormatANDROID>(
+                pCreateInfo,
+                VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID);
         if (extFormatAndroidPtr) {
-            localExtFormatAndroid = *extFormatAndroidPtr;
-        }
-#endif
-
-        vk_struct_common* structChain =
-            vk_init_struct_chain((vk_struct_common*)pCreateInfo_mut);
-
-#ifdef VK_USE_PLATFORM_ANDROID_KHR
-        if (extFormatAndroidPtr) {
-            pCreateInfo_mut->format =
-                vk_format_from_android(extFormatAndroidPtr->externalFormat);
+            if (extFormatAndroidPtr->externalFormat) {
+                localCreateInfo.format =
+                    vk_format_from_android(extFormatAndroidPtr->externalFormat);
+            }
         }
 #endif
 
         VkEncoder* enc = (VkEncoder*)context;
         return enc->vkCreateSamplerYcbcrConversionKHR(
-            device, pCreateInfo, pAllocator, pYcbcrConversion);
+            device, &localCreateInfo, pAllocator, pYcbcrConversion);
     }
 
     void on_vkDestroyImage(
@@ -2473,8 +2411,8 @@ public:
         info.createInfo = *pCreateInfo;
         info.createInfo.pNext = nullptr;
 
-        VkExternalMemoryBufferCreateInfo* extBufCi =
-            (VkExternalMemoryBufferCreateInfo*)vk_find_struct((vk_struct_common*)pCreateInfo,
+        const VkExternalMemoryBufferCreateInfo* extBufCi =
+            vk_find_struct<VkExternalMemoryBufferCreateInfo>(pCreateInfo,
                 VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO);
 
         if (!extBufCi) return res;
@@ -2565,9 +2503,9 @@ public:
 
         VkSemaphoreCreateInfo finalCreateInfo = *pCreateInfo;
 
-        VkExportSemaphoreCreateInfoKHR* exportSemaphoreInfoPtr =
-            (VkExportSemaphoreCreateInfoKHR*)vk_find_struct(
-                (vk_struct_common*)pCreateInfo,
+        const VkExportSemaphoreCreateInfoKHR* exportSemaphoreInfoPtr =
+            vk_find_struct<VkExportSemaphoreCreateInfoKHR>(
+                pCreateInfo,
                 VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR);
 
 #ifdef VK_USE_PLATFORM_FUCHSIA
@@ -3137,8 +3075,8 @@ public:
         (void)input_result;
 
         VkAndroidHardwareBufferUsageANDROID* output_ahw_usage =
-            (VkAndroidHardwareBufferUsageANDROID*)vk_find_struct(
-                (vk_struct_common*)pImageFormatProperties,
+            vk_find_struct<VkAndroidHardwareBufferUsageANDROID>(
+                pImageFormatProperties,
                 VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID);
 
         VkResult hostRes;
diff --git a/system/vulkan_enc/goldfish_vk_private_defs.h b/system/vulkan_enc/goldfish_vk_private_defs.h
index 3eed2442ed3805998e3dc53e7c1f42bfce6a99bd..b1e5dcc29eb48c0020d9cf11879f15f5b4125cf4 100644
--- a/system/vulkan_enc/goldfish_vk_private_defs.h
+++ b/system/vulkan_enc/goldfish_vk_private_defs.h
@@ -425,6 +425,24 @@ typedef void (VKAPI_PTR *PFN_vkResetCommandBufferAsyncGOOGLE)(
     VkCommandBuffer commandBuffer,
     VkCommandBufferResetFlags flags);
 
+#ifdef VK_USE_PLATFORM_FUCHSIA
+#define VK_FUCHSIA_buffer_collection 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBufferCollectionFUCHSIA)
+
+#define VK_FUCHSIA_BUFFER_COLLECTION_SPEC_VERSION 1
+#define VK_FUCHSIA_BUFFER_COLLECTION_EXTENSION_NAME "VK_FUCHSIA_buffer_collection"
+
+typedef struct VkBufferCollectionImageCreateInfoFUCHSIA {
+    VkStructureType              sType;
+    const void*                  pNext;
+    VkBufferCollectionFUCHSIA    collection;
+    uint32_t                     index;
+} VkBufferCollectionImageCreateInfoFUCHSIA;
+
+#define VK_STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA \
+    ((VkStructureType)1001004005)
+#endif  // VK_USE_PLATFORM_FUCHSIA
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/system/vulkan_enc/vk_format_info.h b/system/vulkan_enc/vk_format_info.h
index 3bac828aab8089d82e9bd112d96a5f0225921991..0337f95a088b79428bab3bda9a31173b9eb9e009 100644
--- a/system/vulkan_enc/vk_format_info.h
+++ b/system/vulkan_enc/vk_format_info.h
@@ -58,7 +58,7 @@ vk_format_from_android(unsigned android_format)
 }
 
 static inline unsigned
-android_format_from_vk(unsigned vk_format)
+android_format_from_vk(VkFormat vk_format)
 {
    switch (vk_format) {
    case VK_FORMAT_R8G8B8A8_UNORM:
diff --git a/system/vulkan_enc/vk_util.h b/system/vulkan_enc/vk_util.h
index da394f4ebfc4271b2be790c1c7d1cb89fc05e2c6..41410d34f4c46c98b1f2781517760c68b9507ee7 100644
--- a/system/vulkan_enc/vk_util.h
+++ b/system/vulkan_enc/vk_util.h
@@ -33,6 +33,10 @@ struct vk_struct_common {
     struct vk_struct_common *pNext;
 };
 
+struct vk_struct_chain_iterator {
+    vk_struct_common* value;
+};
+
 #define vk_foreach_struct(__iter, __start) \
    for (struct vk_struct_common *__iter = (struct vk_struct_common *)(__start); \
         __iter; __iter = __iter->pNext)
@@ -192,11 +196,24 @@ __vk_find_struct(void *start, VkStructureType sType)
    return NULL;
 }
 
-#define vk_find_struct(__start, __sType) \
-   __vk_find_struct((__start), __sType)
+template <class T> void vk_is_vk_struct(T *s)
+{
+    static_assert(sizeof(s->sType) == sizeof(VkStructureType), "Vulkan structures has the sType field of type VkStructureType");
+    static_assert(sizeof(s->pNext) == sizeof(void*), "Vulkan structures has the pNext field of void*");
+}
+
+template <class T, class H> T* vk_find_struct(H* head, VkStructureType sType)
+{
+    vk_is_vk_struct(head);
+    return static_cast<T*>(__vk_find_struct(static_cast<void*>(head), sType));
+}
 
-#define vk_find_struct_const(__start, __sType) \
-   (const void *)__vk_find_struct((void *)(__start), __sType)
+template <class T, class H> const T* vk_find_struct(const H* head, VkStructureType sType)
+{
+    vk_is_vk_struct(head);
+    return static_cast<const T*>(__vk_find_struct(const_cast<void*>(static_cast<const void*>(head)),
+                                 sType));
+}
 
 uint32_t vk_get_driver_version(void);
 
@@ -208,36 +225,32 @@ uint32_t vk_get_version_override(void);
 #define VK_ENUM_OFFSET(__enum) \
    ((__enum) >= VK_EXT_OFFSET ? ((__enum) % 1000) : (__enum))
 
-static inline vk_struct_common*
-vk_init_struct_chain(vk_struct_common* start)
+template <class T> T vk_make_orphan_copy(const T& vk_struct) {
+    T copy = vk_struct;
+    copy.pNext = NULL;
+    return copy;
+}
+
+template <class T> vk_struct_chain_iterator vk_make_chain_iterator(T* vk_struct)
 {
-   start->pNext = nullptr;
-   return start;
+    vk_is_vk_struct(vk_struct);
+    vk_struct_chain_iterator result = { reinterpret_cast<vk_struct_common*>(vk_struct) };
+    return result;
 }
 
-static inline vk_struct_common*
-vk_last_struct_chain(vk_struct_common* i)
+template <class T> void vk_append_struct(vk_struct_chain_iterator* i, T* vk_struct)
 {
-    for (int n = 1000000; n > 0; --n) {
-        vk_struct_common* next = i->pNext;
-        if (next) {
-            i = next;
-        } else {
-            return i;
-        }
+    vk_is_vk_struct(vk_struct);
+
+    vk_struct_common* p = i->value;
+    if (p->pNext) {
+        ::abort();
     }
 
-    ::abort();  // crash on loops in the chain
-    return NULL;
-}
+    p->pNext = reinterpret_cast<vk_struct_common *>(vk_struct);
+    vk_struct->pNext = NULL;
 
-static inline vk_struct_common*
-vk_append_struct(vk_struct_common* current, vk_struct_common* next)
-{
-    vk_struct_common* last = vk_last_struct_chain(current);
-    last->pNext = next;
-    next->pNext = nullptr;
-    return current;
+    *i = vk_make_chain_iterator(vk_struct);
 }
 
 #endif /* VK_UTIL_H */