|  | // Copyright 2019 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "vk_image.h" | 
|  |  | 
|  | #include <stdbool.h> | 
|  |  | 
|  | #include "tests/common/utils.h"  // For ASSERT() macros. | 
|  | #include "tests/common/vk_strings.h" | 
|  | #include "tests/common/vk_utils.h"  // For vk() macro. | 
|  |  | 
|  | void | 
|  | vk_image_alloc_generic(vk_image_t *                  image, | 
|  | VkFormat                      image_format, | 
|  | VkExtent2D                    image_extent, | 
|  | VkImageTiling                 image_tiling, | 
|  | VkImageUsageFlags             image_usage, | 
|  | VkImageLayout                 image_layout, | 
|  | VkMemoryPropertyFlags         memory_flags, | 
|  | uint32_t                      queue_families_count, | 
|  | const uint32_t *              queue_families, | 
|  | VkPhysicalDevice              physical_device, | 
|  | VkDevice                      device, | 
|  | const VkAllocationCallbacks * allocator) | 
|  | { | 
|  | *image = (const vk_image_t){}; | 
|  |  | 
|  | const VkImageCreateInfo createInfo = { | 
|  | .sType     = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | 
|  | .imageType = VK_IMAGE_TYPE_2D, | 
|  | .format    = image_format, | 
|  | .extent    = { | 
|  | .width  = image_extent.width, | 
|  | .height = image_extent.height, | 
|  | .depth  = 1, | 
|  | }, | 
|  | .mipLevels             = 1, | 
|  | .arrayLayers           = 1, | 
|  | .samples               = VK_SAMPLE_COUNT_1_BIT, | 
|  | .tiling                = image_tiling, | 
|  | .usage                 = image_usage, | 
|  | .sharingMode           = (queue_families_count ? VK_SHARING_MODE_CONCURRENT : VK_SHARING_MODE_EXCLUSIVE), | 
|  | .queueFamilyIndexCount = queue_families_count, | 
|  | .pQueueFamilyIndices   = (queue_families_count ? queue_families : NULL), | 
|  | .initialLayout         = image_layout, | 
|  | }; | 
|  |  | 
|  | // Sanity checks for |image_usage| and |image_format|. If the values are not | 
|  | // compatible for this device, CreateImage() will work (though the validation | 
|  | // layer will complain about it), but rendering to the image may later fail in | 
|  | // totally unexpected ways depending on the GPU driver. | 
|  | VkFormatProperties format_props; | 
|  | vkGetPhysicalDeviceFormatProperties(physical_device, image_format, &format_props); | 
|  | switch (image_tiling) | 
|  | { | 
|  | case VK_IMAGE_TILING_OPTIMAL: | 
|  | ASSERT_MSG( | 
|  | vk_check_image_usage_vs_format_features(image_tiling, format_props.optimalTilingFeatures), | 
|  | "Creating image with VK_IMAGE_TILING_OPTIMAL is not supported by format %d\n", | 
|  | image_format); | 
|  | break; | 
|  | case VK_IMAGE_TILING_LINEAR: | 
|  | ASSERT_MSG( | 
|  | vk_check_image_usage_vs_format_features(image_tiling, format_props.linearTilingFeatures), | 
|  | "Create image with VK_IMAGE_TILING_LINEAR is not supported by format %d\n", | 
|  | image_format); | 
|  | default: | 
|  | ASSERT_MSG(false, "Unsupported VkImageTiling value %d\n", image_tiling); | 
|  | } | 
|  |  | 
|  | vk(CreateImage(device, &createInfo, allocator, &image->image)); | 
|  |  | 
|  | // Get its memory requirements to ensure we have the right memory type. | 
|  | VkMemoryRequirements memory_requirements; | 
|  | vkGetImageMemoryRequirements(device, image->image, &memory_requirements); | 
|  | image->size                = memory_requirements.size; | 
|  | image->extent              = image_extent; | 
|  | image->memory_requirements = memory_requirements; | 
|  | image->tiling              = image_tiling; | 
|  |  | 
|  | // Find the right memory type for this image. We want it to be host-visible. | 
|  | VkPhysicalDeviceMemoryProperties memory_properties; | 
|  | vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties); | 
|  |  | 
|  | uint32_t memory_type_index = UINT32_MAX; | 
|  | { | 
|  | for (uint32_t n = 0; n < memory_properties.memoryTypeCount; n++) | 
|  | { | 
|  | if ((memory_requirements.memoryTypeBits & (1u << n)) == 0) | 
|  | continue; | 
|  |  | 
|  | if ((memory_properties.memoryTypes[n].propertyFlags & memory_flags) == memory_flags) | 
|  | { | 
|  | memory_type_index = n; | 
|  | break; | 
|  | } | 
|  | } | 
|  | ASSERT_MSG(memory_type_index != UINT32_MAX, "Could not find memory type for image!\n"); | 
|  | image->memory_type_index = memory_type_index; | 
|  | } | 
|  |  | 
|  | // Allocate memory for our buffer. No need for a custom allocator in our | 
|  | // trivial application. | 
|  | const VkMemoryAllocateInfo allocateInfo = { | 
|  | .sType           = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | 
|  | .pNext           = NULL, | 
|  | .allocationSize  = memory_requirements.size, | 
|  | .memoryTypeIndex = memory_type_index, | 
|  | }; | 
|  |  | 
|  | vk(AllocateMemory(device, &allocateInfo, allocator, &image->memory)); | 
|  |  | 
|  | // Bind the memory to the buffer. | 
|  | vk(BindImageMemory(device, image->image, image->memory, 0)); | 
|  |  | 
|  | // Create image view. | 
|  | const VkImageViewCreateInfo viewCreateInfo = { | 
|  | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | 
|  | .image = image->image, | 
|  | .viewType = VK_IMAGE_VIEW_TYPE_2D, | 
|  | .format = image_format, | 
|  | .components = { | 
|  | .r = VK_COMPONENT_SWIZZLE_IDENTITY, | 
|  | .g = VK_COMPONENT_SWIZZLE_IDENTITY, | 
|  | .b = VK_COMPONENT_SWIZZLE_IDENTITY, | 
|  | .a = VK_COMPONENT_SWIZZLE_IDENTITY, | 
|  | }, | 
|  | .subresourceRange = { | 
|  | .aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT, | 
|  | .baseMipLevel   = 0, | 
|  | .levelCount     = 1, | 
|  | .baseArrayLayer = 0, | 
|  | .layerCount     = 1, | 
|  | }, | 
|  | }; | 
|  | vk(CreateImageView(device, &viewCreateInfo, allocator, &image->image_view)); | 
|  |  | 
|  | image->device    = device; | 
|  | image->allocator = allocator; | 
|  | } | 
|  |  | 
|  | void | 
|  | vk_image_alloc_device_local(vk_image_t *                  image, | 
|  | VkFormat                      image_format, | 
|  | VkExtent2D                    image_extent, | 
|  | VkImageUsageFlags             image_usage, | 
|  | VkPhysicalDevice              physical_device, | 
|  | VkDevice                      device, | 
|  | const VkAllocationCallbacks * allocator) | 
|  | { | 
|  | VkFormatProperties format_props; | 
|  | vkGetPhysicalDeviceFormatProperties(physical_device, image_format, &format_props); | 
|  |  | 
|  | VkImageTiling image_tiling; | 
|  | // Use VK_IMAGE_TILING_OPTIONAL unless the device does not support |image_usage| | 
|  | if (vk_check_image_usage_vs_format_features(image_usage, format_props.optimalTilingFeatures)) | 
|  | image_tiling = VK_IMAGE_TILING_OPTIMAL; | 
|  | else if (vk_check_image_usage_vs_format_features(image_usage, format_props.linearTilingFeatures)) | 
|  | image_tiling = VK_IMAGE_TILING_LINEAR; | 
|  | else | 
|  | ASSERT_MSG(false, | 
|  | "Device does not support image usage %s for format %s\n", | 
|  | vk_image_usage_flags_to_string(image_usage), | 
|  | vk_format_to_string(image_format)); | 
|  |  | 
|  | vk_image_alloc_generic(image, | 
|  | image_format, | 
|  | image_extent, | 
|  | image_tiling, | 
|  | image_usage, | 
|  | VK_IMAGE_LAYOUT_UNDEFINED, | 
|  | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, | 
|  | 0, | 
|  | NULL, | 
|  | physical_device, | 
|  | device, | 
|  | allocator); | 
|  | } | 
|  |  | 
|  | void | 
|  | vk_image_free(vk_image_t * image) | 
|  | { | 
|  | if (image->image != VK_NULL_HANDLE) | 
|  | { | 
|  | vkDestroyImageView(image->device, image->image_view, image->allocator); | 
|  | vkDestroyImage(image->device, image->image, image->allocator); | 
|  | vkFreeMemory(image->device, image->memory, image->allocator); | 
|  | image->image_view = VK_NULL_HANDLE; | 
|  | image->image      = VK_NULL_HANDLE; | 
|  | image->memory     = VK_NULL_HANDLE; | 
|  | image->device     = VK_NULL_HANDLE; | 
|  | image->allocator  = NULL; | 
|  | } | 
|  | } |