From f459bf34e3aa8830a4d5b57b54defbf69833e0f3 Mon Sep 17 00:00:00 2001 From: Bill Hollings Date: Sat, 8 Dec 2018 14:09:53 -0500 Subject: [PATCH] Retrieve linear image memory alignment requirements from Metal device. Add mvkMTLPixelFormatLinearTextureAlignment() function. Add MVKDevice::getVkFormatTexelBufferAlignment() and use for Linear image memory alignment. For non-linear image memory alignment, use mvkVkFormatBytesPerBlock(). Rename MVKDevice::mtlPixelFormatFromVkFormat() to getMTLPixelFormatFromVkFormat(). --- MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm | 4 ++-- MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm | 5 ++-- MoltenVK/MoltenVK/GPUObjects/MVKDevice.h | 11 +++++---- MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 7 +++++- MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 8 +++---- MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm | 4 ++-- MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm | 2 +- MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm | 2 +- MoltenVK/MoltenVK/OS/MVKOSExtensions.h | 23 ++++++++++++------- MoltenVK/MoltenVK/OS/MVKOSExtensions.mm | 9 +++++++- 10 files changed, 49 insertions(+), 26 deletions(-) diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm index 723cfd01..18728923 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm @@ -839,13 +839,13 @@ void MVKCmdClearAttachments::encode(MVKCommandEncoder* cmdEncoder) { uint32_t caCnt = subpass->getColorAttachmentCount(); for (uint32_t caIdx = 0; caIdx < caCnt; caIdx++) { VkFormat vkAttFmt = subpass->getColorAttachmentFormat(caIdx); - _rpsKey.attachmentMTLPixelFormats[caIdx] = cmdPool->mtlPixelFormatFromVkFormat(vkAttFmt); + _rpsKey.attachmentMTLPixelFormats[caIdx] = cmdPool->getMTLPixelFormatFromVkFormat(vkAttFmt); MTLClearColor mtlCC = mvkMTLClearColorFromVkClearValue(_vkClearValues[caIdx], vkAttFmt); _clearColors[caIdx] = { (float)mtlCC.red, (float)mtlCC.green, (float)mtlCC.blue, (float)mtlCC.alpha}; } VkFormat vkAttFmt = subpass->getDepthStencilFormat(); - MTLPixelFormat mtlAttFmt = cmdPool->mtlPixelFormatFromVkFormat(vkAttFmt); + MTLPixelFormat mtlAttFmt = cmdPool->getMTLPixelFormatFromVkFormat(vkAttFmt); _rpsKey.attachmentMTLPixelFormats[kMVKAttachmentFormatDepthStencilIndex] = mtlAttFmt; bool isClearingDepth = _isClearingDepth && mvkMTLPixelFormatIsDepthFormat(mtlAttFmt); bool isClearingStencil = _isClearingStencil && mvkMTLPixelFormatIsStencilFormat(mtlAttFmt); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm index c346ba32..9d5ed4b3 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm @@ -155,7 +155,7 @@ id MVKBufferView::getMTLTexture() { MVKBufferView::MVKBufferView(MVKDevice* device, const VkBufferViewCreateInfo* pCreateInfo) : MVKRefCountedDeviceObject(device) { _buffer = (MVKBuffer*)pCreateInfo->buffer; _mtlBufferOffset = _buffer->getMTLBufferOffset() + pCreateInfo->offset; - _mtlPixelFormat = mtlPixelFormatFromVkFormat(pCreateInfo->format); + _mtlPixelFormat = getMTLPixelFormatFromVkFormat(pCreateInfo->format); VkExtent2D fmtBlockSize = mvkVkFormatBlockTexelSize(pCreateInfo->format); // Pixel size of format size_t bytesPerBlock = mvkVkFormatBytesPerBlock(pCreateInfo->format); _mtlTexture = nil; @@ -166,9 +166,10 @@ MVKBufferView::MVKBufferView(MVKDevice* device, const VkBufferViewCreateInfo* pC size_t blockCount = byteCount / bytesPerBlock; // But Metal requires the texture to be a 2D texture. Determine the number of 2D rows we need and their width. + // Multiple rows will automatically align with PoT max texture dimension, but need to align upwards if less than full single row. size_t maxBlocksPerRow = _device->_pMetalFeatures->maxTextureDimension / fmtBlockSize.width; size_t blocksPerRow = min(blockCount, maxBlocksPerRow); - _mtlBytesPerRow = mvkAlignByteOffset(blocksPerRow * bytesPerBlock, _device->_pProperties->limits.minTexelBufferOffsetAlignment); + _mtlBytesPerRow = mvkAlignByteOffset(blocksPerRow * bytesPerBlock, _device->getVkFormatTexelBufferAlignment(pCreateInfo->format)); size_t rowCount = blockCount / blocksPerRow; if (blockCount % blocksPerRow) { rowCount++; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h index 1f7d53f9..94c0a567 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h @@ -502,7 +502,10 @@ public: * * All other pixel formats are returned unchanged. */ - MTLPixelFormat mtlPixelFormatFromVkFormat(VkFormat vkFormat); + MTLPixelFormat getMTLPixelFormatFromVkFormat(VkFormat vkFormat); + + /** Returns the memory alignment required for the format when used in a texel buffer. */ + VkDeviceSize getVkFormatTexelBufferAlignment(VkFormat format); /** * Returns the MTLBuffer used to hold occlusion query results, @@ -608,12 +611,12 @@ public: * Returns the Metal MTLPixelFormat corresponding to the specified Vulkan VkFormat, * or returns MTLPixelFormatInvalid if no corresponding MTLPixelFormat exists. * - * This function delegates to the MVKDevice::mtlPixelFormatFromVkFormat() function. + * This function delegates to the MVKDevice::getMTLPixelFormatFromVkFormat() function. * See the notes for that function for more information about how MTLPixelFormats * are managed for each platform device. */ - inline MTLPixelFormat mtlPixelFormatFromVkFormat(VkFormat vkFormat) { - return _device->mtlPixelFormatFromVkFormat(vkFormat); + inline MTLPixelFormat getMTLPixelFormatFromVkFormat(VkFormat vkFormat) { + return _device->getMTLPixelFormatFromVkFormat(vkFormat); } /** Constructs an instance for the specified device. */ diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index 4c222291..8b720414 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -1635,7 +1635,7 @@ uint32_t MVKDevice::getMetalBufferIndexForVertexAttributeBinding(uint32_t bindin return ((_pMetalFeatures->maxPerStageBufferCount - 1) - binding); } -MTLPixelFormat MVKDevice::mtlPixelFormatFromVkFormat(VkFormat vkFormat) { +MTLPixelFormat MVKDevice::getMTLPixelFormatFromVkFormat(VkFormat vkFormat) { MTLPixelFormat mtlPixFmt = mvkMTLPixelFormatFromVkFormat(vkFormat); #if MVK_MACOS if (mtlPixFmt == MTLPixelFormatDepth24Unorm_Stencil8 && @@ -1646,6 +1646,11 @@ MTLPixelFormat MVKDevice::mtlPixelFormatFromVkFormat(VkFormat vkFormat) { return mtlPixFmt; } +VkDeviceSize MVKDevice::getVkFormatTexelBufferAlignment(VkFormat format) { + VkDeviceSize deviceAlignment = mvkMTLPixelFormatLinearTextureAlignment(getMTLPixelFormatFromVkFormat(format), getMTLDevice()); + return deviceAlignment ? deviceAlignment : _pProperties->limits.minTexelBufferOffsetAlignment; +} + id MVKDevice::getGlobalVisibilityResultMTLBuffer() { lock_guard lock(_vizLock); return _globalVisibilityResultMTLBuffer; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index bdc71672..8bf75760 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -480,8 +480,6 @@ void MVKImage::getMTLTextureContent(MVKImageSubresource& subresource, MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MVKResource(device) { - _byteAlignment = _device->_pProperties->limits.minTexelBufferOffsetAlignment; - if (pCreateInfo->flags & VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT) { mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Metal may not allow uncompressed views of compressed images."); } @@ -508,7 +506,7 @@ MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MV _mtlTexture = nil; _ioSurface = nil; - _mtlPixelFormat = mtlPixelFormatFromVkFormat(pCreateInfo->format); + _mtlPixelFormat = getMTLPixelFormatFromVkFormat(pCreateInfo->format); _mtlTextureType = mvkMTLTextureTypeFromVkImageType(pCreateInfo->imageType, _arrayLayers, (pCreateInfo->samples > 1)); @@ -529,6 +527,8 @@ MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MV _isLinear = validateLinear(pCreateInfo); _usesTexelBuffer = false; + _byteAlignment = _isLinear ? _device->getVkFormatTexelBufferAlignment(pCreateInfo->format) : mvkVkFormatBytesPerBlock(pCreateInfo->format); + // Calc _byteCount after _mtlTexture & _byteAlignment for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) { _byteCount += getBytesPerLayer(mipLvl) * _extent.depth * _arrayLayers; @@ -754,7 +754,7 @@ void MVKImageView::validateImageViewConfig(const VkImageViewCreateInfo* pCreateI // 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, bool& useSwizzle) { - MTLPixelFormat mtlPF = mtlPixelFormatFromVkFormat(format); + MTLPixelFormat mtlPF = getMTLPixelFormatFromVkFormat(format); useSwizzle = false; switch (mtlPF) { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm index c072a664..5a65f51d 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm @@ -412,7 +412,7 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor const VkPipelineColorBlendAttachmentState* pCA = &pCreateInfo->pColorBlendState->pAttachments[caIdx]; MTLRenderPipelineColorAttachmentDescriptor* colorDesc = plDesc.colorAttachments[caIdx]; - colorDesc.pixelFormat = mtlPixelFormatFromVkFormat(mvkRenderSubpass->getColorAttachmentFormat(caIdx)); + colorDesc.pixelFormat = getMTLPixelFormatFromVkFormat(mvkRenderSubpass->getColorAttachmentFormat(caIdx)); colorDesc.writeMask = mvkMTLColorWriteMaskFromVkChannelFlags(pCA->colorWriteMask); // Don't set the blend state if we're not using this attachment. // The pixel format will be MTLPixelFormatInvalid in that case, and @@ -431,7 +431,7 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor } // Depth & stencil attachments - MTLPixelFormat mtlDSFormat = mtlPixelFormatFromVkFormat(mvkRenderSubpass->getDepthStencilFormat()); + MTLPixelFormat mtlDSFormat = getMTLPixelFormatFromVkFormat(mvkRenderSubpass->getDepthStencilFormat()); if (mvkMTLPixelFormatIsDepthFormat(mtlDSFormat)) { plDesc.depthAttachmentPixelFormat = mtlDSFormat; } if (mvkMTLPixelFormatIsStencilFormat(mtlDSFormat)) { plDesc.stencilAttachmentPixelFormat = mtlDSFormat; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm index 3dde4c89..0a63201c 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm @@ -178,7 +178,7 @@ void MVKRenderSubpass::populateClearAttachments(vector& clear cAtt.colorAttachment = 0; cAtt.clearValue = clearValues[attIdx]; - MTLPixelFormat mtlDSFmt = _renderPass->mtlPixelFormatFromVkFormat(getDepthStencilFormat()); + MTLPixelFormat mtlDSFmt = _renderPass->getMTLPixelFormatFromVkFormat(getDepthStencilFormat()); if (mvkMTLPixelFormatIsDepthFormat(mtlDSFmt)) { cAtt.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; } if (mvkMTLPixelFormatIsStencilFormat(mtlDSFmt)) { cAtt.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; } if (cAtt.aspectMask) { clearAtts.push_back(cAtt); } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm index d26732ea..baa916a5 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm @@ -192,7 +192,7 @@ void MVKSwapchain::initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo) MVKSurface* mvkSrfc = (MVKSurface*)pCreateInfo->surface; _mtlLayer = mvkSrfc->getCAMetalLayer(); _mtlLayer.device = getMTLDevice(); - _mtlLayer.pixelFormat = mtlPixelFormatFromVkFormat(pCreateInfo->imageFormat); + _mtlLayer.pixelFormat = getMTLPixelFormatFromVkFormat(pCreateInfo->imageFormat); _mtlLayer.displaySyncEnabledMVK = (pCreateInfo->presentMode != VK_PRESENT_MODE_IMMEDIATE_KHR); _mtlLayer.magnificationFilter = _device->_pMVKConfig->swapchainMagFilterUseNearest ? kCAFilterNearest : kCAFilterLinear; _mtlLayer.framebufferOnly = !mvkIsAnyFlagEnabled(pCreateInfo->imageUsage, (VK_IMAGE_USAGE_TRANSFER_SRC_BIT | diff --git a/MoltenVK/MoltenVK/OS/MVKOSExtensions.h b/MoltenVK/MoltenVK/OS/MVKOSExtensions.h index f2e520b1..58cd4ecb 100644 --- a/MoltenVK/MoltenVK/OS/MVKOSExtensions.h +++ b/MoltenVK/MoltenVK/OS/MVKOSExtensions.h @@ -57,6 +57,15 @@ double mvkGetTimestampPeriod(); */ double mvkGetElapsedMilliseconds(uint64_t startTimestamp = 0, uint64_t endTimestamp = 0); +/** Ensures the block is executed on the main thread. */ +inline void mvkDispatchToMainAndWait(dispatch_block_t block) { + if (NSThread.isMainThread) { + block(); + } else { + dispatch_sync(dispatch_get_main_queue(), block); + } +} + #pragma mark - #pragma mark MTLDevice @@ -67,11 +76,9 @@ uint64_t mvkRecommendedMaxWorkingSetSize(id mtlDevice); /** Populate the propertes with info about the GPU represented by the MTLDevice. */ void mvkPopulateGPUInfo(VkPhysicalDeviceProperties& devProps, id mtlDevice); -/** Ensures the block is executed on the main thread. */ -inline void mvkDispatchToMainAndWait(dispatch_block_t block) { - if (NSThread.isMainThread) { - block(); - } else { - dispatch_sync(dispatch_get_main_queue(), block); - } -} +/** + * If the MTLDevice defines a texture memory alignment for the format, it is retrieved from + * the MTLDevice and returned, or returns zero if the MTLDevice does not define an alignment. + * The format must support linear texture memory (must not be depth, stencil, or compressed). + */ +VkDeviceSize mvkMTLPixelFormatLinearTextureAlignment(MTLPixelFormat mtlPixelFormat, id mtlDevice); diff --git a/MoltenVK/MoltenVK/OS/MVKOSExtensions.mm b/MoltenVK/MoltenVK/OS/MVKOSExtensions.mm index 94092f78..603558e4 100644 --- a/MoltenVK/MoltenVK/OS/MVKOSExtensions.mm +++ b/MoltenVK/MoltenVK/OS/MVKOSExtensions.mm @@ -188,4 +188,11 @@ void mvkPopulateGPUInfo(VkPhysicalDeviceProperties& devProps, id mtlD } #endif //MVK_IOS - +VkDeviceSize mvkMTLPixelFormatLinearTextureAlignment(MTLPixelFormat mtlPixelFormat, + id mtlDevice) { + if ([mtlDevice respondsToSelector: @selector(minimumLinearTextureAlignmentForPixelFormat:)]) { + return [mtlDevice minimumLinearTextureAlignmentForPixelFormat: mtlPixelFormat]; + } else { + return 0; + } +}