Merge pull request #1566 from billhollings/VK_EXT_sample_locations

Add support for VK_EXT_sample_locations extension.
This commit is contained in:
Bill Hollings 2022-04-08 21:05:52 -04:00 committed by GitHub
commit 2a565ef8e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 247 additions and 100 deletions

View File

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

View File

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

View File

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

View File

@ -46,6 +46,7 @@ public:
protected:
MVKSmallVector<MVKSmallVector<MTLSamplePosition>> _subpassSamplePositions;
MVKRenderPass* _renderPass;
MVKFramebuffer* _framebuffer;
VkRect2D _renderArea;
@ -137,6 +138,25 @@ protected:
};
#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

View File

@ -36,6 +36,30 @@ VkResult MVKCmdBeginRenderPassBase::setContent(MVKCommandBuffer* cmdBuff,
_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 @@ VkResult MVKCmdBeginRenderPass<N_CV, N_A>::setContent(MVKCommandBuffer* cmdBuff,
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>;
@ -130,6 +164,24 @@ void MVKCmdEndRenderPass::encode(MVKCommandEncoder* cmdEncoder) {
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

View File

@ -191,74 +191,6 @@ protected:
#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;
@ -293,7 +225,8 @@ public:
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);
@ -301,6 +234,9 @@ public:
/** 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);
@ -509,6 +445,7 @@ protected:
void encodeTimestampStageCounterSamples();
bool hasTimestampStageCounterQueries() { return !_timestampStageCounterQueries.empty(); }
id<MTLFence> getStageCountersMTLFence();
MVKArrayRef<MTLSamplePosition> getCustomSamplePositions();
typedef struct GPUCounterQuery {
MVKGPUCounterQueryPool* queryPool = nullptr;
@ -526,6 +463,8 @@ protected:
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;

View File

@ -322,7 +322,8 @@ void MVKCommandEncoder::beginRenderpass(MVKCommand* passCmd,
MVKFramebuffer* framebuffer,
VkRect2D& renderArea,
MVKArrayRef<VkClearValue> clearValues,
MVKArrayRef<MVKImageView*> attachments) {
MVKArrayRef<MVKImageView*> attachments,
MVKArrayRef<MVKArrayRef<MTLSamplePosition>> subpassSamplePositions) {
_renderPass = renderPass;
_framebuffer = framebuffer;
_renderArea = renderArea;
@ -330,6 +331,14 @@ void MVKCommandEncoder::beginRenderpass(MVKCommand* passCmd,
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 @@ void MVKCommandEncoder::beginNextMultiviewPass() {
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) {
@ -416,6 +429,14 @@ void MVKCommandEncoder::beginMetalRenderPass(MVKCommandUse cmdUse) {
}
}
// 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 @@ void MVKCommandEncoder::beginMetalRenderPass(MVKCommandUse cmdUse) {
_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,

View File

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

View File

@ -128,6 +128,10 @@ public:
/** 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,

View File

@ -75,6 +75,9 @@ static const uint32_t kAMDRadeonRX5500DeviceId = 0x7340;
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 @@ void MVKPhysicalDevice::getProperties(VkPhysicalDeviceProperties2* properties) {
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 @@ void MVKPhysicalDevice::getFormatProperties(VkFormat format, VkFormatProperties2
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 @@ void MVKPhysicalDevice::initMetalFeatures() {
#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 @@ void MVKPhysicalDevice::initExtensions() {
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;
}

View File

@ -655,6 +655,8 @@ void MVKInstance::initProcAddrs() {
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);

View File

@ -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 @@ public:
/** 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<MTLSamplePosition> 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 @@ protected:
id<MTLRenderPipelineState> getOrCompilePipeline(MTLRenderPipelineDescriptor* plDesc, id<MTLRenderPipelineState>& plState);
id<MTLComputePipelineState> getOrCompilePipeline(MTLComputePipelineDescriptor* plDesc, id<MTLComputePipelineState>& 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 @@ protected:
MVKSmallVector<VkViewport, kMVKCachedViewportScissorCount> _viewports;
MVKSmallVector<VkRect2D, kMVKCachedViewportScissorCount> _scissors;
MVKSmallVector<VkDynamicState> _dynamicState;
MVKSmallVector<MTLSamplePosition> _customSamplePositions;
MVKSmallVector<MVKTranslatedVertexBinding> _translatedVertexBindings;
MVKSmallVector<MVKZeroDivisorVertexBinding> _zeroDivisorVertexBindings;
MVKSmallVector<MVKStagedMTLArgumentEncoders> _mtlArgumentEncoders;
@ -350,7 +356,6 @@ protected:
uint32_t _tessCtlPatchOutputBufferIndex = 0;
uint32_t _tessCtlLevelBufferIndex = 0;
bool _dynamicStateEnabled[kMVKVkDynamicStateCount];
bool _needsVertexSwizzleBuffer = false;
bool _needsVertexBufferSizeBuffer = false;
bool _needsVertexDynamicOffsetBuffer = false;
@ -372,6 +377,7 @@ protected:
bool _isRasterizing = false;
bool _isRasterizingColor = false;
bool _isRasterizingDepthStencil = false;
bool _isUsingCustomSamplePositions = false;
};

View File

@ -309,10 +309,8 @@ void MVKGraphicsPipeline::encode(MVKCommandEncoder* cmdEncoder, uint32_t stage)
}
bool MVKGraphicsPipeline::supportsDynamicState(VkDynamicState state) {
// First test if this dynamic state is explicitly turned off
if ( (state >= kMVKVkDynamicStateCount) || !_dynamicStateEnabled[state] ) { return false; }
for (auto& ds : _dynamicState) {
if (state == ds) {
// Some dynamic states have other restrictions
switch (state) {
case VK_DYNAMIC_STATE_DEPTH_BIAS:
@ -320,6 +318,9 @@ bool MVKGraphicsPipeline::supportsDynamicState(VkDynamicState state) {
default:
return true;
}
}
}
return false;
}
static const char vtxCompilerType[] = "Vertex stage pipeline for tessellation";
@ -408,13 +409,11 @@ MVKGraphicsPipeline::MVKGraphicsPipeline(MVKDevice* device,
}
}
// 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 @@ MVKGraphicsPipeline::MVKGraphicsPipeline(MVKDevice* device,
}
}
// Must run after _isRasterizing and _dynamicState are populated
initCustomSamplePositions(pCreateInfo);
// Render pipeline state
initMTLRenderPipelineState(pCreateInfo, reflectData);
@ -472,7 +474,7 @@ MVKGraphicsPipeline::MVKGraphicsPipeline(MVKDevice* device,
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 @@ MVKGraphicsPipeline::MVKGraphicsPipeline(MVKDevice* device,
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 @@ id<MTLComputePipelineState> MVKGraphicsPipeline::getOrCompilePipeline(MTLCompute
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;

View File

@ -261,8 +261,11 @@ public:
/** 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;

View File

@ -818,8 +818,6 @@ VkExtent2D MVKRenderPass::getRenderAreaGranularity() {
return { 1, 1 };
}
MVKRenderSubpass* MVKRenderPass::getSubpass(uint32_t subpassIndex) { return &_subpasses[subpassIndex]; }
bool MVKRenderPass::isMultiview() const { return _subpasses[0].isMultiview(); }
MVKRenderPass::MVKRenderPass(MVKDevice* device,

View File

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

View File

@ -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<Type>& operator=(const MVKArrayRef<Type>& 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) {}
};

View File

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