Merge pull request #724 from billhollings/master
Streamline design and use of MVKSemaphore
This commit is contained in:
commit
90f2b7a1af
@ -28,6 +28,7 @@ Released TBD
|
||||
- Ensure Vulkan loader magic number is set every time before returning any dispatchable Vulkan handle.
|
||||
- Fix crash when `VkDeviceCreateInfo` specifies queue families out of numerical order.
|
||||
- Fix crash in `vkDestroyPipelineLayout()`.
|
||||
- Fix crash when signalling swapchain semaphore using `MTLEvent`.
|
||||
- `vkCmdBlitImage()` support format component swizzling.
|
||||
- `vkCmdClearImage()` set error if attempt made to clear 1D image, and fix validation of depth attachment formats.
|
||||
- `vkCreateRenderPass()` return `VK_ERROR_FORMAT_NOT_SUPPORTED` if format not supported.
|
||||
@ -37,6 +38,9 @@ Released TBD
|
||||
- No longer prefer dedicated allocations for buffer memory, including buffer-backed images.
|
||||
- Handle the `compositeAlpha` member of `VkSwapchainCreateInfoKHR`.
|
||||
- `VkPhysicalDevicePortabilitySubsetFeaturesEXTX::events` set to `true`.
|
||||
- Always submit surface presentations using `MTLCommandBuffer`.
|
||||
`MVKConfiguration::presentWithCommandBuffer` is now obsolete.
|
||||
- Don't use `MTLCommandBuffer push/popDebugGroup` if not available.
|
||||
- Add ability to automatically cause an *Xcode* GPU capture without developer intervention.
|
||||
|
||||
|
||||
|
@ -266,18 +266,7 @@ typedef struct {
|
||||
*/
|
||||
VkBool32 supportLargeQueryPools;
|
||||
|
||||
/**
|
||||
* If enabled, each surface presentation is scheduled using a command buffer. Enabling this
|
||||
* setting may improve rendering frame synchronization, but may result in reduced frame rates.
|
||||
*
|
||||
* The value of this parameter may be changed at any time during application runtime,
|
||||
* and the changed value will immediately effect subsequent MoltenVK behaviour.
|
||||
*
|
||||
* The initial value or this parameter is set by the
|
||||
* MVK_CONFIG_PRESENT_WITH_COMMAND_BUFFER
|
||||
* runtime environment variable or MoltenVK compile-time build setting.
|
||||
* If neither is set, the value of this parameter defaults to true.
|
||||
*/
|
||||
/** Obsolete, ignored, and deprecated. All surface presentations are performed with a command buffer. */
|
||||
VkBool32 presentWithCommandBuffer;
|
||||
|
||||
/**
|
||||
|
@ -95,3 +95,11 @@ void mvkCmdEndDebugUtilsLabel(MVKCommandBuffer* cmdBuff);
|
||||
|
||||
void mvkCmdInsertDebugUtilsLabel(MVKCommandBuffer* cmdBuff, const VkDebugUtilsLabelEXT* pLabelInfo);
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Support functions
|
||||
|
||||
void mvkPushDebugGroup(id<MTLCommandBuffer> mtlCmdBuffer, NSString* name);
|
||||
|
||||
void mvkPopDebugGroup(id<MTLCommandBuffer> mtlCmdBuffer);
|
||||
|
||||
|
@ -47,7 +47,7 @@ void MVKCmdDebugMarkerBegin::encode(MVKCommandEncoder* cmdEncoder) {
|
||||
if (mtlCmdEnc) {
|
||||
[mtlCmdEnc pushDebugGroup: _markerName];
|
||||
} else {
|
||||
[cmdEncoder->_mtlCmdBuffer pushDebugGroup: _markerName];
|
||||
mvkPushDebugGroup(cmdEncoder->_mtlCmdBuffer, _markerName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ void MVKCmdDebugMarkerEnd::encode(MVKCommandEncoder* cmdEncoder) {
|
||||
if (mtlCmdEnc) {
|
||||
[mtlCmdEnc popDebugGroup];
|
||||
} else {
|
||||
[cmdEncoder->_mtlCmdBuffer popDebugGroup];
|
||||
mvkPopDebugGroup(cmdEncoder->_mtlCmdBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,3 +121,18 @@ void mvkCmdInsertDebugUtilsLabel(MVKCommandBuffer* cmdBuff, const VkDebugUtilsLa
|
||||
cmdBuff->addCommand(cmd);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Support functions
|
||||
|
||||
void mvkPushDebugGroup(id<MTLCommandBuffer> mtlCmdBuffer, NSString* name) {
|
||||
if ([mtlCmdBuffer respondsToSelector: @selector(pushDebugGroup:)]) {
|
||||
[mtlCmdBuffer pushDebugGroup: name];
|
||||
}
|
||||
}
|
||||
|
||||
void mvkPopDebugGroup(id<MTLCommandBuffer> mtlCmdBuffer) {
|
||||
if ([mtlCmdBuffer respondsToSelector: @selector(popDebugGroup)]) {
|
||||
[mtlCmdBuffer popDebugGroup];
|
||||
}
|
||||
}
|
||||
|
@ -2359,6 +2359,7 @@ void MVKDevice::initPhysicalDevice(MVKPhysicalDevice* physicalDevice, const VkDe
|
||||
if (_pMetalFeatures->events) {
|
||||
MVK_SET_FROM_ENV_OR_BUILD_BOOL(_useMTLEventsForSemaphores, MVK_ALLOW_METAL_EVENTS);
|
||||
}
|
||||
MVKLogInfo("%s MTLEvent for semaphores.", _useMTLEventsForSemaphores ? "Using" : "NOT using");
|
||||
|
||||
#if MVK_MACOS
|
||||
// If we have selected a high-power GPU and want to force the window system
|
||||
|
@ -414,22 +414,6 @@ public:
|
||||
/** Binds this resource according to the specified bind information. */
|
||||
VkResult bindDeviceMemory2(const void* pBindInfo) override;
|
||||
|
||||
/** Returns the encompassing swapchain. */
|
||||
inline MVKSwapchain* getSwapchain() { return _swapchain; }
|
||||
|
||||
/** Returns the index of this image within the encompassing swapchain. */
|
||||
inline uint32_t getSwapchainIndex() { return _swapchainIndex; }
|
||||
|
||||
/**
|
||||
* Registers a semaphore and/or fence that will be signaled when this image becomes available.
|
||||
* This function accepts both a semaphore and a fence, and either none, one, or both may be provided.
|
||||
* If this image is available already, the semaphore and fence are immediately signaled.
|
||||
*/
|
||||
void signalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence);
|
||||
|
||||
/** Returns the availability status of this image, relative to other images in the swapchain. */
|
||||
const MVKSwapchainImageAvailability* getAvailability();
|
||||
|
||||
|
||||
#pragma mark Metal
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "MVKQueue.h"
|
||||
#include "MVKSwapchain.h"
|
||||
#include "MVKCommandBuffer.h"
|
||||
#include "MVKCmdDebug.h"
|
||||
#include "mvk_datatypes.hpp"
|
||||
#include "MVKFoundation.h"
|
||||
#include "MVKLogging.h"
|
||||
@ -1259,14 +1260,6 @@ VkResult MVKSwapchainImage::bindDeviceMemory2(const void* pBindInfo) {
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
void MVKSwapchainImage::signalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence) {
|
||||
_swapchain->signalWhenAvailable( _swapchainIndex, semaphore, fence );
|
||||
}
|
||||
|
||||
const MVKSwapchainImageAvailability* MVKSwapchainImage::getAvailability() {
|
||||
return _swapchain->getAvailability( _swapchainIndex );
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Metal
|
||||
|
||||
@ -1282,36 +1275,23 @@ id<CAMetalDrawable> MVKSwapchainImage::getCAMetalDrawable() {
|
||||
return mtlDrawable;
|
||||
}
|
||||
|
||||
// Present the drawable and make myself available only once the command buffer has completed.
|
||||
void MVKSwapchainImage::presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
// MVKLogDebug("Presenting swapchain image %p from present.", this);
|
||||
_swapchain->willPresentSurface(getMTLTexture(), mtlCmdBuff);
|
||||
|
||||
id<CAMetalDrawable> mtlDrawable = getCAMetalDrawable();
|
||||
_swapchain->willPresentSurface(getMTLTexture(), mtlCmdBuff);
|
||||
NSString* scName = _swapchain->getDebugName();
|
||||
if (scName) { mvkPushDebugGroup(mtlCmdBuff, scName); }
|
||||
[mtlCmdBuff presentDrawable: getCAMetalDrawable()];
|
||||
if (scName) { mvkPopDebugGroup(mtlCmdBuff); }
|
||||
|
||||
// If using a command buffer, present the drawable through it,
|
||||
// and make myself available only once the command buffer has completed.
|
||||
// Otherwise, immediately present the drawable and make myself available.
|
||||
if (mtlCmdBuff) {
|
||||
NSString* scName = _swapchain->getDebugName();
|
||||
if (scName) { [mtlCmdBuff pushDebugGroup: scName]; }
|
||||
[mtlCmdBuff presentDrawable: mtlDrawable];
|
||||
if (scName) { [mtlCmdBuff popDebugGroup]; }
|
||||
resetMetalSurface();
|
||||
_swapchain->signalPresentationSemaphore(_swapchainIndex, mtlCmdBuff);
|
||||
|
||||
resetMetalSurface();
|
||||
if (_device->_useMTLEventsForSemaphores) {
|
||||
_swapchain->signalOnDevice(_swapchainIndex, mtlCmdBuff);
|
||||
}
|
||||
|
||||
retain(); // Ensure this image is not destroyed while awaiting MTLCommandBuffer completion
|
||||
[mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) {
|
||||
_swapchain->makeAvailable(_swapchainIndex);
|
||||
release();
|
||||
}];
|
||||
} else {
|
||||
[mtlDrawable present];
|
||||
resetMetalSurface();
|
||||
_swapchain->makeAvailable(_swapchainIndex);
|
||||
}
|
||||
retain(); // Ensure this image is not destroyed while awaiting MTLCommandBuffer completion
|
||||
[mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) {
|
||||
_swapchain->makeAvailable(_swapchainIndex);
|
||||
release();
|
||||
}];
|
||||
}
|
||||
|
||||
// Resets the MTLTexture and CAMetalDrawable underlying this image.
|
||||
|
@ -180,7 +180,6 @@ protected:
|
||||
|
||||
MVKQueue* _queue;
|
||||
MVKVectorInline<MVKSemaphore*, 8> _waitSemaphores;
|
||||
bool _isAwaitingSemaphores;
|
||||
bool _trackPerformance;
|
||||
};
|
||||
|
||||
@ -211,7 +210,6 @@ protected:
|
||||
MVKVectorInline<MVKSemaphore*, 16> _signalSemaphores;
|
||||
MVKFence* _fence;
|
||||
id<MTLCommandBuffer> _activeMTLCommandBuffer;
|
||||
bool _isSignalingSemaphores;
|
||||
};
|
||||
|
||||
|
||||
|
@ -204,7 +204,6 @@ MVKQueueSubmission::MVKQueueSubmission(MVKQueue* queue,
|
||||
_queue = queue;
|
||||
_trackPerformance = _queue->_device->_pMVKConfig->performanceTracking;
|
||||
|
||||
_isAwaitingSemaphores = waitSemaphoreCount > 0;
|
||||
_waitSemaphores.reserve(waitSemaphoreCount);
|
||||
for (uint32_t i = 0; i < waitSemaphoreCount; i++) {
|
||||
_waitSemaphores.push_back((MVKSemaphore*)pWaitSemaphores[i]);
|
||||
@ -221,30 +220,14 @@ void MVKQueueCommandBufferSubmission::execute() {
|
||||
|
||||
_queue->_submissionCaptureScope->beginScope();
|
||||
|
||||
MVKDevice* mvkDev = _queue->getDevice();
|
||||
|
||||
// If the device supports it, wait for any semaphores on the device.
|
||||
if (mvkDev->_useMTLEventsForSemaphores && _isAwaitingSemaphores) {
|
||||
_isAwaitingSemaphores = false;
|
||||
for (auto* ws : _waitSemaphores) {
|
||||
ws->encodeWait(getActiveMTLCommandBuffer());
|
||||
}
|
||||
}
|
||||
// If using encoded semaphore waiting, do so now.
|
||||
for (auto* ws : _waitSemaphores) { ws->encodeWait(getActiveMTLCommandBuffer()); }
|
||||
|
||||
// Submit each command buffer.
|
||||
for (auto& cb : _cmdBuffers) { cb->submit(this); }
|
||||
|
||||
// If a fence or semaphores were provided, ensure that a MTLCommandBuffer
|
||||
// is available to trigger them, in case no command buffers were provided.
|
||||
if (_fence || _isSignalingSemaphores) { getActiveMTLCommandBuffer(); }
|
||||
|
||||
// If the device supports it, signal all semaphores on the device.
|
||||
if (mvkDev->_useMTLEventsForSemaphores && _isSignalingSemaphores) {
|
||||
_isSignalingSemaphores = false;
|
||||
for (auto* ss : _signalSemaphores) {
|
||||
ss->encodeSignal(getActiveMTLCommandBuffer());
|
||||
}
|
||||
}
|
||||
// If using encoded semaphore signaling, do so now.
|
||||
for (auto* ss : _signalSemaphores) { ss->encodeSignal(getActiveMTLCommandBuffer()); }
|
||||
|
||||
// Commit the last MTLCommandBuffer.
|
||||
// Nothing after this because callback might destroy this instance before this function ends.
|
||||
@ -274,16 +257,16 @@ void MVKQueueCommandBufferSubmission::setActiveMTLCommandBuffer(id<MTLCommandBuf
|
||||
// allow as much filling of the MTLCommandBuffer as possible before forcing a wait.
|
||||
void MVKQueueCommandBufferSubmission::commitActiveMTLCommandBuffer(bool signalCompletion) {
|
||||
|
||||
if (_isAwaitingSemaphores) {
|
||||
_isAwaitingSemaphores = false;
|
||||
for (auto& ws : _waitSemaphores) { ws->wait(); }
|
||||
}
|
||||
// If using inline semaphore waiting, do so now.
|
||||
for (auto& ws : _waitSemaphores) { ws->encodeWait(nil); }
|
||||
|
||||
MVKDevice* mkvDev = _queue->_device;
|
||||
uint64_t startTime = mkvDev->getPerformanceTimestamp();
|
||||
|
||||
// Use getActiveMTLCommandBuffer() to ensure at least one MTLCommandBuffer is used,
|
||||
// otherwise if this instance has no content, it will not finish() and be destroyed.
|
||||
if (signalCompletion || _trackPerformance) {
|
||||
[_activeMTLCommandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
[getActiveMTLCommandBuffer() addCompletedHandler: ^(id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
_queue->_device->addActivityPerformance(mkvDev->_performanceStatistics.queue.mtlCommandBufferCompletion, startTime);
|
||||
if (signalCompletion) { this->finish(); }
|
||||
}];
|
||||
@ -303,15 +286,13 @@ void MVKQueueCommandBufferSubmission::finish() {
|
||||
// immediately after a waitIdle() is cleared by fence below, taking the capture scope with it.
|
||||
_queue->_submissionCaptureScope->endScope();
|
||||
|
||||
// Signal each of the signal semaphores.
|
||||
if (_isSignalingSemaphores) {
|
||||
for (auto& ss : _signalSemaphores) { ss->signal(); }
|
||||
}
|
||||
// If using inline semaphore signaling, do so now.
|
||||
for (auto& ss : _signalSemaphores) { ss->encodeSignal(nil); }
|
||||
|
||||
// If a fence exists, signal it.
|
||||
if (_fence) { _fence->signal(); }
|
||||
|
||||
this->destroy();
|
||||
this->destroy();
|
||||
}
|
||||
|
||||
MVKQueueCommandBufferSubmission::MVKQueueCommandBufferSubmission(MVKQueue* queue,
|
||||
@ -332,7 +313,6 @@ MVKQueueCommandBufferSubmission::MVKQueueCommandBufferSubmission(MVKQueue* queue
|
||||
}
|
||||
|
||||
uint32_t ssCnt = pSubmit->signalSemaphoreCount;
|
||||
_isSignalingSemaphores = ssCnt > 0;
|
||||
_signalSemaphores.reserve(ssCnt);
|
||||
for (uint32_t i = 0; i < ssCnt; i++) {
|
||||
_signalSemaphores.push_back((MVKSemaphore*)pSubmit->pSignalSemaphores[i]);
|
||||
@ -351,37 +331,21 @@ MVKQueueCommandBufferSubmission::MVKQueueCommandBufferSubmission(MVKQueue* queue
|
||||
#pragma mark MVKQueuePresentSurfaceSubmission
|
||||
|
||||
void MVKQueuePresentSurfaceSubmission::execute() {
|
||||
// If there are semaphores and this device supports MTLEvent, we must present
|
||||
// with a command buffer in order to synchronize with the semaphores.
|
||||
MVKDevice* mvkDev = _queue->getDevice();
|
||||
if (mvkDev->_useMTLEventsForSemaphores && !_waitSemaphores.empty()) {
|
||||
// Create a command buffer, have it wait for the semaphores, then present
|
||||
// surfaces via the command buffer.
|
||||
id<MTLCommandBuffer> mtlCmdBuff = getMTLCommandBuffer();
|
||||
for (auto& ws : _waitSemaphores) { ws->encodeWait(mtlCmdBuff); }
|
||||
for (auto& si : _surfaceImages) { si->presentCAMetalDrawable(mtlCmdBuff); }
|
||||
// If the semaphores are encodable, wait on them by encoding them on the MTLCommandBuffer before presenting.
|
||||
// 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& si : _surfaceImages) { si->presentCAMetalDrawable(mtlCmdBuff); }
|
||||
for (auto& ws : _waitSemaphores) { ws->encodeWait(nil); }
|
||||
[mtlCmdBuff commit];
|
||||
|
||||
[mtlCmdBuff commit];
|
||||
} else if (mvkDev->_pMVKConfig->presentWithCommandBuffer || mvkDev->_pMVKConfig->displayWatermark) {
|
||||
// Create a command buffer, present surfaces via the command buffer,
|
||||
// then wait on the semaphores before committing.
|
||||
id<MTLCommandBuffer> mtlCmdBuff = getMTLCommandBuffer();
|
||||
for (auto& si : _surfaceImages) { si->presentCAMetalDrawable(mtlCmdBuff); }
|
||||
for (auto& ws : _waitSemaphores) { ws->wait(); }
|
||||
|
||||
[mtlCmdBuff commit];
|
||||
} else {
|
||||
// Wait on semaphores, then present directly.
|
||||
for (auto& ws : _waitSemaphores) { ws->wait(); }
|
||||
for (auto& si : _surfaceImages) { si->presentCAMetalDrawable(nil); }
|
||||
}
|
||||
|
||||
// Let Xcode know the current frame is done, then start a new frame
|
||||
// Let Xcode know the current frame is done, then start a new frame
|
||||
auto cs = _queue->_presentationCaptureScope;
|
||||
cs->endScope();
|
||||
cs->beginScope();
|
||||
|
||||
this->destroy();
|
||||
this->destroy();
|
||||
}
|
||||
|
||||
id<MTLCommandBuffer> MVKQueuePresentSurfaceSubmission::getMTLCommandBuffer() {
|
||||
|
@ -87,9 +87,7 @@ public:
|
||||
*/
|
||||
void signalWhenAvailable(uint32_t imageIndex, MVKSemaphore* semaphore, MVKFence* fence);
|
||||
|
||||
/** Returns the availability status of the image at the given index, relative to other images in the swapchain. */
|
||||
const MVKSwapchainImageAvailability* getAvailability(uint32_t imageIndex);
|
||||
|
||||
|
||||
#pragma mark Metal
|
||||
|
||||
/**
|
||||
@ -128,8 +126,8 @@ protected:
|
||||
void renderWatermark(id<MTLTexture> mtlTexture, id<MTLCommandBuffer> mtlCmdBuff);
|
||||
void markFrameInterval();
|
||||
void resetCAMetalDrawable(uint32_t imgIdx);
|
||||
void signal(MVKSwapchainSignaler& signaler);
|
||||
void signalOnDevice(uint32_t imgIdx, id<MTLCommandBuffer> mtlCmdBuff);
|
||||
void signal(MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff);
|
||||
void signalPresentationSemaphore(uint32_t imgIdx, id<MTLCommandBuffer> mtlCmdBuff);
|
||||
static void markAsTracked(MVKSwapchainSignaler& signaler);
|
||||
static void unmarkAsTracked(MVKSwapchainSignaler& signaler);
|
||||
void makeAvailable(uint32_t imgIdx);
|
||||
|
@ -136,22 +136,22 @@ void MVKSwapchain::makeAvailable(uint32_t imgIdx) {
|
||||
|
||||
MVKSwapchainSignaler signaler;
|
||||
if (availability.isAvailable) {
|
||||
// If this image is now available, signal the semaphore and fence that were associated
|
||||
// If this image is available, signal the semaphore and fence that were associated
|
||||
// with the last time this image was acquired while available. This is a workaround for
|
||||
// when an app uses a single semaphore or fence for more than one swapchain image.
|
||||
// Becuase the semaphore or fence will be signaled by more than one image, it will
|
||||
// get out of sync, and the final use of the image would not be signaled as a result.
|
||||
|
||||
signaler = _imageAvailability[imgIdx].preSignaled;
|
||||
} else {
|
||||
// If this image is not yet available, extract and signal the first semaphore and fence.
|
||||
|
||||
signaler = _imageAvailability[imgIdx].signalers.front();
|
||||
_imageAvailability[imgIdx].signalers.erase( _imageAvailability[imgIdx].signalers.begin() );
|
||||
auto& imgSignalers = _imageAvailability[imgIdx].signalers;
|
||||
auto sigIter = imgSignalers.begin();
|
||||
signaler = *sigIter;
|
||||
imgSignalers.erase(sigIter);
|
||||
}
|
||||
|
||||
// Signal the semaphore and fence, and let them know they are no longer being tracked.
|
||||
signal(signaler);
|
||||
signal(signaler, nil);
|
||||
unmarkAsTracked(signaler);
|
||||
|
||||
// MVKLogDebug("Signaling%s swapchain image %p semaphore %p from present, with %lu remaining semaphores.", (_availability.isAvailable ? " pre-signaled" : ""), this, signaler.first, _availabilitySignalers.size());
|
||||
@ -160,19 +160,22 @@ void MVKSwapchain::makeAvailable(uint32_t imgIdx) {
|
||||
void MVKSwapchain::signalWhenAvailable(uint32_t imageIndex, MVKSemaphore* semaphore, MVKFence* fence) {
|
||||
lock_guard<mutex> lock(_availabilityLock);
|
||||
auto signaler = make_pair(semaphore, fence);
|
||||
auto& availability = _imageAvailability[imageIndex].status;
|
||||
auto& availability = _imageAvailability[imageIndex].status;
|
||||
if (availability.isAvailable) {
|
||||
availability.isAvailable = false;
|
||||
signal(signaler);
|
||||
if (_device->_useMTLEventsForSemaphores) {
|
||||
// Unfortunately, we can't assume we have an MTLSharedEvent here.
|
||||
// This means we need to execute a command on the device to signal the semaphore.
|
||||
// Alternatively, we could always use an MTLSharedEvent, but that might impose
|
||||
// unacceptable performance costs just to handle this one case.
|
||||
id<MTLCommandBuffer> mtlCmdBuff = [_device->getQueue()->getMTLCommandQueue() commandBufferWithUnretainedReferences];
|
||||
signaler.first->encodeSignal(mtlCmdBuff);
|
||||
|
||||
// If signalling through a MTLEvent, use an ephemeral MTLCommandBuffer.
|
||||
// Another option would be to use MTLSharedEvent in MVKSemaphore, but that might
|
||||
// impose unacceptable performance costs to handle this particular case.
|
||||
@autoreleasepool {
|
||||
MVKSemaphore* mvkSem = signaler.first;
|
||||
id<MTLCommandBuffer> mtlCmdBuff = (mvkSem && mvkSem->isUsingMTLEvent()
|
||||
? [_device->getQueue()->getMTLCommandQueue() commandBufferWithUnretainedReferences]
|
||||
: nil);
|
||||
signal(signaler, mtlCmdBuff);
|
||||
[mtlCmdBuff commit];
|
||||
}
|
||||
|
||||
_imageAvailability[imageIndex].preSignaled = signaler;
|
||||
} else {
|
||||
_imageAvailability[imageIndex].signalers.push_back(signaler);
|
||||
@ -183,16 +186,19 @@ void MVKSwapchain::signalWhenAvailable(uint32_t imageIndex, MVKSemaphore* semaph
|
||||
}
|
||||
|
||||
// Signal either or both of the semaphore and fence in the specified tracker pair.
|
||||
void MVKSwapchain::signal(MVKSwapchainSignaler& signaler) {
|
||||
if (signaler.first && !_device->_useMTLEventsForSemaphores) { signaler.first->signal(); }
|
||||
void MVKSwapchain::signal(MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
if (signaler.first) { signaler.first->encodeSignal(mtlCmdBuff); }
|
||||
if (signaler.second) { signaler.second->signal(); }
|
||||
}
|
||||
|
||||
// If present, signal the semaphore for the first waiter for the given image.
|
||||
void MVKSwapchain::signalOnDevice(uint32_t imgIdx, id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
void MVKSwapchain::signalPresentationSemaphore(uint32_t imgIdx, id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
lock_guard<mutex> lock(_availabilityLock);
|
||||
MVKSemaphore* mvkSem = _imageAvailability[imgIdx].signalers.front().first;
|
||||
if (mvkSem) { mvkSem->encodeSignal(mtlCmdBuff); }
|
||||
auto& imgSignalers = _imageAvailability[imgIdx].signalers;
|
||||
if ( !imgSignalers.empty() ) {
|
||||
MVKSemaphore* mvkSem = imgSignalers.front().first;
|
||||
if (mvkSem) { mvkSem->encodeSignal(mtlCmdBuff); }
|
||||
}
|
||||
}
|
||||
|
||||
// Tell the semaphore and fence that they are being tracked for future signaling.
|
||||
@ -207,13 +213,6 @@ void MVKSwapchain::unmarkAsTracked(MVKSwapchainSignaler& signaler) {
|
||||
if (signaler.second) { signaler.second->release(); }
|
||||
}
|
||||
|
||||
const MVKSwapchainImageAvailability* MVKSwapchain::getAvailability(uint32_t imageIndex) {
|
||||
lock_guard<mutex> lock(_availabilityLock);
|
||||
auto& availability = _imageAvailability[imageIndex].status;
|
||||
availability.waitCount = (uint32_t)_imageAvailability[imageIndex].signalers.size();
|
||||
return &availability;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Rendering
|
||||
|
||||
|
@ -120,24 +120,39 @@ public:
|
||||
/** Returns the debug report object type of this object. */
|
||||
VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT; }
|
||||
|
||||
/**
|
||||
* Blocks processing on the current thread until this semaphore is
|
||||
* signaled, or until the specified timeout in nanoseconds expires.
|
||||
/**
|
||||
* Wait for this semaphore to be signaled.
|
||||
*
|
||||
* If timeout is the special value UINT64_MAX the timeout is treated as infinite.
|
||||
* If this instance is using MTLEvent AND the mtlCmdBuff is not nil, a MTLEvent wait
|
||||
* is encoded on the mtlCmdBuff, and this call returns immediately. Otherwise, if this
|
||||
* instance is NOT using MTLEvent, AND the mtlCmdBuff is nil, this call blocks
|
||||
* indefinitely until this semaphore is signaled. Other combinations do nothing.
|
||||
*
|
||||
* Returns true if this semaphore was signaled, or false if the timeout interval expired.
|
||||
* This design allows this function to be blindly called twice, from different points in
|
||||
* the code path, once with a mtlCmdBuff to support encoding a wait on the command buffer
|
||||
* if this instance supports MTLEvents, and once without a mtlCmdBuff, at the point in
|
||||
* the code path where the code should block if this instance does not support MTLEvents.
|
||||
*/
|
||||
bool wait(uint64_t timeout = UINT64_MAX);
|
||||
void encodeWait(id<MTLCommandBuffer> mtlCmdBuff);
|
||||
|
||||
/** Signals the semaphore. Unblocks all waiting threads to continue processing. */
|
||||
void signal();
|
||||
/**
|
||||
* Signals this semaphore.
|
||||
*
|
||||
* If this instance is using MTLEvent AND the mtlCmdBuff is not nil, a MTLEvent signal
|
||||
* is encoded on the mtlCmdBuff. Otherwise, if this instance is NOT using MTLEvent,
|
||||
* AND the mtlCmdBuff is nil, this call immediately signals any waiting calls.
|
||||
* Either way, this call returns immediately. Other combinations do nothing.
|
||||
*
|
||||
* This design allows this function to be blindly called twice, from different points in
|
||||
* the code path, once with a mtlCmdBuff to support encoding a signal on the command buffer
|
||||
* if this instance supports MTLEvents, and once without a mtlCmdBuff, at the point in
|
||||
* the code path where the code should immediately signal any existing waits, if this
|
||||
* instance does not support MTLEvents.
|
||||
*/
|
||||
void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff);
|
||||
|
||||
/** Encodes an operation to block command buffer operation until this semaphore is signaled. */
|
||||
void encodeWait(id<MTLCommandBuffer> cmdBuff);
|
||||
|
||||
/** Encodes an operation to signal the semaphore. */
|
||||
void encodeSignal(id<MTLCommandBuffer> cmdBuff);
|
||||
/** Returns whether this semaphore is using a MTLEvent. */
|
||||
bool isUsingMTLEvent() { return _mtlEvent != nil; }
|
||||
|
||||
|
||||
#pragma mark Construction
|
||||
|
@ -80,26 +80,27 @@ MVKSemaphoreImpl::~MVKSemaphoreImpl() {
|
||||
#pragma mark -
|
||||
#pragma mark MVKSemaphore
|
||||
|
||||
bool MVKSemaphore::wait(uint64_t timeout) { return _blocker.wait(timeout, true); }
|
||||
|
||||
void MVKSemaphore::signal() { _blocker.release(); }
|
||||
|
||||
void MVKSemaphore::encodeWait(id<MTLCommandBuffer> cmdBuff) {
|
||||
[cmdBuff encodeWaitForEvent: _mtlEvent value: _mtlEventValue];
|
||||
++_mtlEventValue;
|
||||
void MVKSemaphore::encodeWait(id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
if (_mtlEvent && mtlCmdBuff) {
|
||||
[mtlCmdBuff encodeWaitForEvent: _mtlEvent value: _mtlEventValue++];
|
||||
} else if ( !_mtlEvent && !mtlCmdBuff ) {
|
||||
_blocker.wait(UINT64_MAX, true);
|
||||
}
|
||||
}
|
||||
|
||||
void MVKSemaphore::encodeSignal(id<MTLCommandBuffer> cmdBuff) {
|
||||
[cmdBuff encodeSignalEvent: _mtlEvent value: _mtlEventValue];
|
||||
void MVKSemaphore::encodeSignal(id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
if (_mtlEvent && mtlCmdBuff) {
|
||||
[mtlCmdBuff encodeSignalEvent: _mtlEvent value: _mtlEventValue];
|
||||
} else if ( !_mtlEvent && !mtlCmdBuff ) {
|
||||
_blocker.release();
|
||||
}
|
||||
}
|
||||
|
||||
MVKSemaphore::MVKSemaphore(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo)
|
||||
: MVKVulkanAPIDeviceObject(device), _blocker(false, 1), _mtlEvent(nil), _mtlEventValue(1) {
|
||||
|
||||
if (device->_useMTLEventsForSemaphores) {
|
||||
_mtlEvent = [device->getMTLDevice() newEvent];
|
||||
}
|
||||
}
|
||||
MVKSemaphore::MVKSemaphore(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo) :
|
||||
MVKVulkanAPIDeviceObject(device),
|
||||
_blocker(false, 1),
|
||||
_mtlEvent(device->_useMTLEventsForSemaphores ? [device->getMTLDevice() newEvent] : nil),
|
||||
_mtlEventValue(1) {}
|
||||
|
||||
MVKSemaphore::~MVKSemaphore() {
|
||||
[_mtlEvent release];
|
||||
|
Loading…
x
Reference in New Issue
Block a user