diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index 9e9f4ba5a..3650cc48e 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -25,6 +25,8 @@ Released TBD - Use Metal argument buffers by default when they are available. - Revert `MVKConfiguration::useMetalArgumentBuffers` and env var `MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS` to a boolean value, and enable it by default. + - Support a descriptor pool with less descriptors than the descriptor set layout, + as long as the pool has enough descriptors for the variable descriptor count, - Update max number of bindless buffers and textures per stage to 1M, per Apple Docs. - Add option to generate a GPU capture via a temporary named pipe from an external process. - Fix shader conversion failure when using native texture atomics. diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm index c77373bf0..e3f0c90dc 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm @@ -692,7 +692,7 @@ - (void)setDepthBoundsTestAMD:(BOOL)enable minDepth:(float)minDepth maxDepth:(fl auto* dslBind = dsLayout->getBindingAt(dslBindIdx); if (dslBind->getApplyToStage(stage) && shaderBindingUsage.getBit(dslBindIdx)) { shouldBindArgBuffToStage = true; - uint32_t elemCnt = dslBind->getDescriptorCount(descSet); + uint32_t elemCnt = dslBind->getDescriptorCount(descSet->getVariableDescriptorCount()); for (uint32_t elemIdx = 0; elemIdx < elemCnt; elemIdx++) { uint32_t descIdx = dslBind->getDescriptorIndex(elemIdx); if (resourceUsageDirtyDescs.getBit(descIdx, true)) { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h index c54564e3a..92e6504dc 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h @@ -28,6 +28,10 @@ class MVKCommandEncoder; class MVKResourcesCommandEncoderState; +/** Magic number to indicate the variable descriptor count is currently unknown. */ +static uint32_t kMVKVariableDescriptorCountUnknown = std::numeric_limits::max(); + + #pragma mark MVKShaderStageResourceBinding /** Indicates the Metal resource indexes used by a single shader stage in a descriptor. */ @@ -100,11 +104,10 @@ class MVKDescriptorSetLayoutBinding : public MVKBaseDeviceObject { * Returns the number of descriptors in this layout. * * If this is an inline block data descriptor, always returns 1. If this descriptor - * has a variable descriptor count, and descSet is not null, the variable descriptor - * count provided to that descriptor set is returned. Otherwise returns the value - * defined in VkDescriptorSetLayoutBinding::descriptorCount. + * has a variable descriptor count, and it is provided here, it is returned. + * Otherwise returns the value defined in VkDescriptorSetLayoutBinding::descriptorCount. */ - uint32_t getDescriptorCount(MVKDescriptorSet* descSet = nullptr) const; + uint32_t getDescriptorCount(uint32_t variableDescriptorCount = kMVKVariableDescriptorCountUnknown) const; /** Returns the descriptor type of this layout. */ VkDescriptorType getDescriptorType() { return _info.descriptorType; } @@ -157,7 +160,9 @@ class MVKDescriptorSetLayoutBinding : public MVKBaseDeviceObject { MVKDescriptorSetLayoutBinding(MVKDevice* device, MVKDescriptorSetLayout* layout, const VkDescriptorSetLayoutBinding* pBinding, - VkDescriptorBindingFlagsEXT bindingFlags); + VkDescriptorBindingFlagsEXT bindingFlags, + uint32_t& dslDescCnt, + uint32_t& dslMTLRezCnt); MVKDescriptorSetLayoutBinding(const MVKDescriptorSetLayoutBinding& binding); @@ -168,9 +173,13 @@ class MVKDescriptorSetLayoutBinding : public MVKBaseDeviceObject { friend class MVKDescriptorSet; friend class MVKInlineUniformBlockDescriptor; - void initMetalResourceIndexOffsets(const VkDescriptorSetLayoutBinding* pBinding, uint32_t stage); - void addMTLArgumentDescriptors(NSMutableArray* args); + void initMetalResourceIndexOffsets(const VkDescriptorSetLayoutBinding* pBinding, + uint32_t stage, + uint32_t dslMTLRezCnt); + void addMTLArgumentDescriptors(NSMutableArray* args, + uint32_t variableDescriptorCount); void addMTLArgumentDescriptor(NSMutableArray* args, + uint32_t variableDescriptorCount, uint32_t argIndex, MTLDataType dataType, MTLArgumentAccess access); @@ -180,8 +189,7 @@ class MVKDescriptorSetLayoutBinding : public MVKBaseDeviceObject { bool validate(MVKSampler* mvkSampler); void encodeImmutableSamplersToMetalArgumentBuffer(MVKDescriptorSet* mvkDescSet); uint8_t getMaxPlaneCount(); - uint32_t getMTLResourceCount(); - bool needsBuffSizeAuxBuffer(); + uint32_t getMTLResourceCount(uint32_t variableDescriptorCount = kMVKVariableDescriptorCountUnknown); std::string getLogDescription(); MVKDescriptorSetLayout* _layout; @@ -674,5 +682,12 @@ class MVKStorageTexelBufferDescriptor : public MVKTexelBufferDescriptor { #pragma mark - #pragma mark Support functions +/** + * If the binding defines a buffer type, returns whether there are buffers, and + * therefore an auxilliary buffer is required to hold the lengths of those buffers. + * Returns false if the binding does not define a buffer type. + */ +bool mvkNeedsBuffSizeAuxBuffer(const VkDescriptorSetLayoutBinding* pBinding); + /** Returns the name of the descriptor type. */ const char* mvkVkDescriptorTypeName(VkDescriptorType vkDescType); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm index 725a8475e..36b075d29 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm @@ -190,16 +190,13 @@ void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& s MVKVulkanAPIObject* MVKDescriptorSetLayoutBinding::getVulkanAPIObject() { return _layout; }; -uint32_t MVKDescriptorSetLayoutBinding::getDescriptorCount(MVKDescriptorSet* descSet) const { - +uint32_t MVKDescriptorSetLayoutBinding::getDescriptorCount(uint32_t variableDescriptorCount) const { if (_info.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) { return 1; } - - if (descSet && hasVariableDescriptorCount()) { - return descSet->_variableDescriptorCount; + if (hasVariableDescriptorCount()) { + return std::min(variableDescriptorCount, _info.descriptorCount); } - return _info.descriptorCount; } @@ -215,7 +212,7 @@ void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& s MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; VkDescriptorType descType = getDescriptorType(); - uint32_t descCnt = getDescriptorCount(descSet); + uint32_t descCnt = getDescriptorCount(descSet->_variableDescriptorCount); for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) { MVKDescriptor* mvkDesc = descSet->getDescriptor(getBinding(), descIdx); if (mvkDesc->getDescriptorType() == descType) { @@ -417,53 +414,54 @@ void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& s } // Adds MTLArgumentDescriptors to the array, and updates resource indexes consumed. -void MVKDescriptorSetLayoutBinding::addMTLArgumentDescriptors(NSMutableArray* args) { +void MVKDescriptorSetLayoutBinding::addMTLArgumentDescriptors(NSMutableArray* args, + uint32_t variableDescriptorCount) { switch (getDescriptorType()) { case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: - addMTLArgumentDescriptor(args, getMetalResourceIndexOffsets().bufferIndex, MTLDataTypePointer, MTLArgumentAccessReadOnly); + addMTLArgumentDescriptor(args, variableDescriptorCount, getMetalResourceIndexOffsets().bufferIndex, MTLDataTypePointer, MTLArgumentAccessReadOnly); break; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: - addMTLArgumentDescriptor(args, getMetalResourceIndexOffsets().bufferIndex, MTLDataTypePointer, MTLArgumentAccessReadWrite); + addMTLArgumentDescriptor(args, variableDescriptorCount, getMetalResourceIndexOffsets().bufferIndex, MTLDataTypePointer, MTLArgumentAccessReadWrite); break; case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: - addMTLArgumentDescriptor(args, getMetalResourceIndexOffsets().textureIndex, MTLDataTypeTexture, MTLArgumentAccessReadOnly); + addMTLArgumentDescriptor(args, variableDescriptorCount, getMetalResourceIndexOffsets().textureIndex, MTLDataTypeTexture, MTLArgumentAccessReadOnly); break; case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - addMTLArgumentDescriptor(args, getMetalResourceIndexOffsets().textureIndex, MTLDataTypeTexture, MTLArgumentAccessReadWrite); + addMTLArgumentDescriptor(args, variableDescriptorCount, getMetalResourceIndexOffsets().textureIndex, MTLDataTypeTexture, MTLArgumentAccessReadWrite); if (!getMetalFeatures().nativeTextureAtomics) { // Needed for emulated atomic operations - addMTLArgumentDescriptor(args, getMetalResourceIndexOffsets().bufferIndex, MTLDataTypePointer, MTLArgumentAccessReadWrite); + addMTLArgumentDescriptor(args, variableDescriptorCount, getMetalResourceIndexOffsets().bufferIndex, MTLDataTypePointer, MTLArgumentAccessReadWrite); } break; case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - addMTLArgumentDescriptor(args, getMetalResourceIndexOffsets().textureIndex, MTLDataTypeTexture, MTLArgumentAccessReadOnly); + addMTLArgumentDescriptor(args, variableDescriptorCount, getMetalResourceIndexOffsets().textureIndex, MTLDataTypeTexture, MTLArgumentAccessReadOnly); break; case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: - addMTLArgumentDescriptor(args, getMetalResourceIndexOffsets().textureIndex, MTLDataTypeTexture, MTLArgumentAccessReadWrite); + addMTLArgumentDescriptor(args, variableDescriptorCount, getMetalResourceIndexOffsets().textureIndex, MTLDataTypeTexture, MTLArgumentAccessReadWrite); if (!getMetalFeatures().nativeTextureAtomics) { // Needed for emulated atomic operations - addMTLArgumentDescriptor(args, getMetalResourceIndexOffsets().bufferIndex, MTLDataTypePointer, MTLArgumentAccessReadWrite); + addMTLArgumentDescriptor(args, variableDescriptorCount, getMetalResourceIndexOffsets().bufferIndex, MTLDataTypePointer, MTLArgumentAccessReadWrite); } break; case VK_DESCRIPTOR_TYPE_SAMPLER: - addMTLArgumentDescriptor(args, getMetalResourceIndexOffsets().samplerIndex, MTLDataTypeSampler, MTLArgumentAccessReadOnly); + addMTLArgumentDescriptor(args, variableDescriptorCount, getMetalResourceIndexOffsets().samplerIndex, MTLDataTypeSampler, MTLArgumentAccessReadOnly); break; case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { uint8_t maxPlaneCnt = getMaxPlaneCount(); for (uint8_t planeIdx = 0; planeIdx < maxPlaneCnt; planeIdx++) { - addMTLArgumentDescriptor(args, getMetalResourceIndexOffsets().textureIndex + planeIdx, MTLDataTypeTexture, MTLArgumentAccessReadOnly); + addMTLArgumentDescriptor(args, variableDescriptorCount, getMetalResourceIndexOffsets().textureIndex + planeIdx, MTLDataTypeTexture, MTLArgumentAccessReadOnly); } - addMTLArgumentDescriptor(args, getMetalResourceIndexOffsets().samplerIndex, MTLDataTypeSampler, MTLArgumentAccessReadOnly); + addMTLArgumentDescriptor(args, variableDescriptorCount, getMetalResourceIndexOffsets().samplerIndex, MTLDataTypeSampler, MTLArgumentAccessReadOnly); break; } @@ -473,10 +471,11 @@ void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& s } void MVKDescriptorSetLayoutBinding::addMTLArgumentDescriptor(NSMutableArray* args, + uint32_t variableDescriptorCount, uint32_t argIndex, MTLDataType dataType, MTLArgumentAccess access) { - uint32_t descCnt = getDescriptorCount(); + uint32_t descCnt = getDescriptorCount(variableDescriptorCount); if (descCnt == 0) { return; } auto* argDesc = [MTLArgumentDescriptor argumentDescriptor]; @@ -497,7 +496,7 @@ void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& s return maxPlaneCnt; } -uint32_t MVKDescriptorSetLayoutBinding::getMTLResourceCount() { +uint32_t MVKDescriptorSetLayoutBinding::getMTLResourceCount(uint32_t variableDescriptorCount) { uint32_t rezCntPerElem = 1; switch (_info.descriptorType) { case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: @@ -510,7 +509,7 @@ void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& s default: break; } - return rezCntPerElem * getDescriptorCount(); + return rezCntPerElem * getDescriptorCount(variableDescriptorCount); } // Encodes an immutable sampler to the Metal argument buffer. @@ -532,13 +531,15 @@ void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& s void MVKDescriptorSetLayoutBinding::populateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& shaderConfig, MVKShaderResourceBinding& dslMTLRezIdxOffsets, uint32_t dslIndex) { - uint32_t descCnt = getDescriptorCount(); + // For argument buffers, set variable length arrays to length 1 in shader. bool isUsingMtlArgBuff = _layout->isUsingMetalArgumentBuffers(); - MVKSampler* mvkSamp = !_immutableSamplers.empty() ? _immutableSamplers.front() : nullptr; + uint32_t descCnt = isUsingMtlArgBuff ? getDescriptorCount(1) : getDescriptorCount(); // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; + MVKSampler* mvkSamp = !_immutableSamplers.empty() ? _immutableSamplers.front() : nullptr; + for (uint32_t stage = kMVKShaderStageVertex; stage < kMVKShaderStageCount; stage++) { if (_applyToStage[stage] || isUsingMtlArgBuff) { mvkPopulateShaderConversionConfig(shaderConfig, @@ -600,12 +601,14 @@ void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& s MVKDescriptorSetLayoutBinding::MVKDescriptorSetLayoutBinding(MVKDevice* device, MVKDescriptorSetLayout* layout, const VkDescriptorSetLayoutBinding* pBinding, - VkDescriptorBindingFlagsEXT bindingFlags) : + VkDescriptorBindingFlagsEXT bindingFlags, + uint32_t& dslDescCnt, + uint32_t& dslMTLRezCnt) : MVKBaseDeviceObject(device), _layout(layout), _info(*pBinding), _flags(bindingFlags), - _descriptorIndex(layout->_descriptorCount) { + _descriptorIndex(dslDescCnt) { // If immutable samplers are defined, copy them in. // Do this before anything else, because they are referenced in getMaxPlaneCount(). @@ -624,14 +627,14 @@ void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& s // Determine if this binding is used by this shader stage, and initialize resource indexes. for (uint32_t stage = kMVKShaderStageVertex; stage < kMVKShaderStageCount; stage++) { _applyToStage[stage] = mvkAreAllFlagsEnabled(pBinding->stageFlags, mvkVkShaderStageFlagBitsFromMVKShaderStage(MVKShaderStage(stage))); - initMetalResourceIndexOffsets(pBinding, stage); + initMetalResourceIndexOffsets(pBinding, stage, dslMTLRezCnt); } // Update descriptor set layout counts uint32_t descCnt = getDescriptorCount(); - _layout->_descriptorCount += descCnt; - _layout->_mtlResourceTotalCount += getMTLResourceCount(); - if (needsBuffSizeAuxBuffer()) { + dslDescCnt += descCnt; + dslMTLRezCnt += getMTLResourceCount(); + if (mvkNeedsBuffSizeAuxBuffer(pBinding)) { _layout->_maxBufferIndex = std::max(_layout->_maxBufferIndex, int32_t(_mtlResourceIndexOffsets.getMaxBufferIndex() + descCnt) - 1); } } @@ -661,19 +664,20 @@ void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& s // Sets the appropriate Metal resource indexes within this binding from the // specified descriptor set binding counts, and updates those counts accordingly. -void MVKDescriptorSetLayoutBinding::initMetalResourceIndexOffsets(const VkDescriptorSetLayoutBinding* pBinding, uint32_t stage) { - +void MVKDescriptorSetLayoutBinding::initMetalResourceIndexOffsets(const VkDescriptorSetLayoutBinding* pBinding, + uint32_t stage, + uint32_t dslMTLRezCnt) { // Sets an index offset and updates both that index and the general resource index. // Can be used more than once for combined multi-resource descriptor types. // When using Metal argument buffers, we accumulate the resource indexes cummulatively, across all resource types. -#define setResourceIndexOffset(rezIdx, mtlRezCntPerElem) \ -if (isUsingMtlArgBuff) { \ - bindIdxs.rezIdx = _layout->_mtlResourceTotalCount + descIdxOfst; \ - descIdxOfst += descCnt * mtlRezCntPerElem; \ -} else if (_applyToStage[stage]) { \ - bindIdxs.rezIdx = dslCnts.rezIdx; \ - dslCnts.rezIdx += descCnt * mtlRezCntPerElem; \ -} \ +#define setResourceIndexOffset(rezIdx, mtlRezCntPerElem) \ +if (isUsingMtlArgBuff) { \ + bindIdxs.rezIdx = dslMTLRezCnt + descIdxOfst; \ + descIdxOfst += descCnt * mtlRezCntPerElem; \ +} else if (_applyToStage[stage]) { \ + bindIdxs.rezIdx = dslCnts.rezIdx; \ + dslCnts.rezIdx += descCnt * mtlRezCntPerElem; \ +} \ bool isUsingMtlArgBuff = _layout->isUsingMetalArgumentBuffers(); auto& mtlFeats = getMetalFeatures(); @@ -744,24 +748,6 @@ void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& s } } -bool MVKDescriptorSetLayoutBinding::needsBuffSizeAuxBuffer() { - - if ( !_layout->isUsingMetalArgumentBuffers() ) { return false; } - if ( getDescriptorCount() == 0 ) { return false; } - - switch (getDescriptorType()) { - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: - return true; - - default: - return false; - } -} - #pragma mark - #pragma mark MVKDescriptor @@ -1355,6 +1341,24 @@ void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& s #pragma mark - #pragma mark Support functions + +bool mvkNeedsBuffSizeAuxBuffer(const VkDescriptorSetLayoutBinding* pBinding) { + switch (pBinding->descriptorType) { + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + return pBinding->descriptorCount > 0; + + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: + return true; + + default: + return false; + } +} + + #define CASE_STRINGIFY(V) case V: return #V const char* mvkVkDescriptorTypeName(VkDescriptorType vkDescType) { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h index d7d09ab17..24917db0e 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h @@ -111,12 +111,13 @@ class MVKDescriptorSetLayout : public MVKVulkanAPIDeviceObject { /** Returns the binding at the index in a descriptor set layout. */ MVKDescriptorSetLayoutBinding* getBindingAt(uint32_t index) { return &_bindings[index]; } - /** Overridden becasue descriptor sets may be marked as discrete and not use an argument buffer. */ + /** Overridden because descriptor sets may be marked as discrete and not use an argument buffer. */ bool isUsingMetalArgumentBuffers() override; - MVKDescriptorSetLayout(MVKDevice* device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo); + /** Returns whether descriptor sets from this layout requires an auxilliary buffer-size buffer. */ + bool needsBufferSizeAuxBuffer() { return _maxBufferIndex >= 0; } - ~MVKDescriptorSetLayout(); + MVKDescriptorSetLayout(MVKDevice* device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo); protected: @@ -126,20 +127,17 @@ class MVKDescriptorSetLayout : public MVKVulkanAPIDeviceObject { friend class MVKDescriptorPool; void propagateDebugName() override {} - uint32_t getDescriptorCount() { return _descriptorCount; } + uint32_t getDescriptorCount(uint32_t variableDescriptorCount); uint32_t getDescriptorIndex(uint32_t binding, uint32_t elementIndex = 0) { return getBinding(binding)->getDescriptorIndex(elementIndex); } MVKDescriptorSetLayoutBinding* getBinding(uint32_t binding) { return &_bindings[_bindingToIndex[binding]]; } - const VkDescriptorBindingFlags* getBindingFlags(const VkDescriptorSetLayoutCreateInfo* pCreateInfo); - uint32_t getBufferSizeBufferArgBuferIndex() { return _mtlResourceTotalCount; } + uint32_t getBufferSizeBufferArgBuferIndex() { return 0; } + id getMTLArgumentEncoder(uint32_t variableDescriptorCount); + uint64_t getMetal3ArgumentBufferEncodedLength(uint32_t variableDescriptorCount); std::string getLogDescription(); MVKSmallVector _bindings; std::unordered_map _bindingToIndex; MVKShaderResourceBinding _mtlResourceCounts; - NSArray* _mtlArgumentEncoderArgs = nil; - uint64_t _mtlArgumentBufferEncodedSize = 0; - uint32_t _descriptorCount = 0; - uint32_t _mtlResourceTotalCount = 0; int32_t _maxBufferIndex = -1; bool _isPushDescriptorLayout = false; }; @@ -185,6 +183,9 @@ class MVKDescriptorSet : public MVKVulkanAPIDeviceObject { /** Returns the number of descriptors in this descriptor set. */ uint32_t getDescriptorCount() { return (uint32_t)_descriptors.size(); } + /** Returns the count of descriptors in the binding in this descriptor set that has a variable descriptor count. */ + uint32_t getVariableDescriptorCount() const { return _variableDescriptorCount; } + /** Returns the number of descriptors in this descriptor set that use dynamic offsets. */ uint32_t getDynamicOffsetDescriptorCount() { return _dynamicOffsetDescriptorCount; } @@ -209,7 +210,8 @@ class MVKDescriptorSet : public MVKVulkanAPIDeviceObject { MVKDescriptor* getDescriptor(uint32_t binding, uint32_t elementIndex = 0); VkResult allocate(MVKDescriptorSetLayout* layout, uint32_t variableDescriptorCount, - NSUInteger mtlArgBufferOffset); + NSUInteger mtlArgBufferOffset, + id mtlArgEnc); void free(bool isPoolReset); MVKMTLBufferAllocation* acquireMTLBufferRegion(NSUInteger length); void setBufferSize(uint32_t descIdx, uint32_t value); @@ -282,7 +284,6 @@ class MVKDescriptorPool : public MVKVulkanAPIDeviceObject { template friend class MVKDescriptorTypePool; void propagateDebugName() override {} - const uint32_t* getVariableDecriptorCounts(const VkDescriptorSetAllocateInfo* pAllocateInfo); VkResult allocateDescriptorSet(MVKDescriptorSetLayout* mvkDSL, uint32_t variableDescriptorCount, VkDescriptorSet* pVKDS); void freeDescriptorSet(MVKDescriptorSet* mvkDS, bool isPoolReset); VkResult allocateDescriptor(VkDescriptorType descriptorType, MVKDescriptor** pMVKDesc); @@ -291,6 +292,7 @@ class MVKDescriptorPool : public MVKVulkanAPIDeviceObject { NSUInteger getMetalArgumentBufferEncodedResourceStorageSize(NSUInteger bufferCount, NSUInteger textureCount, NSUInteger samplerCount); MTLArgumentDescriptor* getMTLArgumentDescriptor(MTLDataType resourceType, NSUInteger argIndex, NSUInteger count); size_t getPoolSize(const VkDescriptorPoolCreateInfo* pCreateInfo, VkDescriptorType descriptorType); + std::string getLogDescription(); bool _hasPooledDescriptors; MVKSmallVector _descriptorSets; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm index 06b949b95..b197c2531 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm @@ -258,8 +258,8 @@ static void populateAuxBuffer(mvk::SPIRVToMSLConversionConfiguration& shaderConf _bindings[bindIdx].populateShaderConversionConfig(shaderConfig, dslMTLRezIdxOffsets, descSetIndex); } - // If this descriptor set is using an argument buffer, add the buffer size auxiliary buffer. - if (isUsingMetalArgumentBuffers()) { + // If this descriptor set is using an argument buffer, and needs a buffer size auxiliary buffer, add it. + if (isUsingMetalArgumentBuffers() && needsBufferSizeAuxBuffer()) { MVKShaderStageResourceBinding buffBinding; buffBinding.bufferIndex = getBufferSizeBufferArgBuferIndex(); populateAuxBuffer(shaderConfig, buffBinding, descSetIndex, @@ -313,10 +313,62 @@ static void populateAuxBuffer(mvk::SPIRVToMSLConversionConfiguration& shaderConf return argDesc; } +// Returns an autoreleased MTLArgumentEncoder for a descriptor set, or nil if not needed. +// Make sure any call to this function is wrapped in @autoreleasepool. +id MVKDescriptorSetLayout::getMTLArgumentEncoder(uint32_t variableDescriptorCount) { + auto* encoderArgs = [NSMutableArray arrayWithCapacity: _bindings.size() * 2]; // Double it to cover potential multi-resource descriptors (combo image/samp, multi-planar, etc). + + // Buffer sizes buffer at front + if (needsBufferSizeAuxBuffer()) { + [encoderArgs addObject: getAuxBufferArgumentDescriptor(getBufferSizeBufferArgBuferIndex())]; + } + for (auto& dslBind : _bindings) { + dslBind.addMTLArgumentDescriptors(encoderArgs, variableDescriptorCount); + } + return encoderArgs.count ? [[getMTLDevice() newArgumentEncoderWithArguments: encoderArgs] autorelease] : nil; +} + +// Returns the encoded byte length of the resources from a descriptor set in an argument buffer. +uint64_t MVKDescriptorSetLayout::getMetal3ArgumentBufferEncodedLength(uint32_t variableDescriptorCount) { + uint64_t encodedLen = 0; + + // Buffer sizes buffer at front + if (needsBufferSizeAuxBuffer()) { + encodedLen += kMVKMetal3ArgBuffSlotSizeInBytes; + } + for (auto& dslBind : _bindings) { + encodedLen += dslBind.getMTLResourceCount(variableDescriptorCount) * kMVKMetal3ArgBuffSlotSizeInBytes; + } + return encodedLen; +} + +uint32_t MVKDescriptorSetLayout::getDescriptorCount(uint32_t variableDescriptorCount) { + uint32_t descCnt = 0; + for (auto& dslBind : _bindings) { + descCnt += dslBind.getDescriptorCount(variableDescriptorCount); + } + return descCnt; +} + MVKDescriptorSetLayout::MVKDescriptorSetLayout(MVKDevice* device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) { - uint32_t bindCnt = pCreateInfo->bindingCount; - const auto* pBindingFlags = getBindingFlags(pCreateInfo); + + _isPushDescriptorLayout = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR); + + const VkDescriptorBindingFlags* pBindingFlags = nullptr; + for (const auto* next = (VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) { + switch (next->sType) { + case VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO: { + auto* pDescSetLayoutBindingFlags = (VkDescriptorSetLayoutBindingFlagsCreateInfo*)next; + if (pDescSetLayoutBindingFlags->bindingCount) { + pBindingFlags = pDescSetLayoutBindingFlags->pBindingFlags; + } + break; + } + default: + break; + } + } // The bindings in VkDescriptorSetLayoutCreateInfo do not need to be provided in order of binding number. // However, several subsequent operations, such as the dynamic offsets in vkCmdBindDescriptorSets() @@ -326,75 +378,31 @@ static void populateAuxBuffer(mvk::SPIRVToMSLConversionConfiguration& shaderConf VkDescriptorBindingFlags bindingFlags; }; MVKSmallVector sortedBindings; + + bool needsBuffSizeAuxBuff = false; + uint32_t bindCnt = pCreateInfo->bindingCount; sortedBindings.reserve(bindCnt); for (uint32_t bindIdx = 0; bindIdx < bindCnt; bindIdx++) { - sortedBindings.push_back( { &pCreateInfo->pBindings[bindIdx], pBindingFlags ? pBindingFlags[bindIdx] : 0 } ); + auto* pBind = &pCreateInfo->pBindings[bindIdx]; + sortedBindings.push_back( { pBind, pBindingFlags ? pBindingFlags[bindIdx] : 0 } ); + needsBuffSizeAuxBuff = needsBuffSizeAuxBuff || mvkNeedsBuffSizeAuxBuffer(pBind); } std::sort(sortedBindings.begin(), sortedBindings.end(), [](BindInfo bindInfo1, BindInfo bindInfo2) { return bindInfo1.pBinding->binding < bindInfo2.pBinding->binding; }); - _isPushDescriptorLayout = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR); - + uint32_t dslDescCnt = 0; + uint32_t dslMTLRezCnt = needsBuffSizeAuxBuff ? 1 : 0; // If needed, leave a slot for the buffer sizes buffer at front. _bindings.reserve(bindCnt); for (uint32_t bindIdx = 0; bindIdx < bindCnt; bindIdx++) { BindInfo& bindInfo = sortedBindings[bindIdx]; - _bindings.emplace_back(_device, this, bindInfo.pBinding, bindInfo.bindingFlags); + _bindings.emplace_back(_device, this, bindInfo.pBinding, bindInfo.bindingFlags, dslDescCnt, dslMTLRezCnt); _bindingToIndex[bindInfo.pBinding->binding] = bindIdx; } - if (isUsingMetalArgumentBuffers()) { - bool needsBuffSizeAuxBuff = false; - if (needsMetalArgumentBufferEncoders()) { - @autoreleasepool { - auto* mutableArgs = [[NSMutableArray alloc] initWithCapacity: _bindings.size()]; - for (auto& dslBind : _bindings) { - dslBind.addMTLArgumentDescriptors(mutableArgs); - needsBuffSizeAuxBuff = needsBuffSizeAuxBuff || dslBind.needsBuffSizeAuxBuffer(); - } - - // Possibly add buffer sizes auxiliary buffer. - if (needsBuffSizeAuxBuff) { - [mutableArgs addObject: getAuxBufferArgumentDescriptor(getBufferSizeBufferArgBuferIndex())]; - } - - _mtlArgumentEncoderArgs = mutableArgs; // retained - if (_mtlArgumentEncoderArgs.count) { - _mtlArgumentBufferEncodedSize = [[getMTLDevice() newArgumentEncoderWithArguments: _mtlArgumentEncoderArgs] autorelease].encodedLength; - } - } - } else { - for (auto& dslBind : _bindings) { - _mtlArgumentBufferEncodedSize += dslBind.getMTLResourceCount() * kMVKMetal3ArgBuffSlotSizeInBytes; - needsBuffSizeAuxBuff = needsBuffSizeAuxBuff || dslBind.needsBuffSizeAuxBuffer(); - } - - // Possibly add buffer sizes auxiliary buffer. - if (needsBuffSizeAuxBuff) { - _mtlArgumentBufferEncodedSize += kMVKMetal3ArgBuffSlotSizeInBytes; - } - } - } - MVKLogDebugIf(getMVKConfig().debugMode, "Created %s\n", getLogDescription().c_str()); } -// Find and return an array of binding flags from the pNext chain of pCreateInfo, -// or return nullptr if the chain does not include binding flags. -const VkDescriptorBindingFlags* MVKDescriptorSetLayout::getBindingFlags(const VkDescriptorSetLayoutCreateInfo* pCreateInfo) { - for (const auto* next = (VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) { - switch (next->sType) { - case VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO: { - auto* pDescSetLayoutBindingFlags = (VkDescriptorSetLayoutBindingFlagsCreateInfo*)next; - return pDescSetLayoutBindingFlags->bindingCount ? pDescSetLayoutBindingFlags->pBindingFlags : nullptr; - } - default: - break; - } - } - return nullptr; -} - std::string MVKDescriptorSetLayout::getLogDescription() { std::stringstream descStr; descStr << "VkDescriptorSetLayout " << this << " with " << _bindings.size() << " bindings:"; @@ -404,10 +412,6 @@ static void populateAuxBuffer(mvk::SPIRVToMSLConversionConfiguration& shaderConf return descStr.str(); } -MVKDescriptorSetLayout::~MVKDescriptorSetLayout() { - [_mtlArgumentEncoderArgs release]; -} - #pragma mark - #pragma mark MVKDescriptorSet @@ -474,24 +478,19 @@ static void populateAuxBuffer(mvk::SPIRVToMSLConversionConfiguration& shaderConf VkResult MVKDescriptorSet::allocate(MVKDescriptorSetLayout* layout, uint32_t variableDescriptorCount, - NSUInteger mtlArgBufferOffset) { + NSUInteger mtlArgBufferOffset, + id mtlArgEnc) { _layout = layout; _variableDescriptorCount = variableDescriptorCount; - - id mtlArgEnc = nil; - if (hasMetalArgumentBuffer() && needsMetalArgumentBufferEncoders() && layout->_mtlArgumentEncoderArgs.count) { - mtlArgEnc = [getMTLDevice() newArgumentEncoderWithArguments: layout->_mtlArgumentEncoderArgs]; // temp retain - } _argumentBuffer.setArgumentBuffer(_pool->_metalArgumentBuffer, mtlArgBufferOffset, mtlArgEnc); - [mtlArgEnc release]; // release temp retain - uint32_t descCnt = layout->getDescriptorCount(); + uint32_t descCnt = layout->getDescriptorCount(variableDescriptorCount); _descriptors.reserve(descCnt); uint32_t bindCnt = (uint32_t)layout->_bindings.size(); for (uint32_t bindIdx = 0; bindIdx < bindCnt; bindIdx++) { MVKDescriptorSetLayoutBinding* mvkDSLBind = &layout->_bindings[bindIdx]; - uint32_t elemCnt = mvkDSLBind->getDescriptorCount(this); + uint32_t elemCnt = mvkDSLBind->getDescriptorCount(variableDescriptorCount); for (uint32_t elemIdx = 0; elemIdx < elemCnt; elemIdx++) { VkDescriptorType descType = mvkDSLBind->getDescriptorType(); MVKDescriptor* mvkDesc = nullptr; @@ -504,9 +503,8 @@ static void populateAuxBuffer(mvk::SPIRVToMSLConversionConfiguration& shaderConf } // If needed, allocate a MTLBuffer to track buffer sizes, and add it to the argument buffer. - // If this desc set doesn't contain buffers, buffSizesSlotCount will be zero, since _maxBufferIndex starts at -1. - uint32_t buffSizesSlotCount = _layout->_maxBufferIndex + 1; - if (buffSizesSlotCount) { + if (hasMetalArgumentBuffer() && _layout->needsBufferSizeAuxBuffer()) { + uint32_t buffSizesSlotCount = _layout->_maxBufferIndex + 1; _bufferSizesBuffer = acquireMTLBufferRegion(buffSizesSlotCount * sizeof(uint32_t)); _argumentBuffer.setBuffer(_bufferSizesBuffer->_mtlBuffer, _bufferSizesBuffer->_offset, @@ -609,32 +607,29 @@ static void populateAuxBuffer(mvk::SPIRVToMSLConversionConfiguration& shaderConf VkResult MVKDescriptorPool::allocateDescriptorSets(const VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets) { - VkResult rslt = VK_SUCCESS; - const auto* pVarDescCounts = getVariableDecriptorCounts(pAllocateInfo); - for (uint32_t dsIdx = 0; dsIdx < pAllocateInfo->descriptorSetCount; dsIdx++) { - MVKDescriptorSetLayout* mvkDSL = (MVKDescriptorSetLayout*)pAllocateInfo->pSetLayouts[dsIdx]; - if ( !mvkDSL->_isPushDescriptorLayout ) { - rslt = allocateDescriptorSet(mvkDSL, (pVarDescCounts ? pVarDescCounts[dsIdx] : 0), &pDescriptorSets[dsIdx]); - if (rslt) { return rslt; } - } - } - return rslt; -} - -// Find and return an array of variable descriptor counts from the pNext chain of pCreateInfo, -// or return nullptr if the chain does not include variable descriptor counts. -const uint32_t* MVKDescriptorPool::getVariableDecriptorCounts(const VkDescriptorSetAllocateInfo* pAllocateInfo) { + const uint32_t* pVarDescCounts = nullptr; for (const auto* next = (VkBaseInStructure*)pAllocateInfo->pNext; next; next = next->pNext) { switch (next->sType) { case VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO: { auto* pVarDescSetVarCounts = (VkDescriptorSetVariableDescriptorCountAllocateInfo*)next; - return pVarDescSetVarCounts->descriptorSetCount ? pVarDescSetVarCounts->pDescriptorCounts : nullptr; + pVarDescCounts = pVarDescSetVarCounts->descriptorSetCount ? pVarDescSetVarCounts->pDescriptorCounts : nullptr; } default: break; } } - return nullptr; + + @autoreleasepool { + for (uint32_t dsIdx = 0; dsIdx < pAllocateInfo->descriptorSetCount; dsIdx++) { + MVKDescriptorSetLayout* mvkDSL = (MVKDescriptorSetLayout*)pAllocateInfo->pSetLayouts[dsIdx]; + if ( !mvkDSL->_isPushDescriptorLayout ) { + VkResult rslt = allocateDescriptorSet(mvkDSL, (pVarDescCounts ? pVarDescCounts[dsIdx] : 0), &pDescriptorSets[dsIdx]); + if (rslt) { return rslt; } + } + } + } + + return VK_SUCCESS; } // Retrieves the first available descriptor set from the pool, and configures it. @@ -643,7 +638,17 @@ static void populateAuxBuffer(mvk::SPIRVToMSLConversionConfiguration& shaderConf uint32_t variableDescriptorCount, VkDescriptorSet* pVKDS) { VkResult rslt = VK_ERROR_OUT_OF_POOL_MEMORY; - NSUInteger mtlArgBuffEncSize = mvkDSL->_mtlArgumentBufferEncodedSize; + uint64_t mtlArgBuffEncSize = 0; + id mtlArgEnc = nil; + if (mvkDSL->isUsingMetalArgumentBuffers()) { + if (needsMetalArgumentBufferEncoders()) { + mtlArgEnc = mvkDSL->getMTLArgumentEncoder(variableDescriptorCount); + mtlArgBuffEncSize = mtlArgEnc.encodedLength; + } else { + mtlArgBuffEncSize = mvkDSL->getMetal3ArgumentBufferEncodedLength(variableDescriptorCount); + } + } + size_t dsCnt = _descriptorSetAvailablility.size(); _descriptorSetAvailablility.enumerateEnabledBits(true, [&](size_t dsIdx) { bool isSpaceAvail = true; // If not using Metal arg buffers, space will always be available. @@ -654,7 +659,7 @@ static void populateAuxBuffer(mvk::SPIRVToMSLConversionConfiguration& shaderConf // will fit in the slot that might already have been allocated for it in the Metal argument // buffer from a previous allocation that was returned. If this pool has been reset recently, // then the desc sets will not have had a Metal argument buffer allocation assigned yet. - if (mvkDSL->isUsingMetalArgumentBuffers()) { + if (mtlArgBuffEncSize) { // If the offset has not been set (and it's not the first desc set except // on a reset pool), set the offset and update the next available offset value. @@ -673,7 +678,7 @@ static void populateAuxBuffer(mvk::SPIRVToMSLConversionConfiguration& shaderConf } if (isSpaceAvail) { - rslt = mvkDS->allocate(mvkDSL, variableDescriptorCount, mtlArgBuffOffset); + rslt = mvkDS->allocate(mvkDSL, variableDescriptorCount, mtlArgBuffOffset, mtlArgEnc); if (rslt) { freeDescriptorSet(mvkDS, false); } else { @@ -861,6 +866,24 @@ static void populateAuxBuffer(mvk::SPIRVToMSLConversionConfiguration& shaderConf return descCnt; } +std::string MVKDescriptorPool::getLogDescription() { + std::stringstream descStr; + descStr << "VkDescriptorPool " << this << " with " << _descriptorSetAvailablility.size() << " descriptor sets, and descriptors:"; + descStr << "\n\tVK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: " << _uniformBufferDescriptors._availability.size(); + descStr << "\n\tVK_DESCRIPTOR_TYPE_STORAGE_BUFFER: " << _storageBufferDescriptors._availability.size(); + descStr << "\n\tVK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: " << _uniformBufferDynamicDescriptors._availability.size(); + descStr << "\n\tVK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: " << _storageBufferDynamicDescriptors._availability.size(); + descStr << "\n\tVK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: " << _inlineUniformBlockDescriptors._availability.size(); + descStr << "\n\tVK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: " << _sampledImageDescriptors._availability.size(); + descStr << "\n\tVK_DESCRIPTOR_TYPE_STORAGE_IMAGE: " << _storageImageDescriptors._availability.size(); + descStr << "\n\tVK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: " << _inputAttachmentDescriptors._availability.size(); + descStr << "\n\tVK_DESCRIPTOR_TYPE_SAMPLER: " << _samplerDescriptors._availability.size(); + descStr << "\n\tVK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: " << _combinedImageSamplerDescriptors._availability.size(); + descStr << "\n\tVK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: " << _uniformTexelBufferDescriptors._availability.size(); + descStr << "\n\tVK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: " << _storageTexelBufferDescriptors._availability.size(); + return descStr.str(); +} + MVKDescriptorPool::MVKDescriptorPool(MVKDevice* device, const VkDescriptorPoolCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device), _hasPooledDescriptors(getMVKConfig().preallocateDescriptors), // Set this first! Accessed by MVKDescriptorSet constructor and getPoolSize() in following lines. @@ -879,7 +902,9 @@ static void populateAuxBuffer(mvk::SPIRVToMSLConversionConfiguration& shaderConf _combinedImageSamplerDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)), _uniformTexelBufferDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER)), _storageTexelBufferDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)) { + initMetalArgumentBuffer(pCreateInfo); + MVKLogDebugIf(getMVKConfig().debugMode, "Created %s\n", getLogDescription().c_str()); } void MVKDescriptorPool::initMetalArgumentBuffer(const VkDescriptorPoolCreateInfo* pCreateInfo) { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index edb69c5cc..2d6525788 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -4903,9 +4903,9 @@ static uint32_t mvkGetEntryProperty(io_registry_entry_t entry, CFStringRef prope } #endif - MVKLogInfoIf(getMVKConfig().debugMode, "Descriptor sets binding resources using %s.", - _physicalDevice->_isUsingMetalArgumentBuffers ? (_physicalDevice->_metalFeatures.needsArgumentBufferEncoders - ? "Metal argument buffers" : "Metal3 argument buffers") : "discrete resource indexes"); + MVKLogInfo("Descriptor sets binding resources using %s.", + _physicalDevice->_isUsingMetalArgumentBuffers ? (_physicalDevice->_metalFeatures.needsArgumentBufferEncoders + ? "Metal argument buffers" : "Metal3 argument buffers") : "discrete resource indexes"); _commandResourceFactory = new MVKCommandResourceFactory(this);