Resolve and merge conflicts.
diff --git a/MoltenVK/MoltenVK/API/mvk_datatypes.h b/MoltenVK/MoltenVK/API/mvk_datatypes.h
index e7965bd..41cbf7a 100644
--- a/MoltenVK/MoltenVK/API/mvk_datatypes.h
+++ b/MoltenVK/MoltenVK/API/mvk_datatypes.h
@@ -390,18 +390,24 @@
/** Returns the size, in bytes, of a vertex index of the specified type. */
size_t mvkMTLIndexTypeSizeInBytes(MTLIndexType mtlIdxType);
-/** Returns the MVKShaderStage corresponding to the specified Vulkan VkShaderStageFlagBits. */
+/** Returns the MoltenVK MVKShaderStage corresponding to the specified Vulkan VkShaderStageFlagBits. */
MVKShaderStage mvkShaderStageFromVkShaderStageFlagBits(VkShaderStageFlagBits vkStage);
-/** Returns the VkShaderStageFlagBits corresponding to the specified MoltenVK MVKShaderStage. */
+/** Returns the Vulkan VkShaderStageFlagBits corresponding to the specified MoltenVK MVKShaderStage. */
VkShaderStageFlagBits mvkVkShaderStageFlagBitsFromMVKShaderStage(MVKShaderStage mvkStage);
-/** Returns the MTLWinding corresponding to the specified spv::ExecutionMode. */
+/** Returns the Metal MTLWinding corresponding to the specified SPIR-V spv::ExecutionMode. */
MTLWinding mvkMTLWindingFromSpvExecutionMode(uint32_t spvMode);
-/** Returns the MTLTessellationPartitionMode corresponding to the specified spv::ExecutionMode. */
+/** Returns the Metal MTLTessellationPartitionMode corresponding to the specified SPIR-V spv::ExecutionMode. */
MTLTessellationPartitionMode mvkMTLTessellationPartitionModeFromSpvExecutionMode(uint32_t spvMode);
+/** Returns the combination of Metal MTLRenderStage bits corresponding to the specified Vulkan VkPiplineStageFlags. */
+MTLRenderStages mvkMTLRenderStagesFromVkPipelineStageFlags(VkPipelineStageFlags vkStages);
+
+/** Returns the combination of Metal MTLBarrierScope bits corresponding to the specified Vulkan VkAccessFlags. */
+MTLBarrierScope mvkMTLBarrierScopeFromVkAccessFlags(VkAccessFlags vkAccess);
+
#pragma mark -
#pragma mark Geometry conversions
diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
index 04a1a65..381fd4e 100644
--- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
+++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
@@ -55,7 +55,7 @@
#define MVK_MAKE_VERSION(major, minor, patch) (((major) * 10000) + ((minor) * 100) + (patch))
#define MVK_VERSION MVK_MAKE_VERSION(MVK_VERSION_MAJOR, MVK_VERSION_MINOR, MVK_VERSION_PATCH)
-#define VK_MVK_MOLTENVK_SPEC_VERSION 19
+#define VK_MVK_MOLTENVK_SPEC_VERSION 20
#define VK_MVK_MOLTENVK_EXTENSION_NAME "VK_MVK_moltenvk"
/**
@@ -524,6 +524,8 @@
VkBool32 arrayOfSamplers; /**< If true, arrays of texture samplers is supported. */
MTLLanguageVersion mslVersionEnum; /**< The version of the Metal Shading Language available on this device, as a Metal enumeration. */
VkBool32 depthSampleCompare; /**< If true, depth texture samplers support the comparison of the pixel value against a reference value. */
+ VkBool32 events; /**< If true, Metal synchronization events are supported. */
+ VkBool32 memoryBarriers; /**< If true, full memory barriers within Metal render passes are supported. */
} MVKPhysicalDeviceMetalFeatures;
/**
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
index 580d0e2..5ce849e 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
@@ -66,8 +66,36 @@
#if MVK_MACOS
// Calls below invoke MTLBlitCommandEncoder so must apply this first
- if ( !(_memoryBarriers.empty() && _imageMemoryBarriers.empty()) ) {
- [cmdEncoder->_mtlRenderEncoder textureBarrier];
+ if ( getDevice()->_pMetalFeatures->memoryBarriers ) {
+ MTLRenderStages srcStages = mvkMTLRenderStagesFromVkPipelineStageFlags(_srcStageMask);
+ MTLRenderStages dstStages = mvkMTLRenderStagesFromVkPipelineStageFlags(_dstStageMask);
+ for (auto& mb : _memoryBarriers) {
+ MTLBarrierScope scope = mvkMTLBarrierScopeFromVkAccessFlags(mb.dstAccessMask);
+ scope |= mvkMTLBarrierScopeFromVkAccessFlags(mb.srcAccessMask);
+ [cmdEncoder->_mtlRenderEncoder memoryBarrierWithScope: scope
+ afterStages: srcStages
+ beforeStages: dstStages];
+ }
+ std::vector<id<MTLResource>> resources;
+ resources.reserve(_bufferMemoryBarriers.size() + _imageMemoryBarriers.size());
+ for (auto& mb : _bufferMemoryBarriers) {
+ auto* mvkBuff = (MVKBuffer*)mb.buffer;
+ resources.push_back(mvkBuff->getMTLBuffer());
+ }
+ for (auto& mb : _imageMemoryBarriers) {
+ auto* mvkImg = (MVKImage*)mb.image;
+ resources.push_back(mvkImg->getMTLTexture());
+ }
+ if ( !resources.empty() ) {
+ [cmdEncoder->_mtlRenderEncoder memoryBarrierWithResources: resources.data()
+ count: resources.size()
+ afterStages: srcStages
+ beforeStages: dstStages];
+ }
+ } else {
+ if ( !(_memoryBarriers.empty() && _imageMemoryBarriers.empty()) ) {
+ [cmdEncoder->_mtlRenderEncoder textureBarrier];
+ }
}
#endif
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 4e652b1..58fd374 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -736,6 +736,7 @@
if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v5] ) {
_metalFeatures.mslVersionEnum = MTLLanguageVersion2_1;
+ _metalFeatures.events = true;
}
if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1] ) {
@@ -790,8 +791,10 @@
}
if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v4] ) {
- _metalFeatures.mslVersionEnum = MTLLanguageVersion2_1;
+ _metalFeatures.mslVersionEnum = MTLLanguageVersion2_1;
_metalFeatures.multisampleArrayTextures = true;
+ _metalFeatures.events = true;
+ _metalFeatures.memoryBarriers = true;
}
#endif
@@ -1931,6 +1934,8 @@
VkMemoryBarrier* pMemoryBarrier,
MVKCommandEncoder* cmdEncoder,
MVKCommandUse cmdUse) {
+ if (!mvkIsAnyFlagEnabled(dstStageMask, VK_PIPELINE_STAGE_HOST_BIT) ||
+ !mvkIsAnyFlagEnabled(pMemoryBarrier->dstAccessMask, VK_ACCESS_HOST_READ_BIT) ) { return; }
lock_guard<mutex> lock(_rezLock);
for (auto& rez : _resources) {
rez->applyMemoryBarrier(srcStageMask, dstStageMask, pMemoryBarrier, cmdEncoder, cmdUse);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index 00c5b63..2092f45 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -17,6 +17,7 @@
*/
#include "MVKImage.h"
+#include "MVKQueue.h"
#include "MVKSwapchain.h"
#include "MVKCommandBuffer.h"
#include "mvk_datatypes.hpp"
@@ -1084,6 +1085,19 @@
if (_availability.isAvailable) {
_availability.isAvailable = false;
signal(signaler);
+ if (_device->_pMetalFeatures->events) {
+ // 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.
+ MVKQueue* queue = _device->getQueue(0, 0);
+ id<MTLCommandQueue> mtlQ = queue->getMTLCommandQueue();
+ id<MTLCommandBuffer> mtlCmdBuff = [mtlQ commandBufferWithUnretainedReferences];
+ [mtlCmdBuff enqueue];
+ signaler.first->encodeSignal(mtlCmdBuff);
+ [mtlCmdBuff commit];
+ }
_preSignaled = signaler;
} else {
_availabilitySignalers.push_back(signaler);
@@ -1095,7 +1109,7 @@
// Signal either or both of the semaphore and fence in the specified tracker pair.
void MVKSwapchainImage::signal(MVKSwapchainSignaler& signaler) {
- if (signaler.first) { signaler.first->signal(); }
+ if (signaler.first && !_device->_pMetalFeatures->events) { signaler.first->signal(); }
if (signaler.second) { signaler.second->signal(); }
}
@@ -1148,6 +1162,10 @@
if (mtlCmdBuff) {
[mtlCmdBuff presentDrawable: mtlDrawable];
resetMetalSurface();
+ if (_device->_pMetalFeatures->events && !_availabilitySignalers.empty()) {
+ // Signal the semaphore device-side.
+ _availabilitySignalers.front().first->encodeSignal(mtlCmdBuff);
+ }
[mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) { makeAvailable(); }];
} else {
[mtlDrawable present];
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
index a8b1d30..e30d5ff 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
@@ -162,7 +162,7 @@
/** Returns the Vulkan API opaque object controlling this object. */
MVKVulkanAPIObject* getVulkanAPIObject() override { return _queue->getVulkanAPIObject(); }
- /**
+ /**
* Executes this action on the queue and then disposes of this instance.
*
* Upon completion of this function, no further calls should be made to this instance.
@@ -212,6 +212,7 @@
MVKFence* _fence;
MVKCommandUse _cmdBuffUse;
id<MTLCommandBuffer> _activeMTLCommandBuffer;
+ bool _isSignalingSemaphores;
};
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
index 8e14d82..4efac25 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
@@ -221,12 +221,30 @@
_queue->_submissionCaptureScope->beginScope();
- // Submit each command buffer.
+ MVKDevice* mvkDev = _queue->getDevice();
+
+ // If the device supports it, wait for any semaphores on the device.
+ if (mvkDev->_pMetalFeatures->events && _isAwaitingSemaphores) {
+ _isAwaitingSemaphores = false;
+ 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 || !_signalSemaphores.empty()) { getActiveMTLCommandBuffer(); }
+ if (_fence || _isSignalingSemaphores) { getActiveMTLCommandBuffer(); }
+
+ // If the device supports it, signal all semaphores on the device.
+ if (mvkDev->_pMetalFeatures->events && _isSignalingSemaphores) {
+ _isSignalingSemaphores = false;
+ for (auto* ss : _signalSemaphores) {
+ ss->encodeSignal(getActiveMTLCommandBuffer());
+ }
+ }
// Commit the last MTLCommandBuffer.
// Nothing after this because callback might destroy this instance before this function ends.
@@ -283,7 +301,9 @@
_queue->_submissionCaptureScope->endScope();
// Signal each of the signal semaphores.
- for (auto& ss : _signalSemaphores) { ss->signal(); }
+ if (_isSignalingSemaphores) {
+ for (auto& ss : _signalSemaphores) { ss->signal(); }
+ }
// If a fence exists, signal it.
if (_fence) { _fence->signal(); }
@@ -310,6 +330,7 @@
}
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]);
@@ -331,8 +352,21 @@
void MVKQueuePresentSurfaceSubmission::execute() {
id<MTLCommandQueue> mtlQ = _queue->getMTLCommandQueue();
+ // 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->_pMVKConfig->presentWithCommandBuffer || mvkDev->_pMVKConfig->displayWatermark) {
+ if (mvkDev->_pMetalFeatures->events && !_waitSemaphores.empty()) {
+ // Create a command buffer, have it wait for the semaphores, then present
+ // surfaces via the command buffer.
+ id<MTLCommandBuffer> mtlCmdBuff = [mtlQ commandBufferWithUnretainedReferences];
+ mtlCmdBuff.label = mvkMTLCommandBufferLabel(kMVKCommandUseQueuePresent);
+ [mtlCmdBuff enqueue];
+
+ for (auto& ws : _waitSemaphores) { ws->encodeWait(mtlCmdBuff); }
+ for (auto& si : _surfaceImages) { si->presentCAMetalDrawable(mtlCmdBuff); }
+
+ [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 = [mtlQ commandBufferWithUnretainedReferences];
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSync.h b/MoltenVK/MoltenVK/GPUObjects/MVKSync.h
index 6d397bc..1504c92 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSync.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSync.h
@@ -19,6 +19,7 @@
#pragma once
#include "MVKDevice.h"
+#include <atomic>
#include <mutex>
#include <condition_variable>
#include <unordered_set>
@@ -127,14 +128,23 @@
/** Signals the semaphore. Unblocks all waiting threads to continue processing. */
void signal();
+ /** 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);
+
#pragma mark Construction
- MVKSemaphore(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo)
- : MVKVulkanAPIDeviceObject(device), _blocker(false, 1) {}
+ MVKSemaphore(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo);
+
+ ~MVKSemaphore() override;
protected:
MVKSemaphoreImpl _blocker;
+ id<MTLEvent> _mtlEvent;
+ std::atomic<uint64_t> _mtlEventValue;
};
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm
index 4c677e7..171df2a 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm
@@ -85,6 +85,27 @@
_blocker.release();
}
+void MVKSemaphore::encodeWait(id<MTLCommandBuffer> cmdBuff) {
+ [cmdBuff encodeWaitForEvent: _mtlEvent value: _mtlEventValue];
+ ++_mtlEventValue;
+}
+
+void MVKSemaphore::encodeSignal(id<MTLCommandBuffer> cmdBuff) {
+ [cmdBuff encodeSignalEvent: _mtlEvent value: _mtlEventValue];
+}
+
+MVKSemaphore::MVKSemaphore(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo)
+ : MVKVulkanAPIDeviceObject(device), _blocker(false, 1), _mtlEvent(nil), _mtlEventValue(1) {
+
+ if (device->_pMetalFeatures->events) {
+ _mtlEvent = [device->getMTLDevice() newEvent];
+ }
+}
+
+MVKSemaphore::~MVKSemaphore() {
+ [_mtlEvent release];
+}
+
#pragma mark -
#pragma mark MVKFence
diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
index f6422a4..329fb6d 100644
--- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
+++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
@@ -1318,6 +1318,33 @@
}
}
+MVK_PUBLIC_SYMBOL MTLRenderStages mvkMTLRenderStagesFromVkPipelineStageFlags(VkPipelineStageFlags vkStages) {
+ MTLRenderStages mtlStages = MTLRenderStages(0);
+ if ( mvkIsAnyFlagEnabled(vkStages, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT) ) {
+ mtlStages |= MTLRenderStageVertex;
+ }
+ if ( mvkIsAnyFlagEnabled(vkStages, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT ) ) {
+ mtlStages |= MTLRenderStageFragment;
+ }
+ return mtlStages;
+}
+
+MVK_PUBLIC_SYMBOL MTLBarrierScope mvkMTLBarrierScopeFromVkAccessFlags(VkAccessFlags vkAccess) {
+ MTLBarrierScope mtlScope = MTLBarrierScope(0);
+ if ( mvkIsAnyFlagEnabled(vkAccess, VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_UNIFORM_READ_BIT) ) {
+ mtlScope |= MTLBarrierScopeBuffers;
+ }
+ if ( mvkIsAnyFlagEnabled(vkAccess, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT) ) {
+ mtlScope |= MTLBarrierScopeBuffers | MTLBarrierScopeTextures;
+ }
+#if MVK_MACOS
+ if ( mvkIsAnyFlagEnabled(vkAccess, VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT) ) {
+ mtlScope |= MTLBarrierScopeRenderTargets;
+ }
+#endif
+ return mtlScope;
+}
+
#pragma mark -
#pragma mark Memory options