Create VkImage before importing external memory

This CL is part of a chain of CLs that imports dma-bufs as VkImages
to support WebGPU on Chrome OS.

There are currently two steps for importing external memory into
Vulkan:

  1. DeviceVk::ImportExternalImage: calls into
     MemoryServiceOpaqueFD::ImportMemory which in turn calls into
     vkAllocateMemory and outputs a VkDeviceMemory handle to the
     imported memory.
  2. TextureVk::CreateFromExternal: creates the actual TextureVk
     object, creates the VkImage, and binds the VkDeviceMemory from
     ImportExternalImage to the VkImage.

For dma-buf support, however, we need to re-order these two steps
because importing dma-buf memory requires a handle to the VkImage
that will alias it [1].

This CL splits these two steps into three steps to ensure we create
the VkImage first so we can use it in vkAllocateMemory:

  1. TextureVk::CreateFromExternal: creates the TextureVk and
     VkImage (no longer concerns itself with vkBindImageMemory).
  2. DeviceVk::ImportExternalImage: now takes the VkImage as input
     but is otherwise unchanged.
  3. TextureVk::BindExternalMemory: calls vkBindImageMemory with
     handles to VkDeviceMemory and VkImage.

[1] https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkMemoryDedicatedAllocateInfo.html

BUG=chromium:996470

Change-Id: Id2d5951e9b573af79c44ce8c63be5210a279f946
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13780
Commit-Queue: Brian Ho <hob@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Brian Ho 2019-11-20 01:50:50 +00:00 committed by Commit Bot service account
parent 02dd733454
commit 756a9d7e49
8 changed files with 91 additions and 45 deletions

View File

