Commit 2aa80df1 by David 'Digit' Turner Committed by David Turner

[vulkan]: Support multiple external semaphore implementations.

This CL refactors the implementation of VkSemaphore objects in the following way: - Add the ability to support several external handle types concurrently. Before this CL, each platform could support a single handle type (e.g. on Linux, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT only). The changes here will allow future CLs to support more than one type per platform (e.g. the Linux implementation may support OPAQUE_FD_BIT as well as SYNC_FD_BIT at the same time). - Better implementation of temporary imports. In particular, the following sequence now works properly: 1) Create exportable semaphore A. 2) Export A to an external handle/descriptor. 3) Signal A. 4) Temporarily import _another_ handle into A. 5) A.wait() // waits on the temporary payload, then discard it. Before the CL, A would end up, incorrectly, unsignalled. Because the export operation created an External instance that held the payload modified in 3), which was then discarded after the wait() in 5). - Improved and consistent handling of errors during import/export operations, through the use of templates. + Add a technical note in VkSemaphore.h explaining how everything works, since there are several subtle points in the spec. Bug: b/140421736 Change-Id: I9b6935db3238fec7af8e0c81666e2f5c72075756 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/39880Tested-by: 's avatarDavid Turner <digit@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> Kokoro-Presubmit: David Turner <digit@google.com> Reviewed-by: 's avatarChris Forbes <chrisforbes@google.com>
parent 440fc995
...@@ -101,8 +101,6 @@ swiftshader_source_set("swiftshader_libvulkan_headers") { ...@@ -101,8 +101,6 @@ swiftshader_source_set("swiftshader_libvulkan_headers") {
] ]
} else if (is_fuchsia) { } else if (is_fuchsia) {
sources += [ "VkSemaphoreExternalFuchsia.hpp" ] sources += [ "VkSemaphoreExternalFuchsia.hpp" ]
} else {
sources += [ "VkSemaphoreExternalNone.hpp" ]
} }
} }
......
...@@ -17,6 +17,57 @@ ...@@ -17,6 +17,57 @@
#include "VkConfig.h" #include "VkConfig.h"
#include "VkStringify.hpp" #include "VkStringify.hpp"
#include "marl/blockingcall.h"
#include "marl/conditionvariable.h"
#include <functional>
#include <memory>
#include <utility>
namespace vk {
// This is a base abstract class for all external semaphore implementations
// used in this source file.
class Semaphore::External
{
public:
virtual ~External() = default;
// Initialize new instance with a given initial state.
virtual VkResult init(bool initialState) = 0;
virtual bool tryWait() = 0;
virtual void wait() = 0;
virtual void signal() = 0;
// For VK_KHR_external_semaphore_fd
virtual VkResult importOpaqueFd(int fd)
{
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
virtual VkResult exportOpaqueFd(int *pFd)
{
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
#if VK_USE_PLATFORM_FUCHSIA
// For VK_FUCHSIA_external_semaphore
virtual VkResult importHandle(zx_handle_t handle)
{
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
virtual VkResult exportHandle(zx_handle_t *pHandle)
{
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
#endif
// Pointer to previous temporary external instanc,e used for |tempExternal| only.
External *previous = nullptr;
};
} // namespace vk
#if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD #if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
# if defined(__linux__) || defined(__ANDROID__) # if defined(__linux__) || defined(__ANDROID__)
# include "VkSemaphoreExternalLinux.hpp" # include "VkSemaphoreExternalLinux.hpp"
...@@ -25,24 +76,26 @@ ...@@ -25,24 +76,26 @@
# endif # endif
#elif VK_USE_PLATFORM_FUCHSIA #elif VK_USE_PLATFORM_FUCHSIA
# include "VkSemaphoreExternalFuchsia.hpp" # include "VkSemaphoreExternalFuchsia.hpp"
#else
# include "VkSemaphoreExternalNone.hpp"
#endif #endif
#include "marl/blockingcall.h"
#include "marl/conditionvariable.h"
#include <functional>
#include <memory>
#include <utility>
namespace vk { namespace vk {
// The bitmask of all external semaphore handle types supported by this source file.
static const VkExternalSemaphoreHandleTypeFlags kSupportedTypes =
#if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT |
#endif
#if VK_USE_PLATFORM_FUCHSIA
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA |
#endif
0;
namespace { namespace {
struct SemaphoreCreateInfo struct SemaphoreCreateInfo
{ {
bool exportSemaphore = false; bool exportSemaphore = false;
VkExternalSemaphoreHandleTypeFlags exportHandleTypes = 0;
// 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.
...@@ -57,9 +110,12 @@ struct SemaphoreCreateInfo ...@@ -57,9 +110,12 @@ struct SemaphoreCreateInfo
{ {
const auto *exportInfo = reinterpret_cast<const VkExportSemaphoreCreateInfo *>(nextInfo); const auto *exportInfo = reinterpret_cast<const VkExportSemaphoreCreateInfo *>(nextInfo);
exportSemaphore = true; exportSemaphore = true;
if(exportInfo->handleTypes != Semaphore::External::kExternalSemaphoreHandleType) exportHandleTypes = exportInfo->handleTypes;
if((exportHandleTypes & ~kSupportedTypes) != 0)
{ {
UNSUPPORTED("exportInfo->handleTypes %d", int(exportInfo->handleTypes)); UNSUPPORTED("exportInfo->handleTypes 0x%X (supports 0x%X)",
int(exportHandleTypes),
int(kSupportedTypes));
} }
} }
break; break;
...@@ -74,27 +130,30 @@ struct SemaphoreCreateInfo ...@@ -74,27 +130,30 @@ struct SemaphoreCreateInfo
void Semaphore::wait() void Semaphore::wait()
{ {
if(external) std::unique_lock<std::mutex> lock(mutex);
External *ext = tempExternal ? tempExternal : external;
if(ext)
{ {
if(!external->tryWait()) if(!ext->tryWait())
{ {
// Dispatch the external wait to a background thread. // Dispatch the ext wait to a background thread.
// Even if this creates a new thread on each // Even if this creates a new thread on each
// call, it is assumed that this is negligible // call, it is assumed that this is negligible
// compared with the actual semaphore wait() // compared with the actual semaphore wait()
// operation. // operation.
marl::blocking_call([this]() { marl::blocking_call([ext, &lock]() {
external->wait(); lock.unlock();
ext->wait();
lock.lock();
}); });
} }
// If the import was temporary, reset the semaphore to its // If the import was temporary, reset the semaphore to its previous state.
// permanent state by getting rid of |external|.
// See "6.4.5. Importing Semaphore Payloads" in Vulkan 1.1 spec. // See "6.4.5. Importing Semaphore Payloads" in Vulkan 1.1 spec.
if(temporaryImport) if(ext == tempExternal)
{ {
deallocateExternal(); tempExternal = ext->previous;
temporaryImport = false; deallocateExternal(ext);
} }
} }
else else
...@@ -105,11 +164,13 @@ void Semaphore::wait() ...@@ -105,11 +164,13 @@ void Semaphore::wait()
void Semaphore::signal() void Semaphore::signal()
{ {
if(external) std::unique_lock<std::mutex> lock(mutex);
External *ext = tempExternal ? tempExternal : external;
if(ext)
{ {
// Assumes that signalling an external semaphore is non-blocking, // Assumes that signalling an external semaphore is non-blocking,
// so it can be performed directly either from a fiber or thread. // so it can be performed directly either from a fiber or thread.
external->signal(); ext->signal();
} }
else else
{ {
...@@ -121,16 +182,22 @@ Semaphore::Semaphore(const VkSemaphoreCreateInfo *pCreateInfo, void *mem, const ...@@ -121,16 +182,22 @@ Semaphore::Semaphore(const VkSemaphoreCreateInfo *pCreateInfo, void *mem, const
: allocator(pAllocator) : allocator(pAllocator)
{ {
SemaphoreCreateInfo info(pCreateInfo); SemaphoreCreateInfo info(pCreateInfo);
if(info.exportSemaphore) exportableHandleTypes = info.exportHandleTypes;
{
allocateExternal();
external->init();
}
} }
void Semaphore::destroy(const VkAllocationCallbacks *pAllocator) void Semaphore::destroy(const VkAllocationCallbacks *pAllocator)
{ {
deallocateExternal(); while(tempExternal)
{
External *ext = tempExternal;
tempExternal = ext->previous;
deallocateExternal(ext);
}
if(external)
{
deallocateExternal(external);
external = nullptr;
}
} }
size_t Semaphore::ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo) size_t Semaphore::ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo)
...@@ -139,78 +206,140 @@ size_t Semaphore::ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCr ...@@ -139,78 +206,140 @@ size_t Semaphore::ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCr
return 0; return 0;
} }
void Semaphore::allocateExternal() template<class EXTERNAL>
Semaphore::External *Semaphore::allocateExternal()
{ {
ASSERT(external == nullptr); auto *ext = reinterpret_cast<Semaphore::External *>(
external = reinterpret_cast<Semaphore::External *>( vk::allocate(sizeof(EXTERNAL), alignof(EXTERNAL), allocator));
vk::allocate(sizeof(Semaphore::External), vk::REQUIRED_MEMORY_ALIGNMENT, allocator)); new(ext) EXTERNAL();
new(external) Semaphore::External(); return ext;
} }
void Semaphore::deallocateExternal() void Semaphore::deallocateExternal(Semaphore::External *ext)
{ {
if(external) ext->~External();
{ vk::deallocate(ext, allocator);
vk::deallocate(external, allocator);
external = nullptr;
}
} }
#if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD template<typename ALLOC_FUNC, typename IMPORT_FUNC>
VkResult Semaphore::importFd(int fd, bool tempImport) VkResult Semaphore::importPayload(bool temporaryImport,
ALLOC_FUNC alloc_func,
IMPORT_FUNC import_func)
{ {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(!external)
// Create new External instance if needed.
External *ext = external;
if(temporaryImport || !ext)
{ {
allocateExternal(); ext = alloc_func();
} }
VkResult result = external->importFd(fd); VkResult result = import_func(ext);
if(result != VK_SUCCESS) if(result != VK_SUCCESS)
{ {
deallocateExternal(); if(temporaryImport || !external)
}
else
{ {
temporaryImport = tempImport; deallocateExternal(ext);
} }
return result; return result;
}
if(temporaryImport)
{
ext->previous = tempExternal;
tempExternal = ext;
}
else if(!external)
{
external = ext;
}
return VK_SUCCESS;
} }
VkResult Semaphore::exportFd(int *pFd) template<typename ALLOC_FUNC, typename EXPORT_FUNC>
VkResult Semaphore::exportPayload(ALLOC_FUNC alloc_func, EXPORT_FUNC export_func)
{ {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
// Sanity check, do not try to export a semaphore that has a temporary import.
if(tempExternal != nullptr)
{
TRACE("Cannot export semaphore with a temporary import!");
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
// Allocate |external| if it doesn't exist yet.
if(!external) if(!external)
{ {
TRACE("Cannot export non-external semaphore"); External *ext = alloc_func();
VkResult result = ext->init(internal.isSignalled());
if(result != VK_SUCCESS)
{
deallocateExternal(ext);
return result;
}
external = ext;
}
return export_func(external);
}
#if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
VkResult Semaphore::importFd(int fd, bool temporaryImport)
{
return importPayload(
temporaryImport,
[this]() {
return allocateExternal<OpaqueFdExternalSemaphore>();
},
[fd](External *ext) {
return ext->importOpaqueFd(fd);
});
}
VkResult Semaphore::exportFd(int *pFd)
{
if((exportableHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) == 0)
{
TRACE("Cannot export semaphore as opaque FD (exportableHandleType = 0x%X, want 0x%X)",
exportableHandleTypes,
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT);
return VK_ERROR_INVALID_EXTERNAL_HANDLE; return VK_ERROR_INVALID_EXTERNAL_HANDLE;
} }
return external->exportFd(pFd);
return exportPayload([this]() { return allocateExternal<OpaqueFdExternalSemaphore>(); },
[pFd](External *ext) {
return ext->exportOpaqueFd(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 tempImport) VkResult Semaphore::importHandle(zx_handle_t handle, bool temporaryImport)
{ {
std::unique_lock<std::mutex> lock(mutex); return importPayload(
if(!external) temporaryImport,
{ [this]() {
allocateExternal(); return allocateExternal<ZirconEventExternalSemaphore>();
} },
// NOTE: Imports are just moving a handle so cannot fail. [handle](External *ext) {
external->importHandle(handle); return ext->importHandle(handle);
temporaryImport = tempImport; });
return VK_SUCCESS;
} }
VkResult Semaphore::exportHandle(zx_handle_t *pHandle) VkResult Semaphore::exportHandle(zx_handle_t *pHandle)
{ {
std::unique_lock<std::mutex> lock(mutex); if((exportableHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA) == 0)
if(!external)
{ {
TRACE("Cannot export non-external semaphore"); TRACE("Cannot export semaphore as Zircon handle (exportableHandleType = 0x%X, want 0x%X)",
exportableHandleTypes,
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA);
return VK_ERROR_INVALID_EXTERNAL_HANDLE; return VK_ERROR_INVALID_EXTERNAL_HANDLE;
} }
return external->exportHandle(pHandle);
return exportPayload([this]() { return allocateExternal<ZirconEventExternalSemaphore>(); },
[pHandle](External *ext) {
return ext->exportHandle(pHandle);
});
} }
#endif // VK_USE_PLATFORM_FUCHSIA #endif // VK_USE_PLATFORM_FUCHSIA
......
...@@ -58,14 +58,99 @@ public: ...@@ -58,14 +58,99 @@ public:
class External; class External;
private: private:
void allocateExternal(); // Small technical note on how semaphores are imported/exported with Vulkan:
void deallocateExternal(); //
// - A Vulkan Semaphore objects has a "payload", corresponding to a
// simple atomic boolean flag.
//
// - A Vulkan Semaphore object can be "exported": this creates a
// platform-specific handle / descriptor (which can be passed to other
// processes), and is linked in some way to the original semaphore's
// payload.
//
// - Similarly, said handle / descriptor can be "imported" into a Vulkan
// Semaphore object. By default, that semaphore loses its payload, and
// instead uses the one referenced / shared through the descriptor.
//
// Hence if semaphore A exports its payload through a descriptor that
// is later imported into semaphore B, then both A and B will use/share
// the same payload (i.e. signal flag), making cross-process
// synchronization possible.
//
// - There are also "temporary imports", where the target semaphore's
// payload is not lost, but is simply hidden/stashed. But the next wait()
// operation on the same semaphore should remove the temporary import,
// and restore the previous payload.
//
// - There are many handle / descriptor types, which are listed through
// the VkExternalSemaphoreHandleTypeFlagBits. A given Vulkan
// implementation might support onle one or several at the same time
// (e.g. on Linux or Android, it could support both OPAQUE_FD_BIT and
// SYNC_FD_BIT, while on Windows, it would be OPAQUE_WIN32_BIT +
// OPAQUE_WIN32_KMT_BIT + D3D12_FENCE_BIT).
//
// - To be able to export a semaphore, VkCreateSemaphore() must be called
// with a VkSemaphoreCreateInfo that lists the types of all possible
// platform-specific handles the semaphore could be exported to
// (e.g. on Linux, it is possible to specify that a semaphore might be
// exported as an opaque FD, or as a Linux Sync FD).
//
// However, which exact type is however only determined later by the
// export operation itself (e.g. vkGetSemaphoreFdKHR() could be called to export
// either a VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT or a
// VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT).
//
// Once a semaphore has been exported as one type, it is not possible
// to export the same payload with a different type (though the spec
// doesn't seem to be explicit about this, it's simply impossible in
// general).
//
// This leads to the following design:
//
// - |internal| is a simple marl::Event that represents the semaphore's
// payload when it is not exported, or imported non-temporarily.
//
// - |external| points to an external semaphore payload. It is created
// on demand if the semaphore is exported or imported non-temporarily.
// Note that once |external| is created, |internal| is ignored.
//
// - |tempExternal| points to a linked-list of temporary external
// semaphore payloads. The list head corresponds to the most recent
// temporary import.
//
// Internal template to allocate a new External implementation.
template<class EXTERNAL>
External *allocateExternal();
void deallocateExternal(External *ext);
// Used internally to import an external payload.
// |temporaryImport| is true iff the import is temporary.
// |alloc_func| is callable that allocates a new External instance of the
// appropriate type.
// |import_func| is callable that takes a single parameter, which
// corresponds to the external handle/descriptor, and returns a VkResult
// values.
template<typename ALLOC_FUNC, typename IMPORT_FUNC>
VkResult importPayload(bool temporaryImport,
ALLOC_FUNC alloc_func,
IMPORT_FUNC import_func);
// Used internally to export a given payload.
// |alloc_func| is a callable that allocates a new External instance of
// the appropriate type.
// |export_func| is a callable that takes a pointer to an External instance,
// and a pointer to a handle/descriptor, and returns a VkResult.
template<typename ALLOC_FUNC, typename EXPORT_FUNC>
VkResult exportPayload(ALLOC_FUNC alloc_func, EXPORT_FUNC export_func);
const VkAllocationCallbacks *allocator = nullptr; const VkAllocationCallbacks *allocator = nullptr;
marl::Event internal; VkExternalSemaphoreHandleTypeFlags exportableHandleTypes = (VkExternalSemaphoreHandleTypeFlags)0;
std::mutex mutex; std::mutex mutex;
marl::Event internal;
External *external = nullptr; External *external = nullptr;
bool temporaryImport = false; External *tempExternal = nullptr;
}; };
static inline Semaphore *Cast(VkSemaphore object) static inline Semaphore *Cast(VkSemaphore object)
......
...@@ -26,38 +26,44 @@ ...@@ -26,38 +26,44 @@
namespace vk { namespace vk {
class Semaphore::External class ZirconEventExternalSemaphore : public Semaphore::External
{ {
public: public:
// The type of external semaphore handle types supported by this implementation. ~ZirconEventExternalSemaphore()
static const VkExternalSemaphoreHandleTypeFlags kExternalSemaphoreHandleType =
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA;
// Default constructor. Note that one should call either init() or
// importFd() before any call to wait() or signal().
External() = default;
~External()
{ {
zx_handle_close(handle); zx_handle_close(handle);
} }
void init() VkResult init(bool initialValue) override
{ {
zx_status_t status = zx_event_create(0, &handle); zx_status_t status = zx_event_create(0, &handle);
if(status != ZX_OK) if(status != ZX_OK)
{ {
ABORT("zx_event_create() returned %d", status); TRACE("zx_event_create() returned %d", status);
return VK_ERROR_INITIALIZATION_FAILED;
}
if(initialValue)
{
status = zx_object_signal(handle, 0, ZX_EVENT_SIGNALED);
if(status != ZX_OK)
{
TRACE("zx_object_signal() returned %d", status);
zx_handle_close(handle);
handle = ZX_HANDLE_INVALID;
return VK_ERROR_INITIALIZATION_FAILED;
} }
} }
return VK_SUCCESS;
}
void importHandle(zx_handle_t new_handle) VkResult importHandle(zx_handle_t new_handle) override
{ {
zx_handle_close(handle); zx_handle_close(handle);
handle = new_handle; handle = new_handle;
return VK_SUCCESS;
} }
VkResult exportHandle(zx_handle_t *pHandle) const VkResult exportHandle(zx_handle_t *pHandle) override
{ {
zx_handle_t new_handle = ZX_HANDLE_INVALID; zx_handle_t new_handle = ZX_HANDLE_INVALID;
zx_status_t status = zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, &new_handle); zx_status_t status = zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, &new_handle);
...@@ -70,7 +76,7 @@ public: ...@@ -70,7 +76,7 @@ public:
return VK_SUCCESS; return VK_SUCCESS;
} }
void wait() void wait() override
{ {
zx_signals_t observed = 0; zx_signals_t observed = 0;
zx_status_t status = zx_object_wait_one( zx_status_t status = zx_object_wait_one(
...@@ -91,7 +97,7 @@ public: ...@@ -91,7 +97,7 @@ public:
} }
} }
bool tryWait() bool tryWait() override
{ {
zx_signals_t observed = 0; zx_signals_t observed = 0;
zx_status_t status = zx_object_wait_one( zx_status_t status = zx_object_wait_one(
...@@ -113,7 +119,7 @@ public: ...@@ -113,7 +119,7 @@ public:
return true; return true;
} }
void signal() void signal() override
{ {
zx_status_t status = zx_object_signal(handle, 0, ZX_EVENT_SIGNALED); zx_status_t status = zx_object_signal(handle, 0, ZX_EVENT_SIGNALED);
if(status != ZX_OK) if(status != ZX_OK)
......
...@@ -44,7 +44,8 @@ ...@@ -44,7 +44,8 @@
class SharedSemaphore class SharedSemaphore
{ {
public: public:
SharedSemaphore() SharedSemaphore(bool initialValue)
: signaled(initialValue)
{ {
pthread_mutexattr_t mattr; pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr); pthread_mutexattr_init(&mattr);
...@@ -129,20 +130,13 @@ private: ...@@ -129,20 +130,13 @@ private:
namespace vk { namespace vk {
class Semaphore::External class OpaqueFdExternalSemaphore : public Semaphore::External
{ {
public: public:
// The type of external semaphore handle types supported by this implementation. ~OpaqueFdExternalSemaphore() { unmapRegion(); }
static const VkExternalSemaphoreHandleTypeFlags kExternalSemaphoreHandleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
// Default constructor. Note that one should call either init() or
// importFd() before any call to wait() or signal().
External() = default;
~External() { close(); }
// Initialize instance by creating a new shared memory region. // Initialize instance by creating a new shared memory region.
void init() VkResult init(bool initialState) override
{ {
// Allocate or import the region's file descriptor. // Allocate or import the region's file descriptor.
const size_t size = sw::memoryPageSize(); const size_t size = sw::memoryPageSize();
...@@ -153,24 +147,30 @@ public: ...@@ -153,24 +147,30 @@ public:
snprintf(name, sizeof(name), "SwiftShader.Semaphore.%d", ++counter); snprintf(name, sizeof(name), "SwiftShader.Semaphore.%d", ++counter);
if(!memfd.allocate(name, size)) if(!memfd.allocate(name, size))
{ {
ABORT("memfd.allocate() returned %s", strerror(errno)); TRACE("memfd.allocate() returned %s", strerror(errno));
return VK_ERROR_INITIALIZATION_FAILED;
} }
mapRegion(size, true); if(!mapRegion(size, true, initialState))
return VK_ERROR_INITIALIZATION_FAILED;
return VK_SUCCESS;
} }
// Import an existing semaphore through its file descriptor. // Import an existing semaphore through its file descriptor.
VkResult importFd(int fd) VkResult importOpaqueFd(int fd) override
{ {
close(); unmapRegion();
memfd.importFd(fd); memfd.importFd(fd);
mapRegion(sw::memoryPageSize(), false); if(!mapRegion(sw::memoryPageSize(), false, false))
return VK_ERROR_INITIALIZATION_FAILED;
return VK_SUCCESS; return VK_SUCCESS;
} }
// Export the current semaphore as a duplicated file descriptor to the same // Export the current semaphore as a duplicated file descriptor to the same
// region. This can be consumed by importFd() running in a different // region. This can be consumed by importFd() running in a different
// process. // process.
VkResult exportFd(int *pFd) const VkResult exportOpaqueFd(int *pFd) override
{ {
int fd = memfd.exportFd(); int fd = memfd.exportFd();
if(fd < 0) if(fd < 0)
...@@ -181,24 +181,23 @@ public: ...@@ -181,24 +181,23 @@ public:
return VK_SUCCESS; return VK_SUCCESS;
} }
void wait() void wait() override
{ {
semaphore->wait(); semaphore->wait();
} }
bool tryWait() bool tryWait() override
{ {
return semaphore->tryWait(); return semaphore->tryWait();
} }
void signal() void signal() override
{ {
semaphore->signal(); semaphore->signal();
} }
private: private:
// Unmap the semaphore if needed and close its file descriptor. void unmapRegion()
void close()
{ {
if(semaphore) if(semaphore)
{ {
...@@ -213,23 +212,25 @@ private: ...@@ -213,23 +212,25 @@ private:
} }
// Remap the shared region and setup the semaphore or increment its reference count. // Remap the shared region and setup the semaphore or increment its reference count.
void mapRegion(size_t size, bool needInitialization) bool mapRegion(size_t size, bool needsInitialization, bool initialValue)
{ {
// Map the region into memory and point the semaphore to it. // Map the region into memory and point the semaphore to it.
void *addr = memfd.mapReadWrite(0, size); void *addr = memfd.mapReadWrite(0, size);
if(!addr) if(!addr)
{ {
ABORT("mmap() failed: %s", strerror(errno)); TRACE("mmap() failed: %s", strerror(errno));
return false;
} }
semaphore = reinterpret_cast<SharedSemaphore *>(addr); semaphore = reinterpret_cast<SharedSemaphore *>(addr);
if(needInitialization) if(needsInitialization)
{ {
new(semaphore) SharedSemaphore(); new(semaphore) SharedSemaphore(initialValue);
} }
else else
{ {
semaphore->addRef(); semaphore->addRef();
} }
return true;
} }
LinuxMemFd memfd; LinuxMemFd memfd;
......
// 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.
#ifndef VK_SEMAPHORE_EXTERNAL_NONE_H_
#define VK_SEMAPHORE_EXTERNAL_NONE_H_
namespace vk {
// Empty external sempahore implementation.
class Semaphore::External
{
public:
// The type of external semaphore handle types supported by this implementation.
static const VkExternalSemaphoreHandleTypeFlags kExternalSemaphoreHandleType = 0;
void init() {}
void wait() {}
bool tryWait() { return true; }
void signal() {}
private:
int dummy;
};
} // namespace vk
#endif // VK_SEMAPHORE_EXTERNAL_NONE_H_
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