Merge pull request #603 from billhollings/master

Fix crash with multisample layered rendering on older macOS devices.
This commit is contained in:
Bill Hollings 2019-05-15 13:08:26 -04:00 committed by GitHub
commit e2f7654d1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 143 additions and 96 deletions

View File

@ -23,16 +23,18 @@ Released TBD
- Change log indication of error in logs from `[***MoltenVK ERROR***]` to
`[mvk-error]`, for consistency with other log level indications.
- Tessellation fixes:
- Don't use setVertexBytes() for passing tessellation vertex counts.
- Don't use `setVertexBytes()` for passing tessellation vertex counts.
- Fix intermediate Metal renderpasses load and store actions maintaining
attachments appropriately.
- Use empty depth state for tessellation vertex pre-pass.
- Fix tessellated indirect draws using wrong kernels to map parameters.
- Work around potential Metal bug with stage-in indirect buffers.
- Fix zero local threadgroup size in indirect tessellated rendering.
- Fix zero local threadgroup size in indirect tessellated rendering.
- Fix crash with multisample layered rendering on older macOS devices.
- `MoltenVKShaderConverter` tool: Add MSL version and platform command-line options.
- Allow building external dependency libraries in Debug mode.
- Enable AMD and NV GLSL extensions when building glslang for MoltenVKGLSLToSPIRVConverter.
- Allow building external dependency libraries in `Debug` mode.
- Enable AMD and NV GLSL extensions when building `glslang` for `MoltenVKGLSLToSPIRVConverter`.
- Update `VK_MVK_MOLTENVK_SPEC_VERSION` to 20.
- Update to latest SPIRV-Cross version:
- MSL: Only use constant address space for tessellation control shader.
- MSL: Support native texture_buffer type, throw error on atomics.

View File

