Support linear filtering when using vkCmdBlitImage().

Fixes linear filtering when using vkCmdBlitImage(), useful for mipmap generation.
Move MVKCmdBlitImage sampler definition to shader as a constexpr sampler,
and ensure mip_filter and filter are set correctly.
Add MVKRPSKeyBlitImg::srcFilter.
Include MVKCommonEnvironment.h in MVKLogging.h.
Update MoltenVK version to 1.0.39.
This commit is contained in:
Bill Hollings 2019-11-12 16:47:33 -05:00
parent 025abe501a
commit 9b13006018
9 changed files with 62 additions and 58 deletions

View File

@ -23,6 +23,8 @@
extern "C" {
#endif // __cplusplus
#include "MVKCommonEnvironment.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>

View File

@ -14,6 +14,16 @@ For best results, use a Markdown reader.*
MoltenVK 1.0.39
---------------
Released TBD
- Support linear filtering when using `vkCmdBlitImage()`.
- Support *Xcode 11.2*.
MoltenVK 1.0.38
---------------
@ -47,7 +57,7 @@ Released 2019/10/30
- Set value of `VkPhysicalDeviceLimits::maxTexelBufferElements` to more realistic value.
- Add linking separate shader texts to `GLSLToSPRIVConverter`.
- Move generation of `SPIRV-Cross/mvkSpirvCrossRevisionDerived.h` to separate script.
- Support Xcode 11.1.
- Support *Xcode 11.1*.
- Update dependency libraries to match *Vulkan SDK 1.1.126*.
- Update to latest SPIRV-Cross version:
- MSL: Support option for treating 1D textures as 2D textures of height 1.

View File

@ -50,7 +50,7 @@ typedef unsigned long MTLLanguageVersion;
*/
#define MVK_VERSION_MAJOR 1
#define MVK_VERSION_MINOR 0
#define MVK_VERSION_PATCH 38
#define MVK_VERSION_PATCH 39
#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

@ -116,7 +116,6 @@ protected:
void initMTLRenderPassDescriptor();
MTLRenderPassDescriptor* _mtlRenderPassDescriptor;
MTLSamplerMinMagFilter _mtlFilter;
MVKRPSKeyBlitImg _blitKey;
MVKVectorInline<MVKImageBlitRender, 4> _mvkImageBlitRenders;
};

View File

