From 3c0644f36a20bac048888550a9c72973c2f4bc74 Mon Sep 17 00:00:00 2001 From: Bill Hollings Date: Fri, 8 Apr 2022 14:20:18 -0400 Subject: [PATCH] Add support for VK_EXT_sample_locations extension. Supports only setting custom sample locations in subpasses via vkBeginRenderpass. Does not support setting custom sample locations via vkCmdBindPipeline or vkCmdSetSampleLocationsEXT, although collects that info for possible future enhancements. - MVKPhysicalDevice track platform support and respond to property queries. - MVKCmdBeginRenderPassBase collect subpass custom sample locations. - MVKPipeline support dynamic state values beyond 31. - MVKPipeline collect custom sample locations. - Add MVKCmdSetSampleLocations to support vkCmdSetSampleLocations to collect dynamic custom sample locations. - MVKCommandEncoder support collecting custom sample positions from subpass and dynamic, and set into MTLRenderPassDescriptor for each Metal render pass. - MVKArrayRef add assignment operator. - Add MVKPhysicalDeviceMetalFeatures::programmableSamplePositions. - Update VK_MVK_MOLTENVK_SPEC_VERSION to version 34. - MVKCommandBuffer.h remove obsolete comment documentation. - Update Whats_New.md. --- Docs/MoltenVK_Runtime_UserGuide.md | 1 + Docs/Whats_New.md | 3 + MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h | 5 +- MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h | 20 +++++ .../MoltenVK/Commands/MVKCmdRenderPass.mm | 54 ++++++++++++- MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h | 77 ++----------------- .../MoltenVK/Commands/MVKCommandBuffer.mm | 35 ++++++++- .../MoltenVK/Commands/MVKCommandTypePools.def | 1 + MoltenVK/MoltenVK/GPUObjects/MVKDevice.h | 4 + MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 32 +++++++- MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm | 2 + MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h | 14 +++- MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm | 61 +++++++++++---- MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h | 7 +- MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm | 2 - MoltenVK/MoltenVK/Layers/MVKExtensions.def | 1 + MoltenVK/MoltenVK/Utility/MVKFoundation.h | 5 ++ MoltenVK/MoltenVK/Vulkan/vulkan.mm | 23 ++++++ 18 files changed, 247 insertions(+), 100 deletions(-) diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md index e6f987442..b09d066b7 100644 --- a/Docs/MoltenVK_Runtime_UserGuide.md +++ b/Docs/MoltenVK_Runtime_UserGuide.md @@ -307,6 +307,7 @@ In addition to core *Vulkan* functionality, **MoltenVK** also supports the foll - `VK_EXT_post_depth_coverage` *(iOS and macOS, requires family 4 (A11) or better Apple GPU)* - `VK_EXT_private_data ` - `VK_EXT_robustness2` +- `VK_EXT_sample_locations` - `VK_EXT_scalar_block_layout` - `VK_EXT_shader_stencil_export` *(requires Mac GPU family 2 or iOS GPU family 5)* - `VK_EXT_shader_viewport_index_layer` diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index 80c235a88..8c0b55680 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -18,6 +18,8 @@ MoltenVK 1.1.9 Released TBD +- Add support for extensions: + - `VK_EXT_sample_locations` - Fixes to pipeline layout compatibility. - Reinstate memory barriers on non-Apple GPUs, which were inadvertently disabled in an earlier update. - Support base vertex instance support in shader conversion. @@ -29,6 +31,7 @@ Released TBD - Fixes to optimize resource objects retained by descriptors beyond their lifetimes. - `MoltenVKShaderConverter` tool defaults to the highest MSL version supported on runtime OS. - Update *glslang* version, to use `python3` in *glslang* scripts, to replace missing `python` on *macOS 12.3*. +- Update `VK_MVK_MOLTENVK_SPEC_VERSION` to version `34`. - Update to latest SPIRV-Cross: - MSL: Support input/output blocks containing nested struct arrays. - MSL: Use var name instead of var-type name for flattened interface members. diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h index 583aef92d..d4163a9b7 100644 --- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h +++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h @@ -55,7 +55,7 @@ typedef unsigned long MTLLanguageVersion; #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 33 +#define VK_MVK_MOLTENVK_SPEC_VERSION 34 #define VK_MVK_MOLTENVK_EXTENSION_NAME "VK_MVK_moltenvk" /** Identifies the level of logging MoltenVK should be limited to outputting. */ @@ -786,7 +786,7 @@ typedef struct { * command buffer submission, to a physically removed GPU. In the case where this error does * not impact the VkPhysicalDevice, Vulkan requires that the app destroy and re-create a new * VkDevice. However, not all apps (including CTS) respect that requirement, leading to what - * might be a transient command submission failure causing an unexpected catastophic app failure. + * might be a transient command submission failure causing an unexpected catastrophic app failure. * * If this setting is enabled, in the case of a VK_ERROR_DEVICE_LOST error that does NOT impact * the VkPhysicalDevice, MoltenVK will log the error, but will not mark the VkDevice as lost, @@ -929,6 +929,7 @@ typedef struct { VkBool32 descriptorSetArgumentBuffers; /**< If true, a Metal argument buffer can be assigned to a descriptor set, and used on any pipeline and pipeline stage. If false, a different Metal argument buffer must be used for each pipeline-stage/descriptor-set combination. */ MVKFloatRounding clearColorFloatRounding; /**< Identifies the type of rounding Metal uses for MTLClearColor float to integer conversions. */ MVKCounterSamplingFlags counterSamplingPoints; /**< Identifies the points where pipeline GPU counter sampling may occur. */ + VkBool32 programmableSamplePositions; /**< If true, programmable MSAA sample positions are supported. */ } MVKPhysicalDeviceMetalFeatures; /** MoltenVK performance of a particular type of activity. */ diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h index 15c40fc3f..dcb3b0234 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h +++ b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h @@ -46,6 +46,7 @@ class MVKCmdBeginRenderPassBase : public MVKCommand { protected: + MVKSmallVector> _subpassSamplePositions; MVKRenderPass* _renderPass; MVKFramebuffer* _framebuffer; VkRect2D _renderArea; @@ -137,6 +138,25 @@ class MVKCmdEndRenderPass : public MVKCommand { }; +#pragma mark - +#pragma mark MVKCmdSetSampleLocations + +/** Vulkan command to dynamically set custom sample locations. */ +class MVKCmdSetSampleLocations : public MVKCommand { + +public: + VkResult setContent(MVKCommandBuffer* cmdBuff, + const VkSampleLocationsInfoEXT* pSampleLocationsInfo); + + void encode(MVKCommandEncoder* cmdEncoder) override; + +protected: + MVKCommandTypePool* getTypePool(MVKCommandPool* cmdPool) override; + + MVKSmallVector _samplePositions; +}; + + #pragma mark - #pragma mark MVKCmdExecuteCommands diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm index 56f0d4914..f621be82b 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm @@ -36,6 +36,30 @@ _renderPass = (MVKRenderPass*)pRenderPassBegin->renderPass; _framebuffer = (MVKFramebuffer*)pRenderPassBegin->framebuffer; _renderArea = pRenderPassBegin->renderArea; + _subpassSamplePositions.clear(); + + for (const auto* next = (VkBaseInStructure*)pRenderPassBegin->pNext; next; next = next->pNext) { + switch (next->sType) { + case VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT: { + // Build an array of arrays, one array of sample positions for each subpass index. + // For subpasses not included in VkRenderPassSampleLocationsBeginInfoEXT, the resulting array of samples will be empty. + _subpassSamplePositions.resize(_renderPass->getSubpassCount()); + auto* pRPSampLocnsInfo = (VkRenderPassSampleLocationsBeginInfoEXT*)next; + for (uint32_t spSLIdx = 0; spSLIdx < pRPSampLocnsInfo->postSubpassSampleLocationsCount; spSLIdx++) { + auto& spsl = pRPSampLocnsInfo->pPostSubpassSampleLocations[spSLIdx]; + uint32_t spIdx = spsl.subpassIndex; + auto& spSampPosns = _subpassSamplePositions[spIdx]; + for (uint32_t slIdx = 0; slIdx < spsl.sampleLocationsInfo.sampleLocationsCount; slIdx++) { + auto& sl = spsl.sampleLocationsInfo.pSampleLocations[slIdx]; + spSampPosns.push_back(MTLSamplePositionMake(sl.x, sl.y)); + } + } + break; + } + default: + break; + } + } return VK_SUCCESS; } @@ -61,13 +85,23 @@ template void MVKCmdBeginRenderPass::encode(MVKCommandEncoder* cmdEncoder) { // MVKLogDebug("Encoding vkCmdBeginRenderPass(). Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds()); + + // Convert the sample position array of arrays to an array of array-references, + // so that it can be passed to the command encoder. + size_t spSPCnt = _subpassSamplePositions.size(); + MVKArrayRef spSPRefs[spSPCnt]; + for (uint32_t spSPIdx = 0; spSPIdx < spSPCnt; spSPIdx++) { + spSPRefs[spSPIdx] = _subpassSamplePositions[spSPIdx].contents(); + } + cmdEncoder->beginRenderpass(this, _contents, _renderPass, _framebuffer, _renderArea, _clearValues.contents(), - _attachments.contents()); + _attachments.contents(), + MVKArrayRef(spSPRefs, spSPCnt)); } template class MVKCmdBeginRenderPass<1, 0>; @@ -130,6 +164,24 @@ cmdEncoder->endRenderpass(); } +#pragma mark - +#pragma mark MVKCmdSetSampleLocations + +VkResult MVKCmdSetSampleLocations::setContent(MVKCommandBuffer* cmdBuff, + const VkSampleLocationsInfoEXT* pSampleLocationsInfo) { + + for (uint32_t slIdx = 0; slIdx < pSampleLocationsInfo->sampleLocationsCount; slIdx++) { + auto& sl = pSampleLocationsInfo->pSampleLocations[slIdx]; + _samplePositions.push_back(MTLSamplePositionMake(sl.x, sl.y)); + } + + return VK_SUCCESS; +} + +void MVKCmdSetSampleLocations::encode(MVKCommandEncoder* cmdEncoder) { + cmdEncoder->setDynamicSamplePositions(_samplePositions.contents()); +} + #pragma mark - #pragma mark MVKCmdExecuteCommands diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h index 9ad5e61b8..49a74ed8b 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h +++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h @@ -191,74 +191,6 @@ class MVKCommandBuffer : public MVKDispatchableVulkanAPIObject, #pragma mark - #pragma mark MVKCommandEncoder -// The following commands can be issued both inside and outside a renderpass and their state must -// span multiple MTLRenderCommandEncoders, to allow state to be set before a renderpass, and to -// allow more than one MTLRenderCommandEncoder to be used for a single Vulkan renderpass or subpass. -// -// + vkCmdBindPipeline() : _graphicsPipelineState & _computePipelineState -// + vkCmdBindDescriptorSets() : _graphicsResourcesState & _computeResourcesState -// + vkCmdBindVertexBuffers() : _graphicsResourcesState -// + vkCmdBindIndexBuffer() : _graphicsResourcesState -// + vkCmdPushConstants() : _vertexPushConstants & _tessCtlPushConstants & _tessEvalPushConstants & _fragmentPushConstants & _computePushConstants -// + vkCmdSetViewport() : _viewportState -// + vkCmdSetDepthBias() : _depthBiasState -// + vkCmdSetScissor() : _scissorState -// + vkCmdSetStencilCompareMask() : _depthStencilState -// + vkCmdSetStencilWriteMask() : _depthStencilState -// + vkCmdSetStencilReference() : _stencilReferenceValueState -// + vkCmdSetBlendConstants() : _blendColorState -// + vkCmdBeginQuery() : _occlusionQueryState -// + vkCmdEndQuery() : _occlusionQueryState -// + vkCmdPipelineBarrier() : handled via textureBarrier and MTLBlitCommandEncoder -// + vkCmdWriteTimestamp() : doesn't affect MTLCommandEncoders -// + vkCmdExecuteCommands() : state managed by embedded commands -// - vkCmdSetLineWidth() - unsupported by Metal -// - vkCmdSetDepthBounds() - unsupported by Metal -// - vkCmdWaitEvents() - unsupported by Metal - -// The above list of Vulkan commands covers the following corresponding MTLRenderCommandEncoder state: -// + setBlendColorRed : _blendColorState -// + setCullMode : _graphicsPipelineState -// + setDepthBias : _depthBiasState -// + setDepthClipMode : _graphicsPipelineState -// + setDepthStencilState : _depthStencilState -// + setFrontFacingWinding : _graphicsPipelineState -// + setRenderPipelineState : _graphicsPipelineState -// + setScissorRect : _scissorState -// + setStencilFrontReferenceValue : _stencilReferenceValueState -// + setStencilReferenceValue (unused) : _stencilReferenceValueState -// + setTriangleFillMode : _graphicsPipelineState -// + setViewport : _viewportState -// + setVisibilityResultMode : _occlusionQueryState -// + setVertexBuffer : _graphicsResourcesState & _vertexPushConstants & _tessEvalPushConstants -// + setVertexBuffers (unused) : _graphicsResourcesState -// + setVertexBytes : _vertexPushConstants & _tessEvalPushConstants -// + setVertexBufferOffset (unused) : _graphicsResourcesState -// + setVertexTexture : _graphicsResourcesState -// + setVertexTextures (unused) : _graphicsResourcesState -// + setVertexSamplerState : _graphicsResourcesState -// + setVertexSamplerStates : (unused) : _graphicsResourcesState -// + setFragmentBuffer : _graphicsResourcesState & _fragmentPushConstants -// + setFragmentBuffers (unused) : _graphicsResourcesState -// + setFragmentBytes : _fragmentPushConstants -// + setFragmentBufferOffset (unused) : _graphicsResourcesState -// + setFragmentTexture : _graphicsResourcesState -// + setFragmentTextures (unused) : _graphicsResourcesState -// + setFragmentSamplerState : _graphicsResourcesState -// + setFragmentSamplerStates : (unused) : _graphicsResourcesState - -// The above list of Vulkan commands covers the following corresponding MTLComputeCommandEncoder state: -// + setComputePipelineState : _computePipelineState & _graphicsPipelineState -// + setBuffer : _computeResourcesState & _computePushConstants & _graphicsResourcesState & _tessCtlPushConstants -// + setBuffers (unused) : _computeResourcesState & _graphicsResourcesState -// + setBytes : _computePushConstants & _tessCtlPushConstants -// + setBufferOffset (unused) : _computeResourcesState & _graphicsResourcesState -// + setTexture : _computeResourcesState & _graphicsResourcesState -// + setTextures (unused) : _computeResourcesState & _graphicsResourcesState -// + setSamplerState : _computeResourcesState & _graphicsResourcesState -// + setSamplerStates : (unused) : _computeResourcesState & _graphicsResourcesState - - /*** Holds a collection of active queries for each query pool. */ typedef std::unordered_map> MVKActivatedQueries; @@ -293,7 +225,8 @@ class MVKCommandEncoder : public MVKBaseDeviceObject { MVKFramebuffer* framebuffer, VkRect2D& renderArea, MVKArrayRef clearValues, - MVKArrayRef attachments); + MVKArrayRef attachments, + MVKArrayRef> subpassSamplePositions); /** Begins the next render subpass. */ void beginNextSubpass(MVKCommand* subpassCmd, VkSubpassContents renderpassContents); @@ -301,6 +234,9 @@ class MVKCommandEncoder : public MVKBaseDeviceObject { /** Begins the next multiview Metal render pass. */ void beginNextMultiviewPass(); + /** Sets the dynamic custom sample positions to use when rendering. */ + void setDynamicSamplePositions(MVKArrayRef dynamicSamplePositions); + /** Begins a Metal render pass for the current render subpass. */ void beginMetalRenderPass(MVKCommandUse cmdUse); @@ -509,6 +445,7 @@ class MVKCommandEncoder : public MVKBaseDeviceObject { void encodeTimestampStageCounterSamples(); bool hasTimestampStageCounterQueries() { return !_timestampStageCounterQueries.empty(); } id getStageCountersMTLFence(); + MVKArrayRef getCustomSamplePositions(); typedef struct GPUCounterQuery { MVKGPUCounterQueryPool* queryPool = nullptr; @@ -526,6 +463,8 @@ class MVKCommandEncoder : public MVKBaseDeviceObject { MVKSmallVector _timestampStageCounterQueries; MVKSmallVector _clearValues; MVKSmallVector _attachments; + MVKSmallVector _dynamicSamplePositions; + MVKSmallVector> _subpassSamplePositions; id _mtlComputeEncoder; MVKCommandUse _mtlComputeEncoderUse; id _mtlBlitEncoder; diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm index 574cb9861..e27ccb5c0 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm @@ -322,7 +322,8 @@ MVKFramebuffer* framebuffer, VkRect2D& renderArea, MVKArrayRef clearValues, - MVKArrayRef attachments) { + MVKArrayRef attachments, + MVKArrayRef> subpassSamplePositions) { _renderPass = renderPass; _framebuffer = framebuffer; _renderArea = renderArea; @@ -330,6 +331,14 @@ mvkVkExtent2DsAreEqual(_renderArea.extent, getFramebufferExtent())); _clearValues.assign(clearValues.begin(), clearValues.end()); _attachments.assign(attachments.begin(), attachments.end()); + + // Copy the sample positions array of arrays, one array of sample positions for each subpass index. + _subpassSamplePositions.resize(subpassSamplePositions.size); + for (uint32_t spSPIdx = 0; spSPIdx < subpassSamplePositions.size; spSPIdx++) { + _subpassSamplePositions[spSPIdx].assign(subpassSamplePositions[spSPIdx].begin(), + subpassSamplePositions[spSPIdx].end()); + } + setSubpass(passCmd, subpassContents, 0); } @@ -365,6 +374,10 @@ uint32_t MVKCommandEncoder::getMultiviewPassIndex() { return _multiviewPassIndex; } +void MVKCommandEncoder::setDynamicSamplePositions(MVKArrayRef dynamicSamplePositions) { + _dynamicSamplePositions.assign(dynamicSamplePositions.begin(), dynamicSamplePositions.end()); +} + // Creates _mtlRenderEncoder and marks cached render state as dirty so it will be set into the _mtlRenderEncoder. void MVKCommandEncoder::beginMetalRenderPass(MVKCommandUse cmdUse) { @@ -416,6 +429,14 @@ } } + // If programmable sample positions are supported, set them into the render pass descriptor. + // If no custom sample positions are established, size will be zero, + // and Metal will default to using default sample postions. + if (_pDeviceMetalFeatures->programmableSamplePositions) { + auto cstmSampPosns = getCustomSamplePositions(); + [mtlRPDesc setSamplePositions: cstmSampPosns.data count: cstmSampPosns.size]; + } + _mtlRenderEncoder = [_mtlCmdBuffer renderCommandEncoderWithDescriptor: mtlRPDesc]; // not retained setLabelIfNotNil(_mtlRenderEncoder, getMTLRenderCommandEncoderName(cmdUse)); @@ -439,6 +460,18 @@ _occlusionQueryState.beginMetalRenderPass(); } +// If custom sample positions have been set, return them, otherwise return an empty array. +// For Metal, VkPhysicalDeviceSampleLocationsPropertiesEXT::variableSampleLocations is false. +// As such, Vulkan requires that sample positions must be established at the beginning of +// a renderpass, and that both pipeline and dynamic sample locations must be the same as those +// set for each subpass. Therefore, the only sample positions of use are those set for each +// subpass when the renderpass begins. The pipeline and dynamic sample positions are ignored. +MVKArrayRef MVKCommandEncoder::getCustomSamplePositions() { + return (_renderSubpassIndex < _subpassSamplePositions.size() + ? _subpassSamplePositions[_renderSubpassIndex].contents() + : MVKArrayRef()); +} + void MVKCommandEncoder::encodeStoreActions(bool storeOverride) { getSubpass()->encodeStoreActions(this, _isRenderingEntireAttachment, diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def b/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def index d5785ff73..6c995bbf0 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def +++ b/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def @@ -78,6 +78,7 @@ MVK_CMD_TYPE_POOL(BindComputePipeline) MVK_CMD_TYPE_POOLS_FROM_5_THRESHOLDS(BeginRenderPass, 1, 2, 0, 1, 2) MVK_CMD_TYPE_POOL(NextSubpass) MVK_CMD_TYPE_POOL(EndRenderPass) +MVK_CMD_TYPE_POOL(SetSampleLocations) MVK_CMD_TYPE_POOLS_FROM_THRESHOLD(ExecuteCommands, 1) MVK_CMD_TYPE_POOLS_FROM_2_THRESHOLDS(BindDescriptorSetsStatic, 1, 4) MVK_CMD_TYPE_POOLS_FROM_THRESHOLD(BindDescriptorSetsDynamic, 4) diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h index ceb76a5d6..3cbf9cbc7 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h @@ -128,6 +128,10 @@ class MVKPhysicalDevice : public MVKDispatchableVulkanAPIObject { /** Populates the specified structure with the format properties of this device. */ void getFormatProperties(VkFormat format, VkFormatProperties2* pFormatProperties); + /** Populates the specified structure with the multisample properties of this device. */ + void getMultisampleProperties(VkSampleCountFlagBits samples, + VkMultisamplePropertiesEXT* pMultisampleProperties); + /** Populates the image format properties supported on this device. */ VkResult getImageFormatProperties(VkFormat format, VkImageType type, diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index b22b8b85e..17bc185ce 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -75,6 +75,9 @@ static const uint32_t kAMDRadeonRX6800DeviceId = 0x73bf; static const uint32_t kAMDRadeonRX6700DeviceId = 0x73df; +static const VkExtent2D kMetalSamplePositionGridSize = { 1, 1 }; +static const VkExtent2D kMetalSamplePositionGridSizeNotSupported = { 0, 0 }; + #pragma clang diagnostic pop @@ -457,6 +460,16 @@ portabilityProps->minVertexInputBindingStrideAlignment = (uint32_t)_metalFeatures.vertexStrideAlignment; break; } + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT: { + auto* sampLocnProps = (VkPhysicalDeviceSampleLocationsPropertiesEXT*)next; + sampLocnProps->sampleLocationSampleCounts = _metalFeatures.supportedSampleCounts; + sampLocnProps->maxSampleLocationGridSize = kMetalSamplePositionGridSize; + sampLocnProps->sampleLocationCoordinateRange[0] = 0.0; + sampLocnProps->sampleLocationCoordinateRange[1] = (15.0 / 16.0); + sampLocnProps->sampleLocationSubPixelBits = 4; + sampLocnProps->variableSampleLocations = VK_FALSE; + break; + } default: break; } @@ -526,6 +539,15 @@ getFormatProperties(format, &pFormatProperties->formatProperties); } +void MVKPhysicalDevice::getMultisampleProperties(VkSampleCountFlagBits samples, + VkMultisamplePropertiesEXT* pMultisampleProperties) { + if (pMultisampleProperties) { + pMultisampleProperties->maxSampleLocationGridSize = (mvkIsOnlyAnyFlagEnabled(samples, _metalFeatures.supportedSampleCounts) + ? kMetalSamplePositionGridSize + : kMetalSamplePositionGridSizeNotSupported); + } +} + VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format, VkImageType type, VkImageTiling tiling, @@ -1519,9 +1541,12 @@ #endif - // Note the selector name, which is different from the property name. + if ( [_mtlDevice respondsToSelector: @selector(areProgrammableSamplePositionsSupported)] ) { + _metalFeatures.programmableSamplePositions = _mtlDevice.areProgrammableSamplePositionsSupported; + } + if ( [_mtlDevice respondsToSelector: @selector(areRasterOrderGroupsSupported)] ) { - _metalFeatures.rasterOrderGroups = _mtlDevice.rasterOrderGroupsSupported; + _metalFeatures.rasterOrderGroups = _mtlDevice.areRasterOrderGroupsSupported; } #if MVK_XCODE_12 if ( [_mtlDevice respondsToSelector: @selector(supportsPullModelInterpolation)] ) { @@ -2738,6 +2763,9 @@ static uint32_t mvkGetEntryProperty(io_registry_entry_t entry, CFStringRef prope if (!_metalFeatures.samplerMirrorClampToEdge) { pWritableExtns->vk_KHR_sampler_mirror_clamp_to_edge.enabled = false; } + if (!_metalFeatures.programmableSamplePositions) { + pWritableExtns->vk_EXT_sample_locations.enabled = false; + } if (!_metalFeatures.rasterOrderGroups) { pWritableExtns->vk_EXT_fragment_shader_interlock.enabled = false; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm index 414fb62c8..1313cd06a 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm @@ -655,6 +655,8 @@ ADD_DVC_EXT_ENTRY_POINT(vkDestroyPrivateDataSlotEXT, EXT_PRIVATE_DATA); ADD_DVC_EXT_ENTRY_POINT(vkGetPrivateDataEXT, EXT_PRIVATE_DATA); ADD_DVC_EXT_ENTRY_POINT(vkSetPrivateDataEXT, EXT_PRIVATE_DATA); + ADD_DVC_EXT_ENTRY_POINT(vkGetPhysicalDeviceMultisamplePropertiesEXT, EXT_SAMPLE_LOCATIONS); + ADD_DVC_EXT_ENTRY_POINT(vkCmdSetSampleLocationsEXT, EXT_SAMPLE_LOCATIONS); ADD_DVC_EXT_ENTRY_POINT(vkGetRefreshCycleDurationGOOGLE, GOOGLE_DISPLAY_TIMING); ADD_DVC_EXT_ENTRY_POINT(vkGetPastPresentationTimingGOOGLE, GOOGLE_DISPLAY_TIMING); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h index a7f271d3a..21ffc442b 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h @@ -203,9 +203,6 @@ struct MVKStagedDescriptorBindingUse { MVKBitArray stages[4] = {}; }; -/** The number of dynamic states possible in Vulkan. */ -static const uint32_t kMVKVkDynamicStateCount = 32; - /** Represents an Vulkan graphics pipeline. */ class MVKGraphicsPipeline : public MVKPipeline { @@ -259,6 +256,12 @@ class MVKGraphicsPipeline : public MVKPipeline { /** Returns true if the tessellation control shader needs a buffer to store its per-patch output. */ bool needsTessCtlPatchOutputBuffer() { return _needsTessCtlPatchOutputBuffer; } + /** Returns whether this pipeline has custom sample positions enabled. */ + bool isUsingCustomSamplePositions() { return _isUsingCustomSamplePositions; } + + /** Returns the custom samples used by this pipeline. */ + MVKArrayRef getCustomSamplePositions() { return _customSamplePositions.contents(); } + /** Returns the Metal vertex buffer index to use for the specified vertex attribute binding number. */ uint32_t getMetalBufferIndexForVertexAttributeBinding(uint32_t binding) { return _device->getMetalBufferIndexForVertexAttributeBinding(binding); } @@ -287,6 +290,7 @@ class MVKGraphicsPipeline : public MVKPipeline { id getOrCompilePipeline(MTLRenderPipelineDescriptor* plDesc, id& plState); id getOrCompilePipeline(MTLComputePipelineDescriptor* plDesc, id& plState, const char* compilerType); + void initCustomSamplePositions(const VkGraphicsPipelineCreateInfo* pCreateInfo); void initMTLRenderPipelineState(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData); void initShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData); void addVertexInputToShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, const VkGraphicsPipelineCreateInfo* pCreateInfo); @@ -323,6 +327,8 @@ class MVKGraphicsPipeline : public MVKPipeline { MVKSmallVector _viewports; MVKSmallVector _scissors; + MVKSmallVector _dynamicState; + MVKSmallVector _customSamplePositions; MVKSmallVector _translatedVertexBindings; MVKSmallVector _zeroDivisorVertexBindings; MVKSmallVector _mtlArgumentEncoders; @@ -350,7 +356,6 @@ class MVKGraphicsPipeline : public MVKPipeline { uint32_t _tessCtlPatchOutputBufferIndex = 0; uint32_t _tessCtlLevelBufferIndex = 0; - bool _dynamicStateEnabled[kMVKVkDynamicStateCount]; bool _needsVertexSwizzleBuffer = false; bool _needsVertexBufferSizeBuffer = false; bool _needsVertexDynamicOffsetBuffer = false; @@ -372,6 +377,7 @@ class MVKGraphicsPipeline : public MVKPipeline { bool _isRasterizing = false; bool _isRasterizingColor = false; bool _isRasterizingDepthStencil = false; + bool _isUsingCustomSamplePositions = false; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm index bf545e395..2751bde13 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm @@ -309,17 +309,18 @@ } bool MVKGraphicsPipeline::supportsDynamicState(VkDynamicState state) { - - // First test if this dynamic state is explicitly turned off - if ( (state >= kMVKVkDynamicStateCount) || !_dynamicStateEnabled[state] ) { return false; } - - // Some dynamic states have other restrictions - switch (state) { - case VK_DYNAMIC_STATE_DEPTH_BIAS: - return _rasterInfo.depthBiasEnable; - default: - return true; - } + for (auto& ds : _dynamicState) { + if (state == ds) { + // Some dynamic states have other restrictions + switch (state) { + case VK_DYNAMIC_STATE_DEPTH_BIAS: + return _rasterInfo.depthBiasEnable; + default: + return true; + } + } + } + return false; } static const char vtxCompilerType[] = "Vertex stage pipeline for tessellation"; @@ -408,13 +409,11 @@ } } - // Track dynamic state in _dynamicStateEnabled array - mvkClear(_dynamicStateEnabled, kMVKVkDynamicStateCount); // start with all dynamic state disabled + // Track dynamic state const VkPipelineDynamicStateCreateInfo* pDS = pCreateInfo->pDynamicState; if (pDS) { for (uint32_t i = 0; i < pDS->dynamicStateCount; i++) { - VkDynamicState ds = pDS->pDynamicStates[i]; - _dynamicStateEnabled[ds] = true; + _dynamicState.push_back(pDS->pDynamicStates[i]); } } @@ -457,6 +456,9 @@ } } + // Must run after _isRasterizing and _dynamicState are populated + initCustomSamplePositions(pCreateInfo); + // Render pipeline state initMTLRenderPipelineState(pCreateInfo, reflectData); @@ -472,7 +474,7 @@ for (uint32_t vpIdx = 0; vpIdx < vpCnt; vpIdx++) { // If viewport is dyanamic, we still add a dummy so that the count will be tracked. VkViewport vp; - if ( !_dynamicStateEnabled[VK_DYNAMIC_STATE_VIEWPORT] ) { vp = pVPState->pViewports[vpIdx]; } + if ( !supportsDynamicState(VK_DYNAMIC_STATE_VIEWPORT) ) { vp = pVPState->pViewports[vpIdx]; } _viewports.push_back(vp); } @@ -481,7 +483,7 @@ for (uint32_t sIdx = 0; sIdx < sCnt; sIdx++) { // If scissor is dyanamic, we still add a dummy so that the count will be tracked. VkRect2D sc; - if ( !_dynamicStateEnabled[VK_DYNAMIC_STATE_SCISSOR] ) { sc = pVPState->pScissors[sIdx]; } + if ( !supportsDynamicState(VK_DYNAMIC_STATE_SCISSOR) ) { sc = pVPState->pScissors[sIdx]; } _scissors.push_back(sc); } } @@ -512,6 +514,31 @@ return plState; } +// Must run after _isRasterizing and _dynamicState are populated +void MVKGraphicsPipeline::initCustomSamplePositions(const VkGraphicsPipelineCreateInfo* pCreateInfo) { + + // Must ignore allowed bad pMultisampleState pointer if rasterization disabled + if ( !(_isRasterizing && pCreateInfo->pMultisampleState) ) { return; } + + for (const auto* next = (VkBaseInStructure*)pCreateInfo->pMultisampleState->pNext; next; next = next->pNext) { + switch (next->sType) { + case VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT: { + auto* pSampLocnsCreateInfo = (VkPipelineSampleLocationsStateCreateInfoEXT*)next; + _isUsingCustomSamplePositions = pSampLocnsCreateInfo->sampleLocationsEnable; + if (_isUsingCustomSamplePositions && !supportsDynamicState(VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT)) { + for (uint32_t slIdx = 0; slIdx < pSampLocnsCreateInfo->sampleLocationsInfo.sampleLocationsCount; slIdx++) { + auto& sl = pSampLocnsCreateInfo->sampleLocationsInfo.pSampleLocations[slIdx]; + _customSamplePositions.push_back(MTLSamplePositionMake(sl.x, sl.y)); + } + } + break; + } + default: + break; + } + } +} + // Constructs the underlying Metal render pipeline. void MVKGraphicsPipeline::initMTLRenderPipelineState(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData) { _mtlTessVertexStageState = nil; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h index 81f1b6e1c..2d1c69899 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h @@ -261,8 +261,11 @@ class MVKRenderPass : public MVKVulkanAPIDeviceObject { /** Returns the granularity of the render area of this instance. */ VkExtent2D getRenderAreaGranularity(); - /** Returns the format of the color attachment at the specified index. */ - MVKRenderSubpass* getSubpass(uint32_t subpassIndex); + /** Returns the number of subpasses. */ + size_t getSubpassCount() { return _subpasses.size(); } + + /** Returns the subpass at the specified index. */ + MVKRenderSubpass* getSubpass(uint32_t subpassIndex) { return &_subpasses[subpassIndex]; } /** Returns whether or not this render pass is a multiview render pass. */ bool isMultiview() const; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm index 6d9c04abc..ee9289962 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm @@ -818,8 +818,6 @@ static uint32_t getNextViewMaskGroup(uint32_t viewMask, uint32_t* startView, uin return { 1, 1 }; } -MVKRenderSubpass* MVKRenderPass::getSubpass(uint32_t subpassIndex) { return &_subpasses[subpassIndex]; } - bool MVKRenderPass::isMultiview() const { return _subpasses[0].isMultiview(); } MVKRenderPass::MVKRenderPass(MVKDevice* device, diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def index baf519f05..063809837 100644 --- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def +++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def @@ -95,6 +95,7 @@ MVK_EXTENSION(EXT_metal_surface, EXT_METAL_SURFACE, MVK_EXTENSION(EXT_post_depth_coverage, EXT_POST_DEPTH_COVERAGE, DEVICE, 11.0, 11.0) MVK_EXTENSION(EXT_private_data, EXT_PRIVATE_DATA, DEVICE, 10.11, 8.0) MVK_EXTENSION(EXT_robustness2, EXT_ROBUSTNESS_2, DEVICE, 10.11, 8.0) +MVK_EXTENSION(EXT_sample_locations, EXT_SAMPLE_LOCATIONS, DEVICE, 10.13, 11.0) MVK_EXTENSION(EXT_scalar_block_layout, EXT_SCALAR_BLOCK_LAYOUT, DEVICE, 10.11, 8.0) MVK_EXTENSION(EXT_shader_stencil_export, EXT_SHADER_STENCIL_EXPORT, DEVICE, 10.14, 12.0) MVK_EXTENSION(EXT_shader_viewport_index_layer, EXT_SHADER_VIEWPORT_INDEX_LAYER, DEVICE, 10.11, 8.0) diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.h b/MoltenVK/MoltenVK/Utility/MVKFoundation.h index 5e79965fa..30d0a4bfb 100644 --- a/MoltenVK/MoltenVK/Utility/MVKFoundation.h +++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.h @@ -442,6 +442,11 @@ struct MVKArrayRef { const Type* end() const { return &data[size]; } const Type& operator[]( const size_t i ) const { return data[i]; } Type& operator[]( const size_t i ) { return data[i]; } + MVKArrayRef& operator=(const MVKArrayRef& other) { + data = other.data; + *(size_t*)&size = other.size; + return *this; + } MVKArrayRef() : MVKArrayRef(nullptr, 0) {} MVKArrayRef(Type* d, size_t s) : data(d), size(s) {} }; diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm index b266cf324..93d7c08c2 100644 --- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm +++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm @@ -3098,6 +3098,29 @@ MVK_PUBLIC_VULKAN_SYMBOL VkResult vkGetPastPresentationTimingGOOGLE( return rslt; } +#pragma mark - +#pragma mark VK_EXT_sample_locations extension + +void vkGetPhysicalDeviceMultisamplePropertiesEXT( + VkPhysicalDevice physicalDevice, + VkSampleCountFlagBits samples, + VkMultisamplePropertiesEXT* pMultisampleProperties) { + + MVKTraceVulkanCallStart(); + MVKPhysicalDevice* mvkPD = MVKPhysicalDevice::getMVKPhysicalDevice(physicalDevice); + mvkPD->getMultisampleProperties(samples, pMultisampleProperties); + MVKTraceVulkanCallEnd(); +} + +void vkCmdSetSampleLocationsEXT( + VkCommandBuffer commandBuffer, + const VkSampleLocationsInfoEXT* pSampleLocationsInfo) { + + MVKTraceVulkanCallStart(); + MVKAddCmd(SetSampleLocations, commandBuffer, pSampleLocationsInfo); + MVKTraceVulkanCallEnd(); +} + #pragma mark - #pragma mark iOS & macOS surface extensions