Merge pull request #1124 from cdavis5e/khr-timeline-semaphore
Support the VK_KHR_timeline_semaphore extension.
This commit is contained in:
commit
4a1a5179bd
@ -118,6 +118,11 @@ void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) {
|
||||
shaderSGTypesFeatures->shaderSubgroupExtendedTypes = _metalFeatures.subgroupSize != 0;
|
||||
break;
|
||||
}
|
||||
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES: {
|
||||
auto* timelineSem4Features = (VkPhysicalDeviceTimelineSemaphoreFeatures*)next;
|
||||
timelineSem4Features->timelineSemaphore = true;
|
||||
break;
|
||||
}
|
||||
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR: {
|
||||
auto* uboLayoutFeatures = (VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR*)next;
|
||||
uboLayoutFeatures->uniformBufferStandardLayout = true;
|
||||
@ -305,6 +310,11 @@ void MVKPhysicalDevice::getProperties(VkPhysicalDeviceProperties2* properties) {
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES: {
|
||||
auto* timelineSem4Props = (VkPhysicalDeviceTimelineSemaphoreProperties*)next;
|
||||
timelineSem4Props->maxTimelineSemaphoreValueDifference = std::numeric_limits<uint64_t>::max();
|
||||
break;
|
||||
}
|
||||
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT: {
|
||||
auto* inlineUniformBlockProps = (VkPhysicalDeviceInlineUniformBlockPropertiesEXT*)next;
|
||||
inlineUniformBlockProps->maxInlineUniformBlockSize = _metalFeatures.dynamicMTLBufferSize;
|
||||
@ -2668,12 +2678,30 @@ void MVKDevice::destroyFence(MVKFence* mvkFence,
|
||||
|
||||
MVKSemaphore* MVKDevice::createSemaphore(const VkSemaphoreCreateInfo* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator) {
|
||||
if (_useMTLFenceForSemaphores) {
|
||||
return new MVKSemaphoreMTLFence(this, pCreateInfo);
|
||||
} else if (_useMTLEventForSemaphores) {
|
||||
return new MVKSemaphoreMTLEvent(this, pCreateInfo);
|
||||
const VkSemaphoreTypeCreateInfo* pTypeCreateInfo = nullptr;
|
||||
for (auto* next = (const VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
|
||||
switch (next->sType) {
|
||||
case VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO:
|
||||
pTypeCreateInfo = (VkSemaphoreTypeCreateInfo*)next;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pTypeCreateInfo && pTypeCreateInfo->semaphoreType == VK_SEMAPHORE_TYPE_TIMELINE) {
|
||||
if (_pMetalFeatures->events) {
|
||||
return new MVKTimelineSemaphoreMTLEvent(this, pCreateInfo, pTypeCreateInfo);
|
||||
} else {
|
||||
return new MVKTimelineSemaphoreEmulated(this, pCreateInfo, pTypeCreateInfo);
|
||||
}
|
||||
} else {
|
||||
return new MVKSemaphoreEmulated(this, pCreateInfo);
|
||||
if (_useMTLFenceForSemaphores) {
|
||||
return new MVKSemaphoreMTLFence(this, pCreateInfo);
|
||||
} else if (_useMTLEventForSemaphores) {
|
||||
return new MVKSemaphoreMTLEvent(this, pCreateInfo);
|
||||
} else {
|
||||
return new MVKSemaphoreEmulated(this, pCreateInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1263,13 +1263,13 @@ void MVKPresentableSwapchainImage::signalPresentationSemaphore(id<MTLCommandBuff
|
||||
|
||||
if ( !_availabilitySignalers.empty() ) {
|
||||
MVKSemaphore* mvkSem = _availabilitySignalers.front().first;
|
||||
if (mvkSem) { mvkSem->encodeSignal(mtlCmdBuff); }
|
||||
if (mvkSem) { mvkSem->encodeSignal(mtlCmdBuff, 0); }
|
||||
}
|
||||
}
|
||||
|
||||
// Signal either or both of the semaphore and fence in the specified tracker pair.
|
||||
void MVKPresentableSwapchainImage::signal(MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
if (signaler.first) { signaler.first->encodeSignal(mtlCmdBuff); }
|
||||
if (signaler.first) { signaler.first->encodeSignal(mtlCmdBuff, 0); }
|
||||
if (signaler.second) { signaler.second->signal(); }
|
||||
}
|
||||
|
||||
|
@ -660,6 +660,9 @@ void MVKInstance::initProcAddrs() {
|
||||
ADD_DVC_EXT2_ENTRY_POINT(vkGetDeviceGroupSurfacePresentModesKHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP);
|
||||
ADD_DVC_EXT2_ENTRY_POINT(vkGetPhysicalDevicePresentRectanglesKHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP);
|
||||
ADD_DVC_EXT2_ENTRY_POINT(vkAcquireNextImage2KHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP);
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkGetSemaphoreCounterValueKHR, KHR_TIMELINE_SEMAPHORE);
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkSignalSemaphoreKHR, KHR_TIMELINE_SEMAPHORE);
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkWaitSemaphoresKHR, KHR_TIMELINE_SEMAPHORE);
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectTagEXT, EXT_DEBUG_MARKER);
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectNameEXT, EXT_DEBUG_MARKER);
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkCmdDebugMarkerBeginEXT, EXT_DEBUG_MARKER);
|
||||
|
@ -178,7 +178,7 @@ protected:
|
||||
friend class MVKQueue;
|
||||
|
||||
MVKQueue* _queue;
|
||||
MVKSmallVector<MVKSemaphore*> _waitSemaphores;
|
||||
MVKSmallVector<std::pair<MVKSemaphore*, uint64_t>> _waitSemaphores;
|
||||
bool _trackPerformance;
|
||||
};
|
||||
|
||||
@ -203,7 +203,7 @@ protected:
|
||||
void finish();
|
||||
virtual void submitCommandBuffers() {}
|
||||
|
||||
MVKSmallVector<MVKSemaphore*> _signalSemaphores;
|
||||
MVKSmallVector<std::pair<MVKSemaphore*, uint64_t>> _signalSemaphores;
|
||||
MVKFence* _fence;
|
||||
id<MTLCommandBuffer> _activeMTLCommandBuffer;
|
||||
};
|
||||
|
@ -226,7 +226,7 @@ MVKQueueSubmission::MVKQueueSubmission(MVKQueue* queue,
|
||||
|
||||
_waitSemaphores.reserve(waitSemaphoreCount);
|
||||
for (uint32_t i = 0; i < waitSemaphoreCount; i++) {
|
||||
_waitSemaphores.push_back((MVKSemaphore*)pWaitSemaphores[i]);
|
||||
_waitSemaphores.push_back(make_pair((MVKSemaphore*)pWaitSemaphores[i], (uint64_t)0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,13 +241,13 @@ void MVKQueueCommandBufferSubmission::execute() {
|
||||
_queue->_submissionCaptureScope->beginScope();
|
||||
|
||||
// If using encoded semaphore waiting, do so now.
|
||||
for (auto* ws : _waitSemaphores) { ws->encodeWait(getActiveMTLCommandBuffer()); }
|
||||
for (auto& ws : _waitSemaphores) { ws.first->encodeWait(getActiveMTLCommandBuffer(), ws.second); }
|
||||
|
||||
// Submit each command buffer.
|
||||
submitCommandBuffers();
|
||||
|
||||
// If using encoded semaphore signaling, do so now.
|
||||
for (auto* ss : _signalSemaphores) { ss->encodeSignal(getActiveMTLCommandBuffer()); }
|
||||
for (auto& ss : _signalSemaphores) { ss.first->encodeSignal(getActiveMTLCommandBuffer(), ss.second); }
|
||||
|
||||
// Commit the last MTLCommandBuffer.
|
||||
// Nothing after this because callback might destroy this instance before this function ends.
|
||||
@ -278,7 +278,7 @@ void MVKQueueCommandBufferSubmission::setActiveMTLCommandBuffer(id<MTLCommandBuf
|
||||
void MVKQueueCommandBufferSubmission::commitActiveMTLCommandBuffer(bool signalCompletion) {
|
||||
|
||||
// If using inline semaphore waiting, do so now.
|
||||
for (auto& ws : _waitSemaphores) { ws->encodeWait(nil); }
|
||||
for (auto& ws : _waitSemaphores) { ws.first->encodeWait(nil, ws.second); }
|
||||
|
||||
MVKDevice* mkvDev = _queue->_device;
|
||||
uint64_t startTime = mkvDev->getPerformanceTimestamp();
|
||||
@ -308,7 +308,7 @@ void MVKQueueCommandBufferSubmission::finish() {
|
||||
_queue->_submissionCaptureScope->endScope();
|
||||
|
||||
// If using inline semaphore signaling, do so now.
|
||||
for (auto& ss : _signalSemaphores) { ss->encodeSignal(nil); }
|
||||
for (auto& ss : _signalSemaphores) { ss.first->encodeSignal(nil, ss.second); }
|
||||
|
||||
// If a fence exists, signal it.
|
||||
if (_fence) { _fence->signal(); }
|
||||
@ -325,10 +325,29 @@ MVKQueueCommandBufferSubmission::MVKQueueCommandBufferSubmission(MVKQueue* queue
|
||||
|
||||
// pSubmit can be null if just tracking the fence alone
|
||||
if (pSubmit) {
|
||||
VkTimelineSemaphoreSubmitInfo* pTimelineSubmit = nullptr;
|
||||
for (const auto* next = (const VkBaseInStructure*)pSubmit->pNext; next; next = next->pNext) {
|
||||
switch (next->sType) {
|
||||
case VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO:
|
||||
pTimelineSubmit = (VkTimelineSemaphoreSubmitInfo*)next;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pTimelineSubmit) {
|
||||
// Presentation doesn't support timeline semaphores, so handle wait values here.
|
||||
uint32_t wsCnt = pTimelineSubmit->waitSemaphoreValueCount;
|
||||
for (uint32_t i = 0; i < wsCnt; i++) {
|
||||
_waitSemaphores[i].second = pTimelineSubmit->pWaitSemaphoreValues[i];
|
||||
}
|
||||
}
|
||||
uint32_t ssCnt = pSubmit->signalSemaphoreCount;
|
||||
_signalSemaphores.reserve(ssCnt);
|
||||
for (uint32_t i = 0; i < ssCnt; i++) {
|
||||
_signalSemaphores.push_back((MVKSemaphore*)pSubmit->pSignalSemaphores[i]);
|
||||
auto ss = make_pair((MVKSemaphore*)pSubmit->pSignalSemaphores[i], (uint64_t)0);
|
||||
if (pTimelineSubmit) { ss.second = pTimelineSubmit->pSignalSemaphoreValues[i]; }
|
||||
_signalSemaphores.push_back(ss);
|
||||
}
|
||||
}
|
||||
|
||||
@ -348,12 +367,12 @@ void MVKQueuePresentSurfaceSubmission::execute() {
|
||||
// If the semaphores are not encodable, wait on them inline after presenting.
|
||||
// The semaphores know what to do.
|
||||
id<MTLCommandBuffer> mtlCmdBuff = getMTLCommandBuffer();
|
||||
for (auto& ws : _waitSemaphores) { ws->encodeWait(mtlCmdBuff); }
|
||||
for (auto& ws : _waitSemaphores) { ws.first->encodeWait(mtlCmdBuff, 0); }
|
||||
for (int i = 0; i < _presentInfo.size(); i++ ) {
|
||||
MVKPresentableSwapchainImage *img = _presentInfo[i].presentableImage;
|
||||
img->presentCAMetalDrawable(mtlCmdBuff, _presentInfo[i]);
|
||||
}
|
||||
for (auto& ws : _waitSemaphores) { ws->encodeWait(nil); }
|
||||
for (auto& ws : _waitSemaphores) { ws.first->encodeWait(nil, 0); }
|
||||
[mtlCmdBuff commit];
|
||||
|
||||
// Let Xcode know the current frame is done, then start a new frame
|
||||
|
@ -120,6 +120,9 @@ public:
|
||||
/** Returns the debug report object type of this object. */
|
||||
VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT; }
|
||||
|
||||
/** Returns the type of this semaphore. */
|
||||
virtual VkSemaphoreType getSemaphoreType() { return VK_SEMAPHORE_TYPE_BINARY; }
|
||||
|
||||
/**
|
||||
* Wait for this semaphore to be signaled.
|
||||
*
|
||||
@ -132,8 +135,11 @@ public:
|
||||
* code path, once with a mtlCmdBuff to support encoding a wait on the command buffer if the
|
||||
* subclass supports command encoding, and once without a mtlCmdBuff, at the point in the
|
||||
* code path where the code should block if the subclass does not support command encoding.
|
||||
*
|
||||
* 'value' only applies if this semaphore is a timeline semaphore. It is the value to wait for the
|
||||
* semaphore to have.
|
||||
*/
|
||||
virtual void encodeWait(id<MTLCommandBuffer> mtlCmdBuff) = 0;
|
||||
virtual void encodeWait(id<MTLCommandBuffer> mtlCmdBuff, uint64_t value) = 0;
|
||||
|
||||
/**
|
||||
* Signals this semaphore.
|
||||
@ -147,8 +153,11 @@ public:
|
||||
* code path, once with a mtlCmdBuff to support encoding a wait on the command buffer if the
|
||||
* subclass supports command encoding, and once without a mtlCmdBuff, at the point in the
|
||||
* code path where the code should block if the subclass does not support command encoding.
|
||||
*
|
||||
* 'value' only applies if this semaphore is a timeline semaphore. It is the value to assign the semaphore
|
||||
* upon completion.
|
||||
*/
|
||||
virtual void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff) = 0;
|
||||
virtual void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t value) = 0;
|
||||
|
||||
/** Returns whether this semaphore uses command encoding. */
|
||||
virtual bool isUsingCommandEncoding() = 0;
|
||||
@ -171,8 +180,8 @@ protected:
|
||||
class MVKSemaphoreMTLFence : public MVKSemaphore {
|
||||
|
||||
public:
|
||||
void encodeWait(id<MTLCommandBuffer> mtlCmdBuff) override;
|
||||
void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff) override;
|
||||
void encodeWait(id<MTLCommandBuffer> mtlCmdBuff, uint64_t) override;
|
||||
void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t) override;
|
||||
bool isUsingCommandEncoding() override { return true; }
|
||||
|
||||
MVKSemaphoreMTLFence(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo);
|
||||
@ -191,8 +200,8 @@ protected:
|
||||
class MVKSemaphoreMTLEvent : public MVKSemaphore {
|
||||
|
||||
public:
|
||||
void encodeWait(id<MTLCommandBuffer> mtlCmdBuff) override;
|
||||
void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff) override;
|
||||
void encodeWait(id<MTLCommandBuffer> mtlCmdBuff, uint64_t) override;
|
||||
void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t) override;
|
||||
bool isUsingCommandEncoding() override { return true; }
|
||||
|
||||
MVKSemaphoreMTLEvent(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo);
|
||||
@ -212,8 +221,8 @@ protected:
|
||||
class MVKSemaphoreEmulated : public MVKSemaphore {
|
||||
|
||||
public:
|
||||
void encodeWait(id<MTLCommandBuffer> mtlCmdBuff) override;
|
||||
void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff) override;
|
||||
void encodeWait(id<MTLCommandBuffer> mtlCmdBuff, uint64_t) override;
|
||||
void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t) override;
|
||||
bool isUsingCommandEncoding() override { return false; }
|
||||
|
||||
MVKSemaphoreEmulated(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo);
|
||||
@ -223,6 +232,91 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKTimelineSemaphore
|
||||
|
||||
/** Abstract class that represents a Vulkan timeline semaphore. */
|
||||
class MVKTimelineSemaphore : public MVKSemaphore {
|
||||
|
||||
public:
|
||||
|
||||
VkSemaphoreType getSemaphoreType() override { return VK_SEMAPHORE_TYPE_TIMELINE; }
|
||||
|
||||
/** Returns the current value of the semaphore counter. */
|
||||
virtual uint64_t getCounterValue() = 0;
|
||||
|
||||
/** Signals this semaphore on the host. */
|
||||
virtual void signal(const VkSemaphoreSignalInfo* pSignalInfo) = 0;
|
||||
|
||||
/** Registers a wait for this semaphore on the host. Returns true if the semaphore is already signaled. */
|
||||
virtual bool registerWait(MVKFenceSitter* sitter, const VkSemaphoreWaitInfo* pWaitInfo, uint32_t index) = 0;
|
||||
|
||||
/** Stops waiting for this semaphore. */
|
||||
virtual void unregisterWait(MVKFenceSitter* sitter) = 0;
|
||||
|
||||
#pragma mark Construction
|
||||
|
||||
MVKTimelineSemaphore(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo, const VkSemaphoreTypeCreateInfo* pTypeCreateInfo)
|
||||
: MVKSemaphore(device, pCreateInfo) {}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKTimelineSemaphoreMTLEvent
|
||||
|
||||
/** An MVKTimelineSemaphore that uses MTLSharedEvent to provide synchronization. */
|
||||
class MVKTimelineSemaphoreMTLEvent : public MVKTimelineSemaphore {
|
||||
|
||||
public:
|
||||
void encodeWait(id<MTLCommandBuffer> mtlCmdBuff, uint64_t value) override;
|
||||
void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t value) override;
|
||||
bool isUsingCommandEncoding() override { return true; }
|
||||
|
||||
uint64_t getCounterValue() override { return _mtlEvent.signaledValue; }
|
||||
void signal(const VkSemaphoreSignalInfo* pSignalInfo) override;
|
||||
bool registerWait(MVKFenceSitter* sitter, const VkSemaphoreWaitInfo* pWaitInfo, uint32_t index) override;
|
||||
void unregisterWait(MVKFenceSitter* sitter) override;
|
||||
|
||||
MVKTimelineSemaphoreMTLEvent(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo, const VkSemaphoreTypeCreateInfo* pTypeCreateInfo);
|
||||
|
||||
~MVKTimelineSemaphoreMTLEvent() override;
|
||||
|
||||
protected:
|
||||
id<MTLSharedEvent> _mtlEvent;
|
||||
std::mutex _lock;
|
||||
std::unordered_set<MVKFenceSitter*> _sitters;
|
||||
};
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKTimelineSemaphoreEmulated
|
||||
|
||||
/** An MVKTimelineSemaphore that uses CPU synchronization to provide synchronization functionality. */
|
||||
class MVKTimelineSemaphoreEmulated : public MVKTimelineSemaphore {
|
||||
|
||||
public:
|
||||
void encodeWait(id<MTLCommandBuffer> mtlCmdBuff, uint64_t value) override;
|
||||
void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t value) override;
|
||||
bool isUsingCommandEncoding() override { return false; }
|
||||
|
||||
uint64_t getCounterValue() override { return _value; }
|
||||
void signal(const VkSemaphoreSignalInfo* pSignalInfo) override;
|
||||
bool registerWait(MVKFenceSitter* sitter, const VkSemaphoreWaitInfo* pWaitInfo, uint32_t index) override;
|
||||
void unregisterWait(MVKFenceSitter* sitter) override;
|
||||
|
||||
MVKTimelineSemaphoreEmulated(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo, const VkSemaphoreTypeCreateInfo* pTypeCreateInfo);
|
||||
|
||||
protected:
|
||||
void signalImpl(uint64_t value);
|
||||
|
||||
std::atomic<uint64_t> _value;
|
||||
std::mutex _lock;
|
||||
std::condition_variable _blocker;
|
||||
std::unordered_map<uint64_t, std::unordered_set<MVKFenceSitter*>> _sitters;
|
||||
};
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKFence
|
||||
|
||||
@ -240,11 +334,10 @@ public:
|
||||
/**
|
||||
* If this fence has not been signaled yet, adds the specified fence sitter to the
|
||||
* internal list of fence sitters that will be notified when this fence is signaled,
|
||||
* and then calls addUnsignaledFence() on the fence sitter so it is aware that it
|
||||
* will be signaled.
|
||||
* and then calls await() on the fence sitter so it is aware that it will be signaled.
|
||||
*
|
||||
* Does nothing if this fence has already been signaled, and does not call
|
||||
* addUnsignaledFence() on the fence sitter.
|
||||
* await() on the fence sitter.
|
||||
*
|
||||
* Each fence sitter should only listen once for each fence. Adding the same fence sitter
|
||||
* more than once in between each fence reset and signal results in undefined behaviour.
|
||||
@ -282,7 +375,7 @@ protected:
|
||||
#pragma mark -
|
||||
#pragma mark MVKFenceSitter
|
||||
|
||||
/** An object that responds to signals from MVKFences. */
|
||||
/** An object that responds to signals from MVKFences and MVKTimelineSemaphores. */
|
||||
class MVKFenceSitter : public MVKBaseObject {
|
||||
|
||||
public:
|
||||
@ -309,11 +402,16 @@ public:
|
||||
|
||||
private:
|
||||
friend class MVKFence;
|
||||
friend class MVKTimelineSemaphoreMTLEvent;
|
||||
friend class MVKTimelineSemaphoreEmulated;
|
||||
|
||||
void awaitFence(MVKFence* fence) { _blocker.reserve(); }
|
||||
void fenceSignaled(MVKFence* fence) { _blocker.release(); }
|
||||
MTLSharedEventListener* getMTLSharedEventListener();
|
||||
|
||||
void await() { _blocker.reserve(); }
|
||||
void signaled() { _blocker.release(); }
|
||||
|
||||
MVKSemaphoreImpl _blocker;
|
||||
MTLSharedEventListener* _listener = nil;
|
||||
};
|
||||
|
||||
|
||||
@ -411,6 +509,15 @@ VkResult mvkWaitForFences(MVKDevice* device,
|
||||
VkBool32 waitAll,
|
||||
uint64_t timeout = UINT64_MAX);
|
||||
|
||||
/**
|
||||
* Blocks the current thread until any or all of the specified
|
||||
* semaphores have been signaled at the specified values, or the
|
||||
* specified timeout occurs.
|
||||
*/
|
||||
VkResult mvkWaitSemaphores(MVKDevice* device,
|
||||
const VkSemaphoreWaitInfo* pWaitInfo,
|
||||
uint64_t timeout = UINT64_MAX);
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKMetalCompiler
|
||||
|
@ -82,7 +82,7 @@ MVKSemaphoreImpl::~MVKSemaphoreImpl() {
|
||||
|
||||
// Could use any encoder. Assume BLIT is fastest and lightest.
|
||||
// Nil mtlCmdBuff will do nothing.
|
||||
void MVKSemaphoreMTLFence::encodeWait(id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
void MVKSemaphoreMTLFence::encodeWait(id<MTLCommandBuffer> mtlCmdBuff, uint64_t) {
|
||||
id<MTLBlitCommandEncoder> mtlCmdEnc = mtlCmdBuff.blitCommandEncoder;
|
||||
[mtlCmdEnc waitForFence: _mtlFence];
|
||||
[mtlCmdEnc endEncoding];
|
||||
@ -90,7 +90,7 @@ void MVKSemaphoreMTLFence::encodeWait(id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
|
||||
// Could use any encoder. Assume BLIT is fastest and lightest.
|
||||
// Nil mtlCmdBuff will do nothing.
|
||||
void MVKSemaphoreMTLFence::encodeSignal(id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
void MVKSemaphoreMTLFence::encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t) {
|
||||
id<MTLBlitCommandEncoder> mtlCmdEnc = mtlCmdBuff.blitCommandEncoder;
|
||||
[mtlCmdEnc updateFence: _mtlFence];
|
||||
[mtlCmdEnc endEncoding];
|
||||
@ -109,12 +109,12 @@ MVKSemaphoreMTLFence::~MVKSemaphoreMTLFence() {
|
||||
#pragma mark MVKSemaphoreMTLEvent
|
||||
|
||||
// Nil mtlCmdBuff will do nothing.
|
||||
void MVKSemaphoreMTLEvent::encodeWait(id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
void MVKSemaphoreMTLEvent::encodeWait(id<MTLCommandBuffer> mtlCmdBuff, uint64_t) {
|
||||
[mtlCmdBuff encodeWaitForEvent: _mtlEvent value: _mtlEventValue++];
|
||||
}
|
||||
|
||||
// Nil mtlCmdBuff will do nothing.
|
||||
void MVKSemaphoreMTLEvent::encodeSignal(id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
void MVKSemaphoreMTLEvent::encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t) {
|
||||
[mtlCmdBuff encodeSignalEvent: _mtlEvent value: _mtlEventValue];
|
||||
}
|
||||
|
||||
@ -131,11 +131,11 @@ MVKSemaphoreMTLEvent::~MVKSemaphoreMTLEvent() {
|
||||
#pragma mark -
|
||||
#pragma mark MVKSemaphoreEmulated
|
||||
|
||||
void MVKSemaphoreEmulated::encodeWait(id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
void MVKSemaphoreEmulated::encodeWait(id<MTLCommandBuffer> mtlCmdBuff, uint64_t) {
|
||||
if ( !mtlCmdBuff ) { _blocker.wait(UINT64_MAX, true); }
|
||||
}
|
||||
|
||||
void MVKSemaphoreEmulated::encodeSignal(id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
void MVKSemaphoreEmulated::encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t) {
|
||||
if ( !mtlCmdBuff ) { _blocker.release(); }
|
||||
}
|
||||
|
||||
@ -144,6 +144,116 @@ MVKSemaphoreEmulated::MVKSemaphoreEmulated(MVKDevice* device, const VkSemaphoreC
|
||||
_blocker(false, 1) {}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKTimelineSemaphoreMTLEvent
|
||||
|
||||
// Nil mtlCmdBuff will do nothing.
|
||||
void MVKTimelineSemaphoreMTLEvent::encodeWait(id<MTLCommandBuffer> mtlCmdBuff, uint64_t value) {
|
||||
[mtlCmdBuff encodeWaitForEvent: _mtlEvent value: value];
|
||||
}
|
||||
|
||||
// Nil mtlCmdBuff will do nothing.
|
||||
void MVKTimelineSemaphoreMTLEvent::encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t value) {
|
||||
[mtlCmdBuff encodeSignalEvent: _mtlEvent value: value];
|
||||
}
|
||||
|
||||
void MVKTimelineSemaphoreMTLEvent::signal(const VkSemaphoreSignalInfo* pSignalInfo) {
|
||||
_mtlEvent.signaledValue = pSignalInfo->value;
|
||||
}
|
||||
|
||||
bool MVKTimelineSemaphoreMTLEvent::registerWait(MVKFenceSitter* sitter, const VkSemaphoreWaitInfo* pWaitInfo, uint32_t index) {
|
||||
if (_mtlEvent.signaledValue >= pWaitInfo->pValues[index]) { return true; }
|
||||
lock_guard<mutex> lock(_lock);
|
||||
sitter->await();
|
||||
auto addRslt = _sitters.insert(sitter);
|
||||
if (addRslt.second) {
|
||||
retain();
|
||||
[_mtlEvent notifyListener: sitter->getMTLSharedEventListener()
|
||||
atValue: pWaitInfo->pValues[index]
|
||||
block: ^(id<MTLSharedEvent>, uint64_t) {
|
||||
lock_guard<mutex> blockLock(_lock);
|
||||
if (_sitters.count(sitter)) { sitter->signaled(); }
|
||||
release();
|
||||
}];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MVKTimelineSemaphoreMTLEvent::unregisterWait(MVKFenceSitter* sitter) {
|
||||
lock_guard<mutex> lock(_lock);
|
||||
_sitters.erase(sitter);
|
||||
}
|
||||
|
||||
MVKTimelineSemaphoreMTLEvent::MVKTimelineSemaphoreMTLEvent(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo, const VkSemaphoreTypeCreateInfo* pTypeCreateInfo) :
|
||||
MVKTimelineSemaphore(device, pCreateInfo, pTypeCreateInfo),
|
||||
_mtlEvent([device->getMTLDevice() newSharedEvent]) { //retained
|
||||
|
||||
_mtlEvent.signaledValue = pTypeCreateInfo->initialValue;
|
||||
}
|
||||
|
||||
MVKTimelineSemaphoreMTLEvent::~MVKTimelineSemaphoreMTLEvent() {
|
||||
[_mtlEvent release];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKSemaphoreEmulated
|
||||
|
||||
void MVKTimelineSemaphoreEmulated::encodeWait(id<MTLCommandBuffer> mtlCmdBuff, uint64_t value) {
|
||||
unique_lock<mutex> lock(_lock);
|
||||
if ( !mtlCmdBuff ) { _blocker.wait(lock, [=]() { return _value >= value; }); }
|
||||
}
|
||||
|
||||
void MVKTimelineSemaphoreEmulated::encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t value) {
|
||||
lock_guard<mutex> lock(_lock);
|
||||
if ( !mtlCmdBuff ) { signalImpl(value); }
|
||||
}
|
||||
|
||||
void MVKTimelineSemaphoreEmulated::signal(const VkSemaphoreSignalInfo* pSignalInfo) {
|
||||
lock_guard<mutex> lock(_lock);
|
||||
signalImpl(pSignalInfo->value);
|
||||
}
|
||||
|
||||
void MVKTimelineSemaphoreEmulated::signalImpl(uint64_t value) {
|
||||
if (value > _value) {
|
||||
_value = value;
|
||||
_blocker.notify_all();
|
||||
for (auto& sittersForValue : _sitters) {
|
||||
if (sittersForValue.first < value) { continue; }
|
||||
for (auto* sitter : sittersForValue.second) {
|
||||
sitter->signaled();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MVKTimelineSemaphoreEmulated::registerWait(MVKFenceSitter* sitter, const VkSemaphoreWaitInfo* pWaitInfo, uint32_t index) {
|
||||
lock_guard<mutex> lock(_lock);
|
||||
if (pWaitInfo->pValues[index] >= _value) { return true; }
|
||||
uint64_t value = pWaitInfo->pValues[index];
|
||||
if (!_sitters.count(value)) { _sitters.emplace(make_pair(value, unordered_set<MVKFenceSitter*>())); }
|
||||
auto addRslt = _sitters[value].insert(sitter);
|
||||
if (addRslt.second) { sitter->await(); }
|
||||
return false;
|
||||
}
|
||||
|
||||
void MVKTimelineSemaphoreEmulated::unregisterWait(MVKFenceSitter* sitter) {
|
||||
MVKSmallVector<uint64_t> emptySets;
|
||||
for (auto& sittersForValue : _sitters) {
|
||||
sittersForValue.second.erase(sitter);
|
||||
// Can't destroy while iterating...
|
||||
if (sittersForValue.second.empty()) {
|
||||
emptySets.push_back(sittersForValue.first);
|
||||
}
|
||||
}
|
||||
for (auto value : emptySets) { _sitters.erase(value); }
|
||||
}
|
||||
|
||||
MVKTimelineSemaphoreEmulated::MVKTimelineSemaphoreEmulated(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo, const VkSemaphoreTypeCreateInfo* pTypeCreateInfo) :
|
||||
MVKTimelineSemaphore(device, pCreateInfo, pTypeCreateInfo),
|
||||
_value(pTypeCreateInfo->initialValue) {}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKFence
|
||||
|
||||
@ -156,7 +266,7 @@ void MVKFence::addSitter(MVKFenceSitter* fenceSitter) {
|
||||
|
||||
// Ensure each fence only added once to each fence sitter
|
||||
auto addRslt = _fenceSitters.insert(fenceSitter); // pair with second element true if was added
|
||||
if (addRslt.second) { fenceSitter->awaitFence(this); }
|
||||
if (addRslt.second) { fenceSitter->await(); }
|
||||
}
|
||||
|
||||
void MVKFence::removeSitter(MVKFenceSitter* fenceSitter) {
|
||||
@ -173,7 +283,7 @@ void MVKFence::signal() {
|
||||
|
||||
// Notify all the fence sitters, and clear them from this instance.
|
||||
for (auto& fs : _fenceSitters) {
|
||||
fs->fenceSignaled(this);
|
||||
fs->signaled();
|
||||
}
|
||||
_fenceSitters.clear();
|
||||
}
|
||||
@ -192,6 +302,16 @@ bool MVKFence::getIsSignaled() {
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKFenceSitter
|
||||
|
||||
MTLSharedEventListener* MVKFenceSitter::getMTLSharedEventListener() {
|
||||
// TODO: Use dispatch queue from device?
|
||||
if (!_listener) { _listener = [MTLSharedEventListener new]; }
|
||||
return _listener;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKEventNative
|
||||
|
||||
@ -295,6 +415,33 @@ VkResult mvkWaitForFences(MVKDevice* device,
|
||||
return rslt;
|
||||
}
|
||||
|
||||
// Create a blocking fence sitter, add it to each semaphore, wait, then remove it.
|
||||
VkResult mvkWaitSemaphores(MVKDevice* device,
|
||||
const VkSemaphoreWaitInfo* pWaitInfo,
|
||||
uint64_t timeout) {
|
||||
|
||||
VkResult rslt = VK_SUCCESS;
|
||||
bool waitAny = mvkIsAnyFlagEnabled(pWaitInfo->flags, VK_SEMAPHORE_WAIT_ANY_BIT);
|
||||
bool alreadySignaled = false;
|
||||
MVKFenceSitter fenceSitter(!waitAny);
|
||||
|
||||
for (uint32_t i = 0; i < pWaitInfo->semaphoreCount; i++) {
|
||||
if (((MVKTimelineSemaphore*)pWaitInfo->pSemaphores[i])->registerWait(&fenceSitter, pWaitInfo, i) && waitAny) {
|
||||
// In this case, we don't need to wait.
|
||||
alreadySignaled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !alreadySignaled && !fenceSitter.wait(timeout) ) { rslt = VK_TIMEOUT; }
|
||||
|
||||
for (uint32_t i = 0; i < pWaitInfo->semaphoreCount; i++) {
|
||||
((MVKTimelineSemaphore*)pWaitInfo->pSemaphores[i])->unregisterWait(&fenceSitter);
|
||||
}
|
||||
|
||||
return rslt;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKMetalCompiler
|
||||
|
@ -75,6 +75,7 @@ MVK_EXTENSION(KHR_storage_buffer_storage_class, KHR_STORAGE_BUFFER_STORAGE_CLASS
|
||||
MVK_EXTENSION(KHR_surface, KHR_SURFACE, INSTANCE)
|
||||
MVK_EXTENSION(KHR_swapchain, KHR_SWAPCHAIN, DEVICE)
|
||||
MVK_EXTENSION(KHR_swapchain_mutable_format, KHR_SWAPCHAIN_MUTABLE_FORMAT, DEVICE)
|
||||
MVK_EXTENSION(KHR_timeline_semaphore, KHR_TIMELINE_SEMAPHORE, DEVICE)
|
||||
MVK_EXTENSION(KHR_uniform_buffer_standard_layout, KHR_UNIFORM_BUFFER_STANDARD_LAYOUT, DEVICE)
|
||||
MVK_EXTENSION(KHR_variable_pointers, KHR_VARIABLE_POINTERS, DEVICE)
|
||||
MVK_EXTENSION(EXT_debug_marker, EXT_DEBUG_MARKER, DEVICE)
|
||||
|
@ -2663,6 +2663,45 @@ MVK_PUBLIC_SYMBOL VkResult vkGetPhysicalDeviceSurfaceFormats2KHR(
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark VK_KHR_timeline_semaphore
|
||||
|
||||
MVK_PUBLIC_SYMBOL VkResult vkGetSemaphoreCounterValueKHR(
|
||||
VkDevice device,
|
||||
VkSemaphore semaphore,
|
||||
uint64_t* pValue) {
|
||||
|
||||
MVKTraceVulkanCallStart();
|
||||
auto* mvkSem4 = (MVKTimelineSemaphore*)semaphore;
|
||||
*pValue = mvkSem4->getCounterValue();
|
||||
MVKTraceVulkanCallEnd();
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
MVK_PUBLIC_SYMBOL VkResult vkSignalSemaphoreKHR(
|
||||
VkDevice device,
|
||||
const VkSemaphoreSignalInfoKHR* pSignalInfo) {
|
||||
|
||||
MVKTraceVulkanCallStart();
|
||||
auto* mvkSem4 = (MVKTimelineSemaphore*)pSignalInfo->semaphore;
|
||||
mvkSem4->signal(pSignalInfo);
|
||||
MVKTraceVulkanCallEnd();
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
MVK_PUBLIC_SYMBOL VkResult vkWaitSemaphoresKHR(
|
||||
VkDevice device,
|
||||
const VkSemaphoreWaitInfoKHR* pWaitInfo,
|
||||
uint64_t timeout) {
|
||||
|
||||
MVKTraceVulkanCallStart();
|
||||
MVKDevice* mvkDev = MVKDevice::getMVKDevice(device);
|
||||
VkResult rslt = mvkWaitSemaphores(mvkDev, pWaitInfo, timeout);
|
||||
MVKTraceVulkanCallEnd();
|
||||
return rslt;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark VK_EXT_debug_report extension
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user