| /* |
| * MVKDescriptorSetLayout.mm |
| * |
| * Copyright (c) 2014-2019 The Brenwill Workshop Ltd. (http://www.brenwill.com) |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "MVKDescriptorSet.h" |
| #include "MVKCommandBuffer.h" |
| #include "MVKBuffer.h" |
| #include "MVKFoundation.h" |
| #include "MVKLogging.h" |
| #include "mvk_datatypes.hpp" |
| #include <stdlib.h> |
| |
| using namespace std; |
| using namespace mvk; |
| |
| |
| #pragma mark MVKShaderStageResourceBinding |
| |
| MVKShaderStageResourceBinding MVKShaderStageResourceBinding::operator+ (const MVKShaderStageResourceBinding& rhs) { |
| MVKShaderStageResourceBinding rslt; |
| rslt.bufferIndex = this->bufferIndex + rhs.bufferIndex; |
| rslt.textureIndex = this->textureIndex + rhs.textureIndex; |
| rslt.samplerIndex = this->samplerIndex + rhs.samplerIndex; |
| return rslt; |
| } |
| |
| MVKShaderStageResourceBinding& MVKShaderStageResourceBinding::operator+= (const MVKShaderStageResourceBinding& rhs) { |
| this->bufferIndex += rhs.bufferIndex; |
| this->textureIndex += rhs.textureIndex; |
| this->samplerIndex += rhs.samplerIndex; |
| return *this; |
| } |
| |
| |
| #pragma mark MVKShaderResourceBinding |
| |
| uint32_t MVKShaderResourceBinding::getMaxBufferIndex() { |
| return max({stages[kMVKShaderStageVertex].bufferIndex, stages[kMVKShaderStageTessCtl].bufferIndex, stages[kMVKShaderStageTessEval].bufferIndex, stages[kMVKShaderStageFragment].bufferIndex, stages[kMVKShaderStageCompute].bufferIndex}); |
| } |
| |
| uint32_t MVKShaderResourceBinding::getMaxTextureIndex() { |
| return max({stages[kMVKShaderStageVertex].textureIndex, stages[kMVKShaderStageTessCtl].textureIndex, stages[kMVKShaderStageTessEval].textureIndex, stages[kMVKShaderStageFragment].textureIndex, stages[kMVKShaderStageCompute].textureIndex}); |
| } |
| |
| uint32_t MVKShaderResourceBinding::getMaxSamplerIndex() { |
| return max({stages[kMVKShaderStageVertex].samplerIndex, stages[kMVKShaderStageTessCtl].samplerIndex, stages[kMVKShaderStageTessEval].samplerIndex, stages[kMVKShaderStageFragment].samplerIndex, stages[kMVKShaderStageCompute].samplerIndex}); |
| } |
| |
| MVKShaderResourceBinding MVKShaderResourceBinding::operator+ (const MVKShaderResourceBinding& rhs) { |
| MVKShaderResourceBinding rslt; |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| rslt.stages[i] = this->stages[i] + rhs.stages[i]; |
| } |
| return rslt; |
| } |
| |
| MVKShaderResourceBinding& MVKShaderResourceBinding::operator+= (const MVKShaderResourceBinding& rhs) { |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| this->stages[i] += rhs.stages[i]; |
| } |
| return *this; |
| } |
| |
| |
| #pragma mark - |
| #pragma mark MVKDescriptorSetLayoutBinding |
| |
| MVKVulkanAPIObject* MVKDescriptorSetLayoutBinding::getVulkanAPIObject() { return _layout; }; |
| |
| // A null cmdEncoder can be passed to perform a validation pass |
| void MVKDescriptorSetLayoutBinding::bind(MVKCommandEncoder* cmdEncoder, |
| MVKDescriptorBinding& descBinding, |
| MVKShaderResourceBinding& dslMTLRezIdxOffsets, |
| MVKVector<uint32_t>& dynamicOffsets, |
| uint32_t* pDynamicOffsetIndex) { |
| MVKMTLBufferBinding bb; |
| MVKMTLTextureBinding tb; |
| MVKMTLSamplerStateBinding sb; |
| NSUInteger bufferDynamicOffset = 0; |
| |
| // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. |
| MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; |
| |
| for (uint32_t rezIdx = 0; rezIdx < _info.descriptorCount; rezIdx++) { |
| switch (_info.descriptorType) { |
| |
| // After determining dynamic part of offset (zero otherwise), fall through to non-dynamic handling |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: |
| bufferDynamicOffset = dynamicOffsets[*pDynamicOffsetIndex]; |
| (*pDynamicOffsetIndex)++; // Move on to next dynamic offset (and feedback to caller) |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { |
| MVKBuffer* mvkBuff = (MVKBuffer*)descBinding._bufferBindings[rezIdx].buffer; |
| bb.mtlBuffer = descBinding._mtlBuffers[rezIdx]; |
| bb.offset = descBinding._mtlBufferOffsets[rezIdx] + bufferDynamicOffset; |
| bb.size = mvkBuff ? (uint32_t)mvkBuff->getByteCount() : 0; |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| if (_applyToStage[i]) { |
| bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx; |
| if (i == kMVKShaderStageCompute) { |
| if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } |
| } else { |
| if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } |
| } |
| } |
| } |
| break; |
| } |
| |
| case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: |
| case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: |
| case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { |
| tb.mtlTexture = descBinding._mtlTextures[rezIdx]; |
| if (_info.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE && tb.mtlTexture) { |
| tb.swizzle = ((MVKImageView*)descBinding._imageBindings[rezIdx].imageView)->getPackedSwizzle(); |
| } else { |
| tb.swizzle = 0; |
| } |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| if (_applyToStage[i]) { |
| tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; |
| if (i == kMVKShaderStageCompute) { |
| if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } |
| } else { |
| if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } |
| } |
| } |
| } |
| break; |
| } |
| |
| case VK_DESCRIPTOR_TYPE_SAMPLER: { |
| sb.mtlSamplerState = descBinding._mtlSamplers[rezIdx]; |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| if (_applyToStage[i]) { |
| sb.index = mtlIdxs.stages[i].samplerIndex + rezIdx; |
| if (i == kMVKShaderStageCompute) { |
| if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } |
| } else { |
| if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } |
| } |
| } |
| } |
| break; |
| } |
| |
| case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { |
| tb.mtlTexture = descBinding._mtlTextures[rezIdx]; |
| if (tb.mtlTexture) { |
| tb.swizzle = ((MVKImageView*)descBinding._imageBindings[rezIdx].imageView)->getPackedSwizzle(); |
| } else { |
| tb.swizzle = 0; |
| } |
| sb.mtlSamplerState = descBinding._mtlSamplers[rezIdx]; |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| if (_applyToStage[i]) { |
| tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; |
| sb.index = mtlIdxs.stages[i].samplerIndex + rezIdx; |
| if (i == kMVKShaderStageCompute) { |
| if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } |
| if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } |
| } else { |
| if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } |
| if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } |
| } |
| } |
| } |
| break; |
| } |
| |
| default: |
| break; |
| } |
| } |
| } |
| |
| template<typename T> |
| static const T& get(const void* pData, size_t stride, uint32_t index) { |
| return *(T*)((const char*)pData + stride * index); |
| } |
| |
| // A null cmdEncoder can be passed to perform a validation pass |
| void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder, |
| uint32_t& dstArrayElement, |
| uint32_t& descriptorCount, |
| uint32_t& descriptorsPushed, |
| VkDescriptorType descriptorType, |
| size_t stride, |
| const void* pData, |
| MVKShaderResourceBinding& dslMTLRezIdxOffsets) { |
| MVKMTLBufferBinding bb; |
| MVKMTLTextureBinding tb; |
| MVKMTLSamplerStateBinding sb; |
| |
| if (dstArrayElement >= _info.descriptorCount) { |
| dstArrayElement -= _info.descriptorCount; |
| return; |
| } |
| |
| if (descriptorType != _info.descriptorType) { |
| dstArrayElement = 0; |
| if (_info.descriptorCount > descriptorCount) |
| descriptorCount = 0; |
| else { |
| descriptorCount -= _info.descriptorCount; |
| descriptorsPushed = _info.descriptorCount; |
| } |
| return; |
| } |
| |
| // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. |
| MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; |
| |
| for (uint32_t rezIdx = dstArrayElement; |
| rezIdx < _info.descriptorCount && rezIdx - dstArrayElement < descriptorCount; |
| rezIdx++) { |
| switch (_info.descriptorType) { |
| |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { |
| const auto& bufferInfo = get<VkDescriptorBufferInfo>(pData, stride, rezIdx - dstArrayElement); |
| MVKBuffer* buffer = (MVKBuffer*)bufferInfo.buffer; |
| bb.mtlBuffer = buffer->getMTLBuffer(); |
| bb.offset = bufferInfo.offset; |
| bb.size = (uint32_t)buffer->getByteCount(); |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| if (_applyToStage[i]) { |
| bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx; |
| if (i == kMVKShaderStageCompute) { |
| if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } |
| } else { |
| if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } |
| } |
| } |
| } |
| break; |
| } |
| |
| case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: |
| case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: |
| case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { |
| const auto& imageInfo = get<VkDescriptorImageInfo>(pData, stride, rezIdx - dstArrayElement); |
| MVKImageView* imageView = (MVKImageView*)imageInfo.imageView; |
| tb.mtlTexture = imageView->getMTLTexture(); |
| if (_info.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE && imageView) { |
| tb.swizzle = imageView->getPackedSwizzle(); |
| } else { |
| tb.swizzle = 0; |
| } |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| if (_applyToStage[i]) { |
| tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; |
| if (i == kMVKShaderStageCompute) { |
| if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } |
| } else { |
| if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } |
| } |
| } |
| } |
| break; |
| } |
| |
| case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { |
| auto* bufferView = get<MVKBufferView*>(pData, stride, rezIdx - dstArrayElement); |
| tb.mtlTexture = bufferView->getMTLTexture(); |
| tb.swizzle = 0; |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| if (_applyToStage[i]) { |
| tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; |
| if (i == kMVKShaderStageCompute) { |
| if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } |
| } else { |
| if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } |
| } |
| } |
| } |
| break; |
| } |
| |
| case VK_DESCRIPTOR_TYPE_SAMPLER: { |
| MVKSampler* sampler; |
| if (_immutableSamplers.empty()) { |
| sampler = (MVKSampler*)get<VkDescriptorImageInfo>(pData, stride, rezIdx - dstArrayElement).sampler; |
| validate(sampler); |
| } else { |
| sampler = _immutableSamplers[rezIdx]; |
| } |
| sb.mtlSamplerState = sampler->getMTLSamplerState(); |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| if (_applyToStage[i]) { |
| sb.index = mtlIdxs.stages[i].samplerIndex + rezIdx; |
| if (i == kMVKShaderStageCompute) { |
| if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } |
| } else { |
| if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } |
| } |
| } |
| } |
| break; |
| } |
| |
| case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { |
| const auto& imageInfo = get<VkDescriptorImageInfo>(pData, stride, rezIdx - dstArrayElement); |
| MVKImageView* imageView = (MVKImageView*)imageInfo.imageView; |
| tb.mtlTexture = imageView->getMTLTexture(); |
| if (imageView) { |
| tb.swizzle = imageView->getPackedSwizzle(); |
| } else { |
| tb.swizzle = 0; |
| } |
| MVKSampler* sampler; |
| if (_immutableSamplers.empty()) { |
| sampler = (MVKSampler*)imageInfo.sampler; |
| validate(sampler); |
| } else { |
| sampler = _immutableSamplers[rezIdx]; |
| } |
| sb.mtlSamplerState = sampler->getMTLSamplerState(); |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| if (_applyToStage[i]) { |
| tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; |
| sb.index = mtlIdxs.stages[i].samplerIndex + rezIdx; |
| if (i == kMVKShaderStageCompute) { |
| if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } |
| if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } |
| } else { |
| if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } |
| if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } |
| } |
| } |
| } |
| break; |
| } |
| |
| default: |
| break; |
| } |
| } |
| |
| dstArrayElement = 0; |
| if (_info.descriptorCount > descriptorCount) |
| descriptorCount = 0; |
| else { |
| descriptorCount -= _info.descriptorCount; |
| descriptorsPushed = _info.descriptorCount; |
| } |
| } |
| |
| // If depth compare is required, but unavailable on the device, the sampler can only be used as an immutable sampler |
| bool MVKDescriptorSetLayoutBinding::validate(MVKSampler* mvkSampler) { |
| if (mvkSampler->getRequiresConstExprSampler()) { |
| _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkUpdateDescriptorSets(): Depth texture samplers using a compare operation can only be used as immutable samplers on this device.")); |
| return false; |
| } |
| return true; |
| } |
| |
| void MVKDescriptorSetLayoutBinding::populateShaderConverterContext(SPIRVToMSLConversionConfiguration& context, |
| MVKShaderResourceBinding& dslMTLRezIdxOffsets, |
| uint32_t dslIndex) { |
| |
| MVKSampler* mvkSamp = !_immutableSamplers.empty() ? _immutableSamplers.front() : nullptr; |
| |
| // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. |
| MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; |
| |
| static const spv::ExecutionModel models[] = { |
| spv::ExecutionModelVertex, |
| spv::ExecutionModelTessellationControl, |
| spv::ExecutionModelTessellationEvaluation, |
| spv::ExecutionModelFragment, |
| spv::ExecutionModelGLCompute |
| }; |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| if (_applyToStage[i]) { |
| mvkPopulateShaderConverterContext(context, |
| mtlIdxs.stages[i], |
| models[i], |
| dslIndex, |
| _info.binding, |
| mvkSamp); |
| } |
| } |
| } |
| |
| MVKDescriptorSetLayoutBinding::MVKDescriptorSetLayoutBinding(MVKDevice* device, |
| MVKDescriptorSetLayout* layout, |
| const VkDescriptorSetLayoutBinding* pBinding) : MVKBaseDeviceObject(device), _layout(layout) { |
| |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| // Determine if this binding is used by this shader stage |
| _applyToStage[i] = mvkAreAllFlagsEnabled(pBinding->stageFlags, mvkVkShaderStageFlagBitsFromMVKShaderStage(MVKShaderStage(i))); |
| // If this binding is used by the shader, set the Metal resource index |
| if (_applyToStage[i]) { |
| initMetalResourceIndexOffsets(&_mtlResourceIndexOffsets.stages[i], |
| &layout->_mtlResourceCounts.stages[i], pBinding); |
| } |
| } |
| |
| // If immutable samplers are defined, copy them in |
| if ( pBinding->pImmutableSamplers && |
| (pBinding->descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || |
| pBinding->descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) ) { |
| _immutableSamplers.reserve(pBinding->descriptorCount); |
| for (uint32_t i = 0; i < pBinding->descriptorCount; i++) { |
| _immutableSamplers.push_back((MVKSampler*)pBinding->pImmutableSamplers[i]); |
| _immutableSamplers.back()->retain(); |
| } |
| } |
| |
| _info = *pBinding; |
| _info.pImmutableSamplers = nullptr; // Remove dangling pointer |
| } |
| |
| MVKDescriptorSetLayoutBinding::MVKDescriptorSetLayoutBinding(const MVKDescriptorSetLayoutBinding& binding) : |
| MVKBaseDeviceObject(binding._device), _layout(binding._layout), |
| _info(binding._info), _immutableSamplers(binding._immutableSamplers), |
| _mtlResourceIndexOffsets(binding._mtlResourceIndexOffsets) { |
| |
| for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { |
| _applyToStage[i] = binding._applyToStage[i]; |
| } |
| for (MVKSampler* sampler : _immutableSamplers) { |
| sampler->retain(); |
| } |
| } |
| |
| MVKDescriptorSetLayoutBinding::~MVKDescriptorSetLayoutBinding() { |
| for (MVKSampler* sampler : _immutableSamplers) { |
| sampler->release(); |
| } |
| } |
| |
| // 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) { |
| _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of samplers.", _device->getName())); |
| } |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: |
| pBindingIndexes->textureIndex = pDescSetCounts->textureIndex; |
| pDescSetCounts->textureIndex += pBinding->descriptorCount; |
| pBindingIndexes->samplerIndex = pDescSetCounts->samplerIndex; |
| pDescSetCounts->samplerIndex += pBinding->descriptorCount; |
| |
| if (pBinding->descriptorCount > 1) { |
| if ( !_device->_pMetalFeatures->arrayOfTextures ) { |
| _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of textures.", _device->getName())); |
| } |
| if ( !_device->_pMetalFeatures->arrayOfSamplers ) { |
| _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of samplers.", _device->getName())); |
| } |
| } |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: |
| case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: |
| case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: |
| pBindingIndexes->textureIndex = pDescSetCounts->textureIndex; |
| pDescSetCounts->textureIndex += pBinding->descriptorCount; |
| |
| if (pBinding->descriptorCount > 1 && !_device->_pMetalFeatures->arrayOfTextures) { |
| _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of textures.", _device->getName())); |
| } |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: |
| pBindingIndexes->bufferIndex = pDescSetCounts->bufferIndex; |
| pDescSetCounts->bufferIndex += pBinding->descriptorCount; |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| |
| #pragma mark - |
| #pragma mark MVKDescriptorSetLayout |
| |
| // A null cmdEncoder can be passed to perform a validation pass |
| void MVKDescriptorSetLayout::bindDescriptorSet(MVKCommandEncoder* cmdEncoder, |
| MVKDescriptorSet* descSet, |
| MVKShaderResourceBinding& dslMTLRezIdxOffsets, |
| MVKVector<uint32_t>& dynamicOffsets, |
| uint32_t* pDynamicOffsetIndex) { |
| |
| if (_isPushDescriptorLayout) return; |
| |
| clearConfigurationResult(); |
| uint32_t bindCnt = (uint32_t)_bindings.size(); |
| for (uint32_t bindIdx = 0; bindIdx < bindCnt; bindIdx++) { |
| _bindings[bindIdx].bind(cmdEncoder, descSet->_bindings[bindIdx], |
| dslMTLRezIdxOffsets, dynamicOffsets, |
| pDynamicOffsetIndex); |
| } |
| } |
| |
| static const void* getWriteParameters(VkDescriptorType type, const VkDescriptorImageInfo* pImageInfo, |
| const VkDescriptorBufferInfo* pBufferInfo, const VkBufferView* pTexelBufferView, |
| size_t& stride) { |
| const void* pData; |
| switch (type) { |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: |
| pData = pBufferInfo; |
| stride = sizeof(VkDescriptorBufferInfo); |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: |
| case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: |
| case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: |
| case VK_DESCRIPTOR_TYPE_SAMPLER: |
| case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: |
| pData = pImageInfo; |
| stride = sizeof(VkDescriptorImageInfo); |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: |
| pData = pTexelBufferView; |
| stride = sizeof(MVKBufferView*); |
| break; |
| |
| default: |
| pData = nullptr; |
| stride = 0; |
| } |
| return pData; |
| } |
| |
| // A null cmdEncoder can be passed to perform a validation pass |
| void MVKDescriptorSetLayout::pushDescriptorSet(MVKCommandEncoder* cmdEncoder, |
| MVKVector<VkWriteDescriptorSet>& descriptorWrites, |
| MVKShaderResourceBinding& dslMTLRezIdxOffsets) { |
| |
| if (!_isPushDescriptorLayout) return; |
| |
| clearConfigurationResult(); |
| for (const VkWriteDescriptorSet& descWrite : descriptorWrites) { |
| uint32_t dstBinding = descWrite.dstBinding; |
| uint32_t dstArrayElement = descWrite.dstArrayElement; |
| uint32_t descriptorCount = descWrite.descriptorCount; |
| const VkDescriptorImageInfo* pImageInfo = descWrite.pImageInfo; |
| const VkDescriptorBufferInfo* pBufferInfo = descWrite.pBufferInfo; |
| const VkBufferView* pTexelBufferView = descWrite.pTexelBufferView; |
| if (!_bindingToIndex.count(dstBinding)) continue; |
| // Note: This will result in us walking off the end of the array |
| // in case there are too many updates... but that's ill-defined anyway. |
| for (; descriptorCount; dstBinding++) { |
| if (!_bindingToIndex.count(dstBinding)) continue; |
| size_t stride; |
| const void* pData = getWriteParameters(descWrite.descriptorType, pImageInfo, |
| pBufferInfo, pTexelBufferView, stride); |
| uint32_t descriptorsPushed = 0; |
| uint32_t bindIdx = _bindingToIndex[dstBinding]; |
| _bindings[bindIdx].push(cmdEncoder, dstArrayElement, descriptorCount, |
| descriptorsPushed, descWrite.descriptorType, |
| stride, pData, dslMTLRezIdxOffsets); |
| pBufferInfo += descriptorsPushed; |
| pImageInfo += descriptorsPushed; |
| pTexelBufferView += descriptorsPushed; |
| } |
| } |
| } |
| |
| // A null cmdEncoder can be passed to perform a validation pass |
| void MVKDescriptorSetLayout::pushDescriptorSet(MVKCommandEncoder* cmdEncoder, |
| MVKDescriptorUpdateTemplate* descUpdateTemplate, |
| const void* pData, |
| MVKShaderResourceBinding& dslMTLRezIdxOffsets) { |
| |
| if (!_isPushDescriptorLayout || |
| descUpdateTemplate->getType() != VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR) |
| return; |
| |
| clearConfigurationResult(); |
| for (uint32_t i = 0; i < descUpdateTemplate->getNumberOfEntries(); i++) { |
| const VkDescriptorUpdateTemplateEntryKHR* pEntry = descUpdateTemplate->getEntry(i); |
| uint32_t dstBinding = pEntry->dstBinding; |
| uint32_t dstArrayElement = pEntry->dstArrayElement; |
| uint32_t descriptorCount = pEntry->descriptorCount; |
| const void* pCurData = (const char*)pData + pEntry->offset; |
| if (!_bindingToIndex.count(dstBinding)) continue; |
| // Note: This will result in us walking off the end of the array |
| // in case there are too many updates... but that's ill-defined anyway. |
| for (; descriptorCount; dstBinding++) { |
| if (!_bindingToIndex.count(dstBinding)) continue; |
| uint32_t descriptorsPushed = 0; |
| uint32_t bindIdx = _bindingToIndex[dstBinding]; |
| _bindings[bindIdx].push(cmdEncoder, dstArrayElement, descriptorCount, |
| descriptorsPushed, pEntry->descriptorType, |
| pEntry->stride, pCurData, dslMTLRezIdxOffsets); |
| pCurData = (const char*)pCurData + pEntry->stride * descriptorsPushed; |
| } |
| } |
| } |
| |
| void MVKDescriptorSetLayout::populateShaderConverterContext(SPIRVToMSLConversionConfiguration& context, |
| MVKShaderResourceBinding& dslMTLRezIdxOffsets, |
| uint32_t dslIndex) { |
| uint32_t bindCnt = (uint32_t)_bindings.size(); |
| for (uint32_t bindIdx = 0; bindIdx < bindCnt; bindIdx++) { |
| _bindings[bindIdx].populateShaderConverterContext(context, dslMTLRezIdxOffsets, dslIndex); |
| } |
| } |
| |
| MVKDescriptorSetLayout::MVKDescriptorSetLayout(MVKDevice* device, |
| const VkDescriptorSetLayoutCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) { |
| _isPushDescriptorLayout = (pCreateInfo->flags & VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR) != 0; |
| // Create the descriptor bindings |
| _bindings.reserve(pCreateInfo->bindingCount); |
| for (uint32_t i = 0; i < pCreateInfo->bindingCount; i++) { |
| _bindings.emplace_back(_device, this, &pCreateInfo->pBindings[i]); |
| _bindingToIndex[pCreateInfo->pBindings[i].binding] = i; |
| } |
| } |
| |
| |
| #pragma mark - |
| #pragma mark MVKDescriptorBinding |
| |
| MVKVulkanAPIObject* MVKDescriptorBinding::getVulkanAPIObject() { return _pDescSet->getVulkanAPIObject(); }; |
| |
| uint32_t MVKDescriptorBinding::writeBindings(uint32_t srcStartIndex, |
| uint32_t dstStartIndex, |
| uint32_t count, |
| size_t stride, |
| const void* pData) { |
| |
| uint32_t dstCnt = MIN(count, _pBindingLayout->_info.descriptorCount - dstStartIndex); |
| |
| switch (_pBindingLayout->_info.descriptorType) { |
| case VK_DESCRIPTOR_TYPE_SAMPLER: |
| for (uint32_t i = 0; i < dstCnt; i++) { |
| uint32_t dstIdx = dstStartIndex + i; |
| const auto* pImgInfo = &get<VkDescriptorImageInfo>(pData, stride, srcStartIndex + i); |
| auto* oldSampler = (MVKSampler*)_imageBindings[dstIdx].sampler; |
| _imageBindings[dstIdx] = *pImgInfo; |
| _imageBindings[dstIdx].imageView = nullptr; // Sampler only. Guard against app not explicitly clearing ImageView. |
| if (_hasDynamicSamplers) { |
| auto* mvkSampler = (MVKSampler*)pImgInfo->sampler; |
| validate(mvkSampler); |
| mvkSampler->retain(); |
| _mtlSamplers[dstIdx] = mvkSampler ? mvkSampler->getMTLSamplerState() : nil; |
| } else { |
| _imageBindings[dstIdx].sampler = nullptr; // Guard against app not explicitly clearing Sampler. |
| } |
| if (oldSampler) { |
| oldSampler->release(); |
| } |
| } |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: |
| for (uint32_t i = 0; i < dstCnt; i++) { |
| uint32_t dstIdx = dstStartIndex + i; |
| const auto* pImgInfo = &get<VkDescriptorImageInfo>(pData, stride, srcStartIndex + i); |
| auto* mvkImageView = (MVKImageView*)pImgInfo->imageView; |
| auto* oldImageView = (MVKImageView*)_imageBindings[dstIdx].imageView; |
| auto* oldSampler = (MVKSampler*)_imageBindings[dstIdx].sampler; |
| mvkImageView->retain(); |
| _imageBindings[dstIdx] = *pImgInfo; |
| _mtlTextures[dstIdx] = mvkImageView ? mvkImageView->getMTLTexture() : nil; |
| if (_hasDynamicSamplers) { |
| auto* mvkSampler = (MVKSampler*)pImgInfo->sampler; |
| validate(mvkSampler); |
| mvkSampler->retain(); |
| _mtlSamplers[dstIdx] = mvkSampler ? mvkSampler->getMTLSamplerState() : nil; |
| } else { |
| _imageBindings[dstIdx].sampler = nullptr; // Guard against app not explicitly clearing Sampler. |
| } |
| if (oldImageView) { |
| oldImageView->release(); |
| } |
| if (oldSampler) { |
| oldSampler->release(); |
| } |
| } |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: |
| case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: |
| case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: |
| for (uint32_t i = 0; i < dstCnt; i++) { |
| uint32_t dstIdx = dstStartIndex + i; |
| const auto* pImgInfo = &get<VkDescriptorImageInfo>(pData, stride, srcStartIndex + i); |
| auto* mvkImageView = (MVKImageView*)pImgInfo->imageView; |
| auto* oldImageView = (MVKImageView*)_imageBindings[dstIdx].imageView; |
| if (mvkImageView) { |
| mvkImageView->retain(); |
| } |
| _imageBindings[dstIdx] = *pImgInfo; |
| _imageBindings[dstIdx].sampler = nullptr; // ImageView only. Guard against app not explicitly clearing Sampler. |
| _mtlTextures[dstIdx] = mvkImageView ? mvkImageView->getMTLTexture() : nil; |
| if (oldImageView) { |
| oldImageView->release(); |
| } |
| } |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: |
| for (uint32_t i = 0; i < dstCnt; i++) { |
| uint32_t dstIdx = dstStartIndex + i; |
| const auto* pBuffInfo = &get<VkDescriptorBufferInfo>(pData, stride, srcStartIndex + i); |
| auto* oldBuff = (MVKBuffer*)_bufferBindings[dstIdx].buffer; |
| _bufferBindings[dstIdx] = *pBuffInfo; |
| auto* mtlBuff = (MVKBuffer*)pBuffInfo->buffer; |
| if (mtlBuff) { |
| mtlBuff->retain(); |
| } |
| _mtlBuffers[dstIdx] = mtlBuff ? mtlBuff->getMTLBuffer() : nil; |
| _mtlBufferOffsets[dstIdx] = mtlBuff ? (mtlBuff->getMTLBufferOffset() + pBuffInfo->offset) : 0; |
| if (oldBuff) { |
| oldBuff->release(); |
| } |
| } |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: |
| for (uint32_t i = 0; i < dstCnt; i++) { |
| uint32_t dstIdx = dstStartIndex + i; |
| const auto* pBuffView = &get<VkBufferView>(pData, stride, srcStartIndex + i); |
| auto* mvkBuffView = (MVKBufferView*)*pBuffView; |
| auto* oldBuffView = (MVKBufferView*)_texelBufferBindings[dstIdx]; |
| if (mvkBuffView) { |
| mvkBuffView->retain(); |
| } |
| _texelBufferBindings[dstIdx] = *pBuffView; |
| _mtlTextures[dstIdx] = mvkBuffView ? mvkBuffView->getMTLTexture() : nil; |
| if (oldBuffView) { |
| oldBuffView->release(); |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return count - dstCnt; |
| } |
| |
| uint32_t MVKDescriptorBinding::readBindings(uint32_t srcStartIndex, |
| uint32_t dstStartIndex, |
| uint32_t count, |
| VkDescriptorType& descType, |
| VkDescriptorImageInfo* pImageInfo, |
| VkDescriptorBufferInfo* pBufferInfo, |
| VkBufferView* pTexelBufferView) { |
| |
| uint32_t srcCnt = MIN(count, _pBindingLayout->_info.descriptorCount - srcStartIndex); |
| |
| descType = _pBindingLayout->_info.descriptorType; |
| switch (_pBindingLayout->_info.descriptorType) { |
| case VK_DESCRIPTOR_TYPE_SAMPLER: |
| case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: |
| case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: |
| case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: |
| case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: |
| for (uint32_t i = 0; i < srcCnt; i++) { |
| pImageInfo[dstStartIndex + i] = _imageBindings[srcStartIndex + i]; |
| } |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: |
| for (uint32_t i = 0; i < srcCnt; i++) { |
| pBufferInfo[dstStartIndex + i] = _bufferBindings[srcStartIndex + i]; |
| } |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: |
| for (uint32_t i = 0; i < srcCnt; i++) { |
| pTexelBufferView[dstStartIndex + i] = _texelBufferBindings[srcStartIndex + i]; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| return count - srcCnt; |
| } |
| |
| bool MVKDescriptorBinding::hasBinding(uint32_t binding) { |
| return _pBindingLayout->_info.binding == binding; |
| } |
| |
| MVKDescriptorBinding::MVKDescriptorBinding(MVKDescriptorSet* pDescSet, MVKDescriptorSetLayoutBinding* pBindingLayout) : _pDescSet(pDescSet) { |
| |
| uint32_t descCnt = pBindingLayout->_info.descriptorCount; |
| |
| // Create space for the binding and Metal resources and populate with NULL and zero values |
| switch (pBindingLayout->_info.descriptorType) { |
| case VK_DESCRIPTOR_TYPE_SAMPLER: |
| _imageBindings.resize(descCnt, VkDescriptorImageInfo()); |
| initMTLSamplers(pBindingLayout); |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: |
| _imageBindings.resize(descCnt, VkDescriptorImageInfo()); |
| _mtlTextures.resize(descCnt, nil); |
| initMTLSamplers(pBindingLayout); |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: |
| case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: |
| case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: |
| _imageBindings.resize(descCnt, VkDescriptorImageInfo()); |
| _mtlTextures.resize(descCnt, nil); |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: |
| _bufferBindings.resize(descCnt, VkDescriptorBufferInfo()); |
| _mtlBuffers.resize(descCnt, nil); |
| _mtlBufferOffsets.resize(descCnt, 0); |
| break; |
| |
| case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: |
| _texelBufferBindings.resize(descCnt, nil); |
| _mtlTextures.resize(descCnt, nil); |
| break; |
| |
| default: |
| break; |
| } |
| |
| // Okay to hold layout as a pointer. From the Vulkan spec... |
| // "VkDescriptorSetLayout objects may be accessed by commands that operate on descriptor |
| // sets allocated using that layout, and those descriptor sets must not be updated with |
| // vkUpdateDescriptorSets after the descriptor set layout has been destroyed. |
| _pBindingLayout = pBindingLayout; |
| } |
| |
| MVKDescriptorBinding::~MVKDescriptorBinding() { |
| for (const VkDescriptorImageInfo& imgInfo : _imageBindings) { |
| if (imgInfo.imageView) { |
| ((MVKImageView*)imgInfo.imageView)->release(); |
| } |
| if (imgInfo.sampler) { |
| ((MVKSampler*)imgInfo.sampler)->release(); |
| } |
| } |
| for (const VkDescriptorBufferInfo& buffInfo : _bufferBindings) { |
| if (buffInfo.buffer) { |
| ((MVKBuffer*)buffInfo.buffer)->release(); |
| } |
| } |
| for (VkBufferView buffView : _texelBufferBindings) { |
| if (buffView) { |
| ((MVKBufferView*)buffView)->release(); |
| } |
| } |
| } |
| |
| /** |
| * If the descriptor set layout binding contains immutable samplers, immediately populate |
| * the corresponding Metal sampler in this descriptor binding from it. Otherwise add a null |
| * placeholder that will be populated dynamically at a later time. |
| */ |
| void MVKDescriptorBinding::initMTLSamplers(MVKDescriptorSetLayoutBinding* pBindingLayout) { |
| uint32_t descCnt = pBindingLayout->_info.descriptorCount; |
| auto imtblSamps = pBindingLayout->_immutableSamplers; |
| _hasDynamicSamplers = imtblSamps.empty(); |
| |
| _mtlSamplers.reserve(descCnt); |
| for (uint32_t i = 0; i < descCnt; i++) { |
| _mtlSamplers.push_back(_hasDynamicSamplers ? nil : imtblSamps[i]->getMTLSamplerState()); |
| } |
| } |
| |
| |
| #pragma mark - |
| #pragma mark MVKDescriptorSet |
| |
| template<typename DescriptorAction> |
| void MVKDescriptorSet::writeDescriptorSets(const DescriptorAction* pDescriptorAction, |
| size_t stride, const void* pData) { |
| uint32_t dstStartIdx = pDescriptorAction->dstArrayElement; |
| uint32_t binding = pDescriptorAction->dstBinding; |
| uint32_t origCnt = pDescriptorAction->descriptorCount; |
| uint32_t remainCnt = origCnt; |
| |
| MVKDescriptorBinding* mvkDescBind = getBinding(binding); |
| while (mvkDescBind && remainCnt > 0) { |
| uint32_t srcStartIdx = origCnt - remainCnt; |
| remainCnt = mvkDescBind->writeBindings(srcStartIdx, dstStartIdx, remainCnt, |
| stride, pData); |
| binding++; // If not consumed, move to next consecutive binding point |
| mvkDescBind = getBinding(binding); |
| dstStartIdx = 0; // Subsequent bindings start reading at first element |
| } |
| } |
| |
| // Create concrete implementations of the three variations of the writeDescriptorSets() function. |
| template void MVKDescriptorSet::writeDescriptorSets<VkWriteDescriptorSet>(const VkWriteDescriptorSet* pDescriptorAction, |
| size_t stride, const void *pData); |
| template void MVKDescriptorSet::writeDescriptorSets<VkCopyDescriptorSet>(const VkCopyDescriptorSet* pDescriptorAction, |
| size_t stride, const void *pData); |
| template void MVKDescriptorSet::writeDescriptorSets<VkDescriptorUpdateTemplateEntryKHR>( |
| const VkDescriptorUpdateTemplateEntryKHR* pDescriptorAction, |
| size_t stride, const void *pData); |
| |
| void MVKDescriptorSet::readDescriptorSets(const VkCopyDescriptorSet* pDescriptorCopy, |
| VkDescriptorType& descType, |
| VkDescriptorImageInfo* pImageInfo, |
| VkDescriptorBufferInfo* pBufferInfo, |
| VkBufferView* pTexelBufferView) { |
| uint32_t srcStartIdx = pDescriptorCopy->srcArrayElement; |
| uint32_t binding = pDescriptorCopy->srcBinding; |
| uint32_t origCnt = pDescriptorCopy->descriptorCount; |
| uint32_t remainCnt = origCnt; |
| |
| MVKDescriptorBinding* mvkDescBind = getBinding(binding); |
| while (mvkDescBind && remainCnt > 0) { |
| uint32_t dstStartIdx = origCnt - remainCnt; |
| remainCnt = mvkDescBind->readBindings(srcStartIdx, dstStartIdx, remainCnt, descType, |
| pImageInfo, pBufferInfo, pTexelBufferView); |
| binding++; // If not consumed, move to next consecutive binding point |
| mvkDescBind = getBinding(binding); |
| srcStartIdx = 0; // Subsequent bindings start reading at first element |
| } |
| } |
| |
| // Returns the binding instance that is assigned the specified |
| // binding number, or returns null if no such binding exists. |
| MVKDescriptorBinding* MVKDescriptorSet::getBinding(uint32_t binding) { |
| for (auto& mvkDB : _bindings) { if (mvkDB.hasBinding(binding)) { return &mvkDB; } } |
| return nullptr; |
| } |
| |
| // If the layout has changed, create the binding slots, each referencing a corresponding binding layout |
| void MVKDescriptorSet::setLayout(MVKDescriptorSetLayout* layout) { |
| if (layout != _pLayout) { |
| _pLayout = layout; |
| uint32_t bindCnt = (uint32_t)layout->_bindings.size(); |
| |
| _bindings.clear(); |
| _bindings.reserve(bindCnt); |
| for (uint32_t i = 0; i < bindCnt; i++) { |
| _bindings.emplace_back(this, &layout->_bindings[i]); |
| } |
| } |
| } |
| |
| |
| #pragma mark - |
| #pragma mark MVKDescriptorPool |
| |
| VkResult MVKDescriptorPool::allocateDescriptorSets(uint32_t count, |
| const VkDescriptorSetLayout* pSetLayouts, |
| VkDescriptorSet* pDescriptorSets) { |
| if (_allocatedSets.size() + count > _maxSets) { |
| if (_device->_enabledExtensions.vk_KHR_maintenance1.enabled) { |
| return VK_ERROR_OUT_OF_POOL_MEMORY; // Failure is an acceptable test...don't log as error. |
| } else { |
| return reportError(VK_ERROR_INITIALIZATION_FAILED, "The maximum number of descriptor sets that can be allocated by this descriptor pool is %d.", _maxSets); |
| } |
| } |
| |
| for (uint32_t dsIdx = 0; dsIdx < count; dsIdx++) { |
| MVKDescriptorSetLayout* mvkDSL = (MVKDescriptorSetLayout*)pSetLayouts[dsIdx]; |
| if ( !mvkDSL->isPushDescriptorLayout() ) { |
| MVKDescriptorSet* mvkDS = getDescriptorSetPool(mvkDSL)->acquireObject(); |
| mvkDS->setLayout(mvkDSL); |
| _allocatedSets.insert(mvkDS); |
| pDescriptorSets[dsIdx] = (VkDescriptorSet)mvkDS; |
| } |
| } |
| return VK_SUCCESS; |
| } |
| |
| // Ensure descriptor set was actually allocated, then return to pool |
| VkResult MVKDescriptorPool::freeDescriptorSets(uint32_t count, const VkDescriptorSet* pDescriptorSets) { |
| for (uint32_t dsIdx = 0; dsIdx < count; dsIdx++) { |
| MVKDescriptorSet* mvkDS = (MVKDescriptorSet*)pDescriptorSets[dsIdx]; |
| if (_allocatedSets.erase(mvkDS)) { |
| getDescriptorSetPool(mvkDS->_pLayout)->returnObject(mvkDS); |
| } |
| } |
| return VK_SUCCESS; |
| } |
| |
| // Return any allocated descriptor sets to their pools |
| VkResult MVKDescriptorPool::reset(VkDescriptorPoolResetFlags flags) { |
| for (auto& mvkDS : _allocatedSets) { |
| getDescriptorSetPool(mvkDS->_pLayout)->returnObject(mvkDS); |
| } |
| _allocatedSets.clear(); |
| return VK_SUCCESS; |
| } |
| |
| // Returns the pool of descriptor sets that use a specific layout, lazily creating it if necessary |
| MVKDescriptorSetPool* MVKDescriptorPool::getDescriptorSetPool(MVKDescriptorSetLayout* mvkDescSetLayout) { |
| MVKDescriptorSetPool* dsp = _descriptorSetPools[mvkDescSetLayout]; |
| if ( !dsp ) { |
| dsp = new MVKDescriptorSetPool(_device); |
| _descriptorSetPools[mvkDescSetLayout] = dsp; |
| } |
| return dsp; |
| } |
| |
| MVKDescriptorPool::MVKDescriptorPool(MVKDevice* device, |
| const VkDescriptorPoolCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) { |
| _maxSets = pCreateInfo->maxSets; |
| } |
| |
| // Return any allocated sets to their pools and then destroy all the pools. |
| MVKDescriptorPool::~MVKDescriptorPool() { |
| reset(0); |
| for (auto& pair : _descriptorSetPools) { pair.second->destroy(); } |
| } |
| |
| |
| #pragma mark - |
| #pragma mark MVKDescriptorUpdateTemplate |
| |
| const VkDescriptorUpdateTemplateEntryKHR* MVKDescriptorUpdateTemplate::getEntry(uint32_t n) const { |
| return &_entries[n]; |
| } |
| |
| uint32_t MVKDescriptorUpdateTemplate::getNumberOfEntries() const { |
| return (uint32_t)_entries.size(); |
| } |
| |
| VkDescriptorUpdateTemplateTypeKHR MVKDescriptorUpdateTemplate::getType() const { |
| return _type; |
| } |
| |
| MVKDescriptorUpdateTemplate::MVKDescriptorUpdateTemplate(MVKDevice* device, |
| const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo) : |
| MVKVulkanAPIDeviceObject(device), _type(pCreateInfo->templateType) { |
| |
| for (uint32_t i = 0; i < pCreateInfo->descriptorUpdateEntryCount; i++) |
| _entries.push_back(pCreateInfo->pDescriptorUpdateEntries[i]); |
| } |
| |
| |
| #pragma mark - |
| #pragma mark Support functions |
| |
| // Updates the resource bindings in the descriptor sets inditified in the specified content. |
| void mvkUpdateDescriptorSets(uint32_t writeCount, |
| const VkWriteDescriptorSet* pDescriptorWrites, |
| uint32_t copyCount, |
| const VkCopyDescriptorSet* pDescriptorCopies) { |
| |
| // Perform the write updates |
| for (uint32_t i = 0; i < writeCount; i++) { |
| const VkWriteDescriptorSet* pDescWrite = &pDescriptorWrites[i]; |
| size_t stride; |
| const void* pData = getWriteParameters(pDescWrite->descriptorType, pDescWrite->pImageInfo, |
| pDescWrite->pBufferInfo, pDescWrite->pTexelBufferView, |
| stride); |
| MVKDescriptorSet* dstSet = (MVKDescriptorSet*)pDescWrite->dstSet; |
| dstSet->writeDescriptorSets(pDescWrite, stride, pData); |
| } |
| |
| // Perform the copy updates by reading bindings from one set and writing to other set. |
| for (uint32_t i = 0; i < copyCount; i++) { |
| const VkCopyDescriptorSet* pDescCopy = &pDescriptorCopies[i]; |
| |
| uint32_t descCnt = pDescCopy->descriptorCount; |
| VkDescriptorType descType; |
| VkDescriptorImageInfo imgInfos[descCnt]; |
| VkDescriptorBufferInfo buffInfos[descCnt]; |
| VkBufferView texelBuffInfos[descCnt]; |
| |
| MVKDescriptorSet* srcSet = (MVKDescriptorSet*)pDescCopy->srcSet; |
| srcSet->readDescriptorSets(pDescCopy, descType, imgInfos, buffInfos, texelBuffInfos); |
| |
| MVKDescriptorSet* dstSet = (MVKDescriptorSet*)pDescCopy->dstSet; |
| size_t stride; |
| const void* pData = getWriteParameters(descType, imgInfos, buffInfos, texelBuffInfos, stride); |
| dstSet->writeDescriptorSets(pDescCopy, stride, pData); |
| } |
| } |
| |
| // Updates the resource bindings in the given descriptor set from the specified template. |
| void mvkUpdateDescriptorSetWithTemplate(VkDescriptorSet descriptorSet, |
| VkDescriptorUpdateTemplateKHR updateTemplate, |
| const void* pData) { |
| |
| MVKDescriptorSet* dstSet = (MVKDescriptorSet*)descriptorSet; |
| MVKDescriptorUpdateTemplate* pTemplate = (MVKDescriptorUpdateTemplate*)updateTemplate; |
| |
| if (pTemplate->getType() != VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR) |
| return; |
| |
| // Perform the updates |
| for (uint32_t i = 0; i < pTemplate->getNumberOfEntries(); i++) { |
| const VkDescriptorUpdateTemplateEntryKHR* pEntry = pTemplate->getEntry(i); |
| const void* pCurData = (const char*)pData + pEntry->offset; |
| dstSet->writeDescriptorSets(pEntry, pEntry->stride, pCurData); |
| } |
| } |
| |
| void mvkPopulateShaderConverterContext(SPIRVToMSLConversionConfiguration& context, |
| MVKShaderStageResourceBinding& ssRB, |
| spv::ExecutionModel stage, |
| uint32_t descriptorSetIndex, |
| uint32_t bindingIndex, |
| MVKSampler* immutableSampler) { |
| MSLResourceBinding rb; |
| |
| auto& rbb = rb.resourceBinding; |
| rbb.stage = stage; |
| rbb.desc_set = descriptorSetIndex; |
| rbb.binding = bindingIndex; |
| rbb.msl_buffer = ssRB.bufferIndex; |
| rbb.msl_texture = ssRB.textureIndex; |
| rbb.msl_sampler = ssRB.samplerIndex; |
| |
| if (immutableSampler) { immutableSampler->getConstexprSampler(rb); } |
| |
| context.resourceBindings.push_back(rb); |
| } |