Skip to content

Commit

Permalink
Add support for VK_EXT_sample_locations extension.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
billhollings committed Apr 8, 2022
1 parent 148823a commit 3c0644f
Show file tree
Hide file tree
Showing 18 changed files with 247 additions and 100 deletions.
1 change: 1 addition & 0 deletions Docs/MoltenVK_Runtime_UserGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
3 changes: 3 additions & 0 deletions Docs/Whats_New.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down
5 changes: 3 additions & 2 deletions MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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. */
Expand Down
20 changes: 20 additions & 0 deletions MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class MVKCmdBeginRenderPassBase : public MVKCommand {

protected:

MVKSmallVector<MVKSmallVector<MTLSamplePosition>> _subpassSamplePositions;
MVKRenderPass* _renderPass;
MVKFramebuffer* _framebuffer;
VkRect2D _renderArea;
Expand Down Expand Up @@ -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<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;

MVKSmallVector<MTLSamplePosition, 8> _samplePositions;
};


#pragma mark -
#pragma mark MVKCmdExecuteCommands

Expand Down
54 changes: 53 additions & 1 deletion MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -61,13 +85,23 @@
template <size_t N_CV, size_t N_A>
void MVKCmdBeginRenderPass<N_CV, N_A>::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<MTLSamplePosition> 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>;
Expand Down Expand Up @@ -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
Expand Down
77 changes: 8 additions & 69 deletions MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<MVKQueryPool*, MVKSmallVector<uint32_t, kMVKDefaultQueryCount>> MVKActivatedQueries;

Expand Down Expand Up @@ -293,14 +225,18 @@ class MVKCommandEncoder : public MVKBaseDeviceObject {
MVKFramebuffer* framebuffer,
VkRect2D& renderArea,
MVKArrayRef<VkClearValue> clearValues,
MVKArrayRef<MVKImageView*> attachments);
MVKArrayRef<MVKImageView*> attachments,
MVKArrayRef<MVKArrayRef<MTLSamplePosition>> subpassSamplePositions);

/** Begins the next render subpass. */
void beginNextSubpass(MVKCommand* subpassCmd, VkSubpassContents renderpassContents);

/** Begins the next multiview Metal render pass. */
void beginNextMultiviewPass();

/** Sets the dynamic custom sample positions to use when rendering. */
void setDynamicSamplePositions(MVKArrayRef<MTLSamplePosition> dynamicSamplePositions);

/** Begins a Metal render pass for the current render subpass. */
void beginMetalRenderPass(MVKCommandUse cmdUse);

Expand Down Expand Up @@ -509,6 +445,7 @@ class MVKCommandEncoder : public MVKBaseDeviceObject {
void encodeTimestampStageCounterSamples();
bool hasTimestampStageCounterQueries() { return !_timestampStageCounterQueries.empty(); }
id<MTLFence> getStageCountersMTLFence();
MVKArrayRef<MTLSamplePosition> getCustomSamplePositions();

typedef struct GPUCounterQuery {
MVKGPUCounterQueryPool* queryPool = nullptr;
Expand All @@ -526,6 +463,8 @@ class MVKCommandEncoder : public MVKBaseDeviceObject {
MVKSmallVector<GPUCounterQuery, 16> _timestampStageCounterQueries;
MVKSmallVector<VkClearValue, kMVKDefaultAttachmentCount> _clearValues;
MVKSmallVector<MVKImageView*, kMVKDefaultAttachmentCount> _attachments;
MVKSmallVector<MTLSamplePosition> _dynamicSamplePositions;
MVKSmallVector<MVKSmallVector<MTLSamplePosition>> _subpassSamplePositions;
id<MTLComputeCommandEncoder> _mtlComputeEncoder;
MVKCommandUse _mtlComputeEncoderUse;
id<MTLBlitCommandEncoder> _mtlBlitEncoder;
Expand Down
35 changes: 34 additions & 1 deletion MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -322,14 +322,23 @@
MVKFramebuffer* framebuffer,
VkRect2D& renderArea,
MVKArrayRef<VkClearValue> clearValues,
MVKArrayRef<MVKImageView*> attachments) {
MVKArrayRef<MVKImageView*> attachments,
MVKArrayRef<MVKArrayRef<MTLSamplePosition>> subpassSamplePositions) {
_renderPass = renderPass;
_framebuffer = framebuffer;
_renderArea = renderArea;
_isRenderingEntireAttachment = (mvkVkOffset2DsAreEqual(_renderArea.offset, {0,0}) &&
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);
}

Expand Down Expand Up @@ -365,6 +374,10 @@

uint32_t MVKCommandEncoder::getMultiviewPassIndex() { return _multiviewPassIndex; }

void MVKCommandEncoder::setDynamicSamplePositions(MVKArrayRef<MTLSamplePosition> 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) {

Expand Down Expand Up @@ -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));

Expand All @@ -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<MTLSamplePosition> MVKCommandEncoder::getCustomSamplePositions() {
return (_renderSubpassIndex < _subpassSamplePositions.size()
? _subpassSamplePositions[_renderSubpassIndex].contents()
: MVKArrayRef<MTLSamplePosition>());
}

void MVKCommandEncoder::encodeStoreActions(bool storeOverride) {
getSubpass()->encodeStoreActions(this,
_isRenderingEntireAttachment,
Expand Down
1 change: 1 addition & 0 deletions MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 3c0644f

Please sign in to comment.