blob: a23dd0d97c052ece7452ab382ff3e6a1f0f4799f [file] [log] [blame]
/*
* MVKCommandResourceFactory.h
*
* Copyright (c) 2015-2022 The Brenwill Workshop Ltd. (http://www.brenwill.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "MVKDevice.h"
#include "MVKFoundation.h"
#include "mvk_datatypes.hpp"
#include <string>
#import <Metal/Metal.h>
class MVKQueryPool;
#pragma mark -
#pragma mark MVKRPSKeyBlitImg
/**
* 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.
*/
typedef struct MVKRPSKeyBlitImg {
uint16_t srcMTLPixelFormat = 0; /**< as MTLPixelFormat */
uint16_t dstMTLPixelFormat = 0; /**< as MTLPixelFormat */
uint8_t srcMTLTextureType = 0; /**< as MTLTextureType */
uint8_t srcAspect = 0; /**< as VkImageAspectFlags */
uint8_t srcFilter = 0; /**< as MTLSamplerMinMagFilter */
uint8_t dstSampleCount = 0;
bool operator==(const MVKRPSKeyBlitImg& rhs) const {
if (srcMTLPixelFormat != rhs.srcMTLPixelFormat) { return false; }
if (dstMTLPixelFormat != rhs.dstMTLPixelFormat) { return false; }
if (srcMTLTextureType != rhs.srcMTLTextureType) { return false; }
if (srcAspect != rhs.srcAspect) { return false; }
if (srcFilter != rhs.srcFilter) { return false; }
if (dstSampleCount != rhs.dstSampleCount) { return false; }
return true;
}
inline MTLPixelFormat getSrcMTLPixelFormat() { return (MTLPixelFormat)srcMTLPixelFormat; }
inline MTLPixelFormat getDstMTLPixelFormat() { return (MTLPixelFormat)dstMTLPixelFormat; }
inline MTLSamplerMinMagFilter getSrcMTLSamplerMinMagFilter() { return (MTLSamplerMinMagFilter)srcFilter; }
inline MTLTextureType getSrcMTLTextureType() { return (MTLTextureType)srcMTLTextureType; }
inline bool isSrcArrayType() {
return (srcMTLTextureType == MTLTextureType2DArray ||
#if MVK_MACOS_OR_IOS
srcMTLTextureType == MTLTextureType2DMultisampleArray ||
#endif
srcMTLTextureType == MTLTextureType1DArray);
}
std::size_t hash() const {
std::size_t hash = srcMTLPixelFormat;
hash <<= 16;
hash |= dstMTLPixelFormat;
hash <<= 8;
hash |= srcMTLTextureType;
hash <<= 8;
hash |= srcAspect;
hash <<= 8;
hash |= srcFilter;
hash <<= 8;
hash |= dstSampleCount;
return hash;
}
} MVKRPSKeyBlitImg;
/**
* Hash structure implementation for MVKRPSKeyBlitImg in std namespace,
* so MVKRPSKeyBlitImg can be used as a key in a std::map and std::unordered_map.
*/
namespace std {
template <>
struct hash<MVKRPSKeyBlitImg> {
std::size_t operator()(const MVKRPSKeyBlitImg& k) const { return k.hash(); }
};
}
#pragma mark -
#pragma mark MVKRPSKeyClearAtt
#define kMVKClearAttachmentCount (kMVKCachedColorAttachmentCount + 1)
#define kMVKClearAttachmentDepthStencilIndex (kMVKClearAttachmentCount - 1)
#define kMVKClearAttachmentLayeredRenderingBitIndex kMVKClearAttachmentCount
/**
* Key to use for looking up cached MTLRenderPipelineState instances.
* Indicates which attachments are enabled and used, and holds the Metal pixel formats for
* each color attachment plus one depth/stencil attachment. Also holds the Metal sample count.
* An attachment is considered used if it is enabled and has a valid Metal pixel format.
*
* This structure can be used as a key in a std::map and std::unordered_map.
*/
typedef struct MVKRPSKeyClearAtt {
uint16_t flags; // bitcount > kMVKClearAttachmentLayeredRenderingBitIndex
uint16_t mtlSampleCount;
uint16_t attachmentMTLPixelFormats[kMVKClearAttachmentCount];
const static uint32_t bitFlag = 1;
void enableAttachment(uint32_t attIdx) { mvkEnableFlags(flags, bitFlag << attIdx); }
void disableAttachment(uint32_t attIdx) { mvkDisableFlags(flags, bitFlag << attIdx); }
bool isAttachmentEnabled(uint32_t attIdx) { return mvkIsAnyFlagEnabled(flags, bitFlag << attIdx); }
bool isAttachmentUsed(uint32_t attIdx) { return isAttachmentEnabled(attIdx) && attachmentMTLPixelFormats[attIdx]; }
bool isAnyAttachmentEnabled() { return mvkIsAnyFlagEnabled(flags, (bitFlag << kMVKClearAttachmentCount) - 1); }
void enableLayeredRendering() { mvkEnableFlags(flags, bitFlag << kMVKClearAttachmentLayeredRenderingBitIndex); }
bool isLayeredRenderingEnabled() { return mvkIsAnyFlagEnabled(flags, bitFlag << kMVKClearAttachmentLayeredRenderingBitIndex); }
bool operator==(const MVKRPSKeyClearAtt& rhs) const { return mvkAreEqual(this, &rhs); }
std::size_t hash() const {
std::size_t hash = mvkHash(&flags);
hash = mvkHash(&mtlSampleCount, 1, hash);
return mvkHash(attachmentMTLPixelFormats, kMVKClearAttachmentCount, hash);
}
void reset() {
mvkClear(this);
mtlSampleCount = mvkSampleCountFromVkSampleCountFlagBits(VK_SAMPLE_COUNT_1_BIT);
}
MVKRPSKeyClearAtt() { reset(); }
} MVKRPSKeyClearAtt;
/**
* Hash structure implementation for MVKRPSKeyClearAtt in std namespace,
* so MVKRPSKeyClearAtt can be used as a key in a std::map and std::unordered_map.
*/
namespace std {
template <>
struct hash<MVKRPSKeyClearAtt> {
std::size_t operator()(const MVKRPSKeyClearAtt& k) const { return k.hash(); }
};
}
#pragma mark -
#pragma mark MVKMTLDepthStencilDescriptorData
/**
* A structure to hold configuration data for creating an MTLStencilDescriptor instance.
*
* The order of elements is designed to "fail-fast", with the more commonly changing elements
* situated near the beginning of the structure so that a memory comparison will detect any
* change as early as possible.
*/
typedef struct MVKMTLStencilDescriptorData {
bool enabled; /**< Indicates whether stencil testing for this face is enabled. */
uint8_t stencilCompareFunction; /**< The stencil compare function (interpreted as MTLCompareFunction). */
uint8_t stencilFailureOperation; /**< The operation to take when the stencil test fails (interpreted as MTLStencilOperation). */
uint8_t depthFailureOperation; /**< The operation to take when the stencil test passes, but the depth test fails (interpreted as MTLStencilOperation). */
uint8_t depthStencilPassOperation; /**< The operation to take when both the stencil and depth tests pass (interpreted as MTLStencilOperation). */
uint32_t readMask; /**< The bit-mask to apply when comparing the stencil buffer value to the reference value. */
uint32_t writeMask; /**< The bit-mask to apply when writing values to the stencil buffer. */
MVKMTLStencilDescriptorData() {
// Start with all zeros to ensure memory comparisons will work,
// even if the structure contains alignment gaps.
mvkClear(this);
enabled = false;
stencilCompareFunction = MTLCompareFunctionAlways;
stencilFailureOperation = MTLStencilOperationKeep;
depthFailureOperation = MTLStencilOperationKeep;
depthStencilPassOperation = MTLStencilOperationKeep;
readMask = static_cast<uint32_t>(~0);
writeMask = static_cast<uint32_t>(~0);
}
} MVKMTLStencilDescriptorData;
/** An instance populated with default values, for use in resetting other instances to default state. */
const MVKMTLStencilDescriptorData kMVKMTLStencilDescriptorDataDefault;
/**
* A structure to hold configuration data for creating an MTLDepthStencilDescriptor instance.
* Instances of this structure can be used as a map key.
*
* The order of elements is designed to "fail-fast", with the more commonly changing elements
* situated near the beginning of the structure so that a memory comparison will detect any
* change as early as possible.
*/
typedef struct MVKMTLDepthStencilDescriptorData {
uint8_t depthCompareFunction; /**< The depth compare function (interpreted as MTLCompareFunction). */
bool depthWriteEnabled; /**< Indicates whether depth writing is enabled. */
MVKMTLStencilDescriptorData frontFaceStencilData;
MVKMTLStencilDescriptorData backFaceStencilData;
bool operator==(const MVKMTLDepthStencilDescriptorData& rhs) const { return mvkAreEqual(this, &rhs); }
std::size_t hash() const {
return mvkHash((uint64_t*)this, sizeof(*this) / sizeof(uint64_t));
}
/** Disable depth and/or stencil testing. */
void disable(bool disableDepth, bool disableStencil) {
if (disableDepth) {
depthCompareFunction = MTLCompareFunctionAlways;
depthWriteEnabled = false;
}
if (disableStencil) {
frontFaceStencilData = kMVKMTLStencilDescriptorDataDefault;
backFaceStencilData = kMVKMTLStencilDescriptorDataDefault;
}
}
MVKMTLDepthStencilDescriptorData() {
// Start with all zeros to ensure memory comparisons will work,
// even if the structure contains alignment gaps.
mvkClear(this);
disable(true, true);
}
} __attribute__((aligned(sizeof(uint64_t)))) MVKMTLDepthStencilDescriptorData;
/** An instance populated with default values, for use in resetting other instances to default state. */
const MVKMTLDepthStencilDescriptorData kMVKMTLDepthStencilDescriptorDataDefault;
namespace std {
template <>
struct hash<MVKMTLDepthStencilDescriptorData> {
std::size_t operator()(const MVKMTLDepthStencilDescriptorData& k) const { return k.hash(); }
};
}
#pragma mark -
#pragma mark MVKImageDescriptorData
/**
* Key to use for looking up cached MVKImage instances, and to create a new MVKImage when needed.
* The contents of this structure is a subset of the contents of the VkImageCreateInfo structure.
*
* This structure can be used as a key in a std::map and std::unordered_map.
*/
typedef struct MVKImageDescriptorData {
VkImageType imageType;
VkFormat format;
VkExtent3D extent;
uint32_t mipLevels;
uint32_t arrayLayers;
VkSampleCountFlagBits samples;
VkImageUsageFlags usage;
bool operator==(const MVKImageDescriptorData& rhs) const { return mvkAreEqual(this, &rhs); }
std::size_t hash() const {
return mvkHash((uint64_t*)this, sizeof(*this) / sizeof(uint64_t));
}
MVKImageDescriptorData() { mvkClear(this); }
} __attribute__((aligned(sizeof(uint64_t)))) MVKImageDescriptorData;
/**
* Hash structure implementation for MVKImageDescriptorData in std namespace, so
* MVKImageDescriptorData can be used as a key in a std::map and std::unordered_map.
*/
namespace std {
template <>
struct hash<MVKImageDescriptorData> {
std::size_t operator()(const MVKImageDescriptorData& k) const { return k.hash(); }
};
}
#pragma mark -
#pragma mark MVKBufferDescriptorData
/**
* Key to use for looking up cached MVKBuffer instances, and to create a new MVKBuffer when needed.
* The contents of this structure is a subset of the contents of the VkBufferCreateInfo structure.
*
* This structure can be used as a key in a std::map and std::unordered_map.
*/
typedef struct MVKBufferDescriptorData {
VkDeviceSize size;
VkBufferUsageFlags usage;
bool operator==(const MVKBufferDescriptorData& rhs) const { return mvkAreEqual(this, &rhs); }
std::size_t hash() const {
return mvkHash((uint64_t*)this, sizeof(*this) / sizeof(uint64_t));
}
MVKBufferDescriptorData() { mvkClear(this); }
} __attribute__((aligned(sizeof(uint64_t)))) MVKBufferDescriptorData;
/**
* Hash structure implementation for MVKBufferDescriptorData in std namespace, so
* MVKBufferDescriptorData can be used as a key in a std::map and std::unordered_map.
*/
namespace std {
template <>
struct hash<MVKBufferDescriptorData> {
std::size_t operator()(const MVKBufferDescriptorData& k) const { return k.hash(); }
};
}
/**
* Spec for a query.
*
* This structure can be used as a key in a std::map and std::unordered_map.
*/
typedef struct MVKQuerySpec {
MVKQueryPool* queryPool = nullptr;
uint32_t query = 0;
inline void set(MVKQueryPool* qryPool, uint32_t qry) { queryPool = qryPool; query = qry; }
inline void reset() { set(nullptr, 0); }
bool operator==(const MVKQuerySpec& rhs) const {
return (queryPool == rhs.queryPool) && (query == rhs.query);
}
std::size_t hash() const { return (size_t)queryPool ^ query; }
} MVKQuerySpec;
namespace std {
template <>
struct hash<MVKQuerySpec> {
std::size_t operator()(const MVKQuerySpec& k) const { return k.hash(); }
};
}
#pragma mark -
#pragma mark MVKCommandResourceFactory
/**
* This factory class consolidates the manufacturing of various pipeline components
* for commands whose functionality is realized through render or compute pipelines.
*/
class MVKCommandResourceFactory : public MVKBaseDeviceObject {
public:
/** Returns the Vulkan API opaque object controlling this object. */
MVKVulkanAPIObject* getVulkanAPIObject() override { return _device->getVulkanAPIObject(); };
#pragma mark Command resources
/** Returns a new MTLRenderPipelineState to support certain Vulkan BLIT commands. */
id<MTLRenderPipelineState> newCmdBlitImageMTLRenderPipelineState(MVKRPSKeyBlitImg& blitKey,
MVKVulkanAPIDeviceObject* owner);
/**
* Returns a new MTLSamplerState dedicated to rendering to a texture using the
* specified min/mag filter value to support certain Vulkan BLIT commands.
*/
id<MTLSamplerState> newCmdBlitImageMTLSamplerState(MTLSamplerMinMagFilter mtlFilter);
/**
* Returns a new MTLRenderPipelineState dedicated to rendering to several
* attachments to support clearing regions of those attachments.
*/
id<MTLRenderPipelineState> newCmdClearMTLRenderPipelineState(MVKRPSKeyClearAtt& attKey,
MVKVulkanAPIDeviceObject* owner);
/** Returns a new MTLDepthStencilState that always writes to the depth and/or stencil attachments. */
id<MTLDepthStencilState> newMTLDepthStencilState(bool useDepth, bool useStencil);
/**
* Returns a new MTLDepthStencilState configured from the specified data.
* Returns nil if the specified data indicates depth and stencil testing is disabled.
*/
id<MTLDepthStencilState> newMTLDepthStencilState(MVKMTLDepthStencilDescriptorData& dsData);
/** Returns an retained MTLStencilDescriptor constructed from the stencil data. */
MTLStencilDescriptor* newMTLStencilDescriptor(MVKMTLStencilDescriptorData& sData);
/**
* Returns a new MVKImage configured with content held in Private storage.
* The image returned is bound to an empty device memory, and can be used
* as a temporary image during image transfers.
*/
MVKImage* newMVKImage(MVKImageDescriptorData& imgData);
/**
* Returns a new MVKBuffer configured with content held in Private storage.
* The buffer returned is bound to a new device memory, also returned, and
* can be used as a temporary buffer during buffer-image transfers.
*/
MVKBuffer* newMVKBuffer(MVKBufferDescriptorData& buffData, MVKDeviceMemory*& buffMem);
/** Returns a new MTLComputePipelineState for copying between two buffers with byte-aligned copy regions. */
id<MTLComputePipelineState> newCmdCopyBufferBytesMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner);
/** Returns a new MTLComputePipelineState for filling a buffer. */
id<MTLComputePipelineState> newCmdFillBufferMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner);
/** Returns a new MTLComputePipelineState for clearing an image. */
id<MTLComputePipelineState> newCmdClearColorImageMTLComputePipelineState(MVKFormatType type,
MVKVulkanAPIDeviceObject* owner);
/** Returns a new MTLComputePipelineState for resolving an image. */
id<MTLComputePipelineState> newCmdResolveColorImageMTLComputePipelineState(MVKFormatType type,
MVKVulkanAPIDeviceObject* owner);
/** Returns a new MTLComputePipelineState for copying between a buffer holding compressed data and a 3D image. */
id<MTLComputePipelineState> newCmdCopyBufferToImage3DDecompressMTLComputePipelineState(bool needTempBuf,
MVKVulkanAPIDeviceObject* owner);
/** Returns a new MTLComputePipelineState for converting an indirect buffer for use in a multiview draw. */
id<MTLComputePipelineState> newCmdDrawIndirectMultiviewConvertBuffersMTLComputePipelineState(bool indexed,
MVKVulkanAPIDeviceObject* owner);
/** Returns a new MTLComputePipelineState for converting an indirect buffer for use in a tessellated draw. */
id<MTLComputePipelineState> newCmdDrawIndirectTessConvertBuffersMTLComputePipelineState(bool indexed,
MVKVulkanAPIDeviceObject* owner);
/** Returns a new MTLComputePipelineState for copying an index buffer for use in a tessellated draw. */
id<MTLComputePipelineState> newCmdDrawIndexedCopyIndexBufferMTLComputePipelineState(MTLIndexType type,
MVKVulkanAPIDeviceObject* owner);
/** Returns a new MTLComputePipelineState for copying query results to a buffer. */
id<MTLComputePipelineState> newCmdCopyQueryPoolResultsMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner);
/** Returns a new MTLComputePipelineState for accumulating occlusion query results to a buffer. */
id<MTLComputePipelineState> newAccumulateOcclusionQueryResultsMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner);
#pragma mark Construction
MVKCommandResourceFactory(MVKDevice* device);
~MVKCommandResourceFactory() override;
protected:
void initMTLLibrary();
void initImageDeviceMemory();
id<MTLFunction> newBlitFragFunction(MVKRPSKeyBlitImg& blitKey);
id<MTLFunction> newClearVertFunction(MVKRPSKeyClearAtt& attKey);
id<MTLFunction> newClearFragFunction(MVKRPSKeyClearAtt& attKey);
NSString* getMTLFormatTypeString(MTLPixelFormat mtlPixFmt);
id<MTLFunction> newFunctionNamed(const char* funcName);
id<MTLFunction> newMTLFunction(NSString* mslSrcCode, NSString* funcName);
id<MTLRenderPipelineState> newMTLRenderPipelineState(MTLRenderPipelineDescriptor* plDesc,
MVKVulkanAPIDeviceObject* owner);
id<MTLComputePipelineState> newMTLComputePipelineState(const char* funcName,
MVKVulkanAPIDeviceObject* owner);
id<MTLLibrary> _mtlLibrary;
MVKDeviceMemory* _transferImageMemory;
};