blob: 31eb5fd266b4d0a47fac778bb09d79235aaabcde [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 "VkSemaphore.hpp"
#include "VkConfig.h"
#if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
# if defined(__linux__) || defined(__ANDROID__)
# include "VkSemaphoreExternalLinux.hpp"
# else
# error "Missing VK_KHR_external_semaphore_fd implementation for this platform!"
# endif
#elif VK_USE_PLATFORM_FUCHSIA
# include "VkSemaphoreExternalFuchsia.hpp"
#else
# include "VkSemaphoreExternalNone.hpp"
#endif
#include "marl/blockingcall.h"
#include "marl/conditionvariable.h"
#include <functional>
#include <memory>
#include <utility>
namespace vk {
namespace {
struct SemaphoreCreateInfo
{
bool exportSemaphore = false;
// Create a new instance. The external instance will be allocated only
// the pCreateInfo->pNext chain indicates it needs to be exported.
SemaphoreCreateInfo(const VkSemaphoreCreateInfo *pCreateInfo)
{
for(const auto *nextInfo = reinterpret_cast<const VkBaseInStructure *>(pCreateInfo->pNext);
nextInfo != nullptr; nextInfo = nextInfo->pNext)
{
if(nextInfo->sType == VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO)
{
const auto *exportInfo = reinterpret_cast<const VkExportSemaphoreCreateInfo *>(nextInfo);
exportSemaphore = true;
if(exportInfo->handleTypes != Semaphore::External::kExternalSemaphoreHandleType)
{
UNIMPLEMENTED("exportInfo->handleTypes");
}
break;
}
}
}
};
} // namespace
void Semaphore::wait()
{
if(external)
{
if(!external->tryWait())
{
// Dispatch the external wait to a background thread.
// Even if this creates a new thread on each
// call, it is assumed that this is negligible
// compared with the actual semaphore wait()
// operation.
marl::blocking_call([this]() {
external->wait();
});
}
// If the import was temporary, reset the semaphore to its
// permanent state by getting rid of |external|.
// See "6.4.5. Importing Semaphore Payloads" in Vulkan 1.1 spec.
if(temporaryImport)
{
deallocateExternal();
temporaryImport = false;
}
}
else
{
internal.wait();
}
}
void Semaphore::signal()
{
if(external)
{
// Assumes that signalling an external semaphore is non-blocking,
// so it can be performed directly either from a fiber or thread.
external->signal();
}
else
{
internal.signal();
}
}
Semaphore::Semaphore(const VkSemaphoreCreateInfo *pCreateInfo, void *mem, const VkAllocationCallbacks *pAllocator)
: allocator(pAllocator)
{
SemaphoreCreateInfo info(pCreateInfo);
if(info.exportSemaphore)
{
allocateExternal();
external->init();
}
}
void Semaphore::destroy(const VkAllocationCallbacks *pAllocator)
{
deallocateExternal();
}
size_t Semaphore::ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo)
{
// Semaphore::External instance is created and destroyed on demand so return 0 here.
return 0;
}
void Semaphore::allocateExternal()
{
ASSERT(external == nullptr);
external = reinterpret_cast<Semaphore::External *>(
vk::allocate(sizeof(Semaphore::External), vk::REQUIRED_MEMORY_ALIGNMENT, allocator));
new(external) Semaphore::External();
}
void Semaphore::deallocateExternal()
{
if(external)
{
vk::deallocate(external, allocator);
external = nullptr;
}
}
#if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
VkResult Semaphore::importFd(int fd, bool tempImport)
{
std::unique_lock<std::mutex> lock(mutex);
if(!external)
{
allocateExternal();
}
VkResult result = external->importFd(fd);
if(result != VK_SUCCESS)
{
deallocateExternal();
}
else
{
temporaryImport = tempImport;
}
return result;
}
VkResult Semaphore::exportFd(int *pFd)
{
std::unique_lock<std::mutex> lock(mutex);
if(!external)
{
TRACE("Cannot export non-external semaphore");
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
return external->exportFd(pFd);
}
#endif // SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
#if VK_USE_PLATFORM_FUCHSIA
VkResult Semaphore::importHandle(zx_handle_t handle, bool tempImport)
{
std::unique_lock<std::mutex> lock(mutex);
if(!external)
{
allocateExternal();
}
// NOTE: Imports are just moving a handle so cannot fail.
external->importHandle(handle);
temporaryImport = tempImport;
return VK_SUCCESS;
}
VkResult Semaphore::exportHandle(zx_handle_t *pHandle)
{
std::unique_lock<std::mutex> lock(mutex);
if(!external)
{
TRACE("Cannot export non-external semaphore");
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
return external->exportHandle(pHandle);
}
#endif // VK_USE_PLATFORM_FUCHSIA
} // namespace vk