| // 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, 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; |
| }; |