Commit 43e33165 by David 'Digit' Turner Committed by David Turner

[vulkan] Simplify vk::Semaphore implementation.

This CL simplifies the implementation of vk::Semaphore by getting rid of vk::Semaphore::Impl (moving its fields into vk::Semaphore itself). This requires a minor change to the vk::Semaphore constructor which now takes an allocator parameter. The latter is used to allocate the "External" instance on demand. Before the CL, said instance was 'allocated' from a fixed storage area inside the Impl class. Note that this doesn't change the external semaphore's behaviour. In particular, only one implementation can live in the source tree at the moment, while the Vulkan specification makes it clear that it should be possible to export a single VkSemaphore to several types of platform-specific handles (though this doesn't seem to be tested by the Vulkan-CTS). This last issue will be addressed in a future CL. Bug: 140421736 Change-Id: I3610d9e7e8cb8e49368b658d157408cbd23ee6db Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/39052 Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> Reviewed-by: 's avatarBen Clayton <bclayton@google.com> Tested-by: 's avatarDavid Turner <digit@google.com>
parent 068dd89c
...@@ -33,229 +33,197 @@ ...@@ -33,229 +33,197 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <mutex>
#include <utility> #include <utility>
namespace vk { namespace vk {
// An implementation of VkSemaphore based on Marl primitives. namespace {
class Semaphore::Impl
struct SemaphoreCreateInfo
{ {
public: bool exportSemaphore = false;
// Create a new instance. The external instance will be allocated only // Create a new instance. The external instance will be allocated only
// the pCreateInfo->pNext chain indicates it needs to be exported. // the pCreateInfo->pNext chain indicates it needs to be exported.
Impl(const VkSemaphoreCreateInfo *pCreateInfo) SemaphoreCreateInfo(const VkSemaphoreCreateInfo *pCreateInfo)
{ {
bool exportSemaphore = false;
for(const auto *nextInfo = reinterpret_cast<const VkBaseInStructure *>(pCreateInfo->pNext); for(const auto *nextInfo = reinterpret_cast<const VkBaseInStructure *>(pCreateInfo->pNext);
nextInfo != nullptr; nextInfo = nextInfo->pNext) nextInfo != nullptr; nextInfo = nextInfo->pNext)
{ {
if(nextInfo->sType == VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO) if(nextInfo->sType == VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO)
{ {
const auto *exportInfo = reinterpret_cast<const VkExportSemaphoreCreateInfo *>(nextInfo); const auto *exportInfo = reinterpret_cast<const VkExportSemaphoreCreateInfo *>(nextInfo);
if(exportInfo->handleTypes != External::kExternalSemaphoreHandleType) exportSemaphore = true;
if(exportInfo->handleTypes != Semaphore::External::kExternalSemaphoreHandleType)
{ {
UNIMPLEMENTED("exportInfo->handleTypes"); UNIMPLEMENTED("exportInfo->handleTypes");
} }
exportSemaphore = true;
break; break;
} }
} }
if(exportSemaphore)
{
allocateExternalNoInit();
external->init();
}
} }
};
~Impl() } // namespace
{
deallocateExternal();
}
// Deallocate the External semaphore if any. void Semaphore::wait()
void deallocateExternal() {
if(external)
{ {
if(external) if(!external->tryWait())
{ {
external->~External(); // Dispatch the external wait to a background thread.
external = nullptr; // 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();
});
} }
}
// Allocate the external semaphore. // If the import was temporary, reset the semaphore to its
// Note that this does not allocate the internal resource, which must be // permanent state by getting rid of |external|.
// performed by calling external->init(), or importing one using // See "6.4.5. Importing Semaphore Payloads" in Vulkan 1.1 spec.
// a platform-specific external->importXXX(...) method. if(temporaryImport)
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(); deallocateExternal();
temporaryImport = false;
} }
} }
else
void signal()
{ {
if(external) waitInternal();
{
// 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: void Semaphore::signal()
// Necessary to make ::importXXX() and ::exportXXX() simpler. {
friend Semaphore; if(external)
void waitInternal()
{ {
// Wait on the marl condition variable only. // Assumes that signalling an external semaphore is non-blocking,
std::unique_lock<std::mutex> lock(mutex); // so it can be performed directly either from a fiber or thread.
condition.wait(lock, [this] { return this->signaled; }); external->signal();
signaled = false; // Vulkan requires resetting after waiting.
} }
else
void signalInternal()
{ {
// Signal the marl condition variable only. signalInternal();
std::unique_lock<std::mutex> lock(mutex);
if(!signaled)
{
signaled = true;
condition.notify_one();
}
} }
}
// Implementation of a non-external semaphore based on Marl. void Semaphore::waitInternal()
std::mutex mutex; {
marl::ConditionVariable condition; // Wait on the marl condition variable only.
bool signaled = false; std::unique_lock<std::mutex> lock(mutex);
condition.wait(lock, [this] { return this->signaled; });
// Optional external semaphore data might be referenced and stored here. signaled = false; // Vulkan requires resetting after waiting.
External *external = nullptr; }
// Set to true if |external| comes from a temporary import.
bool temporaryImport = false;
alignas(External) char externalStorage[sizeof(External)]; void Semaphore::signalInternal()
}; {
// Signal the marl condition variable only.
std::unique_lock<std::mutex> lock(mutex);
if(!signaled)
{
signaled = true;
condition.notify_one();
}
}
Semaphore::Semaphore(const VkSemaphoreCreateInfo *pCreateInfo, void *mem) Semaphore::Semaphore(const VkSemaphoreCreateInfo *pCreateInfo, void *mem, const VkAllocationCallbacks *pAllocator)
: allocator(pAllocator)
{ {
impl = new(mem) Impl(pCreateInfo); SemaphoreCreateInfo info(pCreateInfo);
if(info.exportSemaphore)
{
allocateExternal();
external->init();
}
} }
void Semaphore::destroy(const VkAllocationCallbacks *pAllocator) void Semaphore::destroy(const VkAllocationCallbacks *pAllocator)
{ {
impl->~Impl(); deallocateExternal();
vk::deallocate(impl, pAllocator);
} }
size_t Semaphore::ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo) size_t Semaphore::ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo)
{ {
return sizeof(Semaphore::Impl); // Semaphore::External instance is created and destroyed on demand so return 0 here.
return 0;
} }
void Semaphore::wait() void Semaphore::allocateExternal()
{ {
impl->wait(); ASSERT(external == nullptr);
external = reinterpret_cast<Semaphore::External *>(
vk::allocate(sizeof(Semaphore::External), vk::REQUIRED_MEMORY_ALIGNMENT, allocator));
new(external) Semaphore::External();
} }
void Semaphore::signal() void Semaphore::deallocateExternal()
{ {
impl->signal(); if(external)
{
vk::deallocate(external, allocator);
external = nullptr;
}
} }
#if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD #if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
VkResult Semaphore::importFd(int fd, bool temporaryImport) VkResult Semaphore::importFd(int fd, bool tempImport)
{ {
std::unique_lock<std::mutex> lock(impl->mutex); std::unique_lock<std::mutex> lock(mutex);
if(!impl->external) if(!external)
{ {
impl->allocateExternalNoInit(); allocateExternal();
} }
VkResult result = impl->external->importFd(fd); VkResult result = external->importFd(fd);
if(result != VK_SUCCESS) if(result != VK_SUCCESS)
{ {
impl->deallocateExternal(); deallocateExternal();
} }
else else
{ {
impl->temporaryImport = temporaryImport; temporaryImport = tempImport;
} }
return result; return result;
} }
VkResult Semaphore::exportFd(int *pFd) const VkResult Semaphore::exportFd(int *pFd)
{ {
std::unique_lock<std::mutex> lock(impl->mutex); std::unique_lock<std::mutex> lock(mutex);
if(!impl->external) if(!external)
{ {
TRACE("Cannot export non-external semaphore"); TRACE("Cannot export non-external semaphore");
return VK_ERROR_INVALID_EXTERNAL_HANDLE; return VK_ERROR_INVALID_EXTERNAL_HANDLE;
} }
return impl->external->exportFd(pFd); return external->exportFd(pFd);
} }
#endif // SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD #endif // SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
#if VK_USE_PLATFORM_FUCHSIA #if VK_USE_PLATFORM_FUCHSIA
VkResult Semaphore::importHandle(zx_handle_t handle, bool temporaryImport) VkResult Semaphore::importHandle(zx_handle_t handle, bool tempImport)
{ {
std::unique_lock<std::mutex> lock(impl->mutex); std::unique_lock<std::mutex> lock(mutex);
if(!impl->external) if(!external)
{ {
impl->allocateExternalNoInit(); allocateExternal();
} }
// NOTE: Imports are just moving a handle so cannot fail. // NOTE: Imports are just moving a handle so cannot fail.
impl->external->importHandle(handle); external->importHandle(handle);
impl->temporaryImport = temporaryImport; temporaryImport = tempImport;
return VK_SUCCESS; return VK_SUCCESS;
} }
VkResult Semaphore::exportHandle(zx_handle_t *pHandle) const VkResult Semaphore::exportHandle(zx_handle_t *pHandle)
{ {
std::unique_lock<std::mutex> lock(impl->mutex); std::unique_lock<std::mutex> lock(mutex);
if(!impl->external) if(!external)
{ {
TRACE("Cannot export non-external semaphore"); TRACE("Cannot export non-external semaphore");
return VK_ERROR_INVALID_EXTERNAL_HANDLE; return VK_ERROR_INVALID_EXTERNAL_HANDLE;
} }
return impl->external->exportHandle(pHandle); return external->exportHandle(pHandle);
} }
#endif // VK_USE_PLATFORM_FUCHSIA #endif // VK_USE_PLATFORM_FUCHSIA
......
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
#include "VkConfig.h" #include "VkConfig.h"
#include "VkObject.hpp" #include "VkObject.hpp"
#include "marl/conditionvariable.h"
#include <mutex>
#if VK_USE_PLATFORM_FUCHSIA #if VK_USE_PLATFORM_FUCHSIA
# include <zircon/types.h> # include <zircon/types.h>
#endif #endif
...@@ -27,7 +30,7 @@ namespace vk { ...@@ -27,7 +30,7 @@ namespace vk {
class Semaphore : public Object<Semaphore, VkSemaphore> class Semaphore : public Object<Semaphore, VkSemaphore>
{ {
public: public:
Semaphore(const VkSemaphoreCreateInfo *pCreateInfo, void *mem); Semaphore(const VkSemaphoreCreateInfo *pCreateInfo, void *mem, const VkAllocationCallbacks *pAllocator);
void destroy(const VkAllocationCallbacks *pAllocator); void destroy(const VkAllocationCallbacks *pAllocator);
static size_t ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo); static size_t ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo);
...@@ -44,18 +47,30 @@ public: ...@@ -44,18 +47,30 @@ public:
#if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD #if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
VkResult importFd(int fd, bool temporaryImport); VkResult importFd(int fd, bool temporaryImport);
VkResult exportFd(int *pFd) const; VkResult exportFd(int *pFd);
#endif #endif
#if VK_USE_PLATFORM_FUCHSIA #if VK_USE_PLATFORM_FUCHSIA
VkResult importHandle(zx_handle_t handle, bool temporaryImport); VkResult importHandle(zx_handle_t handle, bool temporaryImport);
VkResult exportHandle(zx_handle_t *pHandle) const; VkResult exportHandle(zx_handle_t *pHandle);
#endif #endif
private:
class External; class External;
class Impl;
Impl *impl = nullptr; private:
void waitInternal();
void signalInternal();
void allocateExternal();
void deallocateExternal();
const VkAllocationCallbacks *allocator = nullptr;
std::mutex mutex;
marl::ConditionVariable condition;
bool signaled = false;
External *external = nullptr;
bool temporaryImport = false;
}; };
static inline Semaphore *Cast(VkSemaphore object) static inline Semaphore *Cast(VkSemaphore object)
......
...@@ -1082,7 +1082,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateSemaphore(VkDevice device, const VkSemaph ...@@ -1082,7 +1082,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateSemaphore(VkDevice device, const VkSemaph
UNIMPLEMENTED("pCreateInfo->flags"); UNIMPLEMENTED("pCreateInfo->flags");
} }
return vk::Semaphore::Create(pAllocator, pCreateInfo, pSemaphore); return vk::Semaphore::Create(pAllocator, pCreateInfo, pSemaphore, pAllocator);
} }
VKAPI_ATTR void VKAPI_CALL vkDestroySemaphore(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks *pAllocator) VKAPI_ATTR void VKAPI_CALL vkDestroySemaphore(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks *pAllocator)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment