Add support for VkEvent.

Add MVKEvent class. MVKEventNative subclass uses native MTLEvent. MVKEventEmulated
subclass uses emulation using CPU blocking and MTLCommandBuffer completion handling.
MVKConfiguration::synchronousQueueSubmits now disabled by default if MTLEvents are not supported.
Document updated use of MVK_ALLOW_METAL_EVENTS and MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS
environment variables and synchronousQueueSubmits config setting, in vk_mvk_moltenvk.h.
This commit is contained in:
Bill Hollings 2019-08-08 16:13:47 -04:00
parent e38b7e7d90
commit 7dc642196a
17 changed files with 413 additions and 31 deletions

View File

@ -18,6 +18,7 @@ MoltenVK 1.0.37
Released TBD
- Add support for `VkEvent`, using either native `MTLEvent` or emulation when `MTLEvent` not available.
- Revert to supporting host-coherent memory for linear images on macOS.
- 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.

View File

@ -116,7 +116,7 @@ typedef unsigned long MTLLanguageVersion;
*
* 4. Setting the MVK_ALLOW_METAL_EVENTS runtime environment variable or MoltenVK compile-time build
* setting to 1 will cause MoltenVK to use Metal events, if they are available on the device, for
* Vulkan sychronization components such as VkSemaphore. This is disabled by default.
* for VkSemaphore sychronization behaviour. This is disabled by default.
*/
typedef struct {
@ -167,7 +167,10 @@ typedef struct {
* The initial value or this parameter is set by the
* MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS
* runtime environment variable or MoltenVK compile-time build setting.
* If neither is set, the value of this parameter defaults to true.
* If neither is set, the value of this parameter defaults to true for macOS 10.14
* and above or iOS 12 and above, and false otherwise. The reason for this distinction
* is that this feature should be disabled when emulation is required to support VkEvents
* because native support for events (MTLEvent) is not available.
*/
VkBool32 synchronousQueueSubmits;

View File

@ -189,6 +189,54 @@ private:
};
#pragma mark -
#pragma mark MVKCmdSetResetEvent
/** Vulkan command to set or reset an event. */
class MVKCmdSetResetEvent : public MVKCommand {
public:
void setContent(VkEvent event, VkPipelineStageFlags stageMask, bool status);
void encode(MVKCommandEncoder* cmdEncoder) override;
MVKCmdSetResetEvent(MVKCommandTypePool<MVKCmdSetResetEvent>* pool);
private:
MVKEvent* _mvkEvent;
bool _status;
};
#pragma mark -
#pragma mark MVKCmdWaitEvents
/** Vulkan command to wait for an event to be signaled. */
class MVKCmdWaitEvents : public MVKCommand {
public:
void setContent(uint32_t eventCount,
const VkEvent* pEvents,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
uint32_t memoryBarrierCount,
const VkMemoryBarrier* pMemoryBarriers,
uint32_t bufferMemoryBarrierCount,
const VkBufferMemoryBarrier* pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier* pImageMemoryBarriers);
void encode(MVKCommandEncoder* cmdEncoder) override;
MVKCmdWaitEvents(MVKCommandTypePool<MVKCmdWaitEvents>* pool);
private:
MVKVectorInline<MVKEvent*, 4> _mvkEvents;
};
#pragma mark -
#pragma mark Command creation functions
@ -241,3 +289,27 @@ void mvkCmdPushDescriptorSetWithTemplate(MVKCommandBuffer* cmdBuff,
VkPipelineLayout layout,
uint32_t set,
const void* pData);
/** Adds a set event command to the specified command buffer. */
void mvkCmdSetEvent(MVKCommandBuffer* cmdBuff,
VkEvent event,
VkPipelineStageFlags stageMask);
/** Adds a reset event command to the specified command buffer. */
void mvkCmdResetEvent(MVKCommandBuffer* cmdBuff,
VkEvent event,
VkPipelineStageFlags stageMask);
/** Adds a wait events command to the specified command buffer. */
void mvkCmdWaitEvents(MVKCommandBuffer* cmdBuff,
uint32_t eventCount,
const VkEvent* pEvents,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
uint32_t memoryBarrierCount,
const VkMemoryBarrier* pMemoryBarriers,
uint32_t bufferMemoryBarrierCount,
const VkBufferMemoryBarrier* pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier* pImageMemoryBarriers);

View File

@ -346,6 +346,54 @@ MVKCmdPushDescriptorSetWithTemplate::~MVKCmdPushDescriptorSetWithTemplate() {
}
#pragma mark -
#pragma mark MVKCmdSetResetEvent
void MVKCmdSetResetEvent::setContent(VkEvent event, VkPipelineStageFlags stageMask, bool status) {
_mvkEvent = (MVKEvent*)event;
_status = status;
}
void MVKCmdSetResetEvent::encode(MVKCommandEncoder* cmdEncoder) {
cmdEncoder->signalEvent(_mvkEvent, _status);
}
MVKCmdSetResetEvent::MVKCmdSetResetEvent(MVKCommandTypePool<MVKCmdSetResetEvent>* pool)
: MVKCommand::MVKCommand((MVKCommandTypePool<MVKCommand>*)pool) {}
#pragma mark -
#pragma mark MVKCmdWaitEvents
void MVKCmdWaitEvents::setContent(uint32_t eventCount,
const VkEvent* pEvents,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
uint32_t memoryBarrierCount,
const VkMemoryBarrier* pMemoryBarriers,
uint32_t bufferMemoryBarrierCount,
const VkBufferMemoryBarrier* pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier* pImageMemoryBarriers) {
_mvkEvents.clear(); // Clear for reuse
_mvkEvents.reserve(eventCount);
for (uint32_t i = 0; i < eventCount; i++) {
_mvkEvents.push_back((MVKEvent*)pEvents[i]);
}
}
void MVKCmdWaitEvents::encode(MVKCommandEncoder* cmdEncoder) {
for (MVKEvent* mvkEvt : _mvkEvents) {
mvkEvt->encodeWait(cmdEncoder->_mtlCmdBuffer);
}
}
MVKCmdWaitEvents::MVKCmdWaitEvents(MVKCommandTypePool<MVKCmdWaitEvents>* pool)
: MVKCommand::MVKCommand((MVKCommandTypePool<MVKCommand>*)pool) {}
#pragma mark -
#pragma mark Command creation functions
@ -420,3 +468,40 @@ void mvkCmdPushDescriptorSetWithTemplate(MVKCommandBuffer* cmdBuff,
cmd->setContent(descUpdateTemplate, layout, set, pData);
cmdBuff->addCommand(cmd);
}
void mvkCmdSetEvent(MVKCommandBuffer* cmdBuff,
VkEvent event,
VkPipelineStageFlags stageMask) {
MVKCmdSetResetEvent* cmd = cmdBuff->_commandPool->_cmdSetResetEventPool.acquireObject();
cmd->setContent(event, stageMask, true);
cmdBuff->addCommand(cmd);
}
void mvkCmdResetEvent(MVKCommandBuffer* cmdBuff,
VkEvent event,
VkPipelineStageFlags stageMask) {
MVKCmdSetResetEvent* cmd = cmdBuff->_commandPool->_cmdSetResetEventPool.acquireObject();
cmd->setContent(event, stageMask, false);
cmdBuff->addCommand(cmd);
}
void mvkCmdWaitEvents(MVKCommandBuffer* cmdBuff,
uint32_t eventCount,
const VkEvent* pEvents,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
uint32_t memoryBarrierCount,
const VkMemoryBarrier* pMemoryBarriers,
uint32_t bufferMemoryBarrierCount,
const VkBufferMemoryBarrier* pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier* pImageMemoryBarriers) {
MVKCmdWaitEvents* cmd = cmdBuff->_commandPool->_cmdWaitEventsPool.acquireObject();
cmd->setContent(eventCount, pEvents,
srcStageMask, dstStageMask,
memoryBarrierCount, pMemoryBarriers,
bufferMemoryBarrierCount, pBufferMemoryBarriers,
imageMemoryBarrierCount, pImageMemoryBarriers);
cmdBuff->addCommand(cmd);
}

View File

@ -283,6 +283,9 @@ public:
/** Binds a pipeline to a bind point. */
void bindPipeline(VkPipelineBindPoint pipelineBindPoint, MVKPipeline* pipeline);
/** Encodes an operation to signal an event to a status. */
void signalEvent(MVKEvent* mvkEvent, bool status);
/**
* If a pipeline is currently bound, returns whether the current pipeline permits dynamic
* setting of the specified state. If no pipeline is currently bound, returns true.

View File

@ -359,6 +359,11 @@ void MVKCommandEncoder::bindPipeline(VkPipelineBindPoint pipelineBindPoint, MVKP
}
}
void MVKCommandEncoder::signalEvent(MVKEvent* mvkEvent, bool status) {
endCurrentMetalEncoding();
mvkEvent->encodeSignal(_mtlCmdBuffer, status);
}
bool MVKCommandEncoder::supportsDynamicState(VkDynamicState state) {
MVKGraphicsPipeline* gpl = (MVKGraphicsPipeline*)_graphicsPipelineState.getPipeline();
return !gpl || gpl->supportsDynamicState(state);

View File

@ -149,6 +149,10 @@ public:
MVKCommandTypePool<MVKCmdDebugMarkerInsert> _cmdDebugMarkerInsertPool;
MVKCommandTypePool<MVKCmdSetResetEvent> _cmdSetResetEventPool;
MVKCommandTypePool<MVKCmdWaitEvents> _cmdWaitEventsPool;
#pragma mark Command resources

View File

@ -126,6 +126,8 @@ void MVKCommandPool::trim() {
_cmdDebugMarkerBeginPool.clear();
_cmdDebugMarkerEndPool.clear();
_cmdDebugMarkerInsertPool.clear();
_cmdSetResetEventPool.clear();
_cmdWaitEventsPool.clear();
}
@ -180,7 +182,9 @@ MVKCommandPool::MVKCommandPool(MVKDevice* device,
_cmdPushSetWithTemplatePool(this),
_cmdDebugMarkerBeginPool(this),
_cmdDebugMarkerEndPool(this),
_cmdDebugMarkerInsertPool(this)
_cmdDebugMarkerInsertPool(this),
_cmdSetResetEventPool(this),
_cmdWaitEventsPool(this)
// when extending be sure to add to trim() as well
{}

View File

@ -47,6 +47,7 @@ class MVKSwapchain;
class MVKDeviceMemory;
class MVKFence;
class MVKSemaphore;
class MVKEvent;
class MVKQueryPool;
class MVKShaderModule;
class MVKPipelineCache;
@ -445,6 +446,11 @@ public:
void destroySemaphore(MVKSemaphore* mvkSem4,
const VkAllocationCallbacks* pAllocator);
MVKEvent* createEvent(const VkEventCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator);
void destroyEvent(MVKEvent* mvkEvent,
const VkAllocationCallbacks* pAllocator);
MVKQueryPool* createQueryPool(const VkQueryPoolCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator);
void destroyQueryPool(MVKQueryPool* mvkQP,
@ -640,6 +646,11 @@ public:
/** Performance statistics. */
MVKPerformanceStatistics _performanceStatistics;
// Indicates whether semaphores should use MTLEvents if available.
// Set by the MVK_ALLOW_METAL_EVENTS environment variable if MTLEvents are available.
// This should be a temporary fix after some repair to semaphore handling.
bool _useMTLEventsForSemaphores;
#pragma mark Construction

View File

@ -128,7 +128,7 @@ void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) {
auto* portabilityFeatures = (VkPhysicalDevicePortabilitySubsetFeaturesEXTX*)next;
portabilityFeatures->triangleFans = false;
portabilityFeatures->separateStencilMaskRef = true;
portabilityFeatures->events = false;
portabilityFeatures->events = _metalFeatures.events;
portabilityFeatures->standardImageViews = _mvkInstance->getMoltenVKConfiguration()->fullImageViewSwizzle;
portabilityFeatures->samplerMipLodBias = false;
break;
@ -781,7 +781,7 @@ void MVKPhysicalDevice::initMetalFeatures() {
if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v5] ) {
_metalFeatures.mslVersionEnum = MTLLanguageVersion2_1;
MVK_SET_FROM_ENV_OR_BUILD_BOOL(_metalFeatures.events, MVK_ALLOW_METAL_EVENTS);
_metalFeatures.events = true;
_metalFeatures.textureBuffers = true;
}
@ -844,7 +844,7 @@ void MVKPhysicalDevice::initMetalFeatures() {
if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v4] ) {
_metalFeatures.mslVersionEnum = MTLLanguageVersion2_1;
_metalFeatures.multisampleArrayTextures = true;
MVK_SET_FROM_ENV_OR_BUILD_BOOL(_metalFeatures.events, MVK_ALLOW_METAL_EVENTS);
_metalFeatures.events = true;
_metalFeatures.memoryBarriers = true;
_metalFeatures.textureBuffers = true;
}
@ -1899,6 +1899,19 @@ void MVKDevice::destroySemaphore(MVKSemaphore* mvkSem4,
mvkSem4->destroy();
}
MVKEvent* MVKDevice::createEvent(const VkEventCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator) {
if (_pMetalFeatures->events) {
return new MVKEventNative(this, pCreateInfo);
} else {
return new MVKEventEmulated(this, pCreateInfo);
}
}
void MVKDevice::destroyEvent(MVKEvent* mvkEvent, const VkAllocationCallbacks* pAllocator) {
mvkEvent->destroy();
}
MVKQueryPool* MVKDevice::createQueryPool(const VkQueryPoolCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator) {
switch (pCreateInfo->queryType) {
@ -2301,6 +2314,11 @@ void MVKDevice::initPhysicalDevice(MVKPhysicalDevice* physicalDevice, const VkDe
_pProperties = &_physicalDevice->_properties;
_pMemoryProperties = &_physicalDevice->_memoryProperties;
_useMTLEventsForSemaphores = MVK_ALLOW_METAL_EVENTS;
if (_pMetalFeatures->events) {
MVK_SET_FROM_ENV_OR_BUILD_BOOL(_useMTLEventsForSemaphores, MVK_ALLOW_METAL_EVENTS);
}
#if MVK_MACOS
// If we have selected a high-power GPU and want to force the window system
// to use it, force the window system to use a high-power GPU by calling the

View File

@ -1286,7 +1286,7 @@ void MVKSwapchainImage::signalWhenAvailable(MVKSemaphore* semaphore, MVKFence* f
if (_availability.isAvailable) {
_availability.isAvailable = false;
signal(signaler);
if (_device->_pMetalFeatures->events) {
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,
@ -1310,7 +1310,7 @@ void MVKSwapchainImage::signalWhenAvailable(MVKSemaphore* semaphore, MVKFence* f
// Signal either or both of the semaphore and fence in the specified tracker pair.
void MVKSwapchainImage::signal(MVKSwapchainSignaler& signaler) {
if (signaler.first && !_device->_pMetalFeatures->events) { signaler.first->signal(); }
if (signaler.first && !_device->_useMTLEventsForSemaphores) { signaler.first->signal(); }
if (signaler.second) { signaler.second->signal(); }
}
@ -1367,7 +1367,7 @@ void MVKSwapchainImage::presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff)
if (scName) { [mtlCmdBuff popDebugGroup]; }
resetMetalSurface();
if (_device->_pMetalFeatures->events && !_availabilitySignalers.empty()) {
if (_device->_useMTLEventsForSemaphores && !_availabilitySignalers.empty()) {
// Signal the semaphore device-side.
_availabilitySignalers.front().first->encodeSignal(mtlCmdBuff);
}

View File

@ -644,6 +644,21 @@ void MVKInstance::logVersions() {
}
void MVKInstance::initConfig() {
// The default value for MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS actually depends on whether
// MTLEvents are supported, becuase if MTLEvents are not supported, then synchronous queues
// should be turned off by default to ensure , whereas if MTLEvents are supported, we want
// sychronous queues for better behaviour. The app can of course still override this default
// behaviour by setting the env var, or the config directly.
#undef MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS
#define MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS syncQueueSubmits
#if MVK_MACOS
bool syncQueueSubmits = mvkOSVersion() >= 10.14; // Support for MTLEvents
#endif
#if MVK_IOS
bool syncQueueSubmits = mvkOSVersion() >= 12.0; // Support for MTLEvents
#endif
MVK_SET_FROM_ENV_OR_BUILD_BOOL( _mvkConfig.debugMode, MVK_DEBUG);
MVK_SET_FROM_ENV_OR_BUILD_BOOL( _mvkConfig.shaderConversionFlipVertexY, MVK_CONFIG_SHADER_CONVERSION_FLIP_VERTEX_Y);
MVK_SET_FROM_ENV_OR_BUILD_BOOL( _mvkConfig.synchronousQueueSubmits, MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS);

View File

@ -224,7 +224,7 @@ void MVKQueueCommandBufferSubmission::execute() {
MVKDevice* mvkDev = _queue->getDevice();
// If the device supports it, wait for any semaphores on the device.
if (mvkDev->_pMetalFeatures->events && _isAwaitingSemaphores) {
if (mvkDev->_useMTLEventsForSemaphores && _isAwaitingSemaphores) {
_isAwaitingSemaphores = false;
for (auto* ws : _waitSemaphores) {
ws->encodeWait(getActiveMTLCommandBuffer());
@ -239,7 +239,7 @@ void MVKQueueCommandBufferSubmission::execute() {
if (_fence || _isSignalingSemaphores) { getActiveMTLCommandBuffer(); }
// If the device supports it, signal all semaphores on the device.
if (mvkDev->_pMetalFeatures->events && _isSignalingSemaphores) {
if (mvkDev->_useMTLEventsForSemaphores && _isSignalingSemaphores) {
_isSignalingSemaphores = false;
for (auto* ss : _signalSemaphores) {
ss->encodeSignal(getActiveMTLCommandBuffer());
@ -354,7 +354,7 @@ 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->_pMetalFeatures->events && !_waitSemaphores.empty()) {
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();

View File

@ -60,6 +60,9 @@ public:
*/
bool release();
/** Returns whether this instance is in a reserved state. */
bool isReserved();
/**
* 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.
@ -81,7 +84,7 @@ public:
*
* The waitAll parameter indicates whether a call to the release() function is required
* for each call to the reserve() function (waitAll = true), or whether a single call
* to the release() function will release all outstanding reservations (waitAll = true).
* to the release() function will release all outstanding reservations (waitAll = false).
* This value defaults to true, indicating that each call to the reserve() function will
* require a separate call to the release() function to cause the semaphore to stop blocking.
*/
@ -246,6 +249,84 @@ private:
};
#pragma mark -
#pragma mark MVKEvent
/** Represents a Vulkan semaphore. */
class MVKEvent : public MVKVulkanAPIDeviceObject {
public:
/** Returns the Vulkan type of this object. */
VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_EVENT; }
/** Returns the debug report object type of this object. */
VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT; }
/** Returns whether this event is set. */
virtual bool isSet() = 0;
/** Sets the signal status. */
virtual void signal(bool status) = 0;
/** Encodes an operation to signal the event with a status. */
virtual void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, bool status) = 0;
/** Encodes an operation to block command buffer operation until this event is signaled. */
virtual void encodeWait(id<MTLCommandBuffer> mtlCmdBuff) = 0;
#pragma mark Construction
MVKEvent(MVKDevice* device, const VkEventCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) {}
protected:
void propogateDebugName() override {}
};
#pragma mark -
#pragma mark MVKEventNative
/** An MVKEvent that uses native MTLSharedEvent to provide VkEvent functionality. */
class MVKEventNative : public MVKEvent {
public:
bool isSet() override;
void signal(bool status) override;
void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, bool status) override;
void encodeWait(id<MTLCommandBuffer> mtlCmdBuff) override;
MVKEventNative(MVKDevice* device, const VkEventCreateInfo* pCreateInfo);
~MVKEventNative() override;
protected:
id<MTLSharedEvent> _mtlEvent;
};
#pragma mark -
#pragma mark MVKEventEmulated
/** An MVKEvent that uses CPU synchronization to provide VkEvent functionality. */
class MVKEventEmulated : public MVKEvent {
public:
bool isSet() override;
void signal(bool status) override;
void encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, bool status) override;
void encodeWait(id<MTLCommandBuffer> mtlCmdBuff) override;
MVKEventEmulated(MVKDevice* device, const VkEventCreateInfo* pCreateInfo);
protected:
MVKSemaphoreImpl _blocker;
bool _inlineSignalStatus;
};
#pragma mark -
#pragma mark Support functions

View File

@ -46,6 +46,11 @@ void MVKSemaphoreImpl::reserve() {
_reservationCount++;
}
bool MVKSemaphoreImpl::isReserved() {
lock_guard<mutex> lock(_lock);
return !isClear();
}
bool MVKSemaphoreImpl::wait(uint64_t timeout, bool reserveAgain) {
unique_lock<mutex> lock(_lock);
@ -97,7 +102,7 @@ void MVKSemaphore::encodeSignal(id<MTLCommandBuffer> cmdBuff) {
MVKSemaphore::MVKSemaphore(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo)
: MVKVulkanAPIDeviceObject(device), _blocker(false, 1), _mtlEvent(nil), _mtlEventValue(1) {
if (device->_pMetalFeatures->events) {
if (device->_useMTLEventsForSemaphores) {
_mtlEvent = [device->getMTLDevice() newEvent];
}
}
@ -155,6 +160,76 @@ bool MVKFence::getIsSignaled() {
}
#pragma mark -
#pragma mark MVKEventNative
// Odd == set / Even == reset.
bool MVKEventNative::isSet() { return _mtlEvent.signaledValue & 1; }
void MVKEventNative::signal(bool status) {
if (isSet() != status) {
_mtlEvent.signaledValue += 1;
}
}
void MVKEventNative::encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, bool status) {
if (isSet() != status) {
[mtlCmdBuff encodeSignalEvent: _mtlEvent value: _mtlEvent.signaledValue + 1];
}
}
void MVKEventNative::encodeWait(id<MTLCommandBuffer> mtlCmdBuff) {
if ( !isSet() ) {
[mtlCmdBuff encodeWaitForEvent: _mtlEvent value: _mtlEvent.signaledValue + 1];
}
}
MVKEventNative::MVKEventNative(MVKDevice* device, const VkEventCreateInfo* pCreateInfo) : MVKEvent(device, pCreateInfo) {
_mtlEvent = [_device->getMTLDevice() newSharedEvent]; // retained
}
MVKEventNative::~MVKEventNative() {
[_mtlEvent release];
}
#pragma mark -
#pragma mark MVKEventEmulated
bool MVKEventEmulated::isSet() { return !_blocker.isReserved(); }
void MVKEventEmulated::signal(bool status) {
if (status) {
_blocker.release();
} else {
_blocker.reserve();
}
}
void MVKEventEmulated::encodeSignal(id<MTLCommandBuffer> mtlCmdBuff, bool status) {
if (status) {
[mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) { _blocker.release(); }];
} else {
_blocker.reserve();
}
// An encoded signal followed by an encoded wait should cause the wait to be skipped.
// However, because encoding a signal will not release the blocker until the command buffer
// is finished executing (so the CPU can tell when it really is done) it is possible that
// the encoded wait will block when it shouldn't. To avoid that, we keep track of whether
// the most recent encoded signal was set or reset, so the next encoded wait knows whether
// to really wait or not.
_inlineSignalStatus = status;
}
void MVKEventEmulated::encodeWait(id<MTLCommandBuffer> mtlCmdBuff) {
if ( !_inlineSignalStatus ) { _blocker.wait(); }
}
MVKEventEmulated::MVKEventEmulated(MVKDevice* device, const VkEventCreateInfo* pCreateInfo) :
MVKEvent(device, pCreateInfo), _blocker(false, 1), _inlineSignalStatus(false) {}
#pragma mark -
#pragma mark Support functions

View File

@ -146,7 +146,7 @@
# define MVK_CONFIG_FORCE_LOW_POWER_GPU 0
#endif
/** Allow the use of Metal events for Vulkan synchronizations such as VkSemaphores. Disabled by default. */
/** Allow the use of Metal events for VkSemaphore synchronization behaviour. Disabled by default. */
#ifndef MVK_ALLOW_METAL_EVENTS
# define MVK_ALLOW_METAL_EVENTS 0
#endif

View File

@ -651,9 +651,10 @@ MVK_PUBLIC_SYMBOL VkResult vkCreateEvent(
VkEvent* pEvent) {
MVKTraceVulkanCallStart();
//VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT
MVKDevice* mvkDev = MVKDevice::getMVKDevice(device);
VkResult rslt = mvkDev->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateEvent(): Vukan events are not supported.");
MVKEvent* mvkEvent = mvkDev->createEvent(pCreateInfo, pAllocator);
*pEvent = (VkEvent)mvkEvent;
VkResult rslt = mvkEvent->getConfigurationResult();
MVKTraceVulkanCallEnd();
return rslt;
}
@ -666,7 +667,7 @@ MVK_PUBLIC_SYMBOL void vkDestroyEvent(
MVKTraceVulkanCallStart();
if ( !event ) { return; }
MVKDevice* mvkDev = MVKDevice::getMVKDevice(device);
mvkDev->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkDestroyEvent(): Vukan events are not supported.");
mvkDev->destroyEvent((MVKEvent*)event, pAllocator);
MVKTraceVulkanCallEnd();
}
@ -675,8 +676,8 @@ MVK_PUBLIC_SYMBOL VkResult vkGetEventStatus(
VkEvent event) {
MVKTraceVulkanCallStart();
MVKDevice* mvkDev = MVKDevice::getMVKDevice(device);
VkResult rslt = mvkDev->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkGetEventStatus(): Vukan events are not supported.");
MVKEvent* mvkEvent = (MVKEvent*)event;
VkResult rslt = mvkEvent->isSet() ? VK_EVENT_SET : VK_EVENT_RESET;
MVKTraceVulkanCallEnd();
return rslt;
}
@ -686,10 +687,10 @@ MVK_PUBLIC_SYMBOL VkResult vkSetEvent(
VkEvent event) {
MVKTraceVulkanCallStart();
MVKDevice* mvkDev = MVKDevice::getMVKDevice(device);
VkResult rslt = mvkDev->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkSetEvent(): Vukan events are not supported.");
MVKEvent* mvkEvent = (MVKEvent*)event;
mvkEvent->signal(true);
MVKTraceVulkanCallEnd();
return rslt;
return VK_SUCCESS;
}
MVK_PUBLIC_SYMBOL VkResult vkResetEvent(
@ -697,10 +698,10 @@ MVK_PUBLIC_SYMBOL VkResult vkResetEvent(
VkEvent event) {
MVKTraceVulkanCallStart();
MVKDevice* mvkDev = MVKDevice::getMVKDevice(device);
VkResult rslt = mvkDev->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkResetEvent(): Vukan events are not supported.");
MVKEvent* mvkEvent = (MVKEvent*)event;
mvkEvent->signal(false);
MVKTraceVulkanCallEnd();
return rslt;
return VK_SUCCESS;
}
MVK_PUBLIC_SYMBOL VkResult vkCreateQueryPool(
@ -1709,7 +1710,7 @@ MVK_PUBLIC_SYMBOL void vkCmdSetEvent(
MVKTraceVulkanCallStart();
MVKCommandBuffer* cmdBuff = MVKCommandBuffer::getMVKCommandBuffer(commandBuffer);
cmdBuff->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdSetEvent(): Vukan events are not supported.");
mvkCmdSetEvent(cmdBuff, event, stageMask);
MVKTraceVulkanCallEnd();
}
@ -1720,7 +1721,7 @@ MVK_PUBLIC_SYMBOL void vkCmdResetEvent(
MVKTraceVulkanCallStart();
MVKCommandBuffer* cmdBuff = MVKCommandBuffer::getMVKCommandBuffer(commandBuffer);
cmdBuff->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdResetEvent(): Vukan events are not supported.");
mvkCmdResetEvent(cmdBuff, event, stageMask);
MVKTraceVulkanCallEnd();
}
@ -1739,7 +1740,11 @@ MVK_PUBLIC_SYMBOL void vkCmdWaitEvents(
MVKTraceVulkanCallStart();
MVKCommandBuffer* cmdBuff = MVKCommandBuffer::getMVKCommandBuffer(commandBuffer);
cmdBuff->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdWaitEvents(): Vukan events are not supported.");
mvkCmdWaitEvents(cmdBuff, eventCount, pEvents,
srcStageMask, dstStageMask,
memoryBarrierCount, pMemoryBarriers,
bufferMemoryBarrierCount, pBufferMemoryBarriers,
imageMemoryBarrierCount, pImageMemoryBarriers);
MVKTraceVulkanCallEnd();
}
@ -1757,8 +1762,8 @@ MVK_PUBLIC_SYMBOL void vkCmdPipelineBarrier(
MVKTraceVulkanCallStart();
MVKCommandBuffer* cmdBuff = MVKCommandBuffer::getMVKCommandBuffer(commandBuffer);
mvkCmdPipelineBarrier(cmdBuff, srcStageMask, dstStageMask,
dependencyFlags, memoryBarrierCount, pMemoryBarriers,
mvkCmdPipelineBarrier(cmdBuff, srcStageMask, dstStageMask, dependencyFlags,
memoryBarrierCount, pMemoryBarriers,
bufferMemoryBarrierCount, pBufferMemoryBarriers,
imageMemoryBarrierCount, pImageMemoryBarriers);
MVKTraceVulkanCallEnd();