/* * Copyright 2024 Valve Corporation * Copyright 2024 Alyssa Rosenzweig * Copyright 2022-2023 Collabora Ltd. and Red Hat Inc. * SPDX-License-Identifier: MIT */ #include "hk_buffer.h" #include "agx_bo.h" #include "agx_device.h" #include "hk_device.h" #include "hk_device_memory.h" #include "hk_entrypoints.h" #include "hk_physical_device.h" static uint32_t hk_get_buffer_alignment(const struct hk_physical_device *pdev, VkBufferUsageFlags2KHR usage_flags, VkBufferCreateFlags create_flags) { uint32_t alignment = 16; if (usage_flags & VK_BUFFER_USAGE_2_UNIFORM_BUFFER_BIT_KHR) alignment = MAX2(alignment, HK_MIN_UBO_ALIGNMENT); if (usage_flags & VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT_KHR) alignment = MAX2(alignment, HK_MIN_SSBO_ALIGNMENT); if (usage_flags & (VK_BUFFER_USAGE_2_UNIFORM_TEXEL_BUFFER_BIT_KHR | VK_BUFFER_USAGE_2_STORAGE_TEXEL_BUFFER_BIT_KHR)) alignment = MAX2(alignment, HK_MIN_TEXEL_BUFFER_ALIGNMENT); if (create_flags & (VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT)) alignment = MAX2(alignment, 16384); return alignment; } static uint64_t hk_get_bda_replay_addr(const VkBufferCreateInfo *pCreateInfo) { uint64_t addr = 0; vk_foreach_struct_const(ext, pCreateInfo->pNext) { switch (ext->sType) { case VK_STRUCTURE_TYPE_BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO: { const VkBufferOpaqueCaptureAddressCreateInfo *bda = (void *)ext; if (bda->opaqueCaptureAddress != 0) { #ifdef NDEBUG return bda->opaqueCaptureAddress; #else assert(addr == 0 || bda->opaqueCaptureAddress == addr); addr = bda->opaqueCaptureAddress; #endif } break; } case VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_CREATE_INFO_EXT: { const VkBufferDeviceAddressCreateInfoEXT *bda = (void *)ext; if (bda->deviceAddress != 0) { #ifdef NDEBUG return bda->deviceAddress; #else assert(addr == 0 || bda->deviceAddress == addr); addr = bda->deviceAddress; #endif } break; } default: break; } } return addr; } VKAPI_ATTR VkResult VKAPI_CALL hk_CreateBuffer(VkDevice device, const VkBufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkBuffer *pBuffer) { VK_FROM_HANDLE(hk_device, dev, device); struct hk_buffer *buffer; if (pCreateInfo->size > HK_MAX_BUFFER_SIZE) return vk_error(dev, VK_ERROR_OUT_OF_DEVICE_MEMORY); buffer = vk_buffer_create(&dev->vk, pCreateInfo, pAllocator, sizeof(*buffer)); if (!buffer) return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY); if (buffer->vk.size > 0 && (buffer->vk.create_flags & (VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT))) { const uint32_t alignment = hk_get_buffer_alignment( hk_device_physical(dev), buffer->vk.usage, buffer->vk.create_flags); assert(alignment >= 16384); uint64_t vma_size_B = align64(buffer->vk.size, alignment); const bool bda_capture_replay = buffer->vk.create_flags & VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT; enum agx_va_flags flags = 0; uint64_t bda_fixed_addr = 0; if (bda_capture_replay) { bda_fixed_addr = hk_get_bda_replay_addr(pCreateInfo); if (bda_fixed_addr != 0) flags |= AGX_VA_FIXED; } buffer->va = agx_va_alloc(&dev->dev, vma_size_B, alignment, flags, bda_fixed_addr); if (!buffer->va) { vk_buffer_destroy(&dev->vk, pAllocator, &buffer->vk); return vk_errorf(dev, VK_ERROR_OUT_OF_DEVICE_MEMORY, "Sparse VMA allocation failed"); } buffer->addr = buffer->va->addr; } *pBuffer = hk_buffer_to_handle(buffer); return VK_SUCCESS; } VKAPI_ATTR void VKAPI_CALL hk_DestroyBuffer(VkDevice device, VkBuffer _buffer, const VkAllocationCallbacks *pAllocator) { VK_FROM_HANDLE(hk_device, dev, device); VK_FROM_HANDLE(hk_buffer, buffer, _buffer); if (!buffer) return; if (buffer->va) { // TODO // agx_bo_unbind_vma(dev->ws_dev, buffer->addr, buffer->vma_size_B); agx_va_free(&dev->dev, buffer->va); } vk_buffer_destroy(&dev->vk, pAllocator, &buffer->vk); } VKAPI_ATTR void VKAPI_CALL hk_GetDeviceBufferMemoryRequirements( VkDevice device, const VkDeviceBufferMemoryRequirements *pInfo, VkMemoryRequirements2 *pMemoryRequirements) { VK_FROM_HANDLE(hk_device, dev, device); struct hk_physical_device *pdev = hk_device_physical(dev); const uint32_t alignment = hk_get_buffer_alignment( hk_device_physical(dev), pInfo->pCreateInfo->usage, pInfo->pCreateInfo->flags); pMemoryRequirements->memoryRequirements = (VkMemoryRequirements){ .size = align64(pInfo->pCreateInfo->size, alignment), .alignment = alignment, .memoryTypeBits = BITFIELD_MASK(pdev->mem_type_count), }; vk_foreach_struct_const(ext, pMemoryRequirements->pNext) { switch (ext->sType) { case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: { VkMemoryDedicatedRequirements *dedicated = (void *)ext; dedicated->prefersDedicatedAllocation = false; dedicated->requiresDedicatedAllocation = false; break; } default: vk_debug_ignored_stype(ext->sType); break; } } } VKAPI_ATTR void VKAPI_CALL hk_GetPhysicalDeviceExternalBufferProperties( VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo *pExternalBufferInfo, VkExternalBufferProperties *pExternalBufferProperties) { /* The Vulkan 1.3.256 spec says: * * VUID-VkPhysicalDeviceExternalBufferInfo-handleType-parameter * * "handleType must be a valid VkExternalMemoryHandleTypeFlagBits value" * * This differs from VkPhysicalDeviceExternalImageFormatInfo, which * surprisingly permits handleType == 0. */ assert(pExternalBufferInfo->handleType != 0); /* All of the current flags are for sparse which we don't support yet. * Even when we do support it, doing sparse on external memory sounds * sketchy. Also, just disallowing flags is the safe option. */ if (pExternalBufferInfo->flags) goto unsupported; switch (pExternalBufferInfo->handleType) { case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT: case VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT: pExternalBufferProperties->externalMemoryProperties = hk_dma_buf_mem_props; return; default: goto unsupported; } unsupported: /* From the Vulkan 1.3.256 spec: * * compatibleHandleTypes must include at least handleType. */ pExternalBufferProperties->externalMemoryProperties = (VkExternalMemoryProperties){ .compatibleHandleTypes = pExternalBufferInfo->handleType, }; } VKAPI_ATTR VkResult VKAPI_CALL hk_BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo *pBindInfos) { for (uint32_t i = 0; i < bindInfoCount; ++i) { VK_FROM_HANDLE(hk_device_memory, mem, pBindInfos[i].memory); VK_FROM_HANDLE(hk_buffer, buffer, pBindInfos[i].buffer); if (buffer->va) { VK_FROM_HANDLE(hk_device, dev, device); dev->dev.ops.bo_bind(&dev->dev, mem->bo, buffer->addr, buffer->va->size_B, pBindInfos[i].memoryOffset, ASAHI_BIND_READ | ASAHI_BIND_WRITE, false); } else { buffer->addr = mem->bo->va->addr + pBindInfos[i].memoryOffset; } const VkBindMemoryStatusKHR *status = vk_find_struct_const(pBindInfos[i].pNext, BIND_MEMORY_STATUS_KHR); if (status != NULL && status->pResult != NULL) *status->pResult = VK_SUCCESS; } return VK_SUCCESS; } VKAPI_ATTR VkDeviceAddress VKAPI_CALL hk_GetBufferDeviceAddress(UNUSED VkDevice device, const VkBufferDeviceAddressInfo *pInfo) { VK_FROM_HANDLE(hk_buffer, buffer, pInfo->buffer); return hk_buffer_address(buffer, 0); } VKAPI_ATTR uint64_t VKAPI_CALL hk_GetBufferOpaqueCaptureAddress(UNUSED VkDevice device, const VkBufferDeviceAddressInfo *pInfo) { VK_FROM_HANDLE(hk_buffer, buffer, pInfo->buffer); return hk_buffer_address(buffer, 0); }