Merge pull request #124 from billhollings/master

Queue and device wait idle logic and update to latest version of VulkanSamples.
This commit is contained in:
Bill Hollings 2018-04-06 00:07:04 -04:00 committed by GitHub
commit 9c8117efe6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 36 additions and 103 deletions

View File

@ -42,7 +42,7 @@
self.view.contentScaleFactor = UIScreen.mainScreen.nativeScale; self.view.contentScaleFactor = UIScreen.mainScreen.nativeScale;
demo_main(&demo, self.view); demo_main(&demo, self.view);
demo_update_and_draw(&demo); demo_draw(&demo);
uint32_t fps = 60; uint32_t fps = 60;
_displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderLoop)]; _displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderLoop)];
@ -51,7 +51,7 @@
} }
-(void) renderLoop { -(void) renderLoop {
demo_update_and_draw(&demo); demo_draw(&demo);
} }
@end @end

View File

@ -59,7 +59,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
CVOptionFlags flagsIn, CVOptionFlags flagsIn,
CVOptionFlags* flagsOut, CVOptionFlags* flagsOut,
void* target) { void* target) {
demo_update_and_draw((struct demo*)target); demo_draw((struct demo*)target);
return kCVReturnSuccess; return kCVReturnSuccess;
} }

View File

@ -1 +1 @@
d435e00dc9f614155621cdadd2f00fbd0585cda6 8dc54f70b2f11858b422477e7a9eaa7fb455dee2

View File

@ -90,10 +90,9 @@ VkResult MVKQueue::submitPresentKHR(const VkPresentInfoKHR* pPresentInfo) {
return rslt; return rslt;
} }
// Create an empty submit struct and fence, submit to queue and wait on fence.
VkResult MVKQueue::waitIdle(MVKCommandUse cmdBuffUse) { VkResult MVKQueue::waitIdle(MVKCommandUse cmdBuffUse) {
// Create submit struct including a temp Vulkan reference to a semaphore
VkSemaphore vkSem4;
VkSubmitInfo vkSbmtInfo = { VkSubmitInfo vkSbmtInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = NULL, .pNext = NULL,
@ -101,22 +100,20 @@ VkResult MVKQueue::waitIdle(MVKCommandUse cmdBuffUse) {
.pWaitSemaphores = VK_NULL_HANDLE, .pWaitSemaphores = VK_NULL_HANDLE,
.commandBufferCount = 0, .commandBufferCount = 0,
.pCommandBuffers = VK_NULL_HANDLE, .pCommandBuffers = VK_NULL_HANDLE,
.signalSemaphoreCount = 1, .signalSemaphoreCount = 0,
.pSignalSemaphores = &vkSem4 .pSignalSemaphores = VK_NULL_HANDLE
}; };
VkSemaphoreCreateInfo vkSemInfo = { VkFenceCreateInfo vkFenceInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.pNext = NULL, .pNext = NULL,
.flags = 0, .flags = 0,
}; };
MVKSemaphore mvkSem4(_device, &vkSemInfo); // Construct MVKSemaphore MVKFence mvkFence(_device, &vkFenceInfo);
vkSem4 = (VkSemaphore)&mvkSem4; // Set reference to MVKSemaphore in submit struct VkFence fence = (VkFence)&mvkFence;
submit(1, &vkSbmtInfo, VK_NULL_HANDLE, cmdBuffUse); // Submit semaphore queue submit(1, &vkSbmtInfo, fence, cmdBuffUse);
mvkSem4.wait(); // Wait on the semaphore return mvkWaitForFences(1, &fence, false);
return VK_SUCCESS;
} }
// This function is guarded against conflict with the mtlCommandBufferHasCompleted() // This function is guarded against conflict with the mtlCommandBufferHasCompleted()

View File

