Add AHardwareBuffer support

Initial pass at adding AHardwareBufferSupport so that Dawn can display
content on Android. Confirmed that this will allow many WebGPU pages to
render when paired with
https://chromium-review.googlesource.com/c/chromium/src/+/3877262

Bug: dawn:286
Change-Id: I627fa2ab71f85bd3cb7ea21b0588dbd2089cdf5f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/101460
Commit-Queue: Brandon Jones <bajones@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
Brandon Jones 2022-09-08 23:03:28 +00:00 committed by Dawn LUCI CQ
parent 0d7cd27fd3
commit 7edef20bdd
15 changed files with 279 additions and 8 deletions

View File

@ -229,6 +229,7 @@ enum ExternalImageType {
IOSurface,
DXGISharedHandle,
EGLImage,
AHardwareBuffer,
};
// Common properties of external images

View File

@ -126,6 +126,26 @@ struct DAWN_NATIVE_EXPORT ExternalImageExportInfoDmaBuf : ExternalImageExportInf
ExternalImageExportInfoDmaBuf();
};
#ifdef __ANDROID__
// Descriptor for AHardwareBuffer image import
struct DAWN_NATIVE_EXPORT ExternalImageDescriptorAHardwareBuffer : ExternalImageDescriptorVk {
public:
ExternalImageDescriptorAHardwareBuffer();
struct AHardwareBuffer* handle; // The AHardwareBuffer which contains the memory of the image
std::vector<int> waitFDs; // File descriptors of semaphores which will be waited on
protected:
using ExternalImageDescriptorVk::ExternalImageDescriptorVk;
};
struct DAWN_NATIVE_EXPORT ExternalImageExportInfoAHardwareBuffer : ExternalImageExportInfoFD {
ExternalImageExportInfoAHardwareBuffer();
};
#endif // __ANDROID__
#endif // __linux__
// Imports external memory into a Vulkan image. Internally, this uses external memory /

View File

