blob: 005ae0f151125cf2507f995caff6b8fa8705ea6a [file] [log] [blame]
// 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;
};