@ -68,34 +68,16 @@ VkResult MVKSwapchain::acquireNextImageKHR(uint64_t timeout,
MVKSwapchainImageAvailability minAvailability = { .acquisitionID = numeric_limits<uint64_t>::max(), .waitCount = numeric_limits<uint32_t>::max(), .isAvailable = false }; MVKSwapchainImageAvailability minAvailability = { .acquisitionID = numeric_limits<uint64_t>::max(), .waitCount = numeric_limits<uint32_t>::max(), .isAvailable = false };
for (MVKSwapchainImage* mvkSCImg : _surfaceImages) { for (MVKSwapchainImage* mvkSCImg : _surfaceImages) {
const MVKSwapchainImageAvailability* currAvailability = mvkSCImg->getAvailability(); const MVKSwapchainImageAvailability* currAvailability = mvkSCImg->getAvailability();
// MVKLogDebug("Comparing availability (isAvailable: %d waitCount: %d, acquisitionID: %d) to (isAvailable: %d waitCount: %d, acquisitionID: %d)",
// currAvailability->isAvailable, currAvailability->waitCount, currAvailability->acquisitionID,
// minAvailability.isAvailable, minAvailability.waitCount, minAvailability.acquisitionID);
if (*currAvailability < minAvailability) { if (*currAvailability < minAvailability) {
minAvailability = *currAvailability; minAvailability = *currAvailability;
minWaitIndex = mvkSCImg->getSwapchainIndex(); minWaitIndex = mvkSCImg->getSwapchainIndex();
// MVKLogDebug("Is smaller! Index: %d", minWaitIndex);
} }
} }
// MVKLogDebug("Selected MVKSwapchainImage %p, index: %d to trigger semaphore %p and fence %p", _surfaceImages[minWaitIndex], minWaitIndex, semaphore, fence); // MVKLogDebug("Selected MVKSwapchainImage %p, index: %d to trigger semaphore %p and fence %p", _surfaceImages[minWaitIndex], minWaitIndex, semaphore, fence);
*pImageIndex = minWaitIndex; // Return the index of the image with the shortest wait *pImageIndex = minWaitIndex; // Return the index of the image with the shortest wait
if (semaphore || fence) { _surfaceImages[minWaitIndex]->signalWhenAvailable((MVKSemaphore*)semaphore, (MVKFence*)fence);
MVKSemaphore* mvkSem4 = (MVKSemaphore*)semaphore;
MVKFence* mvkFence = (MVKFence*)fence;
_surfaceImages[minWaitIndex]->signalWhenAvailable(mvkSem4, mvkFence);
} else {
// Interpret a NULL semaphore to mean that this function itself should block
// until the image is available. Create temp semaphore and wait on it here.
VkSemaphoreCreateInfo semaphoreCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = NULL,
};
MVKSemaphore mvkSem4(_device, &semaphoreCreateInfo);
_surfaceImages[minWaitIndex]->signalWhenAvailable(&mvkSem4, NULL);
if ( !mvkSem4.wait(timeout) ) { return VK_TIMEOUT; }
}
return getHasSurfaceSizeChanged() ? VK_SUBOPTIMAL_KHR : VK_SUCCESS; return getHasSurfaceSizeChanged() ? VK_SUBOPTIMAL_KHR : VK_SUCCESS;
} }

View File

