1 // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "VkStringify.hpp"
16 
17 #include "System/Debug.hpp"
18 #include "System/Linux/MemFd.hpp"
19 
20 #include <errno.h>
21 #include <string.h>
22 #include <sys/mman.h>
23 
24 class OpaqueFdExternalMemory : public vk::DeviceMemory::ExternalBase
25 {
26 public:
27 	// Helper struct to parse the VkMemoryAllocateInfo.pNext chain and
28 	// extract relevant information related to the handle type supported
29 	// by this DeviceMemory;:ExternalBase subclass.
30 	struct AllocateInfo
31 	{
32 		bool importFd = false;
33 		bool exportFd = false;
34 		int fd = -1;
35 
36 		AllocateInfo() = default;
37 
38 		// Parse the VkMemoryAllocateInfo.pNext chain to initialize an AllocateInfo.
AllocateInfoOpaqueFdExternalMemory::AllocateInfo39 		AllocateInfo(const VkMemoryAllocateInfo *pAllocateInfo)
40 		{
41 			const auto *createInfo = reinterpret_cast<const VkBaseInStructure *>(pAllocateInfo->pNext);
42 			while(createInfo)
43 			{
44 				switch(createInfo->sType)
45 				{
46 					case VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR:
47 					{
48 						const auto *importInfo = reinterpret_cast<const VkImportMemoryFdInfoKHR *>(createInfo);
49 
50 						if(importInfo->handleType != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
51 						{
52 							UNSUPPORTED("VkImportMemoryFdInfoKHR::handleType %d", int(importInfo->handleType));
53 						}
54 						importFd = true;
55 						fd = importInfo->fd;
56 					}
57 					break;
58 					case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO:
59 					{
60 						const auto *exportInfo = reinterpret_cast<const VkExportMemoryAllocateInfo *>(createInfo);
61 
62 						if(exportInfo->handleTypes != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
63 						{
64 							UNSUPPORTED("VkExportMemoryAllocateInfo::handleTypes %d", int(exportInfo->handleTypes));
65 						}
66 						exportFd = true;
67 					}
68 					break;
69 					case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO:
70 						// This can safely be ignored, as the Vulkan spec mentions:
71 						// "If the pNext chain includes a VkMemoryDedicatedAllocateInfo structure, then that structure
72 						//  includes a handle of the sole buffer or image resource that the memory *can* be bound to."
73 						break;
74 
75 					default:
76 						WARN("VkMemoryAllocateInfo->pNext sType = %s", vk::Stringify(createInfo->sType).c_str());
77 				}
78 				createInfo = createInfo->pNext;
79 			}
80 		}
81 	};
82 
83 	static const VkExternalMemoryHandleTypeFlagBits typeFlagBit = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
84 
SupportsAllocateInfo(const VkMemoryAllocateInfo * pAllocateInfo)85 	static bool SupportsAllocateInfo(const VkMemoryAllocateInfo *pAllocateInfo)
86 	{
87 		AllocateInfo info(pAllocateInfo);
88 		return info.importFd || info.exportFd;
89 	}
90 
OpaqueFdExternalMemory(const VkMemoryAllocateInfo * pAllocateInfo)91 	explicit OpaqueFdExternalMemory(const VkMemoryAllocateInfo *pAllocateInfo)
92 	    : allocateInfo(pAllocateInfo)
93 	{
94 	}
95 
~OpaqueFdExternalMemory()96 	~OpaqueFdExternalMemory()
97 	{
98 		memfd.close();
99 	}
100 
allocate(size_t size,void ** pBuffer)101 	VkResult allocate(size_t size, void **pBuffer) override
102 	{
103 		if(allocateInfo.importFd)
104 		{
105 			memfd.importFd(allocateInfo.fd);
106 			if(!memfd.isValid())
107 			{
108 				return VK_ERROR_INVALID_EXTERNAL_HANDLE;
109 			}
110 		}
111 		else
112 		{
113 			ASSERT(allocateInfo.exportFd);
114 			static int counter = 0;
115 			char name[40];
116 			snprintf(name, sizeof(name), "SwiftShader.Memory.%d", ++counter);
117 			if(!memfd.allocate(name, size))
118 			{
119 				TRACE("memfd.allocate() returned %s", strerror(errno));
120 				return VK_ERROR_OUT_OF_DEVICE_MEMORY;
121 			}
122 		}
123 		void *addr = memfd.mapReadWrite(0, size);
124 		if(!addr)
125 		{
126 			return VK_ERROR_MEMORY_MAP_FAILED;
127 		}
128 		*pBuffer = addr;
129 		return VK_SUCCESS;
130 	}
131 
deallocate(void * buffer,size_t size)132 	void deallocate(void *buffer, size_t size) override
133 	{
134 		memfd.unmap(buffer, size);
135 	}
136 
getFlagBit() const137 	VkExternalMemoryHandleTypeFlagBits getFlagBit() const override
138 	{
139 		return typeFlagBit;
140 	}
141 
exportFd(int * pFd) const142 	VkResult exportFd(int *pFd) const override
143 	{
144 		int fd = memfd.exportFd();
145 		if(fd < 0)
146 		{
147 			return VK_ERROR_INVALID_EXTERNAL_HANDLE;
148 		}
149 		*pFd = fd;
150 		return VK_SUCCESS;
151 	}
152 
153 private:
154 	LinuxMemFd memfd;
155 	AllocateInfo allocateInfo;
156 };
157