| // 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_buffer.h" |
| |
| #include "tests/common/utils.h" // For ASSERT() macros. |
| #include "tests/common/vk_utils.h" // For vk() macro. |
| |
| void |
| vk_buffer_alloc_generic(vk_buffer_t * buffer, |
| VkDeviceSize buffer_size, |
| VkBufferUsageFlags usage, |
| VkMemoryPropertyFlags memory_flags, |
| uint32_t queue_families_count, |
| const uint32_t * queue_families, |
| VkPhysicalDevice physical_device, |
| VkDevice device, |
| const VkAllocationCallbacks * allocator) |
| { |
| *buffer = (vk_buffer_t){}; |
| |
| // First create a buffer that can be used as a transfer source for our |
| // application. |
| VkBufferCreateInfo createInfo = { |
| .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, |
| .pNext = NULL, |
| .flags = 0, |
| .size = buffer_size, |
| .usage = usage, |
| |
| // NOTE: If the buffer was to be accessed from different queues |
| // at the same time, sharingMode should be VK_SHARING_MODE_EXCLUSIVE |
| // and the family queue indices should be listed through |
| // queueFamilyIndexCount and pQueueFamilyIndices... |
| .sharingMode = |
| (queue_families_count > 0) ? VK_SHARING_MODE_CONCURRENT : VK_SHARING_MODE_EXCLUSIVE, |
| |
| .queueFamilyIndexCount = queue_families_count, |
| .pQueueFamilyIndices = queue_families, |
| }; |
| |
| vk(CreateBuffer(device, &createInfo, allocator, &buffer->buffer)); |
| |
| // Get its memory requirements to ensure we have the right memory type. |
| VkMemoryRequirements memory_requirements; |
| vkGetBufferMemoryRequirements(device, buffer->buffer, &memory_requirements); |
| buffer->size = memory_requirements.size; |
| buffer->memory_requirements = memory_requirements; |
| |
| // Find the right memory type for this buffer. 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 buffer!\n"); |
| buffer->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, &buffer->memory)); |
| |
| // Bind the memory to the buffer. |
| vk(BindBufferMemory(device, buffer->buffer, buffer->memory, 0)); |
| |
| buffer->device = device; |
| buffer->allocator = allocator; |
| buffer->memory_flags = memory_flags; |
| buffer->mapped = NULL; |
| |
| // Map it now! |
| if (memory_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) |
| vk_buffer_map(buffer); |
| } |
| |
| void |
| vk_buffer_alloc_host(vk_buffer_t * buffer, |
| VkDeviceSize buffer_size, |
| VkBufferUsageFlags buffer_usage, |
| VkPhysicalDevice physical_device, |
| VkDevice device, |
| const VkAllocationCallbacks * allocator) |
| { |
| VkMemoryPropertyFlags memory_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; |
| |
| vk_buffer_alloc_generic(buffer, |
| buffer_size, |
| buffer_usage, |
| memory_flags, |
| 0, // queue_families_count, |
| NULL, // queue_families, |
| physical_device, |
| device, |
| allocator); |
| } |
| |
| void |
| vk_buffer_alloc_host_coherent(vk_buffer_t * buffer, |
| VkDeviceSize buffer_size, |
| VkBufferUsageFlags buffer_usage, |
| VkPhysicalDevice physical_device, |
| VkDevice device, |
| const VkAllocationCallbacks * allocator) |
| { |
| VkMemoryPropertyFlags memory_flags = |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| |
| vk_buffer_alloc_generic(buffer, |
| buffer_size, |
| buffer_usage, |
| memory_flags, |
| 0, // queue_families_count, |
| NULL, // queue_families, |
| physical_device, |
| device, |
| allocator); |
| } |
| |
| void |
| vk_buffer_alloc_device_local(vk_buffer_t * buffer, |
| VkDeviceSize buffer_size, |
| VkBufferUsageFlags buffer_usage, |
| VkPhysicalDevice physical_device, |
| VkDevice device, |
| const VkAllocationCallbacks * allocator) |
| { |
| VkMemoryPropertyFlags memory_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
| |
| vk_buffer_alloc_generic(buffer, |
| buffer_size, |
| buffer_usage, |
| memory_flags, |
| 0, // queue_families_count, |
| NULL, // queue_families, |
| physical_device, |
| device, |
| allocator); |
| } |
| |
| void |
| vk_buffer_map(vk_buffer_t * buffer) |
| { |
| if (!buffer->mapped) |
| { |
| ASSERT_MSG((buffer->memory_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0, |
| "Cannot map device-local-only GPU buffer!\n"); |
| |
| vk(MapMemory(buffer->device, buffer->memory, 0, buffer->size, 0, &buffer->mapped)); |
| } |
| |
| // Ensure proper refresh for non-coherent memory. |
| if ((buffer->memory_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) |
| { |
| vk(InvalidateMappedMemoryRanges(buffer->device, |
| 1, |
| &(const VkMappedMemoryRange){ |
| .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, |
| .memory = buffer->memory, |
| .offset = 0u, |
| .size = buffer->size, |
| })); |
| } |
| } |
| |
| void |
| vk_buffer_unmap(vk_buffer_t * buffer) |
| { |
| if (buffer->mapped) |
| { |
| // Ensure proper flush for non-coherent memory. |
| vk_buffer_flush_all(buffer); |
| |
| vkUnmapMemory(buffer->device, buffer->memory); |
| buffer->mapped = NULL; |
| } |
| } |
| |
| void |
| vk_buffer_flush_all(const vk_buffer_t * buffer) |
| { |
| if (buffer->mapped && (buffer->memory_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) |
| { |
| vk(FlushMappedMemoryRanges(buffer->device, |
| 1, |
| &(const VkMappedMemoryRange){ |
| .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, |
| .pNext = NULL, |
| .memory = buffer->memory, |
| .offset = 0, |
| .size = buffer->size, |
| })); |
| } |
| } |
| |
| void |
| vk_buffer_free(vk_buffer_t * buffer) |
| { |
| if (buffer->mapped != NULL) |
| { |
| // No need to flush here. Avoid error if any. |
| vkUnmapMemory(buffer->device, buffer->memory); |
| buffer->mapped = NULL; |
| } |
| |
| vkFreeMemory(buffer->device, buffer->memory, buffer->allocator); |
| vkDestroyBuffer(buffer->device, buffer->buffer, buffer->allocator); |
| buffer->memory = VK_NULL_HANDLE; |
| buffer->buffer = VK_NULL_HANDLE; |
| buffer->device = VK_NULL_HANDLE; |
| buffer->allocator = NULL; |
| buffer->memory_flags = 0; |
| } |