@ -55,24 +55,17 @@ public:
*/ */
void release(); void release();
/**
* Indefinitely blocks processing on the current thread until either any or all
* (depending on configuration) outstanding reservations have been released.
*
* If reserveAgain is set to true, a single reservation will be added to this
* instance once the wait is finished.
*/
void wait(bool reserveAgain = false);
/** /**
* Blocks processing on the current thread until any or all (depending on configuration) outstanding * Blocks processing on the current thread until any or all (depending on configuration) outstanding
* reservations have been released, or until the specified timeout interval in nanoseconds expires. * reservations have been released, or until the specified timeout interval in nanoseconds expires.
*
* If timeout is the special value UINT64_MAX the timeout is treated as infinite.
* *
* If reserveAgain is set to true, a single reservation will be added once this wait is finished. * If reserveAgain is set to true, a single reservation will be added once this wait is finished.
* *
* Returns true if all reservations were cleared, or false if the timeout interval expired. * Returns true if all reservations were cleared, or false if the timeout interval expired.
*/ */
bool wait(uint64_t timeout, bool reserveAgain = false); bool wait(uint64_t timeout = UINT64_MAX, bool reserveAgain = false);
#pragma mark Construction #pragma mark Construction
@ -111,16 +104,15 @@ class MVKSemaphore : public MVKBaseDeviceObject {
public: public:
/** Indefinitely blocks processing on the current thread until this semaphore is signaled. */
void wait();
/** /**
* Blocks processing on the current thread until this semaphore is * Blocks processing on the current thread until this semaphore is
* signaled, or until the specified timeout in nanoseconds expires. * signaled, or until the specified timeout in nanoseconds expires.
* *
* If timeout is the special value UINT64_MAX the timeout is treated as infinite.
*
* Returns true if this semaphore was signaled, or false if the timeout interval expired. * Returns true if this semaphore was signaled, or false if the timeout interval expired.
*/ */
bool wait(uint64_t timeout); bool wait(uint64_t timeout = UINT64_MAX);
/** Signals the semaphore. Unblocks all waiting threads to continue processing. */ /** Signals the semaphore. Unblocks all waiting threads to continue processing. */
void signal(); void signal();
@ -195,25 +187,17 @@ class MVKFenceSitter : public MVKBaseObject {
public: public:
/**
* If this instance has been configured to wait for fences, blocks processing on the
* current thread until any or all of the fences that this instance is waiting for are
* signaled. If this instance has not been configured to wait for fences, this function
* immediately returns true.
*
* Returns whether the lock timed out while waiting.
*/
void wait();
/** /**
* If this instance has been configured to wait for fences, blocks processing on the * If this instance has been configured to wait for fences, blocks processing on the
* current thread until any or all of the fences that this instance is waiting for are * current thread until any or all of the fences that this instance is waiting for are
* signaled, or until the specified timeout in nanoseconds expires. If this instance * signaled, or until the specified timeout in nanoseconds expires. If this instance
* has not been configured to wait for fences, this function immediately returns true. * has not been configured to wait for fences, this function immediately returns true.
* *
* If timeout is the special value UINT64_MAX the timeout is treated as infinite.
*
* Returns true if the required fences were triggered, or false if the timeout interval expired. * Returns true if the required fences were triggered, or false if the timeout interval expired.
*/ */
bool wait(uint64_t timeout); bool wait(uint64_t timeout = UINT64_MAX);
#pragma mark Construction #pragma mark Construction
@ -248,6 +232,6 @@ VkResult mvkResetFences(uint32_t fenceCount, const VkFence* pFences);
VkResult mvkWaitForFences(uint32_t fenceCount, VkResult mvkWaitForFences(uint32_t fenceCount,
const VkFence* pFences, const VkFence* pFences,
VkBool32 waitAll, VkBool32 waitAll,
uint64_t timeout); uint64_t timeout = UINT64_MAX);

View File

@ -44,23 +44,20 @@ void MVKSemaphoreImpl::reserve() {
reserveImpl(); reserveImpl();
} }
void MVKSemaphoreImpl::wait(bool reserveAgain) {
unique_lock<mutex> lock(_lock);
_blocker.wait(lock, [this]{ return isClear(); });
if (reserveAgain) { reserveImpl(); }
}
bool MVKSemaphoreImpl::wait(uint64_t timeout, bool reserveAgain) { bool MVKSemaphoreImpl::wait(uint64_t timeout, bool reserveAgain) {
unique_lock<mutex> lock(_lock); unique_lock<mutex> lock(_lock);
bool isDone; bool isDone;
if (timeout > 0) { if (timeout == 0) {
isDone = isClear();
} else if (timeout == UINT64_MAX) {
_blocker.wait(lock, [this]{ return isClear(); });
isDone = true;
} else {
// Limit timeout to avoid overflow since wait_for() uses wait_until() // Limit timeout to avoid overflow since wait_for() uses wait_until()
uint64_t nanoTimeout = min(timeout, numeric_limits<uint64_t>::max() >> 4); uint64_t nanoTimeout = min(timeout, numeric_limits<uint64_t>::max() >> 4);
chrono::nanoseconds nanos(nanoTimeout); chrono::nanoseconds nanos(nanoTimeout);
isDone = _blocker.wait_for(lock, nanos, [this]{ return isClear(); }); isDone = _blocker.wait_for(lock, nanos, [this]{ return isClear(); });
} else {
isDone = isClear();
} }
if (reserveAgain) { reserveImpl(); } if (reserveAgain) { reserveImpl(); }
@ -71,23 +68,14 @@ bool MVKSemaphoreImpl::wait(uint64_t timeout, bool reserveAgain) {
#pragma mark - #pragma mark -
#pragma mark MVKSemaphore #pragma mark MVKSemaphore
void MVKSemaphore::wait() {
// MVKLogDebug("Waiting on semaphore %p. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds());
_blocker.wait(true);
// MVKLogDebug("Done waiting on semaphore %p. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds());
}
bool MVKSemaphore::wait(uint64_t timeout) { bool MVKSemaphore::wait(uint64_t timeout) {
// MVKLogDebug("Waiting on semaphore %p for max timeout %llu. Elapsed time: %.6f ms.", this, timeout, mvkGetElapsedMilliseconds());
bool isDone = _blocker.wait(timeout, true); bool isDone = _blocker.wait(timeout, true);
// MVKLogDebug("Done waiting on semaphore %p. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds());
if ( !isDone && timeout > 0 ) { mvkNotifyErrorWithText(VK_TIMEOUT, "Vulkan semaphore timeout after %llu nanoseconds.", timeout); } if ( !isDone && timeout > 0 ) { mvkNotifyErrorWithText(VK_TIMEOUT, "Vulkan semaphore timeout after %llu nanoseconds.", timeout); }
return isDone; return isDone;
} }
void MVKSemaphore::signal() { void MVKSemaphore::signal() {
_blocker.release(); _blocker.release();
// MVKLogDebug("Signalled semaphore %p. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds());
} }
@ -99,21 +87,15 @@ void MVKFence::addSitter(MVKFenceSitter* fenceSitter) {
// Sitters only care about unsignaled fences. If already signaled, // Sitters only care about unsignaled fences. If already signaled,
// don't add myself to the sitter and don't notify the sitter. // don't add myself to the sitter and don't notify the sitter.
if (_isSignaled) { if (_isSignaled) { return; }
// MVKLogDebug("Fence %p already signaled. Sitter not added. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds());
return;
}
// Ensure each fence only added once to each fence sitter // Ensure each fence only added once to each fence sitter
auto addRslt = _fenceSitters.insert(fenceSitter); // pair with second element true if was added auto addRslt = _fenceSitters.insert(fenceSitter); // pair with second element true if was added
if (addRslt.second) { fenceSitter->addUnsignaledFence(this); } if (addRslt.second) { fenceSitter->addUnsignaledFence(this); }
// MVKLogDebug("Fence %p adding sitter %d. Elapsed time: %.6f ms.", this, _fenceSitters.size(), mvkGetElapsedMilliseconds());
} }
void MVKFence::removeSitter(MVKFenceSitter* fenceSitter) { void MVKFence::removeSitter(MVKFenceSitter* fenceSitter) {
lock_guard<mutex> lock(_lock); lock_guard<mutex> lock(_lock);
// MVKLogDebug("Fence %p removing sitter %d. Elapsed time: %.6f ms.", this, _fenceSitters.size(), mvkGetElapsedMilliseconds());
_fenceSitters.erase(fenceSitter); _fenceSitters.erase(fenceSitter);
} }
@ -123,8 +105,6 @@ void MVKFence::signal() {
if (_isSignaled) { return; } // Only signal once if (_isSignaled) { return; } // Only signal once
_isSignaled = true; _isSignaled = true;
// MVKLogDebug("Fence %p with %d sitters signaled. Elapsed time: %.6f ms.", this , _fenceSitters.size(), mvkGetElapsedMilliseconds());
// Notify all the fence sitters, and clear them from this instance. // Notify all the fence sitters, and clear them from this instance.
for (auto& fs : _fenceSitters) { for (auto& fs : _fenceSitters) {
fs->fenceSignaled(this); fs->fenceSignaled(this);
@ -136,12 +116,10 @@ void MVKFence::reset() {
lock_guard<mutex> lock(_lock); lock_guard<mutex> lock(_lock);
_isSignaled = false; _isSignaled = false;
_fenceSitters.clear(); _fenceSitters.clear();
// MVKLogDebug("Reset fence %p. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds());
} }
bool MVKFence::getIsSignaled() { bool MVKFence::getIsSignaled() {
lock_guard<mutex> lock(_lock); lock_guard<mutex> lock(_lock);
// MVKLogDebug("Checking fence %p status: %ssignaled. Elapsed time: %.6f ms.", this, (_isSignaled ? "" : "un"), mvkGetElapsedMilliseconds());
return _isSignaled; return _isSignaled;
} }
@ -172,8 +150,6 @@ void MVKFenceSitter::fenceSignaled(MVKFence* fence) {
if (_unsignaledFences.erase(fence)) { _blocker.release(); } if (_unsignaledFences.erase(fence)) { _blocker.release(); }
} }
void MVKFenceSitter::wait() { _blocker.wait(); }
bool MVKFenceSitter::wait(uint64_t timeout) { bool MVKFenceSitter::wait(uint64_t timeout) {
bool isDone = _blocker.wait(timeout); bool isDone = _blocker.wait(timeout);
if ( !isDone && timeout > 0 ) { mvkNotifyErrorWithText(VK_TIMEOUT, "Vulkan fence timeout after %llu nanoseconds.", timeout); } if ( !isDone && timeout > 0 ) { mvkNotifyErrorWithText(VK_TIMEOUT, "Vulkan fence timeout after %llu nanoseconds.", timeout); }
@ -206,19 +182,13 @@ VkResult mvkWaitForFences(uint32_t fenceCount,
VkBool32 waitAll, VkBool32 waitAll,
uint64_t timeout) { uint64_t timeout) {
// MVKLogDebug("Waiting for fences. Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds());
// Create a blocking fence sitter and add it to each fence // Create a blocking fence sitter and add it to each fence
MVKFenceSitter fenceSitter(waitAll); MVKFenceSitter fenceSitter(waitAll);
for (uint32_t i = 0; i < fenceCount; i++) { for (uint32_t i = 0; i < fenceCount; i++) {
MVKFence* mvkFence = (MVKFence*)pFences[i]; MVKFence* mvkFence = (MVKFence*)pFences[i];
mvkFence->addSitter(&fenceSitter); mvkFence->addSitter(&fenceSitter);
} }
VkResult rslt = fenceSitter.wait(timeout) ? VK_SUCCESS : VK_TIMEOUT; return fenceSitter.wait(timeout) ? VK_SUCCESS : VK_TIMEOUT;
// MVKLogDebug("Fences done. Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds());
return rslt;
} }