blob: 889082917acbf93e2fcaeaef71feb4b2257b8b50 [file] [log] [blame]
// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
//
// 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.
#if SWIFTSHADER_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER
# include "VkDeviceMemoryExternalAndroid.hpp"
# include "VkDestroy.hpp"
# include "VkDevice.hpp"
# include "VkFormat.hpp"
# include "VkObject.hpp"
# include "VkPhysicalDevice.hpp"
# include "System/Debug.hpp"
# include "System/Linux/MemFd.hpp"
# include <sys/mman.h>
# include <android/hardware_buffer.h>
# include <cutils/native_handle.h>
# include <vndk/hardware_buffer.h>
# include <cros_gralloc/cros_gralloc_handle.h>
# include <external/virgl_hw.h>
# include <unistd.h>
# include <virtgpu_drm.h>
# include <xf86drm.h>
AHardwareBufferExternalMemory::~AHardwareBufferExternalMemory()
{
// correct deallocation of AHB does not require a pointer or size
deallocate(nullptr, 0);
}
VkResult AHardwareBufferExternalMemory::allocate(size_t size, void **pBuffer)
{
if(allocateInfo.importAhb)
{
ahb = allocateInfo.ahb;
AHardwareBuffer_acquire(ahb);
return allocateAndroidHardwareBuffer(size, pBuffer);
}
else
{
ASSERT(allocateInfo.exportAhb);
// Outline ahbDesc
AHardwareBuffer_Desc ahbDesc;
if(allocateInfo.imageHandle)
{
ahbDesc.format = GetAndroidHardwareBufferDescFormat(VkFormat(allocateInfo.imageHandle->getFormat()));
VkExtent3D extent = allocateInfo.imageHandle->getMipLevelExtent(VK_IMAGE_ASPECT_COLOR_BIT, 0);
ahbDesc.width = extent.width;
ahbDesc.height = extent.height;
ahbDesc.layers = allocateInfo.imageHandle->getArrayLayers();
ahbDesc.stride = allocateInfo.imageHandle->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
VkImageCreateFlags createFlags = allocateInfo.imageHandle->getFlags();
VkImageUsageFlags usageFlags = allocateInfo.imageHandle->getUsage();
GetAndroidHardwareBufferUsageFromVkUsage(createFlags, usageFlags, ahbDesc.usage);
}
else
{
ASSERT(allocateInfo.bufferHandle);
ahbDesc.format = AHARDWAREBUFFER_FORMAT_BLOB;
ahbDesc.width = uint32_t(allocateInfo.bufferHandle->getSize());
ahbDesc.height = 1;
ahbDesc.layers = 1;
ahbDesc.stride = uint32_t(allocateInfo.bufferHandle->getSize());
// TODO(b/141698760)
// This will be fairly specific, needs fleshing out
ahbDesc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
}
// create a new ahb from desc
if(AHardwareBuffer_allocate(&ahbDesc, &ahb) != 0)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
return allocateAndroidHardwareBuffer(size, pBuffer);
}
}
VkResult AHardwareBufferExternalMemory::allocateAndroidHardwareBuffer(size_t size, void **pBuffer)
{
// get native_handle_t from ahb
const native_handle_t *h = AHardwareBuffer_getNativeHandle(ahb);
if(h == nullptr)
{
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
// get rendernodeFD and primeHandle from native_handle_t.data
uint32_t primeHandle;
createRenderNodeFD();
VkResult result = getPrimeHandle(h, primeHandle);
if(result != VK_SUCCESS)
{
return result;
}
// get memory pointer from store or mmap it
vk::Device::AHBAddressMap *pDeviceHandleMap = device->getAHBAddressMap();
void *pAddress = pDeviceHandleMap->query(primeHandle);
if(pAddress != nullptr)
{
*pBuffer = pAddress;
pDeviceHandleMap->incrementReference(primeHandle);
}
else
{
// map memory
void *ptr;
VkResult result = mapMemory(primeHandle, &ptr);
if(result != VK_SUCCESS)
{
return result;
}
// Add primeHandle and ptr to deviceHandleMap
pDeviceHandleMap->add(primeHandle, ptr);
*pBuffer = pDeviceHandleMap->query(primeHandle);
}
return VK_SUCCESS;
}
void AHardwareBufferExternalMemory::deallocate(void *buffer, size_t size)
{
if(ahb != nullptr)
{
const native_handle_t *h = AHardwareBuffer_getNativeHandle(ahb);
uint32_t primeHandle;
VkResult result = getPrimeHandle(h, primeHandle);
ASSERT(result == VK_SUCCESS);
// close gpu memory and rendernodeFD
vk::Device::AHBAddressMap *pDeviceHandleMap = device->getAHBAddressMap();
if(pDeviceHandleMap->decrementReference(primeHandle) == 0)
{
closeMemory(primeHandle);
}
close(rendernodeFD);
AHardwareBuffer_release(ahb);
ahb = nullptr;
}
}
VkResult AHardwareBufferExternalMemory::exportAndroidHardwareBuffer(struct AHardwareBuffer **pAhb) const
{
// Each call to vkGetMemoryAndroidHardwareBufferANDROID *must* return an Android hardware buffer with a new reference
// acquired in addition to the reference held by the VkDeviceMemory. To avoid leaking resources, the application *must*
// release the reference by calling AHardwareBuffer_release when it is no longer needed.
AHardwareBuffer_acquire(ahb);
*pAhb = ahb;
return VK_SUCCESS;
}
uint32_t AHardwareBufferExternalMemory::GetAndroidHardwareBufferDescFormat(VkFormat format)
{
switch(format)
{
case VK_FORMAT_D16_UNORM:
return AHARDWAREBUFFER_FORMAT_D16_UNORM;
case VK_FORMAT_X8_D24_UNORM_PACK32:
UNSUPPORTED("AHardwareBufferExternalMemory::VkFormat VK_FORMAT_X8_D24_UNORM_PACK32");
return AHARDWAREBUFFER_FORMAT_D24_UNORM;
case VK_FORMAT_D24_UNORM_S8_UINT:
UNSUPPORTED("AHardwareBufferExternalMemory::VkFormat VK_FORMAT_D24_UNORM_S8_UINT");
return AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT;
case VK_FORMAT_D32_SFLOAT:
return AHARDWAREBUFFER_FORMAT_D32_FLOAT;
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
return AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
case VK_FORMAT_R16G16B16A16_SFLOAT:
return AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT;
case VK_FORMAT_R5G6B5_UNORM_PACK16:
return AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
case VK_FORMAT_R8G8B8A8_UNORM:
return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
case VK_FORMAT_R8G8B8_UNORM:
return AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM;
case VK_FORMAT_S8_UINT:
return AHARDWAREBUFFER_FORMAT_S8_UINT;
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
return AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420;
default:
UNSUPPORTED("AHardwareBufferExternalMemory::VkFormat %d", int(format));
return 0;
}
}
VkFormat AHardwareBufferExternalMemory::GetVkFormat(uint32_t ahbFormat)
{
switch(ahbFormat)
{
case AHARDWAREBUFFER_FORMAT_BLOB:
return VK_FORMAT_UNDEFINED;
case AHARDWAREBUFFER_FORMAT_D16_UNORM:
return VK_FORMAT_D16_UNORM;
case AHARDWAREBUFFER_FORMAT_D24_UNORM:
UNSUPPORTED("AHardwareBufferExternalMemory::AndroidHardwareBuffer_Format AHARDWAREBUFFER_FORMAT_D24_UNORM");
return VK_FORMAT_X8_D24_UNORM_PACK32;
case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
UNSUPPORTED("AHardwareBufferExternalMemory::AndroidHardwareBuffer_Format AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT");
return VK_FORMAT_X8_D24_UNORM_PACK32;
case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
return VK_FORMAT_D32_SFLOAT;
case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
return VK_FORMAT_D32_SFLOAT_S8_UINT;
case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
return VK_FORMAT_R16G16B16A16_SFLOAT;
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
return VK_FORMAT_R5G6B5_UNORM_PACK16;
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
return VK_FORMAT_R8G8B8A8_UNORM;
case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
return VK_FORMAT_R8G8B8A8_UNORM;
case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
return VK_FORMAT_R8G8B8_UNORM;
case AHARDWAREBUFFER_FORMAT_S8_UINT:
return VK_FORMAT_S8_UINT;
case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
return VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM;
default:
UNSUPPORTED("AHardwareBufferExternalMemory::AHardwareBuffer_Format %d", int(ahbFormat));
return VK_FORMAT_UNDEFINED;
}
}
VkFormatFeatureFlags AHardwareBufferExternalMemory::GetVkFormatFeatures(VkFormat format)
{
VkFormatProperties formatProperties;
vk::PhysicalDevice::GetFormatProperties(vk::Format(format), &formatProperties);
formatProperties.optimalTilingFeatures |= VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT;
// TODO: b/167896057
// The correct formatFeatureFlags depends on consumer and format
// So this solution is incomplete without more information
return formatProperties.linearTilingFeatures | formatProperties.optimalTilingFeatures | formatProperties.bufferFeatures;
}
VkResult AHardwareBufferExternalMemory::GetAndroidHardwareBufferFormatProperties(const AHardwareBuffer_Desc &ahbDesc, VkAndroidHardwareBufferFormatPropertiesANDROID *pFormat)
{
pFormat->sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID;
pFormat->pNext = nullptr;
pFormat->format = GetVkFormat(ahbDesc.format);
pFormat->externalFormat = ahbDesc.format;
pFormat->formatFeatures = GetVkFormatFeatures(pFormat->format);
pFormat->samplerYcbcrConversionComponents = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
pFormat->suggestedYcbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
pFormat->suggestedYcbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
pFormat->suggestedXChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN;
pFormat->suggestedYChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN;
return VK_SUCCESS;
}
VkResult AHardwareBufferExternalMemory::GetAndroidHardwareBufferProperties(VkDevice &device, const struct AHardwareBuffer *buffer, VkAndroidHardwareBufferPropertiesANDROID *pProperties)
{
AHardwareBuffer_Desc ahbDesc;
AHardwareBuffer_describe(buffer, &ahbDesc);
GetAndroidHardwareBufferFormatProperties(ahbDesc, (VkAndroidHardwareBufferFormatPropertiesANDROID *)pProperties->pNext);
const VkPhysicalDeviceMemoryProperties phyDeviceMemProps = vk::PhysicalDevice::GetMemoryProperties();
pProperties->memoryTypeBits = phyDeviceMemProps.memoryTypes[0].propertyFlags;
if(ahbDesc.format == AHARDWAREBUFFER_FORMAT_BLOB)
{
pProperties->allocationSize = ahbDesc.width;
}
else
{
VkImageCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
info.imageType = VK_IMAGE_TYPE_2D;
info.format = GetVkFormat(ahbDesc.format);
info.extent.width = ahbDesc.width;
info.extent.height = ahbDesc.height;
info.extent.depth = 1;
info.mipLevels = 1;
info.arrayLayers = 1;
info.samples = VK_SAMPLE_COUNT_1_BIT;
info.tiling = VK_IMAGE_TILING_OPTIMAL;
info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
VkImage Image;
VkResult result = vk::Image::Create(vk::DEVICE_MEMORY, &info, &Image, vk::Cast(device));
pProperties->allocationSize = vk::Cast(Image)->getMemoryRequirements().size;
vk::destroy(Image, vk::DEVICE_MEMORY);
}
return VK_SUCCESS;
}
VkResult AHardwareBufferExternalMemory::GetAndroidHardwareBufferUsageFromVkUsage(const VkImageCreateFlags createFlags, const VkImageUsageFlags usageFlags, uint64_t &ahbDescUsage)
{
if(usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT)
ahbDescUsage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
if(usageFlags & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)
ahbDescUsage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
if(usageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
ahbDescUsage |= AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
if(createFlags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)
ahbDescUsage |= AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP;
if(createFlags & VK_IMAGE_CREATE_PROTECTED_BIT)
ahbDescUsage |= AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
// No usage bits set - set at least one GPU usage
if(ahbDescUsage == 0)
ahbDescUsage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
return VK_SUCCESS;
}
// Call into the native gralloc implementation to request a handle for the
// rendernodeFD
VkResult AHardwareBufferExternalMemory::getPrimeHandle(const native_handle_t *h, uint32_t &primeHandle)
{
cros_gralloc_handle const *crosHandle = reinterpret_cast<cros_gralloc_handle const *>(h);
int ret = drmPrimeFDToHandle(rendernodeFD, crosHandle->fds[0], &primeHandle);
if(ret != 0)
{
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
return VK_SUCCESS;
}
// Create a rendernodeFD
void AHardwareBufferExternalMemory::createRenderNodeFD()
{
rendernodeFD = drmOpenRender(128);
}
// using a primeHandle associated with a specific rendernodeFD, map a new block
// of memory
VkResult AHardwareBufferExternalMemory::mapMemory(uint32_t &primeHandle, void **ptr)
{
drm_virtgpu_map map;
memset(&map, 0, sizeof(drm_virtgpu_map));
map.handle = primeHandle;
int ret = drmIoctl(rendernodeFD, DRM_IOCTL_VIRTGPU_MAP, &map);
if(ret != 0)
{
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
// mmap it
*ptr = static_cast<unsigned char *>(
mmap64(nullptr, 4096, PROT_WRITE, MAP_SHARED, rendernodeFD, map.offset));
if(ptr == MAP_FAILED)
{
return VK_ERROR_MEMORY_MAP_FAILED;
}
return VK_SUCCESS;
}
// Close out the memory block associated with rendernodeFD
VkResult AHardwareBufferExternalMemory::closeMemory(uint32_t &primeHandle)
{
struct drm_gem_close gem_close;
memset(&gem_close, 0x0, sizeof(gem_close));
gem_close.handle = primeHandle;
int ret = drmIoctl(rendernodeFD, DRM_IOCTL_GEM_CLOSE, &gem_close);
if(ret != 0)
{
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
return VK_SUCCESS;
}
AHardwareBufferExternalMemory::AllocateInfo::AllocateInfo(const VkMemoryAllocateInfo *pAllocateInfo)
{
const auto *createInfo = reinterpret_cast<const VkBaseInStructure *>(pAllocateInfo->pNext);
while(createInfo)
{
switch(createInfo->sType)
{
case VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID:
{
const auto *importInfo = reinterpret_cast<const VkImportAndroidHardwareBufferInfoANDROID *>(createInfo);
importAhb = true;
ahb = importInfo->buffer;
}
break;
case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO:
{
const auto *exportInfo = reinterpret_cast<const VkExportMemoryAllocateInfo *>(createInfo);
if(exportInfo->handleTypes != VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID)
{
UNSUPPORTED("VkExportMemoryAllocateInfo::handleTypes %d", int(exportInfo->handleTypes));
}
exportAhb = true;
}
break;
case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO:
{
// AHB requires dedicated allocation -- for images, the gralloc gets to decide the image layout,
// not us.
const auto *dedicatedAllocateInfo = reinterpret_cast<const VkMemoryDedicatedAllocateInfo *>(createInfo);
imageHandle = vk::Cast(dedicatedAllocateInfo->image);
bufferHandle = vk::Cast(dedicatedAllocateInfo->buffer);
}
break;
default:
WARN("VkMemoryAllocateInfo->pNext sType = %s", vk::Stringify(createInfo->sType).c_str());
}
createInfo = createInfo->pNext;
}
}
#endif // SWIFTSHADER_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER