[vulkan] Implement VK_KHR_external_memory_fd for Linux and Android.
This extension allows one to import/export device memory buffers
through shared memory region file descriptors.
This also adds checks to ensure that binding a buffer or image
to an external device memory works only if VkCreate{Buffer,Image}
was called with a VkExternalMemory{Buffer,Image}CreateInfo struct
with compatible handle types.
Test: dEQP-VK.api.external.memory.opaque_fd*
Bug: b/140419396
Change-Id: I5d249685896ae0764bc9d5c635cc3799323db453
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/35152
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: David Turner <digit@google.com>
diff --git a/src/Vulkan/BUILD.gn b/src/Vulkan/BUILD.gn
index 5888f34..bd7d521 100644
--- a/src/Vulkan/BUILD.gn
+++ b/src/Vulkan/BUILD.gn
@@ -91,6 +91,7 @@
]
if (is_linux || is_android) {
sources += [
+ "VkDeviceMemoryExternalLinux.hpp",
"VkSemaphoreExternalLinux.hpp",
]
} else if (is_fuchsia) {
diff --git a/src/Vulkan/VkBuffer.cpp b/src/Vulkan/VkBuffer.cpp
index 7c09a42..f8d1211 100644
--- a/src/Vulkan/VkBuffer.cpp
+++ b/src/Vulkan/VkBuffer.cpp
@@ -31,6 +31,16 @@
queueFamilyIndices = reinterpret_cast<uint32_t*>(mem);
memcpy(queueFamilyIndices, pCreateInfo->pQueueFamilyIndices, sizeof(uint32_t) * queueFamilyIndexCount);
}
+
+ const auto* nextInfo = reinterpret_cast<const VkBaseInStructure*>(pCreateInfo->pNext);
+ for (; nextInfo != nullptr; nextInfo = nextInfo->pNext)
+ {
+ if (nextInfo->sType == VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO)
+ {
+ const auto* externalInfo = reinterpret_cast<const VkExternalMemoryBufferCreateInfo*>(nextInfo);
+ supportedExternalMemoryHandleTypes = externalInfo->handleTypes;
+ }
+ }
}
void Buffer::destroy(const VkAllocationCallbacks* pAllocator)
@@ -68,6 +78,11 @@
return memoryRequirements;
}
+bool Buffer::canBindToMemory(DeviceMemory* pDeviceMemory) const
+{
+ return pDeviceMemory->checkExternalMemoryHandleType(supportedExternalMemoryHandleTypes);
+}
+
void Buffer::bind(DeviceMemory* pDeviceMemory, VkDeviceSize pMemoryOffset)
{
memory = pDeviceMemory->getOffsetPointer(pMemoryOffset);
diff --git a/src/Vulkan/VkBuffer.hpp b/src/Vulkan/VkBuffer.hpp
index 286e0ed..23c42e1 100644
--- a/src/Vulkan/VkBuffer.hpp
+++ b/src/Vulkan/VkBuffer.hpp
@@ -40,6 +40,7 @@
void* getOffsetPointer(VkDeviceSize offset) const;
inline VkDeviceSize getSize() const { return size; }
uint8_t* end() const;
+ bool canBindToMemory(DeviceMemory* pDeviceMemory) const;
private:
void* memory = nullptr;
@@ -49,6 +50,8 @@
VkSharingMode sharingMode = VK_SHARING_MODE_EXCLUSIVE;
uint32_t queueFamilyIndexCount = 0;
uint32_t* queueFamilyIndices = nullptr;
+
+ VkExternalMemoryHandleTypeFlags supportedExternalMemoryHandleTypes = (VkExternalMemoryHandleTypeFlags)0;
};
static inline Buffer* Cast(VkBuffer object)
diff --git a/src/Vulkan/VkConfig.h b/src/Vulkan/VkConfig.h
index ddb8d89..5481d7d 100644
--- a/src/Vulkan/VkConfig.h
+++ b/src/Vulkan/VkConfig.h
@@ -85,6 +85,7 @@
}
#if VK_USE_PLATFORM_XLIB_KHR || VK_USE_PLATFORM_ANDROID_KHR
+#define SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD 1
#define SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD 1
#endif
diff --git a/src/Vulkan/VkDeviceMemory.cpp b/src/Vulkan/VkDeviceMemory.cpp
index 3e9a952..ee56500 100644
--- a/src/Vulkan/VkDeviceMemory.cpp
+++ b/src/Vulkan/VkDeviceMemory.cpp
@@ -19,36 +19,164 @@
namespace vk
{
-DeviceMemory::DeviceMemory(const VkMemoryAllocateInfo* pCreateInfo, void* mem) :
- size(pCreateInfo->allocationSize), memoryTypeIndex(pCreateInfo->memoryTypeIndex)
+// Base abstract interface for a device memory implementation.
+class DeviceMemory::ExternalBase
+{
+public:
+ virtual ~ExternalBase() = default;
+
+ // Allocate the memory according to |size|. On success return VK_SUCCESS
+ // and sets |*pBuffer|.
+ virtual VkResult allocate(size_t size, void** pBuffer) = 0;
+
+ // Deallocate previously allocated memory at |buffer|.
+ virtual void deallocate(void* buffer, size_t size) = 0;
+
+ // Return the handle type flag bit supported by this implementation.
+ // A value of 0 corresponds to non-external memory.
+ virtual VkExternalMemoryHandleTypeFlagBits getFlagBit() const = 0;
+
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+ virtual VkResult exportFd(int* pFd) const
+ {
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
+#endif
+
+protected:
+ ExternalBase() = default;
+};
+
+// Small class describing a given DeviceMemory::ExternalBase derived class.
+// |typeFlagBit| corresponds to the external memory handle type.
+// |instanceSize| is the size of each class instance in bytes.
+// |instanceInit| is a function pointer used to initialize an instance inplace
+// according to a |pAllocateInfo| parameter.
+class ExternalMemoryTraits
+{
+public:
+ VkExternalMemoryHandleTypeFlagBits typeFlagBit;
+ size_t instanceSize;
+ void (*instanceInit)(void* external, const VkMemoryAllocateInfo* pAllocateInfo);
+};
+
+// Template function that parses a |pAllocateInfo.pNext| chain to verify that
+// it asks for the creation or import of a memory type managed by implementation
+// class T. On success, return true and sets |pTraits| accordingly. Otherwise
+// return false.
+template <typename T>
+static bool parseCreateInfo(const VkMemoryAllocateInfo* pAllocateInfo,
+ ExternalMemoryTraits* pTraits)
+{
+ if (T::supportsAllocateInfo(pAllocateInfo))
+ {
+ pTraits->typeFlagBit = T::typeFlagBit;
+ pTraits->instanceSize = sizeof(T);
+ pTraits->instanceInit = [](void* external,
+ const VkMemoryAllocateInfo* pAllocateInfo) {
+ new (external) T(pAllocateInfo);
+ };
+ return true;
+ }
+ return false;
+}
+
+// DeviceMemory::ExternalBase implementation that uses host memory.
+// Not really external, but makes everything simpler.
+class DeviceMemoryHostExternalBase : public DeviceMemory::ExternalBase
+{
+public:
+
+ // Does not support any external memory type at all.
+ static const VkExternalMemoryHandleTypeFlagBits typeFlagBit = (VkExternalMemoryHandleTypeFlagBits)0;
+
+ // Always return true as is used as a fallback in findTraits() below.
+ static bool supportsAllocateInfo(const VkMemoryAllocateInfo* pAllocateInfo)
+ {
+ return true;
+ }
+
+ DeviceMemoryHostExternalBase(const VkMemoryAllocateInfo* pAllocateInfo) {}
+
+ VkResult allocate(size_t size, void** pBuffer) override
+ {
+ void* buffer = vk::allocate(size, REQUIRED_MEMORY_ALIGNMENT, DEVICE_MEMORY);
+ if (!buffer)
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+
+ *pBuffer = buffer;
+ return VK_SUCCESS;
+ }
+
+ void deallocate(void* buffer, size_t size) override
+ {
+ vk::deallocate(buffer, DEVICE_MEMORY);
+ }
+
+ VkExternalMemoryHandleTypeFlagBits getFlagBit() const override
+ {
+ return typeFlagBit;
+ }
+};
+
+} // namespace vk
+
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+#include "VkDeviceMemoryExternalLinux.hpp"
+#endif
+
+namespace vk
+{
+
+static void findTraits(const VkMemoryAllocateInfo* pAllocateInfo,
+ ExternalMemoryTraits* pTraits)
+{
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+ if (parseCreateInfo<LinuxMemfdExternalMemory>(pAllocateInfo, pTraits))
+ {
+ return;
+ }
+#endif
+ parseCreateInfo<DeviceMemoryHostExternalBase>(pAllocateInfo, pTraits);
+}
+
+DeviceMemory::DeviceMemory(const VkMemoryAllocateInfo* pAllocateInfo, void* mem) :
+ size(pAllocateInfo->allocationSize), memoryTypeIndex(pAllocateInfo->memoryTypeIndex),
+ external(reinterpret_cast<ExternalBase *>(mem))
{
ASSERT(size);
+
+ ExternalMemoryTraits traits;
+ findTraits(pAllocateInfo, &traits);
+ traits.instanceInit(external, pAllocateInfo);
}
void DeviceMemory::destroy(const VkAllocationCallbacks* pAllocator)
{
- vk::deallocate(buffer, DEVICE_MEMORY);
+ if (buffer)
+ {
+ external->deallocate(buffer, size);
+ buffer = nullptr;
+ }
+ external->~ExternalBase(); // Call virtual destructor in place.
+ vk::deallocate(external, pAllocator);
}
-size_t DeviceMemory::ComputeRequiredAllocationSize(const VkMemoryAllocateInfo* pCreateInfo)
+size_t DeviceMemory::ComputeRequiredAllocationSize(const VkMemoryAllocateInfo* pAllocateInfo)
{
- // buffer is "GPU memory", so we use device memory for it
- return 0;
+ ExternalMemoryTraits traits;
+ findTraits(pAllocateInfo, &traits);
+ return traits.instanceSize;
}
VkResult DeviceMemory::allocate()
{
- if(!buffer)
+ VkResult result = VK_SUCCESS;
+ if (!buffer)
{
- buffer = vk::allocate(size, REQUIRED_MEMORY_ALIGNMENT, DEVICE_MEMORY);
+ result = external->allocate(size, &buffer);
}
-
- if(!buffer)
- {
- return VK_ERROR_OUT_OF_DEVICE_MEMORY;
- }
-
- return VK_SUCCESS;
+ return result;
}
VkResult DeviceMemory::map(VkDeviceSize pOffset, VkDeviceSize pSize, void** ppData)
@@ -70,4 +198,33 @@
return reinterpret_cast<char*>(buffer) + pOffset;
}
+bool DeviceMemory::checkExternalMemoryHandleType(
+ VkExternalMemoryHandleTypeFlags supportedHandleTypes) const
+{
+ if (!supportedHandleTypes)
+ {
+ // This image or buffer does not need to be stored on external
+ // memory, so this check should always pass.
+ return true;
+ }
+ VkExternalMemoryHandleTypeFlagBits handle_type_bit = external->getFlagBit();
+ if (!handle_type_bit)
+ {
+ // This device memory is not external and can accomodate
+ // any image or buffer as well.
+ return true;
+ }
+ // Return true only if the external memory type is compatible with the
+ // one specified during VkCreate{Image,Buffer}(), through a
+ // VkExternalMemory{Image,Buffer}AllocateInfo struct.
+ return (supportedHandleTypes & handle_type_bit) != 0;
+}
+
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+VkResult DeviceMemory::exportFd(int* pFd) const
+{
+ return external->exportFd(pFd);
+}
+#endif
+
} // namespace vk
diff --git a/src/Vulkan/VkDeviceMemory.hpp b/src/Vulkan/VkDeviceMemory.hpp
index f4eb05f..e63cb7e 100644
--- a/src/Vulkan/VkDeviceMemory.hpp
+++ b/src/Vulkan/VkDeviceMemory.hpp
@@ -15,6 +15,7 @@
#ifndef VK_DEVICE_MEMORY_HPP_
#define VK_DEVICE_MEMORY_HPP_
+#include "VkConfig.h"
#include "VkObject.hpp"
namespace vk
@@ -27,6 +28,10 @@
static size_t ComputeRequiredAllocationSize(const VkMemoryAllocateInfo* pCreateInfo);
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+ VkResult exportFd(int* pFd) const;
+#endif
+
void destroy(const VkAllocationCallbacks* pAllocator);
VkResult allocate();
VkResult map(VkDeviceSize offset, VkDeviceSize size, void** ppData);
@@ -34,10 +39,20 @@
void* getOffsetPointer(VkDeviceSize pOffset) const;
uint32_t getMemoryTypeIndex() const { return memoryTypeIndex; }
+ // If this is external memory, return true iff its handle type matches the bitmask
+ // provided by |supportedExternalHandleTypes|. Otherwise, always return true.
+ bool checkExternalMemoryHandleType(
+ VkExternalMemoryHandleTypeFlags supportedExternalMemoryHandleType) const;
+
+ // Internal implementation class for external memory. Platform-specific.
+ class ExternalBase;
+
private:
- void* buffer = nullptr;
- VkDeviceSize size = 0;
- uint32_t memoryTypeIndex = 0;
+
+ void* buffer = nullptr;
+ VkDeviceSize size = 0;
+ uint32_t memoryTypeIndex = 0;
+ ExternalBase* external = nullptr;
};
static inline DeviceMemory* Cast(VkDeviceMemory object)
diff --git a/src/Vulkan/VkDeviceMemoryExternalLinux.hpp b/src/Vulkan/VkDeviceMemoryExternalLinux.hpp
new file mode 100644
index 0000000..6c4fb5f
--- /dev/null
+++ b/src/Vulkan/VkDeviceMemoryExternalLinux.hpp
@@ -0,0 +1,149 @@
+// Copyright 2019 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.
+
+#include "VkDebug.hpp"
+#include "System/Linux/MemFd.hpp"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+
+class LinuxMemfdExternalMemory : public vk::DeviceMemory::ExternalBase
+{
+public:
+ // Helper struct to parse the VkMemoryAllocateInfo.pNext chain and
+ // extract relevant information related to the handle type supported
+ // by this DeviceMemory;:ExternalBase subclass.
+ struct AllocateInfo
+ {
+ bool importFd = false;
+ bool exportFd = false;
+ int fd = -1;
+
+ AllocateInfo() = default;
+
+ // Parse the VkMemoryAllocateInfo.pNext chain to initialize an AllocateInfo.
+ AllocateInfo(const VkMemoryAllocateInfo* pAllocateInfo)
+ {
+ const auto* createInfo = reinterpret_cast<const VkBaseInStructure*>(pAllocateInfo->pNext);
+ while (createInfo)
+ {
+ switch (createInfo->sType)
+ {
+ case VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR:
+ {
+ const auto* importInfo = reinterpret_cast<const VkImportMemoryFdInfoKHR*>(createInfo);
+
+ if (importInfo->handleType != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+ {
+ UNIMPLEMENTED("importInfo->handleType");
+ }
+ importFd = true;
+ fd = importInfo->fd;
+ }
+ 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_OPAQUE_FD_BIT)
+ {
+ UNIMPLEMENTED("exportInfo->handleTypes");
+ }
+ exportFd = true;
+ }
+ break;
+
+ default:
+ ;
+ }
+ createInfo = createInfo->pNext;
+ }
+ }
+ };
+
+ static const VkExternalMemoryHandleTypeFlagBits typeFlagBit = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+
+ static bool supportsAllocateInfo(const VkMemoryAllocateInfo* pAllocateInfo)
+ {
+ AllocateInfo info(pAllocateInfo);
+ return info.importFd || info.exportFd;
+ }
+
+ explicit LinuxMemfdExternalMemory(const VkMemoryAllocateInfo* pAllocateInfo)
+ : allocateInfo(pAllocateInfo)
+ {
+ }
+
+ ~LinuxMemfdExternalMemory()
+ {
+ memfd.close();
+ }
+
+ VkResult allocate(size_t size, void** pBuffer) override
+ {
+ if (allocateInfo.importFd)
+ {
+ memfd.importFd(allocateInfo.fd);
+ if (!memfd.isValid())
+ {
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
+ }
+ else
+ {
+ ASSERT(allocateInfo.exportFd);
+ static int counter = 0;
+ char name[40];
+ snprintf(name, sizeof(name), "SwiftShader.Memory.%d", ++counter);
+ if (!memfd.allocate(name, size))
+ {
+ TRACE("memfd.allocate() returned %s", strerror(errno));
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ }
+ void* addr = memfd.mapReadWrite(0, size);
+ if (!addr)
+ {
+ return VK_ERROR_MEMORY_MAP_FAILED;
+ }
+ *pBuffer = addr;
+ return VK_SUCCESS;
+ }
+
+ void deallocate(void* buffer, size_t size) override
+ {
+ memfd.unmap(buffer, size);
+ }
+
+ VkExternalMemoryHandleTypeFlagBits getFlagBit() const override
+ {
+ return typeFlagBit;
+ }
+
+ VkResult exportFd(int* pFd) const override
+ {
+ int fd = memfd.exportFd();
+ if (fd < 0)
+ {
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
+ *pFd = fd;
+ return VK_SUCCESS;
+ }
+
+private:
+ LinuxMemFd memfd;
+ AllocateInfo allocateInfo;
+};
diff --git a/src/Vulkan/VkGetProcAddress.cpp b/src/Vulkan/VkGetProcAddress.cpp
index 636995b..e17aa75 100644
--- a/src/Vulkan/VkGetProcAddress.cpp
+++ b/src/Vulkan/VkGetProcAddress.cpp
@@ -361,6 +361,17 @@
}
},
#endif
+
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+ // VK_KHR_external_memory_fd
+ {
+ VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
+ {
+ MAKE_VULKAN_DEVICE_ENTRY(vkGetMemoryFdKHR),
+ MAKE_VULKAN_DEVICE_ENTRY(vkGetMemoryFdPropertiesKHR),
+ }
+ },
+#endif
};
#undef MAKE_VULKAN_DEVICE_ENTRY
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index bda8677..7379839 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -75,6 +75,16 @@
compressedImageCreateInfo.format = format.getDecompressedFormat();
decompressedImage = new (mem) Image(&compressedImageCreateInfo, nullptr, device);
}
+
+ const auto* nextInfo = reinterpret_cast<const VkBaseInStructure*>(pCreateInfo->pNext);
+ for (; nextInfo != nullptr; nextInfo = nextInfo->pNext)
+ {
+ if (nextInfo->sType == VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO)
+ {
+ const auto* externalInfo = reinterpret_cast<const VkExternalMemoryImageCreateInfo*>(nextInfo);
+ supportedExternalMemoryHandleTypes = externalInfo->handleTypes;
+ }
+ }
}
void Image::destroy(const VkAllocationCallbacks* pAllocator)
@@ -100,6 +110,11 @@
return memoryRequirements;
}
+bool Image::canBindToMemory(DeviceMemory* pDeviceMemory) const
+{
+ return pDeviceMemory->checkExternalMemoryHandleType(supportedExternalMemoryHandleTypes);
+}
+
void Image::bind(DeviceMemory* pDeviceMemory, VkDeviceSize pMemoryOffset)
{
deviceMemory = pDeviceMemory;
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index 7d1cf7c..bed9752 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -81,6 +81,7 @@
bool is3DSlice() const;
uint8_t* end() const;
VkDeviceSize getLayerSize(VkImageAspectFlagBits aspect) const;
+ bool canBindToMemory(DeviceMemory* pDeviceMemory) const;
void prepareForSampling(const VkImageSubresourceRange& subresourceRange);
const Image* getSampledImage(const vk::Format& imageViewFormat) const;
@@ -125,6 +126,8 @@
#ifdef __ANDROID__
BackingMemory backingMemory = {};
#endif
+
+ VkExternalMemoryHandleTypeFlags supportedExternalMemoryHandleTypes = (VkExternalMemoryHandleTypeFlags)0;
};
static inline Image* Cast(VkImage object)
@@ -134,4 +137,4 @@
} // namespace vk
-#endif // VK_IMAGE_HPP_
\ No newline at end of file
+#endif // VK_IMAGE_HPP_
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index d61b413..0598a81 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -23,6 +23,22 @@
namespace vk
{
+static void setExternalMemoryProperties(VkExternalMemoryHandleTypeFlagBits handleType, VkExternalMemoryProperties* properties)
+{
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+ if (handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+ {
+ properties->compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+ properties->exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+ properties->externalMemoryFeatures = VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT;
+ return;
+ }
+#endif
+ properties->compatibleHandleTypes = 0;
+ properties->exportFromImportedHandleTypes = 0;
+ properties->externalMemoryFeatures = 0;
+}
+
PhysicalDevice::PhysicalDevice(const void*, void* mem)
{
}
@@ -339,9 +355,7 @@
void PhysicalDevice::getProperties(const VkExternalMemoryHandleTypeFlagBits* handleType, VkExternalImageFormatProperties* properties) const
{
- properties->externalMemoryProperties.compatibleHandleTypes = 0;
- properties->externalMemoryProperties.exportFromImportedHandleTypes = 0;
- properties->externalMemoryProperties.externalMemoryFeatures = 0;
+ setExternalMemoryProperties(*handleType, &properties->externalMemoryProperties);
}
void PhysicalDevice::getProperties(VkSamplerYcbcrConversionImageFormatProperties* properties) const
@@ -358,9 +372,7 @@
void PhysicalDevice::getProperties(const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties) const
{
- pExternalBufferProperties->externalMemoryProperties.compatibleHandleTypes = 0;
- pExternalBufferProperties->externalMemoryProperties.exportFromImportedHandleTypes = 0;
- pExternalBufferProperties->externalMemoryProperties.externalMemoryFeatures = 0;
+ setExternalMemoryProperties(pExternalBufferInfo->handleType, &pExternalBufferProperties->externalMemoryProperties);
}
void PhysicalDevice::getProperties(const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties) const
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index bee8173..16784ce 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -250,6 +250,9 @@
#if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
{ VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION },
#endif
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+ { VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, VK_KHR_EXTERNAL_MEMORY_FD_SPEC_VERSION },
+#endif
#if SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
{ VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME, VK_FUCHSIA_EXTERNAL_SEMAPHORE_SPEC_VERSION },
#endif
@@ -796,8 +799,30 @@
// This extension controls on which physical devices the memory gets allocated.
// SwiftShader only has a single physical device, so this extension does nothing in this case.
break;
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+ case VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR:
+ {
+ auto* importInfo = reinterpret_cast<const VkImportMemoryFdInfoKHR *>(allocationInfo);
+ if (importInfo->handleType != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+ {
+ UNSUPPORTED("importInfo->handleType %u", importInfo->handleType);
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
+ break;
+ }
+ case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO:
+ {
+ auto* exportInfo = reinterpret_cast<const VkExportMemoryAllocateInfo *>(allocationInfo);
+ if (exportInfo->handleTypes != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+ {
+ UNSUPPORTED("exportInfo->handleTypes %u", exportInfo->handleTypes);
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
+ break;
+ }
+#endif
default:
- UNIMPLEMENTED("allocationInfo->sType");
+ UNIMPLEMENTED("allocationInfo->sType %u", allocationInfo->sType);
break;
}
@@ -829,6 +854,46 @@
vk::destroy(memory, pAllocator);
}
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdKHR(VkDevice device, const VkMemoryGetFdInfoKHR* getFdInfo, int* pFd)
+{
+ TRACE("(VkDevice device = %p, const VkMemoryGetFdInfoKHR* getFdInfo = %p, int* pFd = %p",
+ device, getFdInfo, pFd);
+
+ if (getFdInfo->handleType != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+ {
+ UNSUPPORTED("pGetFdInfo->handleType %u", getFdInfo->handleType);
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
+ return vk::Cast(getFdInfo->memory)->exportFd(pFd);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdPropertiesKHR(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, int fd, VkMemoryFdPropertiesKHR* pMemoryFdProperties)
+{
+ TRACE("(VkDevice device = %p, VkExternalMemoryHandleTypeFlagBits handleType = %x, int fd = %d, VkMemoryFdPropertiesKHR* pMemoryFdProperties = %p)",
+ device, handleType, fd, pMemoryFdProperties);
+
+ if (handleType != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+ {
+ UNSUPPORTED("handleType %u", handleType);
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
+
+ if (fd < 0)
+ {
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
+
+ const VkPhysicalDeviceMemoryProperties& memoryProperties =
+ vk::Cast(device)->getPhysicalDevice()->getMemoryProperties();
+
+ // All SwiftShader memory types support this!
+ pMemoryFdProperties->memoryTypeBits = (1U << memoryProperties.memoryTypeCount) - 1U;
+
+ return VK_SUCCESS;
+}
+#endif // SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+
VKAPI_ATTR VkResult VKAPI_CALL vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData)
{
TRACE("(VkDevice device = %p, VkDeviceMemory memory = %p, VkDeviceSize offset = %d, VkDeviceSize size = %d, VkMemoryMapFlags flags = %d, void** ppData = %p)",
@@ -886,8 +951,12 @@
TRACE("(VkDevice device = %p, VkBuffer buffer = %p, VkDeviceMemory memory = %p, VkDeviceSize memoryOffset = %d)",
device, static_cast<void*>(buffer), static_cast<void*>(memory), int(memoryOffset));
+ if (!vk::Cast(buffer)->canBindToMemory(vk::Cast(memory)))
+ {
+ UNSUPPORTED("vkBindBufferMemory with invalid external memory");
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
vk::Cast(buffer)->bind(vk::Cast(memory), memoryOffset);
-
return VK_SUCCESS;
}
@@ -896,8 +965,12 @@
TRACE("(VkDevice device = %p, VkImage image = %p, VkDeviceMemory memory = %p, VkDeviceSize memoryOffset = %d)",
device, static_cast<void*>(image), static_cast<void*>(memory), int(memoryOffset));
+ if (!vk::Cast(image)->canBindToMemory(vk::Cast(memory)))
+ {
+ UNSUPPORTED("vkBindImageMemory with invalid external memory");
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
vk::Cast(image)->bind(vk::Cast(memory), memoryOffset);
-
return VK_SUCCESS;
}
@@ -1158,9 +1231,18 @@
TRACE("(VkDevice device = %p, const VkBufferCreateInfo* pCreateInfo = %p, const VkAllocationCallbacks* pAllocator = %p, VkBuffer* pBuffer = %p)",
device, pCreateInfo, pAllocator, pBuffer);
- if(pCreateInfo->pNext)
+ auto* nextInfo = reinterpret_cast<const VkBaseInStructure*>(pCreateInfo->pNext);
+ while (nextInfo)
{
- UNIMPLEMENTED("pCreateInfo->pNext");
+ switch (nextInfo->sType)
+ {
+ case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO:
+ // Do nothing. Should be handled by vk::Buffer::Create().
+ break;
+ default:
+ UNIMPLEMENTED("pCreateInfo->pNext sType=0x%X", nextInfo->sType);
+ }
+ nextInfo = nextInfo->pNext;
}
return vk::Buffer::Create(pAllocator, pCreateInfo, pBuffer);
@@ -1227,6 +1309,9 @@
}
break;
#endif
+ case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO:
+ // Do nothing. Should be handled by vk::Image::Create()
+ break;
case VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR:
/* Do nothing. We don't actually need the swapchain handle yet; we'll do all the work in vkBindImageMemory2. */
break;
@@ -1279,8 +1364,6 @@
TRACE("(VkDevice device = %p, VkImage image = %p, const VkAllocationCallbacks* pAllocator = %p)",
device, static_cast<void*>(image), pAllocator);
- vk::destroy(image, pAllocator);
-
#ifdef __ANDROID__
vk::Image* img = vk::Cast(image);
if(img && img->hasExternalMemory())
@@ -1288,6 +1371,8 @@
vk::destroy(img->getExternalMemory(), pAllocator);
}
#endif
+
+ vk::destroy(image, pAllocator);
}
VKAPI_ATTR void VKAPI_CALL vkGetImageSubresourceLayout(VkDevice device, VkImage image, const VkImageSubresource* pSubresource, VkSubresourceLayout* pLayout)
@@ -2249,6 +2334,15 @@
UNIMPLEMENTED("pBindInfos[%d].pNext", i);
}
+ if (!vk::Cast(pBindInfos[i].buffer)->canBindToMemory(vk::Cast(pBindInfos[i].memory)))
+ {
+ UNSUPPORTED("vkBindBufferMemory2 with invalid external memory");
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
+ }
+
+ for (uint32_t i = 0; i < bindInfoCount; i++)
+ {
vk::Cast(pBindInfos[i].buffer)->bind(vk::Cast(pBindInfos[i].memory), pBindInfos[i].memoryOffset);
}
@@ -2262,6 +2356,15 @@
for(uint32_t i = 0; i < bindInfoCount; i++)
{
+ if (!vk::Cast(pBindInfos[i].image)->canBindToMemory(vk::Cast(pBindInfos[i].memory)))
+ {
+ UNSUPPORTED("vkBindImageMemory2 with invalid external memory");
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ }
+
+ for(uint32_t i = 0; i < bindInfoCount; i++)
+ {
vk::DeviceMemory *memory = vk::Cast(pBindInfos[i].memory);
VkDeviceSize offset = pBindInfos[i].memoryOffset;