// 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 "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::ExternalBase { public: static const VkExternalMemoryHandleTypeFlagBits typeFlagBit = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; static bool SupportsAllocateInfo(const VkMemoryAllocateInfo *pAllocateInfo) { OpaqueFdAllocateInfo info(pAllocateInfo); return info.importFd || info.exportFd; } explicit OpaqueFdExternalMemory(const VkMemoryAllocateInfo *pAllocateInfo) : allocateInfo(pAllocateInfo) { } ~OpaqueFdExternalMemory() { if(shm_fd_ >= 0) { ::close(shm_fd_); shm_fd_ = -1; } } VkResult allocate(size_t size, void **pBuffer) 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 && size > 0) { if(::ftruncate(fd, size) < 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, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd_, 0); if(addr == MAP_FAILED) { return VK_ERROR_MEMORY_MAP_FAILED; } *pBuffer = addr; return VK_SUCCESS; } void deallocate(void *buffer, size_t size) override { ::munmap(buffer, size); } 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; };