1// Copyright 2018 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 "VkDeviceMemory.hpp"
16
17#include "VkConfig.h"
18
19namespace vk
20{
21
22// Base abstract interface for a device memory implementation.
23class DeviceMemory::ExternalBase
24{
25public:
26 virtual ~ExternalBase() = default;
27
28 // Allocate the memory according to |size|. On success return VK_SUCCESS
29 // and sets |*pBuffer|.
30 virtual VkResult allocate(size_t size, void** pBuffer) = 0;
31
32 // Deallocate previously allocated memory at |buffer|.
33 virtual void deallocate(void* buffer, size_t size) = 0;
34
35 // Return the handle type flag bit supported by this implementation.
36 // A value of 0 corresponds to non-external memory.
37 virtual VkExternalMemoryHandleTypeFlagBits getFlagBit() const = 0;
38
39#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
40 virtual VkResult exportFd(int* pFd) const
41 {
42 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
43 }
44#endif
45
46protected:
47 ExternalBase() = default;
48};
49
50// Small class describing a given DeviceMemory::ExternalBase derived class.
51// |typeFlagBit| corresponds to the external memory handle type.
52// |instanceSize| is the size of each class instance in bytes.
53// |instanceInit| is a function pointer used to initialize an instance inplace
54// according to a |pAllocateInfo| parameter.
55class ExternalMemoryTraits
56{
57public:
58 VkExternalMemoryHandleTypeFlagBits typeFlagBit;
59 size_t instanceSize;
60 void (*instanceInit)(void* external, const VkMemoryAllocateInfo* pAllocateInfo);
61};
62
63// Template function that parses a |pAllocateInfo.pNext| chain to verify that
64// it asks for the creation or import of a memory type managed by implementation
65// class T. On success, return true and sets |pTraits| accordingly. Otherwise
66// return false.
67template <typename T>
68static bool parseCreateInfo(const VkMemoryAllocateInfo* pAllocateInfo,
69 ExternalMemoryTraits* pTraits)
70{
71 if (T::supportsAllocateInfo(pAllocateInfo))
72 {
73 pTraits->typeFlagBit = T::typeFlagBit;
74 pTraits->instanceSize = sizeof(T);
75 pTraits->instanceInit = [](void* external,
76 const VkMemoryAllocateInfo* pAllocateInfo) {
77 new (external) T(pAllocateInfo);
78 };
79 return true;
80 }
81 return false;
82}
83
84// DeviceMemory::ExternalBase implementation that uses host memory.
85// Not really external, but makes everything simpler.
86class DeviceMemoryHostExternalBase : public DeviceMemory::ExternalBase
87{
88public:
89
90 // Does not support any external memory type at all.
91 static const VkExternalMemoryHandleTypeFlagBits typeFlagBit = (VkExternalMemoryHandleTypeFlagBits)0;
92
93 // Always return true as is used as a fallback in findTraits() below.
94 static bool supportsAllocateInfo(const VkMemoryAllocateInfo* pAllocateInfo)
95 {
96 return true;
97 }
98
99 DeviceMemoryHostExternalBase(const VkMemoryAllocateInfo* pAllocateInfo) {}
100
101 VkResult allocate(size_t size, void** pBuffer) override
102 {
103 void* buffer = vk::allocate(size, REQUIRED_MEMORY_ALIGNMENT, DEVICE_MEMORY);
104 if (!buffer)
105 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
106
107 *pBuffer = buffer;
108 return VK_SUCCESS;
109 }
110
111 void deallocate(void* buffer, size_t size) override
112 {
113 vk::deallocate(buffer, DEVICE_MEMORY);
114 }
115
116 VkExternalMemoryHandleTypeFlagBits getFlagBit() const override
117 {
118 return typeFlagBit;
119 }
120};
121
122} // namespace vk
123
124#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
125#include "VkDeviceMemoryExternalLinux.hpp"
126#endif
127
128namespace vk
129{
130
131static void findTraits(const VkMemoryAllocateInfo* pAllocateInfo,
132 ExternalMemoryTraits* pTraits)
133{
134#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
135 if (parseCreateInfo<LinuxMemfdExternalMemory>(pAllocateInfo, pTraits))
136 {
137 return;
138 }
139#endif
140 parseCreateInfo<DeviceMemoryHostExternalBase>(pAllocateInfo, pTraits);
141}
142
143DeviceMemory::DeviceMemory(const VkMemoryAllocateInfo* pAllocateInfo, void* mem) :
144 size(pAllocateInfo->allocationSize), memoryTypeIndex(pAllocateInfo->memoryTypeIndex),
145 external(reinterpret_cast<ExternalBase *>(mem))
146{
147 ASSERT(size);
148
149 ExternalMemoryTraits traits;
150 findTraits(pAllocateInfo, &traits);
151 traits.instanceInit(external, pAllocateInfo);
152}
153
154void DeviceMemory::destroy(const VkAllocationCallbacks* pAllocator)
155{
156 if (buffer)
157 {
158 external->deallocate(buffer, size);
159 buffer = nullptr;
160 }
161 external->~ExternalBase(); // Call virtual destructor in place.
162 vk::deallocate(external, pAllocator);
163}
164
165size_t DeviceMemory::ComputeRequiredAllocationSize(const VkMemoryAllocateInfo* pAllocateInfo)
166{
167 ExternalMemoryTraits traits;
168 findTraits(pAllocateInfo, &traits);
169 return traits.instanceSize;
170}
171
172VkResult DeviceMemory::allocate()
173{
174 VkResult result = VK_SUCCESS;
175 if (!buffer)
176 {
177 result = external->allocate(size, &buffer);
178 }
179 return result;
180}
181
182VkResult DeviceMemory::map(VkDeviceSize pOffset, VkDeviceSize pSize, void** ppData)
183{
184 *ppData = getOffsetPointer(pOffset);
185
186 return VK_SUCCESS;
187}
188
189VkDeviceSize DeviceMemory::getCommittedMemoryInBytes() const
190{
191 return size;
192}
193
194void* DeviceMemory::getOffsetPointer(VkDeviceSize pOffset) const
195{
196 ASSERT(buffer);
197
198 return reinterpret_cast<char*>(buffer) + pOffset;
199}
200
201bool DeviceMemory::checkExternalMemoryHandleType(
202 VkExternalMemoryHandleTypeFlags supportedHandleTypes) const
203{
204 if (!supportedHandleTypes)
205 {
206 // This image or buffer does not need to be stored on external
207 // memory, so this check should always pass.
208 return true;
209 }
210 VkExternalMemoryHandleTypeFlagBits handle_type_bit = external->getFlagBit();
211 if (!handle_type_bit)
212 {
213 // This device memory is not external and can accomodate
214 // any image or buffer as well.
215 return true;
216 }
217 // Return true only if the external memory type is compatible with the
218 // one specified during VkCreate{Image,Buffer}(), through a
219 // VkExternalMemory{Image,Buffer}AllocateInfo struct.
220 return (supportedHandleTypes & handle_type_bit) != 0;
221}
222
223#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
224VkResult DeviceMemory::exportFd(int* pFd) const
225{
226 return external->exportFd(pFd);
227}
228#endif
229
230} // namespace vk
231