| // 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 "VkDeviceMemory.hpp" | |
| #include "System/Debug.hpp" | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <string.h> | |
| #include <sys/mman.h> | |
| #include <unistd.h> | |
| #ifndef __APPLE__ | |
| # error "This file is for macOS only!" | |
| #endif // __APPLE__ | |
| #if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_12 | |
| # include <mach/mach_time.h> | |
| #endif // __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_12 | |
| namespace { | |
| struct timespec GetTime() | |
| { | |
| struct timespec tv; | |
| #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_12 | |
| clock_gettime(CLOCK_REALTIME, &tv); | |
| #else | |
| mach_timebase_info_data_t timebase; | |
| mach_timebase_info(&timebase); | |
| uint64_t time; | |
| time = mach_absolute_time(); | |
| double convert_ratio = (double)timebase.numer / (double)timebase.denom; | |
| uint64_t secs = (uint64_t)((double)time * convert_ratio / 1e-9); | |
| uint64_t usecs = (uint64_t)((double)time * convert_ratio - secs * 1e9); | |
| tv.tv_sec = secs; | |
| tv.tv_nsec = usecs; | |
| #endif | |
| return tv; | |
| } | |
| } // namespace | |
| // An implementation of OpaqueFdExternalMemory that relies on shm_open(). | |
| // Useful on OS X which do not have Linux memfd regions. | |
| class OpaqueFdExternalMemory : public vk::DeviceMemory, public vk::ObjectBase<OpaqueFdExternalMemory, VkDeviceMemory> | |
| { | |
| public: | |
| static const VkExternalMemoryHandleTypeFlagBits typeFlagBit = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; | |
| static bool SupportsAllocateInfo(const vk::DeviceMemory::ExtendedAllocationInfo &extendedAllocationInfo) | |
| { | |
| OpaqueFdAllocateInfo info(extendedAllocationInfo); | |
| return info.importFd || info.exportFd; | |
| } | |
| explicit OpaqueFdExternalMemory(const VkMemoryAllocateInfo *pCreateInfo, void *mem, const vk::DeviceMemory::ExtendedAllocationInfo &extendedAllocationInfo, vk::Device *pDevice) | |
| : vk::DeviceMemory(pCreateInfo, extendedAllocationInfo, pDevice) | |
| , allocateInfo(extendedAllocationInfo) | |
| { | |
| } | |
| ~OpaqueFdExternalMemory() | |
| { | |
| if(shm_fd_ >= 0) | |
| { | |
| ::close(shm_fd_); | |
| shm_fd_ = -1; | |
| } | |
| } | |
| VkResult allocateBuffer() override | |
| { | |
| if(allocateInfo.importFd) | |
| { | |
| shm_fd_ = allocateInfo.fd; | |
| if(shm_fd_ < 0) | |
| { | |
| return VK_ERROR_INVALID_EXTERNAL_HANDLE; | |
| } | |
| } | |
| else | |
| { | |
| ASSERT(allocateInfo.exportFd); | |
| // Create shared memory region with shm_open() and a randomly-generated region name. | |
| static const char kPrefix[] = "/SwiftShader-"; | |
| const size_t kPrefixSize = sizeof(kPrefix) - 1; | |
| const size_t kRandomSize = 8; | |
| char name[kPrefixSize + kRandomSize + 1u]; | |
| memcpy(name, kPrefix, kPrefixSize); | |
| int fd = -1; | |
| for(int tries = 0; tries < 6; ++tries) | |
| { | |
| struct timespec tv = GetTime(); | |
| uint64_t r = (uint64_t)tv.tv_sec + (uint64_t)tv.tv_nsec; | |
| for(size_t pos = 0; pos < kRandomSize; ++pos, r /= 8) | |
| { | |
| name[kPrefixSize + pos] = '0' + (r % 8); | |
| } | |
| name[kPrefixSize + kRandomSize] = '\0'; | |
| fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600); | |
| if(fd >= 0) | |
| break; | |
| if(errno != EEXIST) | |
| { | |
| TRACE("shm_open() failed with: %s", strerror(errno)); | |
| break; | |
| } | |
| } | |
| // Unlink the name since it's not needed anymore. | |
| if(fd >= 0) | |
| { | |
| if(shm_unlink(name) == -1) | |
| { | |
| TRACE("shm_unlink() failed with: %s", strerror(errno)); | |
| close(fd); | |
| fd = -1; | |
| } | |
| } | |
| // Ensure there is enough space. | |
| if(fd >= 0 && allocationSize > 0) | |
| { | |
| if(::ftruncate(fd, allocationSize) < 0) | |
| { | |
| TRACE("ftruncate() failed with: %s", strerror(errno)); | |
| close(fd); | |
| fd = -1; | |
| } | |
| } | |
| if(fd < 0) | |
| { | |
| TRACE("Could not allocate shared memory region"); | |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; | |
| } | |
| shm_fd_ = fd; | |
| } | |
| void *addr = ::mmap(nullptr, allocationSize, PROT_READ | PROT_WRITE, MAP_SHARED, | |
| shm_fd_, 0); | |
| if(addr == MAP_FAILED) | |
| { | |
| return VK_ERROR_MEMORY_MAP_FAILED; | |
| } | |
| buffer = addr; | |
| return VK_SUCCESS; | |
| } | |
| void freeBuffer() override | |
| { | |
| ::munmap(buffer, allocationSize); | |
| } | |
| VkExternalMemoryHandleTypeFlagBits getFlagBit() const override | |
| { | |
| return typeFlagBit; | |
| } | |
| VkResult exportFd(int *pFd) const override | |
| { | |
| int fd = dup(shm_fd_); | |
| if(fd < 0) | |
| { | |
| return VK_ERROR_INVALID_EXTERNAL_HANDLE; | |
| } | |
| // Set the clo-on-exec flag. | |
| int flags = ::fcntl(fd, F_GETFD); | |
| ::fcntl(fd, F_SETFL, flags | FD_CLOEXEC); | |
| *pFd = fd; | |
| return VK_SUCCESS; | |
| } | |
| private: | |
| int shm_fd_ = -1; | |
| OpaqueFdAllocateInfo allocateInfo; | |
| }; |