diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h index 31f5cf03..53725a0b 100644 --- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h +++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h @@ -483,6 +483,8 @@ typedef struct { VkSampleCountFlags supportedSampleCounts; /**< A bitmask identifying the sample counts supported by the device. */ uint32_t minSwapchainImageCount; /**< The minimum number of swapchain images that can be supported by a surface. */ uint32_t maxSwapchainImageCount; /**< The maximum number of swapchain images that can be supported by a surface. */ + VkBool32 arrayOfTextures; /**< If true, arrays of textures is supported. */ + VkBool32 arrayOfSamplers; /**< If true, arrays of texture samplers is supported. */ } MVKPhysicalDeviceMetalFeatures; /** diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h index b94c9ffa..556d3b78 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h @@ -73,7 +73,7 @@ typedef struct MVKShaderResourceBinding { #pragma mark MVKDescriptorSetLayoutBinding /** Represents a Vulkan descriptor set layout binding. */ -class MVKDescriptorSetLayoutBinding : public MVKConfigurableObject { +class MVKDescriptorSetLayoutBinding : public MVKBaseDeviceObject { public: @@ -100,7 +100,8 @@ public: uint32_t dslIndex); /** Constructs an instance. */ - MVKDescriptorSetLayoutBinding(MVKDescriptorSetLayout* layout, + MVKDescriptorSetLayoutBinding(MVKDevice* device, + MVKDescriptorSetLayout* layout, const VkDescriptorSetLayoutBinding* pBinding); MVKDescriptorSetLayoutBinding(const MVKDescriptorSetLayoutBinding& binding); @@ -112,9 +113,9 @@ protected: friend class MVKDescriptorBinding; friend class MVKPipelineLayout; - VkResult initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes, - MVKShaderStageResourceBinding* pDescSetCounts, - const VkDescriptorSetLayoutBinding* pBinding); + void initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes, + MVKShaderStageResourceBinding* pDescSetCounts, + const VkDescriptorSetLayoutBinding* pBinding); VkDescriptorSetLayoutBinding _info; std::vector _immutableSamplers; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm index 4d7e64f1..0477a710 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm @@ -405,30 +405,31 @@ void MVKDescriptorSetLayoutBinding::populateShaderConverterContext(SPIRVToMSLCon } } -MVKDescriptorSetLayoutBinding::MVKDescriptorSetLayoutBinding(MVKDescriptorSetLayout* layout, - const VkDescriptorSetLayoutBinding* pBinding) : MVKConfigurableObject() { +MVKDescriptorSetLayoutBinding::MVKDescriptorSetLayoutBinding(MVKDevice* device, + MVKDescriptorSetLayout* layout, + const VkDescriptorSetLayoutBinding* pBinding) : MVKBaseDeviceObject(device) { // Determine the shader stages used by this binding _applyToVertexStage = mvkAreFlagsEnabled(pBinding->stageFlags, VK_SHADER_STAGE_VERTEX_BIT); _applyToFragmentStage = mvkAreFlagsEnabled(pBinding->stageFlags, VK_SHADER_STAGE_FRAGMENT_BIT); _applyToComputeStage = mvkAreFlagsEnabled(pBinding->stageFlags, VK_SHADER_STAGE_COMPUTE_BIT); - // If this binding is used by the vertex shader, set the Metal resource index - if (_applyToVertexStage) { - setConfigurationResult(initMetalResourceIndexOffsets(&_mtlResourceIndexOffsets.vertexStage, - &layout->_mtlResourceCounts.vertexStage, pBinding)); - } + // If this binding is used by the vertex shader, set the Metal resource index + if (_applyToVertexStage) { + initMetalResourceIndexOffsets(&_mtlResourceIndexOffsets.vertexStage, + &layout->_mtlResourceCounts.vertexStage, pBinding); + } - // If this binding is used by the fragment shader, set the Metal resource index - if (_applyToFragmentStage) { - setConfigurationResult(initMetalResourceIndexOffsets(&_mtlResourceIndexOffsets.fragmentStage, - &layout->_mtlResourceCounts.fragmentStage, pBinding)); - } + // If this binding is used by the fragment shader, set the Metal resource index + if (_applyToFragmentStage) { + initMetalResourceIndexOffsets(&_mtlResourceIndexOffsets.fragmentStage, + &layout->_mtlResourceCounts.fragmentStage, pBinding); + } - // If this binding is used by a compute shader, set the Metal resource index - if (_applyToComputeStage) { - setConfigurationResult(initMetalResourceIndexOffsets(&_mtlResourceIndexOffsets.computeStage, - &layout->_mtlResourceCounts.computeStage, pBinding)); - } + // If this binding is used by a compute shader, set the Metal resource index + if (_applyToComputeStage) { + initMetalResourceIndexOffsets(&_mtlResourceIndexOffsets.computeStage, + &layout->_mtlResourceCounts.computeStage, pBinding); + } // If immutable samplers are defined, copy them in if ( pBinding->pImmutableSamplers && @@ -446,7 +447,7 @@ MVKDescriptorSetLayoutBinding::MVKDescriptorSetLayoutBinding(MVKDescriptorSetLay } MVKDescriptorSetLayoutBinding::MVKDescriptorSetLayoutBinding(const MVKDescriptorSetLayoutBinding& binding) : - MVKConfigurableObject(), _info(binding._info), _immutableSamplers(binding._immutableSamplers), + MVKBaseDeviceObject(binding._device), _info(binding._info), _immutableSamplers(binding._immutableSamplers), _mtlResourceIndexOffsets(binding._mtlResourceIndexOffsets), _applyToVertexStage(binding._applyToVertexStage), _applyToFragmentStage(binding._applyToFragmentStage), _applyToComputeStage(binding._applyToComputeStage) { @@ -461,17 +462,19 @@ MVKDescriptorSetLayoutBinding::~MVKDescriptorSetLayoutBinding() { } } -/** - * Sets the appropriate Metal resource indexes within this binding from the - * specified descriptor set binding counts, and updates those counts accordingly. - */ -VkResult MVKDescriptorSetLayoutBinding::initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes, - MVKShaderStageResourceBinding* pDescSetCounts, - const VkDescriptorSetLayoutBinding* pBinding) { +// Sets the appropriate Metal resource indexes within this binding from the +// specified descriptor set binding counts, and updates those counts accordingly. +void MVKDescriptorSetLayoutBinding::initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes, + MVKShaderStageResourceBinding* pDescSetCounts, + const VkDescriptorSetLayoutBinding* pBinding) { switch (pBinding->descriptorType) { case VK_DESCRIPTOR_TYPE_SAMPLER: pBindingIndexes->samplerIndex = pDescSetCounts->samplerIndex; pDescSetCounts->samplerIndex += pBinding->descriptorCount; + + if (pBinding->descriptorCount > 1 && !_device->_pMetalFeatures->arrayOfSamplers) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of samplers.", _device->getName())); + } break; case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: @@ -479,6 +482,15 @@ VkResult MVKDescriptorSetLayoutBinding::initMetalResourceIndexOffsets(MVKShaderS pDescSetCounts->textureIndex += pBinding->descriptorCount; pBindingIndexes->samplerIndex = pDescSetCounts->samplerIndex; pDescSetCounts->samplerIndex += pBinding->descriptorCount; + + if (pBinding->descriptorCount > 1) { + if ( !_device->_pMetalFeatures->arrayOfTextures ) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of textures.", _device->getName())); + } + if ( !_device->_pMetalFeatures->arrayOfSamplers ) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of samplers.", _device->getName())); + } + } break; case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: @@ -488,6 +500,10 @@ VkResult MVKDescriptorSetLayoutBinding::initMetalResourceIndexOffsets(MVKShaderS case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: pBindingIndexes->textureIndex = pDescSetCounts->textureIndex; pDescSetCounts->textureIndex += pBinding->descriptorCount; + + if (pBinding->descriptorCount > 1 && !_device->_pMetalFeatures->arrayOfTextures) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of textures.", _device->getName())); + } break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: @@ -501,7 +517,6 @@ VkResult MVKDescriptorSetLayoutBinding::initMetalResourceIndexOffsets(MVKShaderS default: break; } - return VK_SUCCESS; } @@ -634,7 +649,7 @@ MVKDescriptorSetLayout::MVKDescriptorSetLayout(MVKDevice* device, // Create the descriptor bindings _bindings.reserve(pCreateInfo->bindingCount); for (uint32_t i = 0; i < pCreateInfo->bindingCount; i++) { - _bindings.emplace_back(this, &pCreateInfo->pBindings[i]); + _bindings.emplace_back(_device, this, &pCreateInfo->pBindings[i]); _bindingToIndex[pCreateInfo->pBindings[i].binding] = i; setConfigurationResult(_bindings.back().getConfigurationResult()); } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h index 969e6bf3..5cd3f240 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h @@ -90,6 +90,9 @@ public: /** Populates the specified structure with the properties of this device. */ void getProperties(VkPhysicalDeviceProperties2* properties); + /** Returns the name of this device. */ + inline const char* getName() { return _properties.deviceName; } + /** Returns whether the specified format is supported on this device. */ bool getFormatIsSupported(VkFormat format); @@ -315,6 +318,9 @@ public: /** Returns the physical device underlying this logical device. */ inline MVKPhysicalDevice* getPhysicalDevice() { return _physicalDevice; } + /** Returns the name of this device. */ + inline const char* getName() { return _pProperties->deviceName; } + /** Returns the common resource factory for creating command resources. */ inline MVKCommandResourceFactory* getCommandResourceFactory() { return _commandResourceFactory; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index de1288cb..c1ae8289 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -618,6 +618,14 @@ void MVKPhysicalDevice::initMetalFeatures() { _metalFeatures.mtlBufferAlignment = 16; // Min float4 alignment for typical vertex buffers. MTLBuffer may go down to 4 bytes for other data. _metalFeatures.maxTextureDimension = (16 * KIBI); } + + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v2] ) { + _metalFeatures.arrayOfTextures = true; + } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v3] ) { + _metalFeatures.arrayOfSamplers = true; + } + #endif #if MVK_MACOS @@ -642,6 +650,8 @@ void MVKPhysicalDevice::initMetalFeatures() { if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v3] ) { _metalFeatures.mslVersion = SPIRVToMSLConverterOptions::makeMSLVersion(2); _metalFeatures.texelBuffers = true; + _metalFeatures.arrayOfTextures = true; + _metalFeatures.arrayOfSamplers = true; _metalFeatures.presentModeImmediate = true; } @@ -683,6 +693,9 @@ void MVKPhysicalDevice::initFeatures() { _features.shaderInt16 = true; _features.multiDrawIndirect = true; + _features.shaderSampledImageArrayDynamicIndexing = _metalFeatures.arrayOfTextures; + _features.shaderStorageImageArrayDynamicIndexing = _metalFeatures.arrayOfTextures; + if (_metalFeatures.indirectDrawing && _metalFeatures.baseVertexInstanceDrawing) { _features.drawIndirectFirstInstance = true; } @@ -696,10 +709,6 @@ void MVKPhysicalDevice::initFeatures() { if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1] ) { _features.occlusionQueryPrecise = true; - } - if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v2] ) { - _features.shaderSampledImageArrayDynamicIndexing = true; - _features.shaderStorageImageArrayDynamicIndexing = true; } if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v4] ) { _features.depthClamp = true; @@ -720,8 +729,6 @@ void MVKPhysicalDevice::initFeatures() { if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v3] ) { _features.multiViewport = true; - _features.shaderSampledImageArrayDynamicIndexing = true; - _features.shaderStorageImageArrayDynamicIndexing = true; } #endif