Merge pull request #303 from cdavis5e/component-swizzle

Support arbitrary swizzles of image data.
This commit is contained in:
Bill Hollings 2018-10-16 14:55:21 -04:00 committed by GitHub
commit 91c1947889
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 256 additions and 41 deletions

View File

@ -1 +1 @@
c9210427b9ab547d41f1af804dedae581b382965
cc5c0204d8bcdadbb4add03e53346df98bf27fa4

View File

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

View File

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

View File

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

View File

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

View File

@ -100,6 +100,7 @@ public:
protected:
friend class MVKDescriptorBinding;
friend class MVKPipelineLayout;
VkResult initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes,
MVKShaderStageResourceBinding* pDescSetCounts,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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