blob: eb7231e75c1dd57aa2afa4b5611a0b21dbfb6693 [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 <mutex>
#include <utility>
namespace vk {
// An implementation of VkSemaphore based on Marl primitives.
class Semaphore::Impl
{
public:
// Create a new instance. The external instance will be allocated only
// the pCreateInfo->pNext chain indicates it needs to be exported.
Impl(const VkSemaphoreCreateInfo* pCreateInfo) {
bool exportSemaphore = false;
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);
if (exportInfo->handleTypes != External::kExternalSemaphoreHandleType)
{
UNIMPLEMENTED("exportInfo->handleTypes");
}
exportSemaphore = true;
break;
}
}
if (exportSemaphore)
{
allocateExternalNoInit();
external->init();
}
}
~Impl() {
deallocateExternal();
}
// Deallocate the External semaphore if any.
void deallocateExternal()
{
if (external)
{
external->~External();
external = nullptr;
}
}
// Allocate the external semaphore.
// Note that this does not allocate the internal resource, which must be
// performed by calling external->init(), or importing one using
// a platform-specific external->importXXX(...) method.
void allocateExternalNoInit()
{
external = new (externalStorage) External();
}
void 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
{
waitInternal();
}
}
void 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
{
signalInternal();
}
}
private:
// Necessary to make ::importXXX() and ::exportXXX() simpler.
friend Semaphore;
void waitInternal()
{
// Wait on the marl condition variable only.
std::unique_lock<std::mutex> lock(mutex);
condition.wait(lock, [this]{ return this->signaled; });
signaled = false; // Vulkan requires resetting after waiting.
}
void signalInternal()
{
// Signal the marl condition variable only.
std::unique_lock<std::mutex> lock(mutex);
if (!signaled)
{
signaled = true;
condition.notify_one();
}
}
// Implementation of a non-external semaphore based on Marl.
std::mutex mutex;
marl::ConditionVariable condition;
bool signaled = false;
// Optional external semaphore data might be referenced and stored here.
External* external = nullptr;
// Set to true if |external| comes from a temporary import.
bool temporaryImport = false;
alignas(External) char externalStorage[sizeof(External)];
};
Semaphore::Semaphore(const VkSemaphoreCreateInfo* pCreateInfo, void* mem)
{
impl = new (mem) Impl(pCreateInfo);
}
void Semaphore::destroy(const VkAllocationCallbacks* pAllocator)
{
impl->~Impl();
vk::deallocate(impl, pAllocator);
}
size_t Semaphore::ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo* pCreateInfo)
{
return sizeof(Semaphore::Impl);
}
void Semaphore::wait()
{
impl->wait();
}
void Semaphore::signal()
{
impl->signal();
}
#if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
VkResult Semaphore::importFd(int fd, bool temporaryImport)
{
std::unique_lock<std::mutex> lock(impl->mutex);
if (!impl->external)
{
impl->allocateExternalNoInit();
}
VkResult result = impl->external->importFd(fd);
if (result != VK_SUCCESS)
{
impl->deallocateExternal();
}
else
{
impl->temporaryImport = temporaryImport;
}
return result;
}
VkResult Semaphore::exportFd(int* pFd) const
{
std::unique_lock<std::mutex> lock(impl->mutex);
if (!impl->external)
{
TRACE("Cannot export non-external semaphore");
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
return impl->external->exportFd(pFd);
}
#endif // SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
#if VK_USE_PLATFORM_FUCHSIA
VkResult Semaphore::importHandle(zx_handle_t handle, bool temporaryImport)
{
std::unique_lock<std::mutex> lock(impl->mutex);
if (!impl->external)
{
impl->allocateExternalNoInit();
}
// NOTE: Imports are just moving a handle so cannot fail.
impl->external->importHandle(handle);
impl->temporaryImport = temporaryImport;
return VK_SUCCESS;
}
VkResult Semaphore::exportHandle(zx_handle_t *pHandle) const
{
std::unique_lock<std::mutex> lock(impl->mutex);
if (!impl->external)
{
TRACE("Cannot export non-external semaphore");
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
return impl->external->exportHandle(pHandle);
}
#endif // VK_USE_PLATFORM_FUCHSIA
} // namespace vk