| // Copyright 2018 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_HPP_ |
| #define VK_SEMAPHORE_HPP_ |
| |
| #include "VkConfig.hpp" |
| #include "VkObject.hpp" |
| |
| #include "marl/event.h" |
| #include "marl/mutex.h" |
| #include "marl/tsa.h" |
| |
| #include "System/Synchronization.hpp" |
| |
| #if VK_USE_PLATFORM_FUCHSIA |
| # include <zircon/types.h> |
| #endif |
| |
| namespace vk { |
| |
| class BinarySemaphore; |
| class TimelineSemaphore; |
| |
| class Semaphore |
| { |
| public: |
| Semaphore(VkSemaphoreType type); |
| |
| virtual ~Semaphore() = default; |
| |
| static inline Semaphore *Cast(VkSemaphore semaphore) |
| { |
| return static_cast<Semaphore *>(static_cast<void *>(semaphore)); |
| } |
| |
| virtual void destroy(const VkAllocationCallbacks *pAllocator) |
| { |
| } |
| |
| VkSemaphoreType getSemaphoreType() const; |
| // static size_t ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo); |
| |
| protected: |
| VkSemaphoreType type; |
| marl::mutex mutex; |
| }; |
| |
| class BinarySemaphore : public Semaphore, public Object<BinarySemaphore, VkSemaphore> |
| { |
| public: |
| BinarySemaphore(const VkSemaphoreCreateInfo *pCreateInfo, void *mem, const VkAllocationCallbacks *pAllocator); |
| void destroy(const VkAllocationCallbacks *pAllocator); |
| |
| static size_t ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo); |
| |
| void wait(); |
| |
| void wait(const VkPipelineStageFlags &flag) |
| { |
| // NOTE: not sure what else to do here? |
| wait(); |
| } |
| |
| void signal(); |
| |
| #if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD |
| VkResult importFd(int fd, bool temporaryImport); |
| VkResult exportFd(int *pFd); |
| #endif |
| |
| #if VK_USE_PLATFORM_FUCHSIA |
| VkResult importHandle(zx_handle_t handle, bool temporaryImport); |
| VkResult exportHandle(zx_handle_t *pHandle); |
| #endif |
| |
| class External; |
| |
| private: |
| // Small technical note on how semaphores are imported/exported with Vulkan: |
| // |
| // - 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; |
| VkExternalSemaphoreHandleTypeFlags exportableHandleTypes = (VkExternalSemaphoreHandleTypeFlags)0; |
| marl::Event internal; |
| External *external GUARDED_BY(mutex) = nullptr; |
| External *tempExternal GUARDED_BY(mutex) = nullptr; |
| }; |
| |
| static inline Semaphore *Cast(VkSemaphore object) |
| { |
| return Semaphore::Cast(object); |
| } |
| |
| template<typename T> |
| static inline T *DynamicCast(VkSemaphore object) |
| { |
| Semaphore *semaphore = vk::Cast(object); |
| if(semaphore == nullptr) |
| { |
| return nullptr; |
| } |
| |
| static_assert(std::is_same_v<T, BinarySemaphore> || std::is_same_v<T, TimelineSemaphore>); |
| if constexpr(std::is_same_v<T, BinarySemaphore>) |
| { |
| if(semaphore->getSemaphoreType() != VK_SEMAPHORE_TYPE_BINARY) |
| { |
| return nullptr; |
| } |
| } |
| else |
| { |
| if(semaphore->getSemaphoreType() != VK_SEMAPHORE_TYPE_TIMELINE) |
| { |
| return nullptr; |
| } |
| } |
| return static_cast<T *>(semaphore); |
| } |
| |
| // This struct helps parse VkSemaphoreCreateInfo. It also looks at the pNext |
| // structures and stores their data flatly in a single struct. The default |
| // values of each data member are what the absence of a pNext struct implies |
| // for those values. |
| struct SemaphoreCreateInfo |
| { |
| bool exportSemaphore = false; |
| VkExternalSemaphoreHandleTypeFlags exportHandleTypes = 0; |
| |
| VkSemaphoreType semaphoreType = VK_SEMAPHORE_TYPE_BINARY; |
| uint64_t initialPayload = 0; |
| |
| SemaphoreCreateInfo(const VkSemaphoreCreateInfo *pCreateInfo); |
| }; |
| |
| } // namespace vk |
| |
| #endif // VK_SEMAPHORE_HPP_ |