@ -595,6 +595,7 @@ namespace dawn_native { namespace vulkan {
MaybeError Device::ImportExternalImage(const ExternalImageDescriptor* descriptor, MaybeError Device::ImportExternalImage(const ExternalImageDescriptor* descriptor,
ExternalMemoryHandle memoryHandle, ExternalMemoryHandle memoryHandle,
VkImage image,
const std::vector<ExternalSemaphoreHandle>& waitHandles, const std::vector<ExternalSemaphoreHandle>& waitHandles,
VkSemaphore* outSignalSemaphore, VkSemaphore* outSignalSemaphore,
VkDeviceMemory* outAllocation, VkDeviceMemory* outAllocation,
@ -620,9 +621,11 @@ namespace dawn_native { namespace vulkan {
mExternalSemaphoreService->CreateExportableSemaphore()); mExternalSemaphoreService->CreateExportableSemaphore());
// Import the external image's memory // Import the external image's memory
external_memory::MemoryImportParams importParams;
DAWN_TRY_ASSIGN(importParams,
mExternalMemoryService->GetMemoryImportParams(descriptor, image));
DAWN_TRY_ASSIGN(*outAllocation, DAWN_TRY_ASSIGN(*outAllocation,
mExternalMemoryService->ImportMemory( mExternalMemoryService->ImportMemory(memoryHandle, importParams, image));
memoryHandle, descriptor->allocationSize, descriptor->memoryTypeIndex));
// Import semaphores we have to wait on before using the texture // Import semaphores we have to wait on before using the texture
for (const ExternalSemaphoreHandle& handle : waitHandles) { for (const ExternalSemaphoreHandle& handle : waitHandles) {
@ -671,11 +674,19 @@ namespace dawn_native { namespace vulkan {
// Cleanup in case of a failure, the image creation doesn't acquire the external objects // Cleanup in case of a failure, the image creation doesn't acquire the external objects
// if a failure happems. // if a failure happems.
Texture* result = nullptr; Texture* result = nullptr;
if (ConsumedError(ImportExternalImage(descriptor, memoryHandle, waitHandles, // TODO(crbug.com/1026480): Consolidate this into a single CreateFromExternal call.
&signalSemaphore, &allocation, &waitSemaphores)) || if (ConsumedError(Texture::CreateFromExternal(this, descriptor, textureDescriptor),
ConsumedError(Texture::CreateFromExternal(this, descriptor, textureDescriptor, &result) ||
signalSemaphore, allocation, waitSemaphores), ConsumedError(ImportExternalImage(descriptor, memoryHandle, result->GetHandle(),
&result)) { waitHandles, &signalSemaphore, &allocation,
&waitSemaphores)) ||
ConsumedError(result->BindExternalMemory(descriptor, signalSemaphore, allocation,
waitSemaphores))) {
// Delete the Texture if it was created
if (result != nullptr) {
delete result;
}
// Clear the signal semaphore // Clear the signal semaphore
fn.DestroySemaphore(GetVkDevice(), signalSemaphore, nullptr); fn.DestroySemaphore(GetVkDevice(), signalSemaphore, nullptr);

View File

@ -171,6 +171,7 @@ namespace dawn_native { namespace vulkan {
MaybeError ImportExternalImage(const ExternalImageDescriptor* descriptor, MaybeError ImportExternalImage(const ExternalImageDescriptor* descriptor,
ExternalMemoryHandle memoryHandle, ExternalMemoryHandle memoryHandle,
VkImage image,
const std::vector<ExternalSemaphoreHandle>& waitHandles, const std::vector<ExternalSemaphoreHandle>& waitHandles,
VkSemaphore* outSignalSemaphore, VkSemaphore* outSignalSemaphore,
VkDeviceMemory* outAllocation, VkDeviceMemory* outAllocation,

View File

@ -406,16 +406,13 @@ namespace dawn_native { namespace vulkan {
} }
// static // static
ResultOrError<Texture*> Texture::CreateFromExternal(Device* device, ResultOrError<Texture*> Texture::CreateFromExternal(
Device* device,
const ExternalImageDescriptor* descriptor, const ExternalImageDescriptor* descriptor,
const TextureDescriptor* textureDescriptor, const TextureDescriptor* textureDescriptor) {
VkSemaphore signalSemaphore,
VkDeviceMemory externalMemoryAllocation,
std::vector<VkSemaphore> waitSemaphores) {
std::unique_ptr<Texture> texture = std::unique_ptr<Texture> texture =
std::make_unique<Texture>(device, textureDescriptor, TextureState::OwnedInternal); std::make_unique<Texture>(device, textureDescriptor, TextureState::OwnedInternal);
DAWN_TRY(texture->InitializeFromExternal( DAWN_TRY(texture->InitializeFromExternal(descriptor));
descriptor, signalSemaphore, externalMemoryAllocation, std::move((waitSemaphores))));
return texture.release(); return texture.release();
} }
@ -484,10 +481,7 @@ namespace dawn_native { namespace vulkan {
} }
// Internally managed, but imported from external handle // Internally managed, but imported from external handle
MaybeError Texture::InitializeFromExternal(const ExternalImageDescriptor* descriptor, MaybeError Texture::InitializeFromExternal(const ExternalImageDescriptor* descriptor) {
VkSemaphore signalSemaphore,
VkDeviceMemory externalMemoryAllocation,
std::vector<VkSemaphore> waitSemaphores) {
mExternalState = ExternalState::PendingAcquire; mExternalState = ExternalState::PendingAcquire;
Device* device = ToBackend(GetDevice()); Device* device = ToBackend(GetDevice());
@ -519,7 +513,14 @@ namespace dawn_native { namespace vulkan {
device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &mHandle), device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
"CreateImage")); "CreateImage"));
// Create the image memory and associate it with the container return {};
}
MaybeError Texture::BindExternalMemory(const ExternalImageDescriptor* descriptor,
VkSemaphore signalSemaphore,
VkDeviceMemory externalMemoryAllocation,
std::vector<VkSemaphore> waitSemaphores) {
Device* device = ToBackend(GetDevice());
VkMemoryRequirements requirements; VkMemoryRequirements requirements;
device->fn.GetImageMemoryRequirements(device->GetVkDevice(), mHandle, &requirements); device->fn.GetImageMemoryRequirements(device->GetVkDevice(), mHandle, &requirements);
@ -538,7 +539,6 @@ namespace dawn_native { namespace vulkan {
mExternalAllocation = externalMemoryAllocation; mExternalAllocation = externalMemoryAllocation;
mSignalSemaphore = signalSemaphore; mSignalSemaphore = signalSemaphore;
mWaitRequirements = std::move(waitSemaphores); mWaitRequirements = std::move(waitSemaphores);
return {}; return {};
} }

View File

@ -39,16 +39,13 @@ namespace dawn_native { namespace vulkan {
// Used to create a regular texture from a descriptor. // Used to create a regular texture from a descriptor.
static ResultOrError<Texture*> Create(Device* device, const TextureDescriptor* descriptor); static ResultOrError<Texture*> Create(Device* device, const TextureDescriptor* descriptor);
// Used to create a texture from Vulkan external memory objects. // Creates a texture and initializes it with a VkImage that references an external memory
// Ownership of semaphores and the memory allocation is taken only if the creation is // object. Before the texture can be used, the VkDeviceMemory associated with the external
// a success. // image must be bound via Texture::BindExternalMemory.
static ResultOrError<Texture*> CreateFromExternal( static ResultOrError<Texture*> CreateFromExternal(
Device* device, Device* device,
const ExternalImageDescriptor* descriptor, const ExternalImageDescriptor* descriptor,
const TextureDescriptor* textureDescriptor, const TextureDescriptor* textureDescriptor);
VkSemaphore signalSemaphore,
VkDeviceMemory externalMemoryAllocation,
std::vector<VkSemaphore> waitSemaphores);
Texture(Device* device, const TextureDescriptor* descriptor, VkImage nativeImage); Texture(Device* device, const TextureDescriptor* descriptor, VkImage nativeImage);
~Texture(); ~Texture();
@ -68,14 +65,18 @@ namespace dawn_native { namespace vulkan {
uint32_t layerCount); uint32_t layerCount);
MaybeError SignalAndDestroy(VkSemaphore* outSignalSemaphore); MaybeError SignalAndDestroy(VkSemaphore* outSignalSemaphore);
// Binds externally allocated memory to the VkImage and on success, takes ownership of
// semaphores.
MaybeError BindExternalMemory(const ExternalImageDescriptor* descriptor,
VkSemaphore signalSemaphore,
VkDeviceMemory externalMemoryAllocation,
std::vector<VkSemaphore> waitSemaphores);
private: private:
using TextureBase::TextureBase; using TextureBase::TextureBase;
MaybeError InitializeAsInternalTexture(); MaybeError InitializeAsInternalTexture();
MaybeError InitializeFromExternal(const ExternalImageDescriptor* descriptor,
VkSemaphore signalSemaphore, MaybeError InitializeFromExternal(const ExternalImageDescriptor* descriptor);
VkDeviceMemory externalMemoryAllocation,
std::vector<VkSemaphore> waitSemaphores);
void DestroyImpl() override; void DestroyImpl() override;
MaybeError ClearTexture(CommandRecordingContext* recordingContext, MaybeError ClearTexture(CommandRecordingContext* recordingContext,

View File

@ -17,6 +17,7 @@
#include "common/vulkan_platform.h" #include "common/vulkan_platform.h"
#include "dawn_native/Error.h" #include "dawn_native/Error.h"
#include "dawn_native/VulkanBackend.h"
#include "dawn_native/vulkan/ExternalHandle.h" #include "dawn_native/vulkan/ExternalHandle.h"
namespace dawn_native { namespace vulkan { namespace dawn_native { namespace vulkan {
@ -25,6 +26,11 @@ namespace dawn_native { namespace vulkan {
namespace dawn_native { namespace vulkan { namespace external_memory { namespace dawn_native { namespace vulkan { namespace external_memory {
struct MemoryImportParams {
VkDeviceSize allocationSize;
uint32_t memoryTypeIndex;
};
class Service { class Service {
public: public:
explicit Service(Device* device); explicit Service(Device* device);
@ -37,10 +43,15 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
VkImageUsageFlags usage, VkImageUsageFlags usage,
VkImageCreateFlags flags); VkImageCreateFlags flags);
// Returns the parameters required for importing memory
ResultOrError<MemoryImportParams> GetMemoryImportParams(
const ExternalImageDescriptor* descriptor,
VkImage image);
// Given an external handle pointing to memory, import it into a VkDeviceMemory // Given an external handle pointing to memory, import it into a VkDeviceMemory
ResultOrError<VkDeviceMemory> ImportMemory(ExternalMemoryHandle handle, ResultOrError<VkDeviceMemory> ImportMemory(ExternalMemoryHandle handle,
VkDeviceSize allocationSize, const MemoryImportParams& importParams,
uint32_t memoryTypeIndex); VkImage image);
private: private:
Device* mDevice = nullptr; Device* mDevice = nullptr;

View File

@ -32,10 +32,16 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
return false; return false;
} }
ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
const ExternalImageDescriptor* descriptor,
VkImage image) {
return DAWN_UNIMPLEMENTED_ERROR("Using null memory service to interop inside Vulkan");
}
ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle, ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
VkDeviceSize allocationSize, const MemoryImportParams& importParams,
uint32_t memoryTypeIndex) { VkImage image) {
return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan"); return DAWN_UNIMPLEMENTED_ERROR("Using null memory service to interop inside Vulkan");
} }
}}} // namespace dawn_native::vulkan::external_memory }}} // namespace dawn_native::vulkan::external_memory

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "common/Assert.h"
#include "dawn_native/vulkan/AdapterVk.h" #include "dawn_native/vulkan/AdapterVk.h"
#include "dawn_native/vulkan/BackendVk.h" #include "dawn_native/vulkan/BackendVk.h"
#include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/DeviceVk.h"
@ -79,9 +80,16 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
!(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR); !(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR);
} }
ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
const ExternalImageDescriptor* descriptor,
VkImage image) {
MemoryImportParams params = {descriptor->allocationSize, descriptor->memoryTypeIndex};
return params;
}
ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle, ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
VkDeviceSize allocationSize, const MemoryImportParams& importParams,
uint32_t memoryTypeIndex) { VkImage image) {
if (handle < 0) { if (handle < 0) {
return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle"); return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle");
} }
@ -95,8 +103,8 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
VkMemoryAllocateInfo allocateInfo; VkMemoryAllocateInfo allocateInfo;
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.pNext = &importMemoryFdInfo; allocateInfo.pNext = &importMemoryFdInfo;
allocateInfo.allocationSize = allocationSize; allocateInfo.allocationSize = importParams.allocationSize;
allocateInfo.memoryTypeIndex = memoryTypeIndex; allocateInfo.memoryTypeIndex = importParams.memoryTypeIndex;
VkDeviceMemory allocatedMemory = VK_NULL_HANDLE; VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo, DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo,

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "common/Assert.h"
#include "dawn_native/vulkan/AdapterVk.h" #include "dawn_native/vulkan/AdapterVk.h"
#include "dawn_native/vulkan/BackendVk.h" #include "dawn_native/vulkan/BackendVk.h"
#include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/DeviceVk.h"
@ -79,9 +80,16 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
!(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR); !(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR);
} }
ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
const ExternalImageDescriptor* descriptor,
VkImage image) {
MemoryImportParams params = {descriptor->allocationSize, descriptor->memoryTypeIndex};
return params;
}
ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle, ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
VkDeviceSize allocationSize, const MemoryImportParams& importParams,
uint32_t memoryTypeIndex) { VkImage image) {
if (handle == ZX_HANDLE_INVALID) { if (handle == ZX_HANDLE_INVALID) {
return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle"); return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle");
} }
@ -97,8 +105,8 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
VkMemoryAllocateInfo allocateInfo; VkMemoryAllocateInfo allocateInfo;
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.pNext = &importMemoryHandleInfo; allocateInfo.pNext = &importMemoryHandleInfo;
allocateInfo.allocationSize = allocationSize; allocateInfo.allocationSize = importParams.allocationSize;
allocateInfo.memoryTypeIndex = memoryTypeIndex; allocateInfo.memoryTypeIndex = importParams.memoryTypeIndex;
VkDeviceMemory allocatedMemory = VK_NULL_HANDLE; VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo, DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo,