Merge pull request #724 from billhollings/master

Streamline design and use of MVKSemaphore
This commit is contained in:
Bill Hollings 2019-08-27 15:58:01 -04:00 committed by GitHub
commit 90f2b7a1af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 141 additions and 185 deletions

View File

@ -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.

View File

@ -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;
/**

View File

@ -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);

View File

@ -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];
}
}

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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;
};

View File

@ -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() {

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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];