@ -526,6 +526,7 @@ typedef struct {
VkBool32 depthSampleCompare; /**< If true, depth texture samplers support the comparison of the pixel value against a reference value. */
VkBool32 events; /**< If true, Metal synchronization events are supported. */
VkBool32 memoryBarriers; /**< If true, full memory barriers within Metal render passes are supported. */
VkBool32 multisampleLayeredRendering; /**< If true, layered rendering to multiple multi-sampled cube or texture array layers is supported. */
} MVKPhysicalDeviceMetalFeatures;
/**

View File

@ -181,7 +181,7 @@ id<MTLFunction> MVKCommandResourceFactory::getBlitFragFunction(MVKRPSKeyBlitImg&
id<MTLFunction> MVKCommandResourceFactory::getClearVertFunction(MVKRPSKeyClearAtt& attKey) {
id<MTLFunction> mtlFunc = nil;
bool allowLayers = _device->_pMetalFeatures->layeredRendering && (attKey.mtlSampleCount == 1 || _device->_pMetalFeatures->multisampleArrayTextures);
bool allowLayers = _device->_pMetalFeatures->layeredRendering && (attKey.mtlSampleCount == 1 || _device->_pMetalFeatures->multisampleLayeredRendering);
@autoreleasepool {
NSMutableString* msl = [NSMutableString stringWithCapacity: (2 * KIBI) ];
[msl appendLineMVK: @"#include <metal_stdlib>"];

View File

@ -797,6 +797,10 @@ void MVKPhysicalDevice::initMetalFeatures() {
_metalFeatures.memoryBarriers = true;
}
if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily2_v1] ) {
_metalFeatures.multisampleLayeredRendering = _metalFeatures.layeredRendering;
}
#endif
if ( [_mtlDevice respondsToSelector: @selector(maxBufferLength)] ) {

View File

@ -28,7 +28,7 @@ MVKFramebuffer::MVKFramebuffer(MVKDevice* device,
_extent = { .width = pCreateInfo->width, .height = pCreateInfo->height };
_layerCount = pCreateInfo->layers;
// Add clear values
// Add attachments
_attachments.reserve(pCreateInfo->attachmentCount);
for (uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) {
_attachments.push_back((MVKImageView*)pCreateInfo->pAttachments[i]);

View File

@ -213,6 +213,9 @@ protected:
using MVKResource::needsHostReadSync;
MVKImageSubresource* getSubresource(uint32_t mipLevel, uint32_t arrayLayer);
void validateConfig(const VkImageCreateInfo* pCreateInfo);
VkSampleCountFlagBits validateSamples(const VkImageCreateInfo* pCreateInfo);
uint32_t validateMipLevels(const VkImageCreateInfo* pCreateInfo);
bool validateLinear(const VkImageCreateInfo* pCreateInfo);
bool validateUseTexelBuffer();
void initSubresources(const VkImageCreateInfo* pCreateInfo);

View File

@ -551,74 +551,36 @@ void MVKImage::getMTLTextureContent(MVKImageSubresource& subresource,
MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MVKResource(device) {
if (pCreateInfo->flags & VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Metal does not allow uncompressed views of compressed images."));
}
_mtlTexture = nil;
_ioSurface = nil;
_usesTexelBuffer = false;
#if MVK_IOS
if ( (pCreateInfo->imageType != VK_IMAGE_TYPE_2D) && (mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatCompressed) ) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, compressed formats may only be used with 2D images."));
}
#else
if ( (pCreateInfo->imageType != VK_IMAGE_TYPE_2D) && (mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatCompressed) && !mvkCanDecodeFormat(pCreateInfo->format) ) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, compressed formats may only be used with 2D images."));
}
#endif
if ( (pCreateInfo->imageType != VK_IMAGE_TYPE_2D) && (mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatDepthStencil) ) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, depth/stencil formats may only be used with 2D images."));
}
// Adjust the info components to be compatible with Metal, then use the modified versions
// to set other config info. Vulkan allows unused extent dimensions to be zero, but Metal
// requires minimum of one. Adjust samples and miplevels for the right texture type.
// Adjust the info components to be compatible with Metal, then use the modified versions to set other
// config info. Vulkan allows unused extent dimensions to be zero, but Metal requires minimum of one.
uint32_t minDim = 1;
_usage = pCreateInfo->usage;
_extent.width = max(pCreateInfo->extent.width, minDim);
_extent.height = max(pCreateInfo->extent.height, minDim);
_extent.depth = max(pCreateInfo->extent.depth, minDim);
_arrayLayers = max(pCreateInfo->arrayLayers, minDim);
_mipLevels = max(pCreateInfo->mipLevels, minDim);
if ( (_mipLevels > 1) && (pCreateInfo->imageType == VK_IMAGE_TYPE_1D) ) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, 1D images cannot use mipmaps. Setting mip levels to 1."));
_mipLevels = 1;
}
// Perform validation and adjustments before configuring other settings
validateConfig(pCreateInfo);
_samples = validateSamples(pCreateInfo);
_mipLevels = validateMipLevels(pCreateInfo);
_isLinear = validateLinear(pCreateInfo);
_mtlTexture = nil;
_ioSurface = nil;
_mtlPixelFormat = getMTLPixelFormatFromVkFormat(pCreateInfo->format);
_mtlTextureType = mvkMTLTextureTypeFromVkImageType(pCreateInfo->imageType,
_arrayLayers,
(pCreateInfo->samples > 1));
_samples = pCreateInfo->samples;
if ( (_samples > 1) && (_mtlTextureType != MTLTextureType2DMultisample) &&
(pCreateInfo->imageType != VK_IMAGE_TYPE_2D || !_device->_pMetalFeatures->multisampleArrayTextures) ) {
if (pCreateInfo->imageType != VK_IMAGE_TYPE_2D) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, multisampling can only be used with a 2D image type. Setting sample count to 1."));
} else {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : This version of Metal does not support multisampled array textures. Setting sample count to 1."));
}
_samples = VK_SAMPLE_COUNT_1_BIT;
if (pCreateInfo->imageType == VK_IMAGE_TYPE_2D) {
_mtlTextureType = MTLTextureType2DArray;
}
}
if ( (_samples > 1) && (mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatCompressed) ) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, multisampling cannot be used with compressed images. Setting sample count to 1."));
_samples = VK_SAMPLE_COUNT_1_BIT;
}
_mtlTextureType = mvkMTLTextureTypeFromVkImageType(pCreateInfo->imageType, _arrayLayers, _samples > VK_SAMPLE_COUNT_1_BIT);
_usage = pCreateInfo->usage;
_is3DCompressed = (pCreateInfo->imageType == VK_IMAGE_TYPE_3D) && (mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatCompressed);
_isDepthStencilAttachment = (mvkAreFlagsEnabled(pCreateInfo->usage, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ||
mvkAreFlagsEnabled(mvkVkFormatProperties(pCreateInfo->format).optimalTilingFeatures, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT));
_canSupportMTLTextureView = !_isDepthStencilAttachment || _device->_pMetalFeatures->stencilViews;
_hasExpectedTexelSize = (mvkMTLPixelFormatBytesPerBlock(_mtlPixelFormat) == mvkVkFormatBytesPerBlock(pCreateInfo->format));
_isLinear = validateLinear(pCreateInfo);
_usesTexelBuffer = false;
_is3DCompressed = _mtlTextureType == MTLTextureType3D && mvkFormatTypeFromMTLPixelFormat(_mtlPixelFormat) == kMVKFormatCompressed;
// Calc _byteCount after _byteAlignment
_byteAlignment = _isLinear ? _device->getVkFormatTexelBufferAlignment(pCreateInfo->format) : mvkEnsurePowerOfTwo(mvkVkFormatBytesPerBlock(pCreateInfo->format));
// Calc _byteCount after _mtlTexture & _byteAlignment
for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) {
_byteCount += getBytesPerLayer(mipLvl) * _extent.depth * _arrayLayers;
}
@ -626,42 +588,116 @@ MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MV
initSubresources(pCreateInfo);
}
bool MVKImage::validateLinear(const VkImageCreateInfo* pCreateInfo) {
if (pCreateInfo->tiling != VK_IMAGE_TILING_LINEAR) { return false; }
void MVKImage::validateConfig(const VkImageCreateInfo* pCreateInfo) {
if (pCreateInfo->imageType != VK_IMAGE_TYPE_2D) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, imageType must be VK_IMAGE_TYPE_2D."));
return false;
bool is2D = pCreateInfo->imageType == VK_IMAGE_TYPE_2D;
bool isCompressed = mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatCompressed;
#if MVK_IOS
if (isCompressed && !is2D) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, compressed formats may only be used with 2D images."));
}
if (_isDepthStencilAttachment) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, format must not be a depth/stencil format."));
return false;
}
if (_mipLevels > 1) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, mipLevels must be 1."));
return false;
}
if (_arrayLayers > 1) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, arrayLayers must be 1."));
return false;
}
if (_samples > 1) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, samples must be VK_SAMPLE_COUNT_1_BIT."));
return false;
}
#endif
#if MVK_MACOS
if ( mvkIsAnyFlagEnabled(_usage, (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) ) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, usage must not include VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, and/or VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT."));
return false;
if (isCompressed && !is2D && !mvkCanDecodeFormat(pCreateInfo->format)) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, the %s compressed format may only be used with 2D images.", mvkVkFormatName(pCreateInfo->format)));
}
#endif
return true;
if ((mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatDepthStencil) && !is2D ) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, depth/stencil formats may only be used with 2D images."));
}
if (mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT)) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Metal does not allow uncompressed views of compressed images."));
}
}
VkSampleCountFlagBits MVKImage::validateSamples(const VkImageCreateInfo* pCreateInfo) {
VkSampleCountFlagBits validSamples = pCreateInfo->samples;
if (validSamples == VK_SAMPLE_COUNT_1_BIT) { return validSamples; }
if (pCreateInfo->imageType != VK_IMAGE_TYPE_2D) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, multisampling can only be used with a 2D image type. Setting sample count to 1."));
validSamples = VK_SAMPLE_COUNT_1_BIT;
}
if (mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatCompressed) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, multisampling cannot be used with compressed images. Setting sample count to 1."));
validSamples = VK_SAMPLE_COUNT_1_BIT;
}
if (pCreateInfo->arrayLayers > 1) {
if ( !_device->_pMetalFeatures->multisampleArrayTextures ) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : This device does not support multisampled array textures. Setting sample count to 1."));
validSamples = VK_SAMPLE_COUNT_1_BIT;
}
if ( !_device->_pMetalFeatures->multisampleLayeredRendering && mvkIsAnyFlagEnabled(pCreateInfo->usage, (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT))) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : This device does not support rendering to multisampled array (layered) attachments. Setting sample count to 1."));
validSamples = VK_SAMPLE_COUNT_1_BIT;
}
}
return validSamples;
}
uint32_t MVKImage::validateMipLevels(const VkImageCreateInfo* pCreateInfo) {
uint32_t minDim = 1;
uint32_t validMipLevels = max(pCreateInfo->mipLevels, minDim);
if (validMipLevels == 1) { return validMipLevels; }
if (pCreateInfo->imageType == VK_IMAGE_TYPE_1D) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, 1D images cannot use mipmaps. Setting mip levels to 1."));
validMipLevels = 1;
}
return validMipLevels;
}
bool MVKImage::validateLinear(const VkImageCreateInfo* pCreateInfo) {
if (pCreateInfo->tiling != VK_IMAGE_TILING_LINEAR ) { return false; }
bool isLin = true;
if (pCreateInfo->imageType != VK_IMAGE_TYPE_2D) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, imageType must be VK_IMAGE_TYPE_2D."));
isLin = false;
}
if (mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatDepthStencil) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, format must not be a depth/stencil format."));
isLin = false;
}
if (pCreateInfo->mipLevels > 1) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, mipLevels must be 1."));
isLin = false;
}
if (pCreateInfo->arrayLayers > 1) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, arrayLayers must be 1."));
isLin = false;
}
if (pCreateInfo->samples > VK_SAMPLE_COUNT_1_BIT) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, samples must be VK_SAMPLE_COUNT_1_BIT."));
isLin = false;
}
#if MVK_MACOS
if ( mvkIsAnyFlagEnabled(pCreateInfo->usage, (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) ) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, usage must not include VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, or VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT."));
isLin = false;
}
#endif
return isLin;
}
@ -767,8 +803,9 @@ id<MTLTexture> MVKImageView::newMTLTexture() {
MTLTextureType mtlTextureType = _mtlTextureType;
// Fake support for 2D views of 3D textures.
if (_image->getImageType() == VK_IMAGE_TYPE_3D &&
(mtlTextureType == MTLTextureType2D || mtlTextureType == MTLTextureType2DArray))
(mtlTextureType == MTLTextureType2D || mtlTextureType == MTLTextureType2DArray)) {
mtlTextureType = MTLTextureType3D;
}
return [_image->getMTLTexture() newTextureViewWithPixelFormat: _mtlPixelFormat
textureType: mtlTextureType
levels: NSMakeRange(_subresourceRange.baseMipLevel, _subresourceRange.levelCount)

View File

@ -798,7 +798,7 @@ MVK_PUBLIC_SYMBOL MTLTextureType mvkMTLTextureTypeFromVkImageType(VkImageType vk
case VK_IMAGE_TYPE_3D: return MTLTextureType3D;
case VK_IMAGE_TYPE_2D:
default: {
#if !MVK_IOS // This is marked unavailable on iOS :/
#if MVK_MACOS
if (arraySize > 1 && isMultisample) { return MTLTextureType2DMultisampleArray; }
#endif
if (arraySize > 1) { return MTLTextureType2DArray; }