Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  *
     16  */
     17 
     18 #define LOG_TAG "VulkanTestHelpers"
     19 
     20 #include "VulkanTestHelpers.h"
     21 
     22 #include <android/asset_manager.h>
     23 #include <android/asset_manager_jni.h>
     24 #include <android/hardware_buffer.h>
     25 #include <android/log.h>
     26 
     27 #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
     28 
     29 #define ASSERT(a)                                                              \
     30   if (!(a)) {                                                                  \
     31     ALOGE("Failure: " #a " at " __FILE__ ":%d", __LINE__);                     \
     32     return false;                                                              \
     33   }
     34 
     35 #define VK_CALL(a) ASSERT(VK_SUCCESS == (a))
     36 
     37 namespace {
     38 
     39 void addImageTransitionBarrier(VkCommandBuffer commandBuffer, VkImage image,
     40                                VkPipelineStageFlags srcStageMask,
     41                                VkPipelineStageFlags dstStageMask,
     42                                VkAccessFlags srcAccessMask,
     43                                VkAccessFlags dstAccessMask,
     44                                VkImageLayout oldLayout, VkImageLayout newLayout,
     45                                uint32_t srcQueue = VK_QUEUE_FAMILY_IGNORED,
     46                                uint32_t dstQueue = VK_QUEUE_FAMILY_IGNORED) {
     47   const VkImageSubresourceRange subResourcerange{
     48       .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
     49       .baseMipLevel = 0,
     50       .levelCount = 1,
     51       .baseArrayLayer = 0,
     52       .layerCount = 1,
     53   };
     54   const VkImageMemoryBarrier imageBarrier{
     55       .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
     56       .pNext = nullptr,
     57       .srcAccessMask = srcAccessMask,
     58       .dstAccessMask = dstAccessMask,
     59       .oldLayout = oldLayout,
     60       .newLayout = newLayout,
     61       .srcQueueFamilyIndex = srcQueue,
     62       .dstQueueFamilyIndex = dstQueue,
     63       .image = image,
     64       .subresourceRange = subResourcerange,
     65   };
     66   vkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, 0, 0, nullptr,
     67                        0, nullptr, 1, &imageBarrier);
     68 }
     69 
     70 } // namespace
     71 
     72 bool VkInit::init() {
     73   VkApplicationInfo appInfo = {
     74       .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
     75       .pNext = nullptr,
     76       .apiVersion = VK_MAKE_VERSION(1, 1, 0),
     77       .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
     78       .engineVersion = VK_MAKE_VERSION(1, 0, 0),
     79       .pApplicationName = "VulkanGpuTest",
     80       .pEngineName = "VulkanGpuTestEngine",
     81   };
     82   std::vector<const char *> instanceExt, deviceExt;
     83   instanceExt.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
     84   instanceExt.push_back(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME);
     85   instanceExt.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
     86   instanceExt.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
     87   deviceExt.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
     88   deviceExt.push_back(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
     89   deviceExt.push_back(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
     90   deviceExt.push_back(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
     91   deviceExt.push_back(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
     92   deviceExt.push_back(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME);
     93   deviceExt.push_back(VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME);
     94   VkInstanceCreateInfo createInfo = {
     95       .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
     96       .pNext = nullptr,
     97       .pApplicationInfo = &appInfo,
     98       .enabledExtensionCount = static_cast<uint32_t>(instanceExt.size()),
     99       .ppEnabledExtensionNames = instanceExt.data(),
    100       .enabledLayerCount = 0,
    101       .ppEnabledLayerNames = nullptr,
    102   };
    103   VK_CALL(vkCreateInstance(&createInfo, nullptr, &mInstance));
    104 
    105   // Find a GPU to use.
    106   uint32_t gpuCount = 1;
    107   int status = vkEnumeratePhysicalDevices(mInstance, &gpuCount, &mGpu);
    108   ASSERT(status == VK_SUCCESS || status == VK_INCOMPLETE);
    109   ASSERT(gpuCount > 0);
    110 
    111   uint32_t queueFamilyCount = 0;
    112   vkGetPhysicalDeviceQueueFamilyProperties(mGpu, &queueFamilyCount, nullptr);
    113   ASSERT(queueFamilyCount != 0);
    114   std::vector<VkQueueFamilyProperties> queueFamilyProperties(queueFamilyCount);
    115   vkGetPhysicalDeviceQueueFamilyProperties(mGpu, &queueFamilyCount,
    116                                            queueFamilyProperties.data());
    117 
    118   uint32_t queueFamilyIndex;
    119   for (queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount;
    120        ++queueFamilyIndex) {
    121     if (queueFamilyProperties[queueFamilyIndex].queueFlags &
    122         VK_QUEUE_GRAPHICS_BIT)
    123       break;
    124   }
    125   ASSERT(queueFamilyIndex < queueFamilyCount);
    126   mQueueFamilyIndex = queueFamilyIndex;
    127 
    128   float priorities[] = {1.0f};
    129   VkDeviceQueueCreateInfo queueCreateInfo{
    130       .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
    131       .pNext = nullptr,
    132       .flags = 0,
    133       .queueCount = 1,
    134       .queueFamilyIndex = queueFamilyIndex,
    135       .pQueuePriorities = priorities,
    136   };
    137 
    138   VkDeviceCreateInfo deviceCreateInfo{
    139       .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
    140       .pNext = nullptr,
    141       .queueCreateInfoCount = 1,
    142       .pQueueCreateInfos = &queueCreateInfo,
    143       .enabledLayerCount = 0,
    144       .ppEnabledLayerNames = nullptr,
    145       .enabledExtensionCount = static_cast<uint32_t>(deviceExt.size()),
    146       .ppEnabledExtensionNames = deviceExt.data(),
    147       .pEnabledFeatures = nullptr,
    148   };
    149 
    150   VK_CALL(vkCreateDevice(mGpu, &deviceCreateInfo, nullptr, &mDevice));
    151 
    152   mPfnGetAndroidHardwareBufferPropertiesANDROID =
    153       (PFN_vkGetAndroidHardwareBufferPropertiesANDROID)vkGetDeviceProcAddr(
    154           mDevice, "vkGetAndroidHardwareBufferPropertiesANDROID");
    155   ASSERT(mPfnGetAndroidHardwareBufferPropertiesANDROID);
    156 
    157   VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR ycbcrFeatures{
    158       .sType =
    159           VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR,
    160       .pNext = nullptr,
    161   };
    162   VkPhysicalDeviceFeatures2KHR physicalDeviceFeatures{
    163       .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR,
    164       .pNext = &ycbcrFeatures,
    165   };
    166   PFN_vkGetPhysicalDeviceFeatures2KHR getFeatures =
    167       (PFN_vkGetPhysicalDeviceFeatures2KHR)vkGetInstanceProcAddr(
    168           mInstance, "vkGetPhysicalDeviceFeatures2KHR");
    169   ASSERT(getFeatures);
    170   getFeatures(mGpu, &physicalDeviceFeatures);
    171   ASSERT(ycbcrFeatures.samplerYcbcrConversion == VK_TRUE);
    172 
    173   vkGetDeviceQueue(mDevice, 0, 0, &mQueue);
    174   vkGetPhysicalDeviceMemoryProperties(mGpu, &mMemoryProperties);
    175 
    176   return true;
    177 }
    178 
    179 VkInit::~VkInit() {
    180   if (mQueue != VK_NULL_HANDLE) {
    181     // Queues are implicitly destroyed with the device.
    182     mQueue = VK_NULL_HANDLE;
    183   }
    184   if (mDevice != VK_NULL_HANDLE) {
    185     vkDestroyDevice(mDevice, nullptr);
    186     mDevice = VK_NULL_HANDLE;
    187   }
    188   if (mInstance != VK_NULL_HANDLE) {
    189     vkDestroyInstance(mInstance, nullptr);
    190     mInstance = VK_NULL_HANDLE;
    191   }
    192 }
    193 
    194 uint32_t VkInit::findMemoryType(uint32_t memoryTypeBitsRequirement,
    195                                 VkFlags requirementsMask) {
    196   for (uint32_t memoryIndex = 0; memoryIndex < VK_MAX_MEMORY_TYPES;
    197        ++memoryIndex) {
    198     const uint32_t memoryTypeBits = (1 << memoryIndex);
    199     const bool isRequiredMemoryType =
    200         memoryTypeBitsRequirement & memoryTypeBits;
    201     const bool satisfiesFlags =
    202         (mMemoryProperties.memoryTypes[memoryIndex].propertyFlags &
    203          requirementsMask) == requirementsMask;
    204     if (isRequiredMemoryType && satisfiesFlags)
    205       return memoryIndex;
    206   }
    207 
    208   // failed to find memory type.
    209   ALOGE("Couldn't find required memory type.");
    210   return 0;
    211 }
    212 
    213 VkAHardwareBufferImage::VkAHardwareBufferImage(VkInit *init) : mInit(init) {}
    214 
    215 bool VkAHardwareBufferImage::init(AHardwareBuffer *buffer, bool useExternalFormat, int syncFd) {
    216   AHardwareBuffer_Desc bufferDesc;
    217   AHardwareBuffer_describe(buffer, &bufferDesc);
    218   ASSERT(bufferDesc.layers == 1);
    219 
    220   VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
    221       .sType =
    222           VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
    223       .pNext = nullptr,
    224   };
    225   VkAndroidHardwareBufferPropertiesANDROID properties = {
    226       .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
    227       .pNext = &formatInfo,
    228   };
    229   VK_CALL(mInit->getHardwareBufferPropertiesFn()(mInit->device(), buffer,
    230                                                  &properties));
    231   ASSERT(useExternalFormat || formatInfo.format != VK_FORMAT_UNDEFINED);
    232   // Create an image to bind to our AHardwareBuffer.
    233   VkExternalFormatANDROID externalFormat{
    234       .sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
    235       .pNext = nullptr,
    236       .externalFormat = formatInfo.externalFormat,
    237   };
    238   VkExternalMemoryImageCreateInfo externalCreateInfo{
    239       .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
    240       .pNext = useExternalFormat ? &externalFormat : nullptr,
    241       .handleTypes =
    242           VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
    243   };
    244   VkImageCreateInfo createInfo{
    245       .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
    246       .pNext = &externalCreateInfo,
    247       .flags = 0u,
    248       .imageType = VK_IMAGE_TYPE_2D,
    249       .format = useExternalFormat ? VK_FORMAT_UNDEFINED : formatInfo.format,
    250       .extent =
    251           {
    252               bufferDesc.width, bufferDesc.height, 1u,
    253           },
    254       .mipLevels = 1u,
    255       .arrayLayers = 1u,
    256       .samples = VK_SAMPLE_COUNT_1_BIT,
    257       .tiling = VK_IMAGE_TILING_OPTIMAL,
    258       .usage = VK_IMAGE_USAGE_SAMPLED_BIT,
    259       .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
    260       .queueFamilyIndexCount = 0,
    261       .pQueueFamilyIndices = nullptr,
    262       .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
    263   };
    264   VK_CALL(vkCreateImage(mInit->device(), &createInfo, nullptr, &mImage));
    265 
    266   VkImageMemoryRequirementsInfo2 memReqsInfo;
    267   memReqsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;
    268   memReqsInfo.pNext = nullptr;
    269   memReqsInfo.image = mImage;
    270 
    271   VkMemoryDedicatedRequirements dedicatedMemReqs;
    272   dedicatedMemReqs.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS;
    273   dedicatedMemReqs.pNext = nullptr;
    274 
    275   VkMemoryRequirements2 memReqs;
    276   memReqs.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
    277   memReqs.pNext = &dedicatedMemReqs;
    278 
    279   PFN_vkGetImageMemoryRequirements2KHR getImageMemoryRequirements =
    280       (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(
    281           mInit->device(), "vkGetImageMemoryRequirements2KHR");
    282   ASSERT(getImageMemoryRequirements);
    283   getImageMemoryRequirements(mInit->device(), &memReqsInfo, &memReqs);
    284   ASSERT(VK_TRUE == dedicatedMemReqs.prefersDedicatedAllocation);
    285   ASSERT(VK_TRUE == dedicatedMemReqs.requiresDedicatedAllocation);
    286 
    287   VkImportAndroidHardwareBufferInfoANDROID androidHardwareBufferInfo{
    288       .sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
    289       .pNext = nullptr,
    290       .buffer = buffer,
    291   };
    292   VkMemoryDedicatedAllocateInfo memoryAllocateInfo{
    293       .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
    294       .pNext = &androidHardwareBufferInfo,
    295       .image = mImage,
    296       .buffer = VK_NULL_HANDLE,
    297   };
    298   VkMemoryAllocateInfo allocateInfo{
    299       .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
    300       .pNext = &memoryAllocateInfo,
    301       .allocationSize = properties.allocationSize,
    302       .memoryTypeIndex = mInit->findMemoryType(
    303           properties.memoryTypeBits, 0u /* requirementsMask */),
    304   };
    305 
    306   VK_CALL(vkAllocateMemory(mInit->device(), &allocateInfo, nullptr, &mMemory));
    307   VkBindImageMemoryInfo bindImageInfo;
    308   bindImageInfo.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
    309   bindImageInfo.pNext = nullptr;
    310   bindImageInfo.image = mImage;
    311   bindImageInfo.memory = mMemory;
    312   bindImageInfo.memoryOffset = 0;
    313 
    314   PFN_vkBindImageMemory2KHR bindImageMemory =
    315       (PFN_vkBindImageMemory2KHR)vkGetDeviceProcAddr(mInit->device(),
    316                                                      "vkBindImageMemory2KHR");
    317   ASSERT(bindImageMemory);
    318   VK_CALL(bindImageMemory(mInit->device(), 1, &bindImageInfo));
    319 
    320   if (useExternalFormat /* TODO: || explicit format requires conversion */) {
    321     VkSamplerYcbcrConversionCreateInfo conversionCreateInfo{
    322         .sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
    323         .pNext = &externalFormat,
    324         .format = useExternalFormat ? VK_FORMAT_UNDEFINED : formatInfo.format,
    325         .ycbcrModel = formatInfo.suggestedYcbcrModel,
    326         .ycbcrRange = formatInfo.suggestedYcbcrRange,
    327         .components = formatInfo.samplerYcbcrConversionComponents,
    328         .xChromaOffset = formatInfo.suggestedXChromaOffset,
    329         .yChromaOffset = formatInfo.suggestedYChromaOffset,
    330         .chromaFilter = VK_FILTER_NEAREST,
    331         .forceExplicitReconstruction = VK_FALSE,
    332     };
    333     PFN_vkCreateSamplerYcbcrConversionKHR createSamplerYcbcrConversion =
    334         (PFN_vkCreateSamplerYcbcrConversionKHR)vkGetDeviceProcAddr(
    335             mInit->device(), "vkCreateSamplerYcbcrConversionKHR");
    336     ASSERT(createSamplerYcbcrConversion);
    337     VK_CALL(createSamplerYcbcrConversion(mInit->device(), &conversionCreateInfo,
    338                                          nullptr, &mConversion));
    339   }
    340   VkSamplerYcbcrConversionInfo samplerConversionInfo{
    341       .sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
    342       .pNext = &externalFormat,
    343       .conversion = mConversion,
    344   };
    345 
    346   VkSamplerCreateInfo samplerCreateInfo{
    347       .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
    348       .pNext =
    349           (mConversion == VK_NULL_HANDLE) ? nullptr : &samplerConversionInfo,
    350       .magFilter = VK_FILTER_NEAREST,
    351       .minFilter = VK_FILTER_NEAREST,
    352       .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
    353       .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
    354       .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
    355       .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
    356       .mipLodBias = 0.0f,
    357       .anisotropyEnable = VK_FALSE,
    358       .maxAnisotropy = 1,
    359       .compareEnable = VK_FALSE,
    360       .compareOp = VK_COMPARE_OP_NEVER,
    361       .minLod = 0.0f,
    362       .maxLod = 0.0f,
    363       .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
    364       .unnormalizedCoordinates = VK_FALSE,
    365   };
    366   VK_CALL(
    367       vkCreateSampler(mInit->device(), &samplerCreateInfo, nullptr, &mSampler));
    368 
    369   VkImageViewCreateInfo viewCreateInfo{
    370       .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
    371       .pNext =
    372           (mConversion == VK_NULL_HANDLE) ? nullptr : &samplerConversionInfo,
    373       .image = mImage,
    374       .viewType = VK_IMAGE_VIEW_TYPE_2D,
    375       .format = useExternalFormat ? VK_FORMAT_UNDEFINED : formatInfo.format,
    376       .components =
    377           {
    378               VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
    379               VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
    380           },
    381       .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
    382       .flags = 0,
    383   };
    384   VK_CALL(vkCreateImageView(mInit->device(), &viewCreateInfo, nullptr, &mView));
    385 
    386   // Create semaphore if necessary.
    387   if (syncFd != -1) {
    388     VkSemaphoreCreateInfo semaphoreCreateInfo{
    389         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
    390         .pNext = nullptr,
    391         .flags = 0,
    392     };
    393     VK_CALL(vkCreateSemaphore(mInit->device(), &semaphoreCreateInfo, nullptr,
    394                               &mSemaphore));
    395 
    396     // Import the fd into a semaphore.
    397     VkImportSemaphoreFdInfoKHR importSemaphoreInfo{
    398         .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR,
    399         .pNext = nullptr,
    400         .semaphore = mSemaphore,
    401         .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT,
    402         .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
    403         .fd = syncFd,
    404     };
    405 
    406     PFN_vkImportSemaphoreFdKHR importSemaphoreFd =
    407         (PFN_vkImportSemaphoreFdKHR)vkGetDeviceProcAddr(
    408             mInit->device(), "vkImportSemaphoreFdKHR");
    409     ASSERT(importSemaphoreFd);
    410     VK_CALL(importSemaphoreFd(mInit->device(), &importSemaphoreInfo));
    411   }
    412 
    413   return true;
    414 }
    415 
    416 VkAHardwareBufferImage::~VkAHardwareBufferImage() {
    417   if (mView != VK_NULL_HANDLE) {
    418     vkDestroyImageView(mInit->device(), mView, nullptr);
    419     mView = VK_NULL_HANDLE;
    420   }
    421   if (mSampler != VK_NULL_HANDLE) {
    422     vkDestroySampler(mInit->device(), mSampler, nullptr);
    423     mSampler = VK_NULL_HANDLE;
    424   }
    425   if (mConversion != VK_NULL_HANDLE) {
    426     PFN_vkDestroySamplerYcbcrConversionKHR destroySamplerYcbcrConversion =
    427         (PFN_vkDestroySamplerYcbcrConversionKHR)vkGetDeviceProcAddr(
    428             mInit->device(), "vkDestroySamplerYcbcrConversionKHR");
    429     destroySamplerYcbcrConversion(mInit->device(), mConversion, nullptr);
    430   }
    431   if (mMemory != VK_NULL_HANDLE) {
    432     vkFreeMemory(mInit->device(), mMemory, nullptr);
    433     mMemory = VK_NULL_HANDLE;
    434   }
    435   if (mImage != VK_NULL_HANDLE) {
    436     vkDestroyImage(mInit->device(), mImage, nullptr);
    437     mImage = VK_NULL_HANDLE;
    438   }
    439   if (mSemaphore != VK_NULL_HANDLE) {
    440     vkDestroySemaphore(mInit->device(), mSemaphore, nullptr);
    441     mSemaphore = VK_NULL_HANDLE;
    442   }
    443 }
    444 
    445 VkImageRenderer::VkImageRenderer(VkInit *init, uint32_t width, uint32_t height,
    446                                  VkFormat format, uint32_t bytesPerPixel)
    447     : mInit(init), mWidth(width), mHeight(height), mFormat(format),
    448       mResultBufferSize(width * height * bytesPerPixel) {}
    449 
    450 bool VkImageRenderer::init(JNIEnv *env, jobject assetMgr) {
    451   // Create an image to back our framebuffer.
    452   {
    453     const VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
    454                                          VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
    455                                          VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    456     const VkImageCreateInfo imageCreateInfo{
    457         .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
    458         .pNext = nullptr,
    459         .flags = 0u,
    460         .imageType = VK_IMAGE_TYPE_2D,
    461         .format = mFormat,
    462         .extent = {mWidth, mHeight, 1u},
    463         .mipLevels = 1u,
    464         .arrayLayers = 1u,
    465         .samples = VK_SAMPLE_COUNT_1_BIT,
    466         .tiling = VK_IMAGE_TILING_OPTIMAL,
    467         .usage = imageUsage,
    468         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
    469         .queueFamilyIndexCount = 0u,
    470         .pQueueFamilyIndices = nullptr,
    471         .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
    472     };
    473     VK_CALL(
    474         vkCreateImage(mInit->device(), &imageCreateInfo, nullptr, &mDestImage));
    475 
    476     // Get memory requirements for image and allocate memory backing.
    477     VkMemoryRequirements memoryRequirements;
    478     vkGetImageMemoryRequirements(mInit->device(), mDestImage,
    479                                  &memoryRequirements);
    480 
    481     VkMemoryAllocateInfo memoryAllocateInfo{
    482         .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
    483         .pNext = nullptr,
    484         .allocationSize = memoryRequirements.size,
    485         .memoryTypeIndex =
    486             mInit->findMemoryType(memoryRequirements.memoryTypeBits, 0u),
    487     };
    488     VK_CALL(vkAllocateMemory(mInit->device(), &memoryAllocateInfo, nullptr,
    489                              &mDestImageMemory));
    490     VK_CALL(
    491         vkBindImageMemory(mInit->device(), mDestImage, mDestImageMemory, 0));
    492   }
    493 
    494   // Create image view.
    495   {
    496     VkImageViewCreateInfo imageViewCreateInfo{
    497         .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
    498         .pNext = nullptr,
    499         .flags = 0u,
    500         .image = mDestImage,
    501         .viewType = VK_IMAGE_VIEW_TYPE_2D,
    502         .format = mFormat,
    503         .components = {VK_COMPONENT_SWIZZLE_IDENTITY,
    504                        VK_COMPONENT_SWIZZLE_IDENTITY,
    505                        VK_COMPONENT_SWIZZLE_IDENTITY,
    506                        VK_COMPONENT_SWIZZLE_IDENTITY},
    507         .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u},
    508     };
    509     VK_CALL(vkCreateImageView(mInit->device(), &imageViewCreateInfo, nullptr,
    510                               &mDestImageView));
    511   }
    512 
    513   // Create render pass
    514   {
    515     VkAttachmentDescription attachmentDesc{
    516         .flags = 0u,
    517         .format = mFormat,
    518         .samples = VK_SAMPLE_COUNT_1_BIT,
    519         .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
    520         .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
    521         .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
    522         .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
    523         .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    524         .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
    525     VkAttachmentReference attachmentRef{
    526         .attachment = 0u, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    527     };
    528     VkSubpassDescription subpassDesc{
    529         .flags = 0u,
    530         .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
    531         .inputAttachmentCount = 0u,
    532         .pInputAttachments = nullptr,
    533         .colorAttachmentCount = 1u,
    534         .pColorAttachments = &attachmentRef,
    535         .pResolveAttachments = nullptr,
    536         .pDepthStencilAttachment = nullptr,
    537         .preserveAttachmentCount = 0u,
    538         .pPreserveAttachments = nullptr,
    539     };
    540     VkRenderPassCreateInfo renderPassCreateInfo{
    541         .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
    542         .pNext = nullptr,
    543         .flags = 0u,
    544         .attachmentCount = 1u,
    545         .pAttachments = &attachmentDesc,
    546         .subpassCount = 1u,
    547         .pSubpasses = &subpassDesc,
    548         .dependencyCount = 0u,
    549         .pDependencies = nullptr,
    550     };
    551     VK_CALL(vkCreateRenderPass(mInit->device(), &renderPassCreateInfo, nullptr,
    552                                &mRenderPass));
    553   }
    554 
    555   // Create vertex buffer.
    556   {
    557     const float vertexData[] = {
    558         -1.0f, -1.0f, 0.0f, 0.0f, 1.0f,  -1.0f, 1.0f, 0.0f,
    559         1.0f,  1.0f,  1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
    560         1.0f,  1.0f,  1.0f, 1.0f, -1.0f, 1.0f,  0.0f, 1.0f,
    561     };
    562     VkBufferCreateInfo createBufferInfo{
    563         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
    564         .pNext = nullptr,
    565         .size = sizeof(vertexData),
    566         .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
    567         .flags = 0,
    568         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
    569         .queueFamilyIndexCount = 0,
    570         .pQueueFamilyIndices = nullptr,
    571     };
    572     VK_CALL(vkCreateBuffer(mInit->device(), &createBufferInfo, nullptr,
    573                            &mVertexBuffer));
    574 
    575     VkMemoryRequirements memReq;
    576     vkGetBufferMemoryRequirements(mInit->device(), mVertexBuffer, &memReq);
    577     VkMemoryAllocateInfo allocInfo{
    578         .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
    579         .pNext = nullptr,
    580         .allocationSize = memReq.size,
    581         .memoryTypeIndex = mInit->findMemoryType(
    582             memReq.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
    583                                        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT),
    584     };
    585     VK_CALL(vkAllocateMemory(mInit->device(), &allocInfo, nullptr,
    586                              &mVertexBufferMemory));
    587 
    588     void *mappedData;
    589     VK_CALL(vkMapMemory(mInit->device(), mVertexBufferMemory, 0,
    590                         sizeof(vertexData), 0, &mappedData));
    591     memcpy(mappedData, vertexData, sizeof(vertexData));
    592     vkUnmapMemory(mInit->device(), mVertexBufferMemory);
    593 
    594     VK_CALL(vkBindBufferMemory(mInit->device(), mVertexBuffer,
    595                                mVertexBufferMemory, 0));
    596   }
    597 
    598   // Create framebuffer.
    599   {
    600     VkFramebufferCreateInfo framebufferCreateInfo{
    601         .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
    602         .pNext = nullptr,
    603         .flags = 0u,
    604         .renderPass = mRenderPass,
    605         .attachmentCount = 1u,
    606         .pAttachments = &mDestImageView,
    607         .width = mWidth,
    608         .height = mHeight,
    609         .layers = 1u,
    610     };
    611     VK_CALL(vkCreateFramebuffer(mInit->device(), &framebufferCreateInfo,
    612                                 nullptr, &mFramebuffer));
    613   }
    614 
    615   // Create the host-side buffer which will be used to read back the results.
    616   {
    617     VkBufferCreateInfo bufferCreateInfo{
    618         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
    619         .pNext = nullptr,
    620         .flags = 0u,
    621         .size = mResultBufferSize,
    622         .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
    623         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
    624         .queueFamilyIndexCount = 0u,
    625         .pQueueFamilyIndices = nullptr,
    626     };
    627     VK_CALL(vkCreateBuffer(mInit->device(), &bufferCreateInfo, nullptr,
    628                            &mResultBuffer));
    629 
    630     VkMemoryRequirements memReq;
    631     vkGetBufferMemoryRequirements(mInit->device(), mResultBuffer, &memReq);
    632     VkMemoryAllocateInfo allocInfo{
    633         .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
    634         .pNext = nullptr,
    635         .allocationSize = memReq.size,
    636         .memoryTypeIndex = mInit->findMemoryType(
    637             memReq.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
    638                                        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT),
    639     };
    640     VK_CALL(vkAllocateMemory(mInit->device(), &allocInfo, nullptr,
    641                              &mResultBufferMemory));
    642     VK_CALL(vkBindBufferMemory(mInit->device(), mResultBuffer,
    643                                mResultBufferMemory, 0));
    644   }
    645 
    646   // Create shaders.
    647   {
    648     AAsset *vertFile =
    649         AAssetManager_open(AAssetManager_fromJava(env, assetMgr),
    650                            "passthrough_vsh.spv", AASSET_MODE_BUFFER);
    651     ASSERT(vertFile);
    652     size_t vertShaderLength = AAsset_getLength(vertFile);
    653     std::vector<uint8_t> vertShader;
    654     vertShader.resize(vertShaderLength);
    655     AAsset_read(vertFile, static_cast<void *>(vertShader.data()),
    656                 vertShaderLength);
    657     AAsset_close(vertFile);
    658 
    659     AAsset *pixelFile =
    660         AAssetManager_open(AAssetManager_fromJava(env, assetMgr),
    661                            "passthrough_fsh.spv", AASSET_MODE_BUFFER);
    662     ASSERT(pixelFile);
    663     size_t pixelShaderLength = AAsset_getLength(pixelFile);
    664     std::vector<uint8_t> pixelShader;
    665     pixelShader.resize(pixelShaderLength);
    666     AAsset_read(pixelFile, static_cast<void *>(pixelShader.data()),
    667                 pixelShaderLength);
    668     AAsset_close(pixelFile);
    669 
    670     VkShaderModuleCreateInfo vertexShaderInfo{
    671         .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
    672         .pNext = nullptr,
    673         .flags = 0u,
    674         .codeSize = vertShaderLength,
    675         .pCode = reinterpret_cast<const uint32_t *>(vertShader.data()),
    676     };
    677     VK_CALL(vkCreateShaderModule(mInit->device(), &vertexShaderInfo, nullptr,
    678                                  &mVertModule));
    679 
    680     VkShaderModuleCreateInfo pixelShaderInfo{
    681         .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
    682         .pNext = nullptr,
    683         .flags = 0u,
    684         .codeSize = pixelShaderLength,
    685         .pCode = reinterpret_cast<const uint32_t *>(pixelShader.data()),
    686     };
    687     VK_CALL(vkCreateShaderModule(mInit->device(), &pixelShaderInfo, nullptr,
    688                                  &mPixelModule));
    689   }
    690 
    691   VkPipelineCacheCreateInfo pipelineCacheInfo{
    692       .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
    693       .pNext = nullptr,
    694       .initialDataSize = 0,
    695       .pInitialData = nullptr,
    696       .flags = 0, // reserved, must be 0
    697   };
    698   VK_CALL(vkCreatePipelineCache(mInit->device(), &pipelineCacheInfo, nullptr,
    699                                 &mCache));
    700 
    701   // Create Descriptor Pool
    702   {
    703     const VkDescriptorPoolSize typeCount = {
    704         .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1,
    705     };
    706     const VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{
    707         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
    708         .pNext = nullptr,
    709         .maxSets = 1,
    710         .poolSizeCount = 1,
    711         .pPoolSizes = &typeCount,
    712     };
    713 
    714     VK_CALL(vkCreateDescriptorPool(mInit->device(), &descriptorPoolCreateInfo,
    715                                    nullptr, &mDescriptorPool));
    716   }
    717 
    718   // Create command pool.
    719   {
    720     VkCommandPoolCreateInfo cmdPoolCreateInfo{
    721         .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
    722         .pNext = nullptr,
    723         .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
    724         .queueFamilyIndex = 0,
    725     };
    726     VK_CALL(vkCreateCommandPool(mInit->device(), &cmdPoolCreateInfo, nullptr,
    727                                 &mCmdPool));
    728   }
    729 
    730   // Create a fence
    731   {
    732     VkFenceCreateInfo fenceInfo = {
    733         .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
    734         .pNext = nullptr,
    735         .flags = 0,
    736     };
    737     VK_CALL(vkCreateFence(mInit->device(), &fenceInfo, nullptr, &mFence));
    738   }
    739 
    740   return true;
    741 }
    742 
    743 VkImageRenderer::~VkImageRenderer() {
    744   cleanUpTemporaries();
    745 
    746   if (mCmdPool != VK_NULL_HANDLE) {
    747     vkDestroyCommandPool(mInit->device(), mCmdPool, nullptr);
    748     mCmdPool = VK_NULL_HANDLE;
    749   }
    750   if (mDescriptorPool != VK_NULL_HANDLE) {
    751     vkDestroyDescriptorPool(mInit->device(), mDescriptorPool, nullptr);
    752     mDescriptorPool = VK_NULL_HANDLE;
    753   }
    754   if (mDestImageView != VK_NULL_HANDLE) {
    755     vkDestroyImageView(mInit->device(), mDestImageView, nullptr);
    756     mDestImageView = VK_NULL_HANDLE;
    757   }
    758   if (mDestImage != VK_NULL_HANDLE) {
    759     vkDestroyImage(mInit->device(), mDestImage, nullptr);
    760     mDestImage = VK_NULL_HANDLE;
    761   }
    762   if (mDestImageMemory != VK_NULL_HANDLE) {
    763     vkFreeMemory(mInit->device(), mDestImageMemory, nullptr);
    764     mDestImageMemory = VK_NULL_HANDLE;
    765   }
    766   if (mResultBuffer != VK_NULL_HANDLE) {
    767     vkDestroyBuffer(mInit->device(), mResultBuffer, nullptr);
    768     mResultBuffer = VK_NULL_HANDLE;
    769   }
    770   if (mResultBufferMemory != VK_NULL_HANDLE) {
    771     vkFreeMemory(mInit->device(), mResultBufferMemory, nullptr);
    772     mResultBufferMemory = VK_NULL_HANDLE;
    773   }
    774   if (mRenderPass != VK_NULL_HANDLE) {
    775     vkDestroyRenderPass(mInit->device(), mRenderPass, nullptr);
    776     mRenderPass = VK_NULL_HANDLE;
    777   }
    778   if (mVertexBuffer != VK_NULL_HANDLE) {
    779     vkDestroyBuffer(mInit->device(), mVertexBuffer, nullptr);
    780     mVertexBuffer = VK_NULL_HANDLE;
    781   }
    782   if (mVertexBufferMemory != VK_NULL_HANDLE) {
    783     vkFreeMemory(mInit->device(), mVertexBufferMemory, nullptr);
    784     mVertexBufferMemory = VK_NULL_HANDLE;
    785   }
    786   if (mFramebuffer != VK_NULL_HANDLE) {
    787     vkDestroyFramebuffer(mInit->device(), mFramebuffer, nullptr);
    788     mFramebuffer = VK_NULL_HANDLE;
    789   }
    790   if (mCache != VK_NULL_HANDLE) {
    791     vkDestroyPipelineCache(mInit->device(), mCache, nullptr);
    792     mCache = VK_NULL_HANDLE;
    793   }
    794   if (mVertModule != VK_NULL_HANDLE) {
    795     vkDestroyShaderModule(mInit->device(), mVertModule, nullptr);
    796     mVertModule = VK_NULL_HANDLE;
    797   }
    798   if (mPixelModule != VK_NULL_HANDLE) {
    799     vkDestroyShaderModule(mInit->device(), mPixelModule, nullptr);
    800     mPixelModule = VK_NULL_HANDLE;
    801   }
    802   if (mFence != VK_NULL_HANDLE) {
    803     vkDestroyFence(mInit->device(), mFence, nullptr);
    804     mFence = VK_NULL_HANDLE;
    805   }
    806 }
    807 
    808 bool VkImageRenderer::renderImageAndReadback(VkImage image, VkSampler sampler,
    809                                              VkImageView view,
    810                                              VkSemaphore semaphore,
    811                                              bool useExternalFormat,
    812                                              std::vector<uint32_t> *data) {
    813   std::vector<uint8_t> unconvertedData;
    814   ASSERT(renderImageAndReadback(image, sampler, view, semaphore,
    815                                 useExternalFormat, &unconvertedData));
    816   if ((unconvertedData.size() % sizeof(uint32_t)) != 0)
    817     return false;
    818 
    819   const uint32_t *dataPtr =
    820       reinterpret_cast<const uint32_t *>(unconvertedData.data());
    821   *data = std::vector<uint32_t>(dataPtr, dataPtr + unconvertedData.size() /
    822                                                        sizeof(uint32_t));
    823   return true;
    824 }
    825 
    826 bool VkImageRenderer::renderImageAndReadback(VkImage image, VkSampler sampler,
    827                                              VkImageView view,
    828                                              VkSemaphore semaphore,
    829                                              bool useImmutableSampler,
    830                                              std::vector<uint8_t> *data) {
    831   cleanUpTemporaries();
    832 
    833   // Create graphics pipeline.
    834   {
    835     VkDescriptorSetLayoutBinding descriptorSetLayoutBinding{
    836         .binding = 0,
    837         .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
    838         .descriptorCount = 1u,
    839         .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
    840         .pImmutableSamplers = useImmutableSampler ? &sampler : nullptr,
    841     };
    842     const VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
    843         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
    844         .pNext = nullptr,
    845         .bindingCount = 1,
    846         .pBindings = &descriptorSetLayoutBinding,
    847     };
    848     VK_CALL(vkCreateDescriptorSetLayout(mInit->device(),
    849                                         &descriptorSetLayoutCreateInfo, nullptr,
    850                                         &mDescriptorLayout));
    851 
    852     VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{
    853         .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
    854         .pNext = nullptr,
    855         .setLayoutCount = 1,
    856         .pSetLayouts = &mDescriptorLayout,
    857         .pushConstantRangeCount = 0,
    858         .pPushConstantRanges = nullptr,
    859     };
    860 
    861     VK_CALL(vkCreatePipelineLayout(mInit->device(), &pipelineLayoutCreateInfo,
    862                                    nullptr, &mLayout));
    863 
    864     VkPipelineShaderStageCreateInfo shaderStageParams[2] = {
    865         {
    866             .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
    867             .pNext = nullptr,
    868             .flags = 0,
    869             .stage = VK_SHADER_STAGE_VERTEX_BIT,
    870             .module = mVertModule,
    871             .pName = "main",
    872             .pSpecializationInfo = nullptr,
    873         },
    874         {
    875             .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
    876             .pNext = nullptr,
    877             .flags = 0,
    878             .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
    879             .module = mPixelModule,
    880             .pName = "main",
    881             .pSpecializationInfo = nullptr,
    882         }};
    883 
    884     VkViewport viewports{
    885         .minDepth = 0.0f,
    886         .maxDepth = 1.0f,
    887         .x = 0,
    888         .y = 0,
    889         .width = static_cast<float>(mWidth),
    890         .height = static_cast<float>(mHeight),
    891     };
    892 
    893     VkRect2D scissor = {.extent = {mWidth, mHeight}, .offset = {0, 0}};
    894     VkPipelineViewportStateCreateInfo viewportInfo{
    895         .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
    896         .pNext = nullptr,
    897         .viewportCount = 1,
    898         .pViewports = &viewports,
    899         .scissorCount = 1,
    900         .pScissors = &scissor,
    901     };
    902 
    903     VkSampleMask sampleMask = ~0u;
    904     VkPipelineMultisampleStateCreateInfo multisampleInfo{
    905         .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
    906         .pNext = nullptr,
    907         .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
    908         .sampleShadingEnable = VK_FALSE,
    909         .minSampleShading = 0,
    910         .pSampleMask = &sampleMask,
    911         .alphaToCoverageEnable = VK_FALSE,
    912         .alphaToOneEnable = VK_FALSE,
    913     };
    914     VkPipelineColorBlendAttachmentState attachmentStates{
    915         .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
    916                           VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
    917         .blendEnable = VK_FALSE,
    918     };
    919     VkPipelineColorBlendStateCreateInfo colorBlendInfo{
    920         .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
    921         .pNext = nullptr,
    922         .logicOpEnable = VK_FALSE,
    923         .logicOp = VK_LOGIC_OP_COPY,
    924         .attachmentCount = 1,
    925         .pAttachments = &attachmentStates,
    926         .flags = 0,
    927     };
    928     VkPipelineRasterizationStateCreateInfo rasterInfo{
    929         .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
    930         .pNext = nullptr,
    931         .depthClampEnable = VK_FALSE,
    932         .rasterizerDiscardEnable = VK_FALSE,
    933         .polygonMode = VK_POLYGON_MODE_FILL,
    934         .cullMode = VK_CULL_MODE_NONE,
    935         .frontFace = VK_FRONT_FACE_CLOCKWISE,
    936         .depthBiasEnable = VK_FALSE,
    937         .lineWidth = 1,
    938     };
    939     VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo{
    940         .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
    941         .pNext = nullptr,
    942         .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
    943         .primitiveRestartEnable = VK_FALSE,
    944     };
    945 
    946     // Specify vertex input state
    947     VkVertexInputBindingDescription vertexInputBindingDescription{
    948         .binding = 0u,
    949         .stride = 4 * sizeof(float),
    950         .inputRate = VK_VERTEX_INPUT_RATE_VERTEX};
    951     VkVertexInputAttributeDescription vertex_input_attributes[2]{
    952         {
    953             .binding = 0,
    954             .location = 0,
    955             .format = VK_FORMAT_R32G32_SFLOAT,
    956             .offset = 0,
    957         },
    958         {
    959             .binding = 0,
    960             .location = 1,
    961             .format = VK_FORMAT_R32G32_SFLOAT,
    962             .offset = sizeof(float) * 2,
    963         }};
    964     VkPipelineVertexInputStateCreateInfo vertexInputInfo{
    965         .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
    966         .pNext = nullptr,
    967         .vertexBindingDescriptionCount = 1,
    968         .pVertexBindingDescriptions = &vertexInputBindingDescription,
    969         .vertexAttributeDescriptionCount = 2,
    970         .pVertexAttributeDescriptions = vertex_input_attributes,
    971     };
    972 
    973     // Create the pipeline
    974     VkGraphicsPipelineCreateInfo pipelineCreateInfo{
    975         .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
    976         .pNext = nullptr,
    977         .flags = 0,
    978         .stageCount = 2,
    979         .pStages = shaderStageParams,
    980         .pVertexInputState = &vertexInputInfo,
    981         .pInputAssemblyState = &inputAssemblyInfo,
    982         .pTessellationState = nullptr,
    983         .pViewportState = &viewportInfo,
    984         .pRasterizationState = &rasterInfo,
    985         .pMultisampleState = &multisampleInfo,
    986         .pDepthStencilState = nullptr,
    987         .pColorBlendState = &colorBlendInfo,
    988         .pDynamicState = 0u,
    989         .layout = mLayout,
    990         .renderPass = mRenderPass,
    991         .subpass = 0,
    992         .basePipelineHandle = VK_NULL_HANDLE,
    993         .basePipelineIndex = 0,
    994     };
    995 
    996     VK_CALL(vkCreateGraphicsPipelines(
    997         mInit->device(), mCache, 1, &pipelineCreateInfo, nullptr, &mPipeline));
    998   }
    999 
   1000   // Create a command buffer.
   1001   {
   1002     VkCommandBufferAllocateInfo cmdBufferCreateInfo{
   1003         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
   1004         .pNext = nullptr,
   1005         .commandPool = mCmdPool,
   1006         .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
   1007         .commandBufferCount = 1,
   1008     };
   1009     VK_CALL(vkAllocateCommandBuffers(mInit->device(), &cmdBufferCreateInfo,
   1010                                      &mCmdBuffer));
   1011   }
   1012 
   1013   // Create the descriptor sets.
   1014   {
   1015     VkDescriptorSetAllocateInfo allocInfo{
   1016         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
   1017         .pNext = nullptr,
   1018         .descriptorPool = mDescriptorPool,
   1019         .descriptorSetCount = 1,
   1020         .pSetLayouts = &mDescriptorLayout};
   1021     VK_CALL(
   1022         vkAllocateDescriptorSets(mInit->device(), &allocInfo, &mDescriptorSet));
   1023 
   1024     VkDescriptorImageInfo texDesc{
   1025         .sampler = sampler,
   1026         .imageView = view,
   1027         .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
   1028     };
   1029 
   1030     VkWriteDescriptorSet writeDst{
   1031         .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
   1032         .pNext = nullptr,
   1033         .dstSet = mDescriptorSet,
   1034         .dstBinding = 0,
   1035         .dstArrayElement = 0,
   1036         .descriptorCount = 1,
   1037         .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
   1038         .pImageInfo = &texDesc,
   1039         .pBufferInfo = nullptr,
   1040         .pTexelBufferView = nullptr};
   1041     vkUpdateDescriptorSets(mInit->device(), 1, &writeDst, 0, nullptr);
   1042   }
   1043 
   1044   // Begin Command Buffer
   1045   {
   1046     VkCommandBufferBeginInfo cmdBufferBeginInfo{
   1047         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
   1048         .pNext = nullptr,
   1049         .flags = 0u,
   1050         .pInheritanceInfo = nullptr,
   1051     };
   1052 
   1053     VK_CALL(vkBeginCommandBuffer(mCmdBuffer, &cmdBufferBeginInfo));
   1054   }
   1055 
   1056   // Transition the provided resource so we can sample from it.
   1057   addImageTransitionBarrier(
   1058       mCmdBuffer, image, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
   1059       VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, VK_ACCESS_SHADER_READ_BIT,
   1060       VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
   1061       VK_QUEUE_FAMILY_EXTERNAL_KHR, mInit->queueFamilyIndex());
   1062 
   1063   // Transition the destination texture for use as a framebuffer.
   1064   addImageTransitionBarrier(
   1065       mCmdBuffer, mDestImage, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
   1066       VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0,
   1067       VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
   1068       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
   1069 
   1070   // Begin Render Pass to draw the source resource to the framebuffer.
   1071   {
   1072     const VkClearValue clearValue{
   1073         .color =
   1074             {
   1075                 .float32 = {0.0f, 0.0f, 0.0f, 0.0f},
   1076             },
   1077     };
   1078     VkRenderPassBeginInfo renderPassBeginInfo{
   1079         .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
   1080         .pNext = nullptr,
   1081         .renderPass = mRenderPass,
   1082         .framebuffer = mFramebuffer,
   1083         .renderArea = {{0, 0}, {mWidth, mHeight}},
   1084         .clearValueCount = 1u,
   1085         .pClearValues = &clearValue,
   1086     };
   1087     vkCmdBeginRenderPass(mCmdBuffer, &renderPassBeginInfo,
   1088                          VK_SUBPASS_CONTENTS_INLINE);
   1089   }
   1090 
   1091   /// Draw texture to renderpass.
   1092   vkCmdBindPipeline(mCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mPipeline);
   1093   vkCmdBindDescriptorSets(mCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mLayout,
   1094                           0u, 1, &mDescriptorSet, 0u, nullptr);
   1095   VkDeviceSize offset = 0;
   1096   vkCmdBindVertexBuffers(mCmdBuffer, 0, 1, &mVertexBuffer, &offset);
   1097   vkCmdDraw(mCmdBuffer, 6u, 1u, 0u, 0u);
   1098   vkCmdEndRenderPass(mCmdBuffer);
   1099 
   1100   // Copy to our staging buffer.
   1101   {
   1102     VkBufferMemoryBarrier bufferBarrier{
   1103         .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
   1104         .pNext = nullptr,
   1105         .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
   1106         .dstAccessMask = VK_ACCESS_HOST_READ_BIT,
   1107         .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
   1108         .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
   1109         .buffer = mResultBuffer,
   1110         .offset = 0u,
   1111         .size = mResultBufferSize,
   1112     };
   1113 
   1114     VkBufferImageCopy copyRegion{
   1115         .bufferOffset = 0u,
   1116         .bufferRowLength = mWidth,
   1117         .bufferImageHeight = mHeight,
   1118         .imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u},
   1119         .imageOffset = {0, 0, 0},
   1120         .imageExtent = {mWidth, mHeight, 1u}};
   1121 
   1122     addImageTransitionBarrier(
   1123         mCmdBuffer, mDestImage, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
   1124         VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
   1125         VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
   1126         VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
   1127 
   1128     vkCmdCopyImageToBuffer(mCmdBuffer, mDestImage,
   1129                            VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, mResultBuffer,
   1130                            1, &copyRegion);
   1131 
   1132     vkCmdPipelineBarrier(mCmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
   1133                          VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0,
   1134                          (const VkMemoryBarrier *)nullptr, 1, &bufferBarrier, 0,
   1135                          (const VkImageMemoryBarrier *)nullptr);
   1136   }
   1137 
   1138   VK_CALL(vkEndCommandBuffer(mCmdBuffer));
   1139 
   1140   VkPipelineStageFlags semaphoreWaitFlags =
   1141       VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
   1142   VkSubmitInfo submitInfo{
   1143       .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
   1144       .pNext = nullptr,
   1145       .waitSemaphoreCount = semaphore != VK_NULL_HANDLE ? 1u : 0u,
   1146       .pWaitSemaphores = semaphore != VK_NULL_HANDLE ? &semaphore : nullptr,
   1147       .pWaitDstStageMask =
   1148           semaphore != VK_NULL_HANDLE ? &semaphoreWaitFlags : nullptr,
   1149       .commandBufferCount = 1u,
   1150       .pCommandBuffers = &mCmdBuffer,
   1151       .signalSemaphoreCount = 0u,
   1152       .pSignalSemaphores = nullptr,
   1153   };
   1154   VK_CALL(vkResetFences(mInit->device(), 1, &mFence))
   1155   VK_CALL(vkQueueSubmit(mInit->queue(), 1, &submitInfo, mFence));
   1156   VK_CALL(vkWaitForFences(mInit->device(), 1, &mFence, VK_TRUE,
   1157                           ~(0ull) /* infinity */));
   1158 
   1159   void *outData;
   1160   VK_CALL(vkMapMemory(mInit->device(), mResultBufferMemory, 0u,
   1161                       mResultBufferSize, 0u, &outData));
   1162   uint8_t *uData = reinterpret_cast<uint8_t *>(outData);
   1163   *data = std::vector<uint8_t>(uData, uData + mResultBufferSize);
   1164   vkUnmapMemory(mInit->device(), mResultBufferMemory);
   1165 
   1166   return true;
   1167 }
   1168 
   1169 void VkImageRenderer::cleanUpTemporaries() {
   1170   if (mCmdBuffer != VK_NULL_HANDLE) {
   1171     vkFreeCommandBuffers(mInit->device(), mCmdPool, 1, &mCmdBuffer);
   1172     mCmdBuffer = VK_NULL_HANDLE;
   1173   }
   1174   if (mDescriptorSet != VK_NULL_HANDLE) {
   1175     vkResetDescriptorPool(mInit->device(), mDescriptorPool, 0 /*flags*/);
   1176     mDescriptorSet = VK_NULL_HANDLE;
   1177   }
   1178   if (mPipeline != VK_NULL_HANDLE) {
   1179     vkDestroyPipeline(mInit->device(), mPipeline, nullptr);
   1180     mPipeline = VK_NULL_HANDLE;
   1181   }
   1182   if (mDescriptorLayout != VK_NULL_HANDLE) {
   1183     vkDestroyDescriptorSetLayout(mInit->device(), mDescriptorLayout, nullptr);
   1184     mDescriptorLayout = VK_NULL_HANDLE;
   1185   }
   1186   if (mLayout != VK_NULL_HANDLE) {
   1187     vkDestroyPipelineLayout(mInit->device(), mLayout, nullptr);
   1188     mLayout = VK_NULL_HANDLE;
   1189   }
   1190 }
   1191