Support arbitrary swizzles of image data.
The swizzles are passed to shaders that need them using an "auxiliary buffer", for which there must be room in the function argument table. Code is inserted into the shaders to swizzle reads from sampled textures--per the Vulkan spec, component mappings must only be applied to sampled images. Update SPIRV-Cross to pull in some fixes for handling swizzled image data. Bump MoltenVK version.
This commit is contained in:
parent
7dd143411a
commit
082d282b28
@ -1 +1 @@
|
||||
c9210427b9ab547d41f1af804dedae581b382965
|
||||
cc5c0204d8bcdadbb4add03e53346df98bf27fa4
|
||||
|
@ -48,7 +48,7 @@ extern "C" {
|
||||
*/
|
||||
#define MVK_VERSION_MAJOR 1
|
||||
#define MVK_VERSION_MINOR 0
|
||||
#define MVK_VERSION_PATCH 23
|
||||
#define MVK_VERSION_PATCH 24
|
||||
|
||||
#define MVK_MAKE_VERSION(major, minor, patch) (((major) * 10000) + ((minor) * 100) + (patch))
|
||||
#define MVK_VERSION MVK_MAKE_VERSION(MVK_VERSION_MAJOR, MVK_VERSION_MINOR, MVK_VERSION_PATCH)
|
||||
|
@ -25,6 +25,8 @@
|
||||
class MVKCommandEncoder;
|
||||
class MVKOcclusionQueryPool;
|
||||
|
||||
struct MVKShaderAuxBufferBinding;
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKCommandEncoderState
|
||||
@ -388,6 +390,16 @@ protected:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AuxBuffer {
|
||||
uint32_t swizzleConst[1];
|
||||
};
|
||||
|
||||
// Updates the swizzle for an image in the given buffer.
|
||||
void updateSwizzle(id<MTLBuffer> buffer, uint32_t index, uint32_t swizzle) {
|
||||
auto* aux = (AuxBuffer*)buffer.contents;
|
||||
aux->swizzleConst[index] = swizzle;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -425,6 +437,9 @@ public:
|
||||
_mtlIndexBufferBinding = binding; // No need to track dirty state
|
||||
}
|
||||
|
||||
/** Sets the current auxiliary buffer state. */
|
||||
void bindAuxBuffer(id<MTLBuffer> buffer, const MVKShaderAuxBufferBinding& binding, bool needVertexAuxBuffer, bool needFragmentAuxBuffer);
|
||||
|
||||
|
||||
#pragma mark Construction
|
||||
|
||||
@ -442,6 +457,8 @@ protected:
|
||||
std::vector<MVKMTLTextureBinding> _fragmentTextureBindings;
|
||||
std::vector<MVKMTLSamplerStateBinding> _vertexSamplerStateBindings;
|
||||
std::vector<MVKMTLSamplerStateBinding> _fragmentSamplerStateBindings;
|
||||
MVKMTLBufferBinding _vertexAuxBufferBinding;
|
||||
MVKMTLBufferBinding _fragmentAuxBufferBinding;
|
||||
|
||||
bool _areVertexBufferBindingsDirty = false;
|
||||
bool _areFragmentBufferBindingsDirty = false;
|
||||
@ -469,6 +486,8 @@ public:
|
||||
/** Binds the specified sampler state. */
|
||||
void bindSamplerState(const MVKMTLSamplerStateBinding& binding);
|
||||
|
||||
/** Sets the current auxiliary buffer state. */
|
||||
void bindAuxBuffer(id<MTLBuffer> buffer, const MVKShaderAuxBufferBinding& binding);
|
||||
|
||||
#pragma mark Construction
|
||||
|
||||
@ -483,6 +502,7 @@ protected:
|
||||
std::vector<MVKMTLBufferBinding> _bufferBindings;
|
||||
std::vector<MVKMTLTextureBinding> _textureBindings;
|
||||
std::vector<MVKMTLSamplerStateBinding> _samplerStateBindings;
|
||||
MVKMTLBufferBinding _auxBufferBinding;
|
||||
|
||||
bool _areBufferBindingsDirty = false;
|
||||
bool _areTextureBindingsDirty = false;
|
||||
|
@ -425,6 +425,15 @@ void MVKGraphicsResourcesCommandEncoderState::bindFragmentSamplerState(const MVK
|
||||
bind(binding, _fragmentSamplerStateBindings, _areFragmentSamplerStateBindingsDirty);
|
||||
}
|
||||
|
||||
void MVKGraphicsResourcesCommandEncoderState::bindAuxBuffer(id<MTLBuffer> buffer, const MVKShaderAuxBufferBinding& binding, bool needVertexAuxBuffer, bool needFragmentAuxBuffer) {
|
||||
_vertexAuxBufferBinding.mtlBuffer = needVertexAuxBuffer ? buffer : nil;
|
||||
_vertexAuxBufferBinding.index = binding.vertex;
|
||||
_vertexAuxBufferBinding.isDirty = needVertexAuxBuffer;
|
||||
_fragmentAuxBufferBinding.mtlBuffer = needFragmentAuxBuffer ? buffer : nil;
|
||||
_fragmentAuxBufferBinding.index = binding.fragment;
|
||||
_fragmentAuxBufferBinding.isDirty = needFragmentAuxBuffer;
|
||||
}
|
||||
|
||||
// Mark everything as dirty
|
||||
void MVKGraphicsResourcesCommandEncoderState::markDirty() {
|
||||
MVKCommandEncoderState::markDirty();
|
||||
@ -434,10 +443,26 @@ void MVKGraphicsResourcesCommandEncoderState::markDirty() {
|
||||
MVKResourcesCommandEncoderState::markDirty(_fragmentTextureBindings, _areFragmentTextureBindingsDirty);
|
||||
MVKResourcesCommandEncoderState::markDirty(_vertexSamplerStateBindings, _areVertexSamplerStateBindingsDirty);
|
||||
MVKResourcesCommandEncoderState::markDirty(_fragmentSamplerStateBindings, _areFragmentSamplerStateBindingsDirty);
|
||||
_vertexAuxBufferBinding.isDirty = true;
|
||||
_fragmentAuxBufferBinding.isDirty = true;
|
||||
}
|
||||
|
||||
void MVKGraphicsResourcesCommandEncoderState::encodeImpl() {
|
||||
|
||||
if (_vertexAuxBufferBinding.mtlBuffer) {
|
||||
for (auto& b : _vertexTextureBindings) {
|
||||
if (b.isDirty)
|
||||
updateSwizzle(_vertexAuxBufferBinding.mtlBuffer, b.index, b.swizzle);
|
||||
}
|
||||
}
|
||||
|
||||
if (_fragmentAuxBufferBinding.mtlBuffer) {
|
||||
for (auto& b : _fragmentTextureBindings) {
|
||||
if (b.isDirty)
|
||||
updateSwizzle(_fragmentAuxBufferBinding.mtlBuffer, b.index, b.swizzle);
|
||||
}
|
||||
}
|
||||
|
||||
encodeBinding<MVKMTLBufferBinding>(_vertexBufferBindings, _areVertexBufferBindingsDirty,
|
||||
[](MVKCommandEncoder* cmdEncoder, MVKMTLBufferBinding& b)->void {
|
||||
[cmdEncoder->_mtlRenderEncoder setVertexBuffer: b.mtlBuffer
|
||||
@ -452,6 +477,20 @@ void MVKGraphicsResourcesCommandEncoderState::encodeImpl() {
|
||||
atIndex: b.index];
|
||||
});
|
||||
|
||||
if (_vertexAuxBufferBinding.isDirty) {
|
||||
_vertexAuxBufferBinding.isDirty = false;
|
||||
[_cmdEncoder->_mtlRenderEncoder setVertexBuffer: _vertexAuxBufferBinding.mtlBuffer
|
||||
offset: 0
|
||||
atIndex: _vertexAuxBufferBinding.index];
|
||||
}
|
||||
|
||||
if (_fragmentAuxBufferBinding.isDirty) {
|
||||
_fragmentAuxBufferBinding.isDirty = false;
|
||||
[_cmdEncoder->_mtlRenderEncoder setFragmentBuffer: _fragmentAuxBufferBinding.mtlBuffer
|
||||
offset: 0
|
||||
atIndex: _fragmentAuxBufferBinding.index];
|
||||
}
|
||||
|
||||
encodeBinding<MVKMTLTextureBinding>(_vertexTextureBindings, _areVertexTextureBindingsDirty,
|
||||
[](MVKCommandEncoder* cmdEncoder, MVKMTLTextureBinding& b)->void {
|
||||
[cmdEncoder->_mtlRenderEncoder setVertexTexture: b.mtlTexture
|
||||
@ -484,6 +523,8 @@ void MVKGraphicsResourcesCommandEncoderState::resetImpl() {
|
||||
_fragmentTextureBindings.clear();
|
||||
_vertexSamplerStateBindings.clear();
|
||||
_fragmentSamplerStateBindings.clear();
|
||||
_vertexAuxBufferBinding.mtlBuffer = nil;
|
||||
_fragmentAuxBufferBinding.mtlBuffer = nil;
|
||||
|
||||
_areVertexBufferBindingsDirty = false;
|
||||
_areFragmentBufferBindingsDirty = false;
|
||||
@ -491,6 +532,8 @@ void MVKGraphicsResourcesCommandEncoderState::resetImpl() {
|
||||
_areFragmentTextureBindingsDirty = false;
|
||||
_areVertexSamplerStateBindingsDirty = false;
|
||||
_areFragmentSamplerStateBindingsDirty = false;
|
||||
_vertexAuxBufferBinding.isDirty = false;
|
||||
_fragmentAuxBufferBinding.isDirty = false;
|
||||
}
|
||||
|
||||
|
||||
@ -509,16 +552,30 @@ void MVKComputeResourcesCommandEncoderState::bindSamplerState(const MVKMTLSample
|
||||
bind(binding, _samplerStateBindings, _areSamplerStateBindingsDirty);
|
||||
}
|
||||
|
||||
void MVKComputeResourcesCommandEncoderState::bindAuxBuffer(id<MTLBuffer> buffer, const MVKShaderAuxBufferBinding& binding) {
|
||||
_auxBufferBinding.mtlBuffer = buffer;
|
||||
_auxBufferBinding.index = binding.compute;
|
||||
_auxBufferBinding.isDirty = buffer != nil;
|
||||
}
|
||||
|
||||
// Mark everything as dirty
|
||||
void MVKComputeResourcesCommandEncoderState::markDirty() {
|
||||
MVKCommandEncoderState::markDirty();
|
||||
MVKResourcesCommandEncoderState::markDirty(_bufferBindings, _areBufferBindingsDirty);
|
||||
MVKResourcesCommandEncoderState::markDirty(_textureBindings, _areTextureBindingsDirty);
|
||||
MVKResourcesCommandEncoderState::markDirty(_samplerStateBindings, _areSamplerStateBindingsDirty);
|
||||
_auxBufferBinding.isDirty = true;
|
||||
}
|
||||
|
||||
void MVKComputeResourcesCommandEncoderState::encodeImpl() {
|
||||
|
||||
if (_auxBufferBinding.mtlBuffer) {
|
||||
for (auto& b : _textureBindings) {
|
||||
if (b.isDirty)
|
||||
updateSwizzle(_auxBufferBinding.mtlBuffer, b.index, b.swizzle);
|
||||
}
|
||||
}
|
||||
|
||||
encodeBinding<MVKMTLBufferBinding>(_bufferBindings, _areBufferBindingsDirty,
|
||||
[](MVKCommandEncoder* cmdEncoder, MVKMTLBufferBinding& b)->void {
|
||||
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setBuffer: b.mtlBuffer
|
||||
@ -526,6 +583,13 @@ void MVKComputeResourcesCommandEncoderState::encodeImpl() {
|
||||
atIndex: b.index];
|
||||
});
|
||||
|
||||
if (_auxBufferBinding.isDirty) {
|
||||
_auxBufferBinding.isDirty = false;
|
||||
[_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setBuffer: _auxBufferBinding.mtlBuffer
|
||||
offset: 0
|
||||
atIndex: _auxBufferBinding.index];
|
||||
}
|
||||
|
||||
encodeBinding<MVKMTLTextureBinding>(_textureBindings, _areTextureBindingsDirty,
|
||||
[](MVKCommandEncoder* cmdEncoder, MVKMTLTextureBinding& b)->void {
|
||||
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setTexture: b.mtlTexture
|
||||
@ -543,10 +607,12 @@ void MVKComputeResourcesCommandEncoderState::resetImpl() {
|
||||
_bufferBindings.clear();
|
||||
_textureBindings.clear();
|
||||
_samplerStateBindings.clear();
|
||||
_auxBufferBinding.mtlBuffer = nil;
|
||||
|
||||
_areBufferBindingsDirty = false;
|
||||
_areTextureBindingsDirty = false;
|
||||
_areSamplerStateBindingsDirty = false;
|
||||
_auxBufferBinding.isDirty = false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
typedef struct {
|
||||
union { id<MTLTexture> mtlTexture = nil; id<MTLTexture> mtlResource; }; // aliases
|
||||
uint32_t index = 0;
|
||||
uint32_t swizzle = 0;
|
||||
bool isDirty = true;
|
||||
} MVKMTLTextureBinding;
|
||||
|
||||
|
@ -100,6 +100,7 @@ public:
|
||||
|
||||
protected:
|
||||
friend class MVKDescriptorBinding;
|
||||
friend class MVKPipelineLayout;
|
||||
|
||||
VkResult initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes,
|
||||
MVKShaderStageResourceBinding* pDescSetCounts,
|
||||
|
@ -111,6 +111,11 @@ void MVKDescriptorSetLayoutBinding::bind(MVKCommandEncoder* cmdEncoder,
|
||||
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;
|
||||
}
|
||||
if (_applyToVertexStage) {
|
||||
tb.index = mtlIdxs.vertexStage.textureIndex + rezIdx;
|
||||
cmdEncoder->_graphicsResourcesState.bindVertexTexture(tb);
|
||||
@ -145,6 +150,11 @@ void MVKDescriptorSetLayoutBinding::bind(MVKCommandEncoder* cmdEncoder,
|
||||
|
||||
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];
|
||||
if (_applyToVertexStage) {
|
||||
tb.index = mtlIdxs.vertexStage.textureIndex + rezIdx;
|
||||
@ -243,6 +253,11 @@ void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder,
|
||||
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;
|
||||
}
|
||||
if (_applyToVertexStage) {
|
||||
tb.index = mtlIdxs.vertexStage.textureIndex + rezIdx;
|
||||
cmdEncoder->_graphicsResourcesState.bindVertexTexture(tb);
|
||||
@ -262,6 +277,7 @@ void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder,
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: {
|
||||
auto* bufferView = get<MVKBufferView*>(pData, stride, rezIdx - dstArrayElement);
|
||||
tb.mtlTexture = bufferView->getMTLTexture();
|
||||
tb.swizzle = 0;
|
||||
if (_applyToVertexStage) {
|
||||
tb.index = mtlIdxs.vertexStage.textureIndex + rezIdx;
|
||||
cmdEncoder->_graphicsResourcesState.bindVertexTexture(tb);
|
||||
@ -304,6 +320,11 @@ void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder,
|
||||
MVKImageView* imageView = (MVKImageView*)imageInfo.imageView;
|
||||
MVKSampler* sampler = _immutableSamplers.empty() ? (MVKSampler*)imageInfo.sampler : _immutableSamplers[rezIdx];
|
||||
tb.mtlTexture = imageView->getMTLTexture();
|
||||
if (imageView) {
|
||||
tb.swizzle = imageView->getPackedSwizzle();
|
||||
} else {
|
||||
tb.swizzle = 0;
|
||||
}
|
||||
sb.mtlSamplerState = sampler->getMTLSamplerState();
|
||||
if (_applyToVertexStage) {
|
||||
tb.index = mtlIdxs.vertexStage.textureIndex + rezIdx;
|
||||
|
@ -261,6 +261,9 @@ public:
|
||||
/** Returns the Metal texture type of this image view. */
|
||||
inline MTLTextureType getMTLTextureType() { return _mtlTextureType; }
|
||||
|
||||
/** Returns the packed component swizzle of this image view. */
|
||||
inline uint32_t getPackedSwizzle() { return _packedSwizzle; }
|
||||
|
||||
/**
|
||||
* Populates the texture of the specified render pass descriptor
|
||||
* with the Metal texture underlying this image.
|
||||
@ -283,10 +286,10 @@ public:
|
||||
protected:
|
||||
id<MTLTexture> newMTLTexture();
|
||||
void initMTLTextureViewSupport();
|
||||
MTLPixelFormat getSwizzledMTLPixelFormat(VkFormat format, VkComponentMapping components);
|
||||
MTLPixelFormat getSwizzledMTLPixelFormat(VkFormat format, VkComponentMapping components, bool& useSwizzle);
|
||||
bool matchesSwizzle(VkComponentMapping components, VkComponentMapping pattern);
|
||||
const char* getSwizzleName(VkComponentSwizzle swizzle);
|
||||
void setSwizzleFormatError(VkFormat format, VkComponentMapping components);
|
||||
uint32_t packSwizzle(VkComponentMapping components);
|
||||
void validateImageViewConfig(const VkImageViewCreateInfo* pCreateInfo);
|
||||
|
||||
MVKImage* _image;
|
||||
@ -296,6 +299,7 @@ protected:
|
||||
std::mutex _lock;
|
||||
MTLPixelFormat _mtlPixelFormat;
|
||||
MTLTextureType _mtlTextureType;
|
||||
uint32_t _packedSwizzle;
|
||||
bool _useMTLTextureView;
|
||||
};
|
||||
|
||||
|
@ -702,10 +702,12 @@ MVKImageView::MVKImageView(MVKDevice* device, const VkImageViewCreateInfo* pCrea
|
||||
_subresourceRange.layerCount = _image->getLayerCount() - _subresourceRange.baseArrayLayer;
|
||||
}
|
||||
|
||||
bool useSwizzle;
|
||||
_mtlTexture = nil;
|
||||
_mtlPixelFormat = getSwizzledMTLPixelFormat(pCreateInfo->format, pCreateInfo->components);
|
||||
_mtlPixelFormat = getSwizzledMTLPixelFormat(pCreateInfo->format, pCreateInfo->components, useSwizzle);
|
||||
_mtlTextureType = mvkMTLTextureTypeFromVkImageViewType(pCreateInfo->viewType, (_image->getSampleCount() != VK_SAMPLE_COUNT_1_BIT));
|
||||
initMTLTextureViewSupport();
|
||||
_packedSwizzle = useSwizzle ? packSwizzle(pCreateInfo->components) : 0;
|
||||
}
|
||||
|
||||
// Validate whether the image view configuration can be supported
|
||||
@ -731,9 +733,10 @@ void MVKImageView::validateImageViewConfig(const VkImageViewCreateInfo* pCreateI
|
||||
// Metal does not support general per-texture swizzles, and so this function relies on a few coincidental
|
||||
// alignments of existing MTLPixelFormats of the same structure. If swizzling is not possible for a
|
||||
// particular combination of format and swizzle spec, the original MTLPixelFormat is returned.
|
||||
MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkComponentMapping components) {
|
||||
MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkComponentMapping components, bool& useSwizzle) {
|
||||
MTLPixelFormat mtlPF = mtlPixelFormatFromVkFormat(format);
|
||||
|
||||
useSwizzle = false;
|
||||
switch (mtlPF) {
|
||||
case MTLPixelFormatR8Unorm:
|
||||
if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_R} ) ) {
|
||||
@ -741,16 +744,6 @@ MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkCompon
|
||||
}
|
||||
break;
|
||||
|
||||
case MTLPixelFormatR8Snorm:
|
||||
#if MVK_IOS
|
||||
case MTLPixelFormatR8Unorm_sRGB:
|
||||
#endif
|
||||
if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_R} ) ) {
|
||||
setSwizzleFormatError(format, components);
|
||||
return MTLPixelFormatA8Unorm;
|
||||
}
|
||||
break;
|
||||
|
||||
case MTLPixelFormatA8Unorm:
|
||||
if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_A, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_ZERO} ) ) {
|
||||
return MTLPixelFormatR8Unorm;
|
||||
@ -769,13 +762,6 @@ MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkCompon
|
||||
}
|
||||
break;
|
||||
|
||||
case MTLPixelFormatRGBA8Snorm:
|
||||
if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A} ) ) {
|
||||
setSwizzleFormatError(format, components);
|
||||
return MTLPixelFormatBGRA8Unorm;
|
||||
}
|
||||
break;
|
||||
|
||||
case MTLPixelFormatBGRA8Unorm:
|
||||
if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A} ) ) {
|
||||
return MTLPixelFormatRGBA8Unorm;
|
||||
@ -789,16 +775,20 @@ MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkCompon
|
||||
break;
|
||||
|
||||
case MTLPixelFormatDepth32Float_Stencil8:
|
||||
if (_subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT &&
|
||||
matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM} ) ) {
|
||||
if (_subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {
|
||||
if (!matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM} ) ) {
|
||||
useSwizzle = true;
|
||||
}
|
||||
return MTLPixelFormatX32_Stencil8;
|
||||
}
|
||||
break;
|
||||
|
||||
#if MVK_MACOS
|
||||
case MTLPixelFormatDepth24Unorm_Stencil8:
|
||||
if (_subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT &&
|
||||
matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM} ) ) {
|
||||
if (_subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {
|
||||
if (!matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM} ) ) {
|
||||
useSwizzle = true;
|
||||
}
|
||||
return MTLPixelFormatX24_Stencil8;
|
||||
}
|
||||
break;
|
||||
@ -809,7 +799,7 @@ MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkCompon
|
||||
}
|
||||
|
||||
if ( !matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A} ) ) {
|
||||
setSwizzleFormatError(format, components);
|
||||
useSwizzle = true;
|
||||
}
|
||||
return mtlPF;
|
||||
}
|
||||
@ -827,17 +817,6 @@ const char* MVKImageView::getSwizzleName(VkComponentSwizzle swizzle) {
|
||||
}
|
||||
}
|
||||
|
||||
// Sets a standard swizzle format error during instance construction.
|
||||
void MVKImageView::setSwizzleFormatError(VkFormat format, VkComponentMapping components) {
|
||||
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FORMAT_NOT_SUPPORTED,
|
||||
"VkImageView format %s and swizzle (%s, %s, %s, %s) does not map to a valid MTLPixelFormat.\n",
|
||||
mvkVkFormatName(format),
|
||||
getSwizzleName(components.r),
|
||||
getSwizzleName(components.g),
|
||||
getSwizzleName(components.b),
|
||||
getSwizzleName(components.a)));
|
||||
}
|
||||
|
||||
// Returns whether the swizzle components of the internal VkComponentMapping matches the
|
||||
// swizzle pattern, by comparing corresponding elements of the two structures. The pattern
|
||||
// supports wildcards, in that any element of pattern can be set to VK_COMPONENT_SWIZZLE_MAX_ENUM
|
||||
@ -855,6 +834,12 @@ bool MVKImageView::matchesSwizzle(VkComponentMapping components, VkComponentMapp
|
||||
return true;
|
||||
}
|
||||
|
||||
// Packs a VkComponentMapping structure into a single 32-bit word.
|
||||
uint32_t MVKImageView::packSwizzle(VkComponentMapping components) {
|
||||
return ((components.r & 0xFF) << 0) | ((components.g & 0xFF) << 8) |
|
||||
((components.b & 0xFF) << 16) | ((components.a & 0xFF) << 24);
|
||||
}
|
||||
|
||||
// Determine whether this image view should use a Metal texture view,
|
||||
// and set the _useMTLTextureView variable appropriately.
|
||||
void MVKImageView::initMTLTextureViewSupport() {
|
||||
|
@ -33,6 +33,16 @@ class MVKCommandEncoder;
|
||||
class MVKPipelineCache;
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKShaderAuxBufferBinding
|
||||
|
||||
struct MVKShaderAuxBufferBinding {
|
||||
uint32_t vertex;
|
||||
uint32_t fragment;
|
||||
uint32_t compute;
|
||||
};
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MVKPipelineLayout
|
||||
|
||||
@ -61,6 +71,12 @@ public:
|
||||
uint32_t set,
|
||||
const void* pData);
|
||||
|
||||
/** Returns the current auxiliary buffer bindings. */
|
||||
const MVKShaderAuxBufferBinding& getAuxBufferIndex() { return _auxBufferIndex; }
|
||||
|
||||
/** Returns the number of textures in this layout. This is used to calculate the size of the auxiliary buffer. */
|
||||
uint32_t getNumTextures() { return _numTextures; }
|
||||
|
||||
/** Constructs an instance for the specified device. */
|
||||
MVKPipelineLayout(MVKDevice* device, const VkPipelineLayoutCreateInfo* pCreateInfo);
|
||||
|
||||
@ -69,6 +85,8 @@ private:
|
||||
std::vector<MVKShaderResourceBinding> _dslMTLResourceIndexOffsets;
|
||||
std::vector<VkPushConstantRange> _pushConstants;
|
||||
MVKShaderResourceBinding _pushConstantsMTLResourceIndexOffsets;
|
||||
MVKShaderAuxBufferBinding _auxBufferIndex;
|
||||
uint32_t _numTextures;
|
||||
};
|
||||
|
||||
|
||||
@ -83,12 +101,17 @@ public:
|
||||
/** Binds this pipeline to the specified command encoder. */
|
||||
virtual void encode(MVKCommandEncoder* cmdEncoder) = 0;
|
||||
|
||||
/** Returns the current auxiliary buffer bindings. */
|
||||
const MVKShaderAuxBufferBinding& getAuxBufferIndex() { return _auxBufferIndex; }
|
||||
|
||||
/** Constructs an instance for the device. layout, and parent (which may be NULL). */
|
||||
MVKPipeline(MVKDevice* device, MVKPipelineCache* pipelineCache, MVKPipeline* parent) : MVKBaseDeviceObject(device),
|
||||
_pipelineCache(pipelineCache) {}
|
||||
|
||||
protected:
|
||||
MVKPipelineCache* _pipelineCache;
|
||||
MVKShaderAuxBufferBinding _auxBufferIndex;
|
||||
id<MTLBuffer> _auxBuffer = nil;
|
||||
|
||||
};
|
||||
|
||||
@ -107,6 +130,12 @@ public:
|
||||
/** Returns whether this pipeline permits dynamic setting of the specifie state. */
|
||||
bool supportsDynamicState(VkDynamicState state);
|
||||
|
||||
/** Returns whether or not the vertex shader needs a buffer to hold auxiliary state. */
|
||||
bool needsVertexAuxBuffer() { return _needsVertexAuxBuffer; }
|
||||
|
||||
/** Returns whether or not the fragment shader needs a buffer to hold auxiliary state. */
|
||||
bool needsFragmentAuxBuffer() { return _needsFragmentAuxBuffer; }
|
||||
|
||||
/** Constructs an instance for the device and parent (which may be NULL). */
|
||||
MVKGraphicsPipeline(MVKDevice* device,
|
||||
MVKPipelineCache* pipelineCache,
|
||||
@ -138,6 +167,8 @@ protected:
|
||||
|
||||
bool _dynamicStateEnabled[VK_DYNAMIC_STATE_RANGE_SIZE];
|
||||
bool _hasDepthStencilInfo;
|
||||
bool _needsVertexAuxBuffer;
|
||||
bool _needsFragmentAuxBuffer;
|
||||
};
|
||||
|
||||
|
||||
@ -152,6 +183,9 @@ public:
|
||||
/** Binds this pipeline to the specified command encoder. */
|
||||
void encode(MVKCommandEncoder* cmdEncoder) override;
|
||||
|
||||
/** Returns whether or not the compute shader needs a buffer to hold auxiliary state. */
|
||||
bool needsAuxBuffer() { return _needsAuxBuffer; }
|
||||
|
||||
/** Constructs an instance for the device and parent (which may be NULL). */
|
||||
MVKComputePipeline(MVKDevice* device,
|
||||
MVKPipelineCache* pipelineCache,
|
||||
@ -165,6 +199,7 @@ protected:
|
||||
|
||||
id<MTLComputePipelineState> _mtlPipelineState;
|
||||
MTLSize _mtlThreadgroupSize;
|
||||
bool _needsAuxBuffer;
|
||||
};
|
||||
|
||||
|
||||
|
@ -107,6 +107,46 @@ void MVKPipelineLayout::populateShaderConverterContext(SPIRVToMSLConverterContex
|
||||
spv::ExecutionModelGLCompute,
|
||||
kPushConstDescSet,
|
||||
kPushConstBinding);
|
||||
|
||||
// Scan the resource bindings, looking for an unused buffer index.
|
||||
// FIXME: If we ever encounter a device that supports more than 31 buffer
|
||||
// bindings, we'll need to update this code.
|
||||
unordered_map<uint32_t, uint32_t> freeBufferMasks;
|
||||
freeBufferMasks[spv::ExecutionModelVertex] = freeBufferMasks[spv::ExecutionModelFragment] = freeBufferMasks[spv::ExecutionModelGLCompute] = (1 << _device->_pMetalFeatures->maxPerStageBufferCount) - 1;
|
||||
_numTextures = 0;
|
||||
for (auto& binding : context.resourceBindings) {
|
||||
if (binding.descriptorSet == kPushConstDescSet && binding.binding == kPushConstBinding) {
|
||||
// This is the special push constant buffer.
|
||||
freeBufferMasks[binding.stage] &= ~(1 << binding.mslBuffer);
|
||||
continue;
|
||||
}
|
||||
VkDescriptorType descriptorType = _descriptorSetLayouts[binding.descriptorSet]._bindings[binding.binding]._info.descriptorType;
|
||||
switch (descriptorType) {
|
||||
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:
|
||||
// This buffer is being used.
|
||||
freeBufferMasks[binding.stage] &= ~(1 << binding.mslBuffer);
|
||||
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:
|
||||
// If it results in a texture binding, we need to account for it so
|
||||
// we know how big to make the auxiliary buffer.
|
||||
if (binding.mslTexture + 1 > _numTextures)
|
||||
_numTextures = binding.mslTexture + 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Pick the lowest index that isn't used.
|
||||
_auxBufferIndex.vertex = ffs(freeBufferMasks[spv::ExecutionModelVertex]) - 1;
|
||||
_auxBufferIndex.fragment = ffs(freeBufferMasks[spv::ExecutionModelFragment]) - 1;
|
||||
_auxBufferIndex.compute = ffs(freeBufferMasks[spv::ExecutionModelGLCompute]) - 1;
|
||||
}
|
||||
|
||||
MVKPipelineLayout::MVKPipelineLayout(MVKDevice* device,
|
||||
@ -174,6 +214,8 @@ void MVKGraphicsPipeline::encode(MVKCommandEncoder* cmdEncoder) {
|
||||
if (_device->_pFeatures->depthClamp) {
|
||||
[mtlCmdEnc setDepthClipMode: _mtlDepthClipMode];
|
||||
}
|
||||
|
||||
cmdEncoder->_graphicsResourcesState.bindAuxBuffer(_auxBuffer, _auxBufferIndex, _needsVertexAuxBuffer, _needsFragmentAuxBuffer);
|
||||
}
|
||||
|
||||
bool MVKGraphicsPipeline::supportsDynamicState(VkDynamicState state) {
|
||||
@ -295,6 +337,9 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor
|
||||
|
||||
SPIRVToMSLConverterContext shaderContext;
|
||||
initMVKShaderConverterContext(shaderContext, pCreateInfo);
|
||||
auto* mvkLayout = (MVKPipelineLayout*)pCreateInfo->layout;
|
||||
_auxBufferIndex = mvkLayout->getAuxBufferIndex();
|
||||
uint32_t auxBufferSize = sizeof(uint32_t) * mvkLayout->getNumTextures();
|
||||
|
||||
// Retrieve the render subpass for which this pipeline is being constructed
|
||||
MVKRenderPass* mvkRendPass = (MVKRenderPass*)pCreateInfo->renderPass;
|
||||
@ -302,6 +347,8 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor
|
||||
|
||||
MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease];
|
||||
|
||||
uint32_t vbCnt = pCreateInfo->pVertexInputState->vertexBindingDescriptionCount;
|
||||
|
||||
// Add shader stages
|
||||
for (uint32_t i = 0; i < pCreateInfo->stageCount; i++) {
|
||||
const VkPipelineShaderStageCreateInfo* pSS = &pCreateInfo->pStages[i];
|
||||
@ -312,6 +359,7 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor
|
||||
// Vertex shader
|
||||
if (mvkAreFlagsEnabled(pSS->stage, VK_SHADER_STAGE_VERTEX_BIT)) {
|
||||
shaderContext.options.entryPointStage = spv::ExecutionModelVertex;
|
||||
shaderContext.options.auxBufferIndex = _auxBufferIndex.vertex;
|
||||
id<MTLFunction> mtlFunction = mvkShdrMod->getMTLFunction(&shaderContext, pSS->pSpecializationInfo, _pipelineCache).mtlFunction;
|
||||
if ( !mtlFunction ) {
|
||||
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Vertex shader function could not be compiled into pipeline. See previous error."));
|
||||
@ -319,19 +367,36 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor
|
||||
}
|
||||
plDesc.vertexFunction = mtlFunction;
|
||||
plDesc.rasterizationEnabled = !shaderContext.options.isRasterizationDisabled;
|
||||
_needsVertexAuxBuffer = shaderContext.options.needsAuxBuffer;
|
||||
// If we need the auxiliary buffer and there's no place to put it,
|
||||
// we're in serious trouble.
|
||||
if (_needsVertexAuxBuffer && (_auxBufferIndex.vertex == ~0u || _auxBufferIndex.vertex >= _device->_pMetalFeatures->maxPerStageBufferCount - vbCnt) ) {
|
||||
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Vertex shader requires auxiliary buffer, but there is no free slot to pass it."));
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Fragment shader
|
||||
if (mvkAreFlagsEnabled(pSS->stage, VK_SHADER_STAGE_FRAGMENT_BIT)) {
|
||||
shaderContext.options.entryPointStage = spv::ExecutionModelFragment;
|
||||
shaderContext.options.auxBufferIndex = _auxBufferIndex.fragment;
|
||||
id<MTLFunction> mtlFunction = mvkShdrMod->getMTLFunction(&shaderContext, pSS->pSpecializationInfo, _pipelineCache).mtlFunction;
|
||||
if ( !mtlFunction ) {
|
||||
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Fragment shader function could not be compiled into pipeline. See previous error."));
|
||||
}
|
||||
plDesc.fragmentFunction = mtlFunction;
|
||||
_needsFragmentAuxBuffer = shaderContext.options.needsAuxBuffer;
|
||||
if (_needsFragmentAuxBuffer && _auxBufferIndex.fragment == ~0u) {
|
||||
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Fragment shader requires auxiliary buffer, but there is no free slot to pass it."));
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_needsVertexAuxBuffer || _needsFragmentAuxBuffer) {
|
||||
_auxBuffer = [_device->getMTLDevice() newBufferWithLength: auxBufferSize options: MTLResourceStorageModeShared];
|
||||
}
|
||||
|
||||
// Vertex attributes
|
||||
uint32_t vaCnt = pCreateInfo->pVertexInputState->vertexAttributeDescriptionCount;
|
||||
for (uint32_t i = 0; i < vaCnt; i++) {
|
||||
@ -345,7 +410,6 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor
|
||||
}
|
||||
|
||||
// Vertex buffer bindings
|
||||
uint32_t vbCnt = pCreateInfo->pVertexInputState->vertexBindingDescriptionCount;
|
||||
for (uint32_t i = 0; i < vbCnt; i++) {
|
||||
const VkVertexInputBindingDescription* pVKVB = &pCreateInfo->pVertexInputState->pVertexBindingDescriptions[i];
|
||||
uint32_t vbIdx = _device->getMetalBufferIndexForVertexAttributeBinding(pVKVB->binding);
|
||||
@ -481,6 +545,7 @@ MVKGraphicsPipeline::~MVKGraphicsPipeline() {
|
||||
void MVKComputePipeline::encode(MVKCommandEncoder* cmdEncoder) {
|
||||
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setComputePipelineState: _mtlPipelineState];
|
||||
cmdEncoder->_mtlThreadgroupSize = _mtlThreadgroupSize;
|
||||
cmdEncoder->_computeResourcesState.bindAuxBuffer(_auxBuffer, _auxBufferIndex);
|
||||
}
|
||||
|
||||
MVKComputePipeline::MVKComputePipeline(MVKDevice* device,
|
||||
@ -499,6 +564,10 @@ MVKComputePipeline::MVKComputePipeline(MVKDevice* device,
|
||||
} else {
|
||||
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Compute shader function could not be compiled into pipeline. See previous error."));
|
||||
}
|
||||
|
||||
if (_needsAuxBuffer && _auxBufferIndex.compute == ~0u) {
|
||||
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Compute shader requires auxiliary buffer, but there is no free slot to pass it."));
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a MTLFunction to use when creating the MTLComputePipelineState.
|
||||
@ -514,9 +583,17 @@ MVKMTLFunction MVKComputePipeline::getMTLFunction(const VkComputePipelineCreateI
|
||||
|
||||
MVKPipelineLayout* layout = (MVKPipelineLayout*)pCreateInfo->layout;
|
||||
layout->populateShaderConverterContext(shaderContext);
|
||||
_auxBufferIndex = layout->getAuxBufferIndex();
|
||||
uint32_t auxBufferSize = sizeof(uint32_t) * layout->getNumTextures();
|
||||
shaderContext.options.auxBufferIndex = _auxBufferIndex.compute;
|
||||
|
||||
MVKShaderModule* mvkShdrMod = (MVKShaderModule*)pSS->module;
|
||||
return mvkShdrMod->getMTLFunction(&shaderContext, pSS->pSpecializationInfo, _pipelineCache);
|
||||
auto func = mvkShdrMod->getMTLFunction(&shaderContext, pSS->pSpecializationInfo, _pipelineCache);
|
||||
_needsAuxBuffer = shaderContext.options.needsAuxBuffer;
|
||||
if (_needsAuxBuffer) {
|
||||
_auxBuffer = [_device->getMTLDevice() newBufferWithLength: auxBufferSize options: MTLResourceStorageModeShared];
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
|
||||
|
@ -201,9 +201,11 @@ MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverter::convert(SPIRVToMSLConverterContext&
|
||||
|
||||
mslOpts.msl_version = context.options.mslVersion;
|
||||
mslOpts.texel_buffer_texture_width = context.options.texelBufferTextureWidth;
|
||||
mslOpts.aux_buffer_index = context.options.auxBufferIndex;
|
||||
mslOpts.enable_point_size_builtin = context.options.isRenderingPoints;
|
||||
mslOpts.disable_rasterization = context.options.isRasterizationDisabled;
|
||||
mslOpts.resolve_specialized_array_lengths = true;
|
||||
mslOpts.swizzle_texture_samples = true;
|
||||
pMSLCompiler->set_msl_options(mslOpts);
|
||||
|
||||
auto scOpts = pMSLCompiler->get_common_options();
|
||||
@ -229,6 +231,7 @@ MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverter::convert(SPIRVToMSLConverterContext&
|
||||
// Populate content extracted from the SPRI-V compiler.
|
||||
populateEntryPoint(_entryPoint, pMSLCompiler, context.options);
|
||||
context.options.isRasterizationDisabled = pMSLCompiler && pMSLCompiler->get_is_rasterization_disabled();
|
||||
context.options.needsAuxBuffer = pMSLCompiler && pMSLCompiler->needs_aux_buffer();
|
||||
delete pMSLCompiler;
|
||||
|
||||
// Copy whether the vertex attributes and resource bindings are used by the shader
|
||||
|
@ -37,10 +37,12 @@ namespace mvk {
|
||||
|
||||
uint32_t mslVersion = makeMSLVersion(2);
|
||||
uint32_t texelBufferTextureWidth = 4096;
|
||||
uint32_t auxBufferIndex = 0;
|
||||
bool shouldFlipVertexY = true;
|
||||
bool isRenderingPoints = false;
|
||||
|
||||
bool isRasterizationDisabled = false;
|
||||
bool needsAuxBuffer = false;
|
||||
|
||||
/**
|
||||
* Returns whether the specified options match this one.
|
||||
|
Loading…
x
Reference in New Issue
Block a user