@ -220,11 +220,10 @@ void MVKCmdBlitImage::setContent(VkImage srcImage,
MVKCmdCopyImage::setContent(srcImage, srcImageLayout, dstImage, dstImageLayout, true, commandUse);
_mtlFilter = mvkMTLSamplerMinMagFilterFromVkFilter(filter);
_blitKey.srcMTLPixelFormat = (uint32_t)_srcMTLPixFmt;
_blitKey.srcMTLTextureType = (uint32_t)_srcImage->getMTLTextureType();
_blitKey.dstMTLPixelFormat = (uint32_t)_dstMTLPixFmt;
_blitKey.srcMTLPixelFormat = _srcMTLPixFmt;
_blitKey.srcMTLTextureType = _srcImage->getMTLTextureType();
_blitKey.dstMTLPixelFormat = _dstMTLPixFmt;
_blitKey.srcFilter = mvkMTLSamplerMinMagFilterFromVkFilter(filter);
_blitKey.dstSampleCount = _dstSampleCount;
_mvkImageBlitRenders.clear(); // Clear for reuse
@ -355,31 +354,31 @@ void MVKCmdBlitImage::encode(MVKCommandEncoder* cmdEncoder) {
mtlColorAttDesc.texture = dstMTLTex;
uint32_t vtxBuffIdx = getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex);
MVKCommandEncodingPool* cmdEncPool = getCommandEncodingPool();
id<MTLRenderPipelineState> mtlRPS = getCommandEncodingPool()->getCmdBlitImageMTLRenderPipelineState(_blitKey);
for (auto& bltRend : _mvkImageBlitRenders) {
mtlColorAttDesc.level = bltRend.region.dstSubresource.mipLevel;
uint32_t srcBaseLayer = bltRend.region.srcSubresource.baseArrayLayer;
uint32_t dstBaseLayer = bltRend.region.dstSubresource.baseArrayLayer;
uint32_t layCnt = bltRend.region.srcSubresource.layerCount;
for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) {
// Update the render pass descriptor for the texture level and slice, and create a render encoder.
mtlColorAttDesc.slice = dstBaseLayer + layIdx;
mtlColorAttDesc.slice = bltRend.region.dstSubresource.baseArrayLayer + layIdx;
id<MTLRenderCommandEncoder> mtlRendEnc = [cmdEncoder->_mtlCmdBuffer renderCommandEncoderWithDescriptor: _mtlRenderPassDescriptor];
setLabelIfNotNil(mtlRendEnc, mvkMTLRenderCommandEncoderLabel(_commandUse));
[mtlRendEnc pushDebugGroup: @"vkCmdBlitImage"];
[mtlRendEnc setRenderPipelineState: cmdEncPool->getCmdBlitImageMTLRenderPipelineState(_blitKey)];
[mtlRendEnc setRenderPipelineState: mtlRPS];
cmdEncoder->setVertexBytes(mtlRendEnc, bltRend.vertices, sizeof(bltRend.vertices), vtxBuffIdx);
[mtlRendEnc setFragmentTexture: srcMTLTex atIndex: 0];
[mtlRendEnc setFragmentSamplerState: cmdEncPool->getCmdBlitImageMTLSamplerState(_mtlFilter) atIndex: 0];
uint32_t srcSlice = srcBaseLayer + layIdx;
cmdEncoder->setFragmentBytes(mtlRendEnc, &srcSlice, sizeof(srcSlice), 0);
struct {
uint slice;
float lod;
} texSubRez;
texSubRez.slice = bltRend.region.srcSubresource.baseArrayLayer + layIdx;
texSubRez.lod = bltRend.region.srcSubresource.mipLevel;
cmdEncoder->setFragmentBytes(mtlRendEnc, &texSubRez, sizeof(texSubRez), 0);
[mtlRendEnc drawPrimitives: MTLPrimitiveTypeTriangleStrip vertexStart: 0 vertexCount: kMVKBlitVertexCount];
[mtlRendEnc popDebugGroup];

View File

@ -51,12 +51,6 @@ public:
/** Returns a MTLRenderPipelineState to support certain Vulkan BLIT commands. */
id<MTLRenderPipelineState> getCmdBlitImageMTLRenderPipelineState(MVKRPSKeyBlitImg& blitKey);
/**
* Returns a MTLSamplerState dedicated to rendering to a texture using the
* specified min/mag filter value to support certain Vulkan BLIT commands.
*/
id<MTLSamplerState> getCmdBlitImageMTLSamplerState(MTLSamplerMinMagFilter mtlFilter);
/**
* Returns a MTLDepthStencilState dedicated to rendering to several attachments
* to support clearing regions of those attachments.
@ -88,10 +82,10 @@ public:
* with content held in Private storage. The object returned can be used as a
* temporary image during image transfers.
*
* The same image instance will be returned for two calls to this funciton with
* The same image instance will be returned for two calls to this function with
* the same image descriptor data. This implies that the same image instance could
* be used by two transfers within the same encoder or queue. This is acceptable
* becuase the content only needss to be valid during the transfer, and it can be
* becuase the content only needs to be valid during the transfer, and it can be
* reused by subsequent transfers in the same encoding run.
*/
MVKImage* getTransferMVKImage(MVKImageDescriptorData& imgData);
@ -148,8 +142,6 @@ protected:
std::unordered_map<MVKBufferDescriptorData, MVKBuffer*> _transferBuffers;
std::unordered_map<MVKBufferDescriptorData, MVKDeviceMemory*> _transferBufferMemory;
MVKMTLBufferAllocator _mtlBufferAllocator;
id<MTLSamplerState> _cmdBlitImageLinearMTLSamplerState = nil;
id<MTLSamplerState> _cmdBlitImageNearestMTLSamplerState = nil;
id<MTLDepthStencilState> _cmdClearDepthOnlyDepthStencilState = nil;
id<MTLDepthStencilState> _cmdClearStencilOnlyDepthStencilState = nil;
id<MTLDepthStencilState> _cmdClearDepthAndStencilDepthStencilState = nil;

View File

@ -60,18 +60,6 @@ id<MTLRenderPipelineState> MVKCommandEncodingPool::getCmdBlitImageMTLRenderPipel
MVK_ENC_REZ_ACCESS(_cmdBlitImageMTLRenderPipelineStates[blitKey], newCmdBlitImageMTLRenderPipelineState(blitKey, _commandPool));
}
id<MTLSamplerState> MVKCommandEncodingPool::getCmdBlitImageMTLSamplerState(MTLSamplerMinMagFilter mtlFilter) {
switch (mtlFilter) {
case MTLSamplerMinMagFilterNearest: {
MVK_ENC_REZ_ACCESS(_cmdBlitImageNearestMTLSamplerState, newCmdBlitImageMTLSamplerState(mtlFilter));
}
case MTLSamplerMinMagFilterLinear: {
MVK_ENC_REZ_ACCESS(_cmdBlitImageLinearMTLSamplerState, newCmdBlitImageMTLSamplerState(mtlFilter));
}
}
}
id<MTLDepthStencilState> MVKCommandEncodingPool::getMTLDepthStencilState(bool useDepth, bool useStencil) {
if (useDepth && useStencil) {
@ -168,12 +156,6 @@ void MVKCommandEncodingPool::destroyMetalResources() {
for (auto& pair : _transferBufferMemory) { mvkDev->freeMemory(pair.second, nullptr); }
_transferBufferMemory.clear();
[_cmdBlitImageLinearMTLSamplerState release];
_cmdBlitImageLinearMTLSamplerState = nil;
[_cmdBlitImageNearestMTLSamplerState release];
_cmdBlitImageNearestMTLSamplerState = nil;
[_cmdClearDepthAndStencilDepthStencilState release];
_cmdClearDepthAndStencilDepthStencilState = nil;

View File

@ -30,7 +30,7 @@
#pragma mark MVKRPSKeyBlitImg
/**
* Key to use for looking up cached MTLRenderPipelineState instances based on MTLPixelFormat and MTLTextureType.
* Key to use for looking up cached MTLRenderPipelineState instances based on BLIT info.
*
* This structure can be used as a key in a std::map and std::unordered_map.
*/
@ -38,12 +38,14 @@ typedef struct MVKRPSKeyBlitImg_t {
uint16_t srcMTLPixelFormat = 0; /**< as MTLPixelFormat */
uint16_t srcMTLTextureType = 0; /**< as MTLTextureType */
uint16_t dstMTLPixelFormat = 0; /**< as MTLPixelFormat */
uint16_t dstSampleCount = 0;
uint8_t srcFilter = 0; /**< as MTLSamplerMinMagFilter */
uint8_t dstSampleCount = 0;
bool operator==(const MVKRPSKeyBlitImg_t& rhs) const {
if (srcMTLPixelFormat != rhs.srcMTLPixelFormat) { return false; }
if (srcMTLTextureType != rhs.srcMTLTextureType) { return false; }
if (dstMTLPixelFormat != rhs.dstMTLPixelFormat) { return false; }
if (srcFilter != rhs.srcFilter) { return false; }
if (dstSampleCount != rhs.dstSampleCount) { return false; }
return true;
}
@ -52,20 +54,29 @@ typedef struct MVKRPSKeyBlitImg_t {
inline MTLPixelFormat getDstMTLPixelFormat() { return (MTLPixelFormat)dstMTLPixelFormat; }
inline MTLSamplerMinMagFilter getSrcMTLSamplerMinMagFilter() { return (MTLSamplerMinMagFilter)srcFilter; }
inline bool isSrcArrayType() {
return (srcMTLTextureType == MTLTextureType2DArray ||
#if MVK_MACOS
srcMTLTextureType == MTLTextureType2DMultisampleArray ||
#endif
srcMTLTextureType == MTLTextureType1DArray); }
srcMTLTextureType == MTLTextureType1DArray);
}
std::size_t hash() const {
std::size_t hash = srcMTLPixelFormat;
hash <<= 16;
hash |= srcMTLTextureType;
hash <<= 16;
hash |= dstMTLPixelFormat;
hash <<= 16;
hash <<= 8;
hash |= srcFilter;
hash <<= 8;
hash |= dstSampleCount;
return hash;
}

View File

@ -156,8 +156,10 @@ id<MTLFunction> MVKCommandResourceFactory::newBlitFragFunction(MVKRPSKeyBlitImg&
NSString* typeStr = getMTLFormatTypeString(blitKey.getSrcMTLPixelFormat());
bool isArrayType = blitKey.isSrcArrayType();
bool isLinearFilter = (blitKey.getSrcMTLSamplerMinMagFilter() == MTLSamplerMinMagFilterLinear);
NSString* arraySuffix = isArrayType ? @"_array" : @"";
NSString* sliceArg = isArrayType ? @", srcSlice" : @"";
NSString* sliceArg = isArrayType ? @", subRez.slice" : @"";
NSString* srcFilter = isLinearFilter ? @"linear" : @"nearest";
NSMutableString* msl = [NSMutableString stringWithCapacity: (2 * KIBI) ];
[msl appendLineMVK: @"#include <metal_stdlib>"];
@ -168,19 +170,26 @@ id<MTLFunction> MVKCommandResourceFactory::newBlitFragFunction(MVKRPSKeyBlitImg&
[msl appendLineMVK: @" float2 v_texCoord;"];
[msl appendLineMVK: @"} VaryingsPosTex;"];
[msl appendLineMVK];
[msl appendLineMVK: @"typedef struct {"];
[msl appendLineMVK: @" uint slice;"];
[msl appendLineMVK: @" float lod;"];
[msl appendLineMVK: @"} TexSubrez;"];
[msl appendLineMVK];
NSString* funcName = @"fragBlit";
[msl appendFormat: @"constexpr sampler ce_sampler(mip_filter::nearest, filter::%@);", srcFilter];
[msl appendLineMVK];
NSString* funcName = @"fragCmdBlitImage";
[msl appendFormat: @"fragment %@4 %@(VaryingsPosTex varyings [[stage_in]],", typeStr, funcName];
[msl appendLineMVK];
[msl appendFormat: @" texture2d%@<%@> texture [[texture(0)]],", arraySuffix, typeStr];
[msl appendFormat: @" texture2d%@<%@> tex [[texture(0)]],", arraySuffix, typeStr];
[msl appendLineMVK];
[msl appendLineMVK: @" sampler sampler [[sampler(0)]],"];
[msl appendLineMVK: @" constant uint& srcSlice [[buffer(0)]]) {"];
[msl appendFormat: @" return texture.sample(sampler, varyings.v_texCoord%@);", sliceArg];
[msl appendLineMVK: @" constant TexSubrez& subRez [[buffer(0)]]) {"];
[msl appendFormat: @" return tex.sample(ce_sampler, varyings.v_texCoord%@, level(subRez.lod));", sliceArg];
[msl appendLineMVK];
[msl appendLineMVK: @"}"];
// MVKLogDebug("\n%s", msl.UTF8String);
MVKLogDebug("\n%s", msl.UTF8String);
return newMTLFunction(msl, funcName);
}