@ -690,6 +690,12 @@ source_set("sources") {
"vulkan/external_memory/MemoryServiceZirconHandle.cpp",
"vulkan/external_semaphore/SemaphoreServiceZirconHandle.cpp",
]
} else if (is_android) {
sources += [
"vulkan/external_memory/MemoryServiceAHardwareBuffer.cpp",
"vulkan/external_semaphore/SemaphoreServiceFD.cpp",
]
defines += [ "DAWN_USE_SYNC_FDS" ]
} else {
sources += [
"vulkan/external_memory/MemoryServiceNull.cpp",

View File

@ -19,7 +19,13 @@
namespace dawn::native::vulkan {
#if DAWN_PLATFORM_IS(LINUX)
#if DAWN_PLATFORM_IS(ANDROID)
// AHardwareBuffer
using ExternalMemoryHandle = struct AHardwareBuffer*;
// File descriptor
using ExternalSemaphoreHandle = int;
const ExternalSemaphoreHandle kNullExternalSemaphoreHandle = -1;
#elif DAWN_PLATFORM_IS(LINUX)
// File descriptor
using ExternalMemoryHandle = int;
// File descriptor

View File

@ -732,6 +732,7 @@ MaybeError Texture::InitializeFromExternal(const ExternalImageDescriptorVk* desc
}
mExternalState = ExternalState::PendingAcquire;
mExportQueueFamilyIndex = externalMemoryService->GetQueueFamilyIndex();
mPendingAcquireOldLayout = descriptor->releasedOldLayout;
mPendingAcquireNewLayout = descriptor->releasedNewLayout;
@ -829,7 +830,7 @@ void Texture::TransitionEagerlyForExport(CommandRecordingContext* recordingConte
Device* device = ToBackend(GetDevice());
barrier.srcQueueFamilyIndex = device->GetGraphicsQueueFamily();
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR;
barrier.dstQueueFamilyIndex = mExportQueueFamilyIndex;
// We don't know when the importing queue will need the texture, so pass
// VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT to ensure the barrier happens-before any usage in the
@ -950,7 +951,7 @@ void Texture::TweakTransitionForExternalUsage(CommandRecordingContext* recording
VkImageMemoryBarrier* barrier = &(*barriers)[transitionBarrierStart];
// Transfer texture from external queue to graphics queue
barrier->srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR;
barrier->srcQueueFamilyIndex = mExportQueueFamilyIndex;
barrier->dstQueueFamilyIndex = ToBackend(GetDevice())->GetGraphicsQueueFamily();
// srcAccessMask means nothing when importing. Queue transfers require a barrier on

View File

@ -176,6 +176,7 @@ class Texture final : public TextureBase {
};
ExternalState mExternalState = ExternalState::InternalOnly;
ExternalState mLastExternalState = ExternalState::InternalOnly;
uint32_t mExportQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR;
VkImageLayout mPendingAcquireOldLayout;
VkImageLayout mPendingAcquireNewLayout;

View File

@ -74,9 +74,26 @@ ExternalImageExportInfoDmaBuf::ExternalImageExportInfoDmaBuf()
: ExternalImageExportInfoFD(ExternalImageType::DmaBuf) {}
#endif // DAWN_PLATFORM_IS(LINUX)
#if DAWN_PLATFORM_IS(ANDROID)
ExternalImageDescriptorAHardwareBuffer::ExternalImageDescriptorAHardwareBuffer()
: ExternalImageDescriptorVk(ExternalImageType::AHardwareBuffer) {}
ExternalImageExportInfoAHardwareBuffer::ExternalImageExportInfoAHardwareBuffer()
: ExternalImageExportInfoFD(ExternalImageType::AHardwareBuffer) {}
#endif
WGPUTexture WrapVulkanImage(WGPUDevice device, const ExternalImageDescriptorVk* descriptor) {
#if DAWN_PLATFORM_IS(LINUX)
switch (descriptor->GetType()) {
#if DAWN_PLATFORM_IS(ANDROID)
case ExternalImageType::AHardwareBuffer: {
Device* backendDevice = ToBackend(FromAPI(device));
const ExternalImageDescriptorAHardwareBuffer* ahbDescriptor =
static_cast<const ExternalImageDescriptorAHardwareBuffer*>(descriptor);
return ToAPI(backendDevice->CreateTextureWrappingVulkanImage(
ahbDescriptor, ahbDescriptor->handle, ahbDescriptor->waitFDs));
}
#elif DAWN_PLATFORM_IS(LINUX)
case ExternalImageType::OpaqueFD:
case ExternalImageType::DmaBuf: {
Device* backendDevice = ToBackend(FromAPI(device));
@ -86,12 +103,11 @@ WGPUTexture WrapVulkanImage(WGPUDevice device, const ExternalImageDescriptorVk*
return ToAPI(backendDevice->CreateTextureWrappingVulkanImage(
fdDescriptor, fdDescriptor->memoryFD, fdDescriptor->waitFDs));
}
#endif // DAWN_PLATFORM_IS(LINUX)
default:
return nullptr;
}
#else
return nullptr;
#endif // DAWN_PLATFORM_IS(LINUX)
}
bool ExportVulkanImage(WGPUTexture texture,
@ -100,8 +116,9 @@ bool ExportVulkanImage(WGPUTexture texture,
if (texture == nullptr) {
return false;
}
#if DAWN_PLATFORM_IS(LINUX)
#if DAWN_PLATFORM_IS(ANDROID) || DAWN_PLATFORM_IS(LINUX)
switch (info->GetType()) {
case ExternalImageType::AHardwareBuffer:
case ExternalImageType::OpaqueFD:
case ExternalImageType::DmaBuf: {
Texture* backendTexture = ToBackend(FromAPI(texture));

View File

@ -386,6 +386,13 @@ MaybeError VulkanFunctions::LoadDeviceProcs(VkDevice device, const VulkanDeviceI
}
#endif
#if DAWN_PLATFORM_IS(ANDROID)
if (deviceInfo.HasExt(DeviceExt::ExternalMemoryAndroidHardwareBuffer)) {
GET_DEVICE_PROC(GetAndroidHardwareBufferPropertiesANDROID);
GET_DEVICE_PROC(GetMemoryAndroidHardwareBufferANDROID);
}
#endif // DAWN_PLATFORM_IS(ANDROID)
return {};
}

View File

@ -165,7 +165,14 @@ struct VulkanFunctions {
#endif // DAWN_PLATFORM_IS(WINDOWS)
#if DAWN_PLATFORM_IS(ANDROID)
// KHR_android_surface
VkFn<PFN_vkCreateAndroidSurfaceKHR> CreateAndroidSurfaceKHR = nullptr;
// VK_ANDROID_external_memory_android_hardware_buffer
VkFn<PFN_vkGetAndroidHardwareBufferPropertiesANDROID>
GetAndroidHardwareBufferPropertiesANDROID = nullptr;
VkFn<PFN_vkGetMemoryAndroidHardwareBufferANDROID> GetMemoryAndroidHardwareBufferANDROID =
nullptr;
#endif // DAWN_PLATFORM_IS(ANDROID)
#if defined(DAWN_USE_X11)

View File

@ -57,6 +57,9 @@ class Service {
const ExternalImageDescriptor* descriptor,
VkImage image);
// Returns the index of the queue memory from this services should be exported with.
uint32_t GetQueueFamilyIndex();
// Given an external handle pointing to memory, import it into a VkDeviceMemory
ResultOrError<VkDeviceMemory> ImportMemory(ExternalMemoryHandle handle,
const MemoryImportParams& importParams,

View File

@ -0,0 +1,186 @@
// Copyright 2022 The Dawn Authors
//
// 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.
#include "dawn/common/Assert.h"
#include "dawn/native/vulkan/AdapterVk.h"
#include "dawn/native/vulkan/BackendVk.h"
#include "dawn/native/vulkan/DeviceVk.h"
#include "dawn/native/vulkan/TextureVk.h"
#include "dawn/native/vulkan/UtilsVulkan.h"
#include "dawn/native/vulkan/VulkanError.h"
#include "dawn/native/vulkan/external_memory/MemoryService.h"
namespace dawn::native::vulkan::external_memory {
Service::Service(Device* device)
: mDevice(device), mSupported(CheckSupport(device->GetDeviceInfo())) {}
Service::~Service() = default;
// static
bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo) {
return deviceInfo.HasExt(DeviceExt::ExternalMemoryAndroidHardwareBuffer);
}
bool Service::SupportsImportMemory(VkFormat format,
VkImageType type,
VkImageTiling tiling,
VkImageUsageFlags usage,
VkImageCreateFlags flags) {
// Early out before we try using extension functions
if (!mSupported) {
return false;
}
VkPhysicalDeviceImageFormatInfo2 formatInfo = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR,
.pNext = nullptr,
.format = format,
.type = type,
.tiling = tiling,
.usage = usage,
.flags = flags,
};
PNextChainBuilder formatInfoChain(&formatInfo);
VkPhysicalDeviceExternalImageFormatInfo externalFormatInfo = {
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};
formatInfoChain.Add(&externalFormatInfo,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR);
VkImageFormatProperties2 formatProperties = {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR,
.pNext = nullptr,
};
PNextChainBuilder formatPropertiesChain(&formatProperties);
VkExternalImageFormatProperties externalFormatProperties;
formatPropertiesChain.Add(&externalFormatProperties,
VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR);
VkResult result = VkResult::WrapUnsafe(mDevice->fn.GetPhysicalDeviceImageFormatProperties2(
ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, &formatProperties));
// If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED
if (result != VK_SUCCESS) {
return false;
}
// TODO(http://crbug.com/dawn/206): Investigate dedicated only images
VkFlags memoryFlags = externalFormatProperties.externalMemoryProperties.externalMemoryFeatures;
return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) != 0;
}
bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
VkFormat format,
VkImageUsageFlags usage,
bool* supportsDisjoint) {
*supportsDisjoint = false;
return mSupported;
}
ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
const ExternalImageDescriptor* descriptor,
VkImage image) {
DAWN_INVALID_IF(descriptor->GetType() != ExternalImageType::AHardwareBuffer,
"ExternalImageDescriptor is not an AHardwareBuffer descriptor.");
const ExternalImageDescriptorAHardwareBuffer* aHardwareBufferDescriptor =
static_cast<const ExternalImageDescriptorAHardwareBuffer*>(descriptor);
VkAndroidHardwareBufferPropertiesANDROID bufferProperties = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
.pNext = nullptr,
};
PNextChainBuilder bufferPropertiesChain(&bufferProperties);
VkAndroidHardwareBufferFormatPropertiesANDROID bufferFormatProperties;
bufferPropertiesChain.Add(&bufferFormatProperties,
VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID);
DAWN_TRY(CheckVkSuccess(
mDevice->fn.GetAndroidHardwareBufferPropertiesANDROID(
mDevice->GetVkDevice(), aHardwareBufferDescriptor->handle, &bufferProperties),
"vkGetAndroidHardwareBufferPropertiesANDROID"));
MemoryImportParams params = {bufferProperties.allocationSize, bufferProperties.memoryTypeBits};
return params;
}
uint32_t Service::GetQueueFamilyIndex() {
return VK_QUEUE_FAMILY_FOREIGN_EXT;
}
ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
const MemoryImportParams& importParams,
VkImage image) {
DAWN_INVALID_IF(handle == nullptr, "Importing memory with an invalid handle.");
VkMemoryRequirements requirements;
mDevice->fn.GetImageMemoryRequirements(mDevice->GetVkDevice(), image, &requirements);
DAWN_INVALID_IF(requirements.size > importParams.allocationSize,
"Requested allocation size (%u) is smaller than the image requires (%u).",
importParams.allocationSize, requirements.size);
VkMemoryAllocateInfo allocateInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = nullptr,
.allocationSize = importParams.allocationSize,
.memoryTypeIndex = importParams.memoryTypeIndex,
};
PNextChainBuilder allocateInfoChain(&allocateInfo);
VkImportAndroidHardwareBufferInfoANDROID importMemoryAHBInfo = {
.buffer = handle,
};
allocateInfoChain.Add(&importMemoryAHBInfo,
VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID);
VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo,
nullptr, &*allocatedMemory),
"vkAllocateMemory"));
return allocatedMemory;
}
ResultOrError<VkImage> Service::CreateImage(const ExternalImageDescriptor* descriptor,
const VkImageCreateInfo& baseCreateInfo) {
VkImageCreateInfo createInfo = baseCreateInfo;
createInfo.flags |= VK_IMAGE_CREATE_ALIAS_BIT_KHR;
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
PNextChainBuilder createInfoChain(&createInfo);
VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo = {
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};
createInfoChain.Add(&externalMemoryImageCreateInfo,
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
ASSERT(IsSampleCountSupported(mDevice, createInfo));
VkImage image;
DAWN_TRY(CheckVkSuccess(
mDevice->fn.CreateImage(mDevice->GetVkDevice(), &createInfo, nullptr, &*image),
"CreateImage"));
return image;
}
} // namespace dawn::native::vulkan::external_memory

View File

@ -256,6 +256,10 @@ ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
return params;
}
uint32_t Service::GetQueueFamilyIndex() {
return VK_QUEUE_FAMILY_EXTERNAL_KHR;
}
ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
const MemoryImportParams& importParams,
VkImage image) {

View File

@ -51,6 +51,10 @@ ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
return DAWN_UNIMPLEMENTED_ERROR("Using null memory service to interop inside Vulkan");
}
uint32_t Service::GetQueueFamilyIndex() {
return VK_QUEUE_FAMILY_EXTERNAL_KHR;
}
ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
const MemoryImportParams& importParams,
VkImage image) {

View File

@ -100,6 +100,10 @@ ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
return params;
}
uint32_t Service::GetQueueFamilyIndex() {
return VK_QUEUE_FAMILY_EXTERNAL_KHR;
}
ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
const MemoryImportParams& importParams,
VkImage image) {

View File

@ -100,6 +100,10 @@ ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
return params;
}
uint32_t Service::GetQueueFamilyIndex() {
return VK_QUEUE_FAMILY_EXTERNAL_KHR;
}
ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
const MemoryImportParams& importParams,
VkImage image) {