基于金属的图像处理框架。
MTIImageMTIImageMTIImageCVPixelBuffer的颜色空间SwiftAppleSiliconMetalPetal是一个基于金属的图像处理框架,旨在为静止图像和视频提供实时处理,并易于使用编程界面。
本章涵盖了金属斑的关键概念,并将帮助您更好地了解其设计,实现,绩效含义和最佳实践。
Metalpetal的设计考虑了以下目标。
易于使用API
提供便利的API并避免常见的陷阱。
表现
有效地使用CPU,GPU和内存。
可扩展性
易于创建自定义过滤器以及插件您的自定义图像处理单元。
sw
为Swift程序员提供流畅的体验。
金属斑的一些核心概念与苹果核心图像框架中的核心概念非常相似。
提供了呈现MTIImage s的评估环境。它还存储许多缓存和状态信息,因此,尽可能重复上下文更有效。
MTIImage对象是要处理或生成的图像的表示。它确实直接表示图像位图数据,它具有产生图像或更精确的MTLTexture所需的所有信息。它由两个部分组成,即如何产生纹理( MTIImagePromise )以及其他信息,例如上下文如何缓存图像( cachePolicy )以及如何采样纹理( samplerDescriptor )。
MTIFilter代表图像处理效果和控制该效果的任何参数。它会产生MTIImage对象作为输出。要使用过滤器,请创建一个过滤器对象,设置其输入图像和参数,然后访问其输出图像。通常,滤波器类拥有静态内核( MTIKernel ),当您访问其outputImage属性时,它会要求带有输入图像和参数的内核生成输出MTIImage 。
MTIKernel表示图像处理程序。 MTIKernel负责为过滤器创建相应的渲染或计算管道状态,并为MTIImage构建MTIImagePromise 。
Metalpetal在引擎盖下为您提供了很多优化。
它会自动caches函数,内核状态,采样器状态等。
它利用金属功能,例如可编程混合,无内存的渲染目标,资源堆和金属性能着色器,以使渲染效率快速有效。在MacOS上,Metalpetal还可以利用Apple Silicon的TBDR架构。
在渲染之前,MetalPetal可以查看您的图像渲染图,并找出执行渲染,节省内存,能量和时间所需的中间纹理数量的最小数量。
如果可以连接多个“配方”以消除冗余渲染通行证,则它还可以重新组织图像渲染图。 ( MTIContext.isRenderGraphOptimizationEnabled )
MTIImage对象是不变的,这意味着它们可以在线程之间安全共享。
但是, MTIFilter对象是可变的,因此不能在线程之间安全地共享。
MTIContext包含许多状态和缓存。有一个用于MTIContext对象的线程安全机制,使得在线程之间共享MTIContext对象是安全的。
完全自定义的顶点和片段功能。
MRT(多个渲染目标)支持。
通常表现更好。 (需要详细的基准数据)
颜色矩阵
颜色查找
使用颜色查找表来重塑图像中的颜色。
不透明度
接触
饱和
亮度
对比
颜色反转
充满活力
调整图像的饱和度,同时保持令人愉悦的肤色。
RGB音调曲线
混合模式
与面具混合
转换
庄稼
像素化
多层复合材料
国会议员卷积
国会议员高斯模糊
MPS定义
MPS SOBEL
MPS UNSHARP面具
MPS盒子模糊
高通皮平滑
Clahe(对比度限制的自适应直方图均衡)
镜头Blur(Hexagonal散景模糊)
表面模糊
凸起失真
色度钥匙混合物
彩色半径
点屏幕
圆角(圆形/连续曲线)
所有核心图像过滤器
MTIImage您可以从几乎任何图像数据来源创建MTIImage对象,包括:
URL s引用要加载的图像文件CVImageBufferRef或CVPixelBufferRef )CIImage对象MDLTexture对象 let imageFromCGImage = MTIImage ( cgImage : cgImage , isOpaque : true )
let imageFromCIImage = MTIImage ( ciImage : ciImage )
let imageFromCoreVideoPixelBuffer = MTIImage ( cvPixelBuffer : pixelBuffer , alphaType : . alphaIsOne )
let imageFromContentsOfURL = MTIImage ( contentsOf : url )
// unpremultiply alpha if needed
let unpremultipliedAlphaImage = image . unpremultiplyingAlpha ( ) let inputImage = ...
let filter = MTISaturationFilter ( )
filter . saturation = 0
filter . inputImage = inputImage
let outputImage = filter . outputImageMTIImage let options = MTIContextOptions ( )
guard let device = MTLCreateSystemDefaultDevice ( ) , let context = try ? MTIContext ( device : device , options : options ) else {
return
}
let image : MTIImage = ...
do {
try context . render ( image , to : pixelBuffer )
//context.makeCIImage(from: image)
//context.makeCGImage(from: image)
} catch {
print ( error )
}MTIImage let imageView = MTIImageView ( frame : self . view . bounds )
// You can optionally assign a `MTIContext` to the image view. If no context is assigned and `automaticallyCreatesContext` is set to `true` (the default value), a `MTIContext` is created automatically when the image view renders its content.
imageView . context = ...
imageView . image = image如果您想将GPU命令编码过程移出主线程,则可以使用MTIThreadSafeImageView 。您可以将MTIImage分配给任何线程中的MTIThreadSafeImageView 。
MetalPetal具有用于连接过滤器的类型Swift API。您可以在FilterGraph.makeImage函数中使用=>运算符来连接过滤器并获取输出图像。
这里有一些例子:
let image = try ? FilterGraph . makeImage { output in
inputImage => saturationFilter => exposureFilter => output
} let image = try ? FilterGraph . makeImage { output in
inputImage => saturationFilter => exposureFilter => contrastFilter => blendFilter . inputPorts . inputImage
exposureFilter => blendFilter . inputPorts . inputBackgroundImage
blendFilter => output
}您可以直接使用=>直接连接一元过滤器( MTIUnaryFilter )。
对于具有多个输入的过滤器,您需要连接到其一个inputPorts之一。
=>操作员仅在FilterGraph.makeImage方法中起作用。
一个和只有一个过滤器的输出可以连接到output 。
与AVPlayer合作:
let context = try MTIContext ( device : device )
let asset = AVAsset ( url : videoURL )
let composition = MTIVideoComposition ( asset : asset , context : context , queue : DispatchQueue . main , filter : { request in
return FilterGraph . makeImage { output in
request . anySourceImage! => filterA => filterB => output
} !
}
let playerItem = AVPlayerItem ( asset : asset )
playerItem . videoComposition = composition . makeAVVideoComposition ( )
player . replaceCurrentItem ( with : playerItem )
player . play ( )导出视频:
以下示例需要Videoio。
import VideoIO
var configuration = AssetExportSession . Configuration ( fileType : . mp4 , videoSettings : . h264 ( videoSize : composition . renderSize ) , audioSettings : . aac ( channels : 2 , sampleRate : 44100 , bitRate : 128 * 1000 ) )
configuration . videoComposition = composition . makeAVVideoComposition ( )
self . exporter = try ! AssetExportSession ( asset : asset , outputURL : outputURL , configuration : configuration )
exporter . export ( progress : { progress in
} , completion : { error in
} )此示例需要Videoio。
import VideoIO
// Setup Image View
let imageView = MTIImageView ( frame : self . view . bounds )
...
// Setup Camera
let camera = Camera ( captureSessionPreset : . hd1920x1080 , configurator : . portraitFrontMirroredVideoOutput )
try camera . enableVideoDataOutput ( on : DispatchQueue . main , delegate : self )
camera . videoDataOutput ? . videoSettings = [ kCVPixelBufferPixelFormatTypeKey as String : kCVPixelFormatType_420YpCbCr8BiPlanarFullRange ]
...
// AVCaptureVideoDataOutputSampleBufferDelegate
let filter = MTIColorInvertFilter ( )
func captureOutput ( _ output : AVCaptureOutput , didOutput sampleBuffer : CMSampleBuffer , from connection : AVCaptureConnection ) {
guard let pixelBuffer = CMSampleBufferGetImageBuffer ( sampleBuffer ) else {
return
}
let inputImage = MTIImage ( cvPixelBuffer : pixelBuffer , alphaType : . alphaIsOne )
filter . inputImage = inputImage
self . imageView . image = filter . outputImage
}请参阅示例项目中的CameraFilterView.swift ,以了解有关预览和录制过滤的实时视频的更多信息。
尽可能重复使用MTIContext 。
上下文是重量级对象,因此,如果您确实创建了一个对象,请尽早进行操作,并在每次需要渲染图像时重复使用它。
明智地使用MTIImage.cachePolicy 。
当您不想保留图像的渲染结果时,请使用MTIImageCachePolicyTransient ,例如,当图像只是过滤器链中的中间结果时,因此可以重复使用渲染结果的基本纹理。这是最有效的内存效率选项。但是,当您要求上下文渲染先前渲染的图像时,它可能会重新渲染该图像,因为它的基本纹理已被重复使用。
默认情况下,过滤器的输出图像具有transient策略。
当您要防止基础纹理被重复使用时,请使用MTIImageCachePolicyPersistent 。
默认情况下,从外部来源创建的图像具有persistent策略。
了解MTIFilter.outputImage是计算属性。
每当您向过滤器询问其输出图像时,该过滤器即使输入与上一个呼叫相同,该过滤器也可能会给您一个新的输出图像对象。因此,请尽可能重复使用输出图像。
例如,
// ╭→ filterB
// filterA ─┤
// ╰→ filterC
//
// filterB and filterC use filterA's output as their input.在这种情况下,以下解决方案:
let filterOutputImage = filterA . outputImage
filterB . inputImage = filterOutputImage
filterC . inputImage = filterOutputImage比:
filterB . inputImage = filterA . outputImage
filterC . inputImage = filterA . outputImage如果要在.metal文件中包含MTIShaderLib.h ,则需要将MTIShaderLib.h文件的路径添加到Metal Compiler - Header Search Paths ( MTL_HEADER_SEARCH_PATHS )设置。
例如,如果您使用Cocoapods,则可以将MTL_HEADER_SEARCH_PATHS设置为${PODS_CONFIGURATION_BUILD_DIR}/MetalPetal/MetalPetal.framework/Headers或${PODS_ROOT}/MetalPetal/Frameworks/MetalPetal/Shaders 。如果使用Swift软件包管理器,请将MTL_HEADER_SEARCH_PATHS设置为$(HEADER_SEARCH_PATHS)
MetalPetal具有内置机制,可以为您编码着色器功能参数。您可以将着色器函数参数作为name: value字典到MTIRenderPipelineKernel.apply(toInputImages:parameters:outputDescriptors:) , MTIRenderCommand(kernel:geometry:images:parameters:)等。
例如,金属函数元素的参数vibranceAdjust可以是:
// Swift
let amount : Float = 1.0
let vibranceVector = float4 ( 1 , 1 , 1 , 1 )
let parameters = [ " amount " : amount ,
" vibranceVector " : MTIVector ( value : vibranceVector ) ,
" avoidsSaturatingSkinTones " : true ,
" grayColorTransform " : MTIVector ( value : float3 ( 0 , 0 , 0 ) ) ] // vibranceAdjust metal function
fragment float4 vibranceAdjust (...,
constant float & amount [[ buffer( 0 ) ]],
constant float4 & vibranceVector [[ buffer( 1 ) ]],
constant bool & avoidsSaturatingSkinTones [[ buffer( 2 ) ]],
constant float3 & grayColorTransform [[ buffer( 3 ) ]])
{
...
}
下面列出了着色器函数参数类型和在参数字典中使用的相应类型。
| 着色器功能参数类型 | 迅速 | Objective-C |
|---|---|---|
| 漂浮 | 漂浮 | 漂浮 |
| int | INT32 | int |
| Uint | Uint32 | Uint |
| 布尔 | 布尔 | 布尔 |
| SIMD(float2,float4,float4x4,int4等) | SIMD(带有MetalPetal/Swift ) / mtivector | mtivector |
| 结构 | 数据 / mtidatabuffer | NSDATA / MTIDATABUFFER |
| 其他(float *,struct *等)不可变 | 数据 / mtidatabuffer | NSDATA / MTIDATABUFFER |
| 其他(float *,struct *等)可变 | mtidatabuffer | mtidatabuffer |
要构建自定义的一元过滤器,您可以子类MTIUnaryImageRenderingFilter并覆盖SubclassingHooks类别中的方法。示例: MTIPixellateFilter , MTIVibranceFilter , MTIUnpremultiplyAlphaFilter , MTIPremultiplyAlphaFilter等
// Objective-C
@interface MTIPixellateFilter : MTIUnaryImageRenderingFilter
@property ( nonatomic ) float fractionalWidthOfAPixel;
@end
@implementation MTIPixellateFilter
- ( instancetype ) init {
if (self = [ super init ]) {
_fractionalWidthOfAPixel = 0.05 ;
}
return self;
}
+ (MTIFunctionDescriptor *) fragmentFunctionDescriptor {
return [[MTIFunctionDescriptor alloc ] initWithName: @" pixellateEffect " libraryURL: [bundle URLForResource: @" default " withExtension: @" metallib " ]];
}
- ( NSDictionary <NSString *,id> *) parameters {
return @{ @" fractionalWidthOfAPixel " : @(self. fractionalWidthOfAPixel )};
}
@end //Swift
class MTIPixellateFilter : MTIUnaryImageRenderingFilter {
var fractionalWidthOfAPixel : Float = 0.05
override var parameters : [ String : Any ] {
return [ " fractionalWidthOfAPixel " : fractionalWidthOfAPixel ]
}
override class func fragmentFunctionDescriptor ( ) -> MTIFunctionDescriptor {
return MTIFunctionDescriptor ( name : " pixellateEffect " , libraryURL : MTIDefaultLibraryURLForBundle ( Bundle . main ) )
}
}要构建更复杂的过滤器,您需要做的就是创建一个内核( MTIRenderPipelineKernel / MTIComputePipelineKernel / MTIMPSKernel ),然后将内核应用于输入图像。示例: MTIChromaKeyBlendFilter , MTIBlendWithMaskFilter , MTIColorLookupFilter等
@interface MTIChromaKeyBlendFilter : NSObject <MTIFilter>
@property ( nonatomic , strong , nullable ) MTIImage *inputImage;
@property ( nonatomic , strong , nullable ) MTIImage *inputBackgroundImage;
@property ( nonatomic ) float thresholdSensitivity;
@property ( nonatomic ) float smoothing;
@property ( nonatomic ) MTIColor color;
@end
@implementation MTIChromaKeyBlendFilter
@synthesize outputPixelFormat = _outputPixelFormat;
+ (MTIRenderPipelineKernel *) kernel {
static MTIRenderPipelineKernel *kernel;
static dispatch_once_t onceToken;
dispatch_once (&onceToken, ^{
kernel = [[MTIRenderPipelineKernel alloc ] initWithVertexFunctionDescriptor: [[MTIFunctionDescriptor alloc ] initWithName: MTIFilterPassthroughVertexFunctionName] fragmentFunctionDescriptor: [[MTIFunctionDescriptor alloc ] initWithName: @" chromaKeyBlend " ]];
});
return kernel;
}
- ( instancetype ) init {
if (self = [ super init ]) {
_thresholdSensitivity = 0.4 ;
_smoothing = 0.1 ;
_color = MTIColorMake ( 0.0 , 1.0 , 0.0 , 1.0 );
}
return self;
}
- (MTIImage *) outputImage {
if (!self. inputImage || !self. inputBackgroundImage ) {
return nil ;
}
return [ self .class.kernel applyToInputImages: @[ self .inputImage, self .inputBackgroundImage]
parameters: @{ @" color " : [MTIVector vectorWithFloat4: (simd_float4){self. color . red , self. color . green , self. color . blue ,self. color . alpha }],
@" thresholdSensitivity " : @(self. thresholdSensitivity ),
@" smoothing " : @(self. smoothing )}
outputTextureDimensions: MTITextureDimensionsMake2DFromCGSize ( self .inputImage.size)
outputPixelFormat: self .outputPixelFormat];
}
@end您可以使用MTIRenderCommand在一个渲染通道中发出多个拉动呼叫。
// Create a draw call with kernelA, geometryA, and imageA.
let renderCommandA = MTIRenderCommand ( kernel : self . kernelA , geometry : self . geometryA , images : [ imageA ] , parameters : [ : ] )
// Create a draw call with kernelB, geometryB, and imageB.
let renderCommandB = MTIRenderCommand ( kernel : self . kernelB , geometry : self . geometryB , images : [ imageB ] , parameters : [ : ] )
// Create an output descriptor
let outputDescriptor = MTIRenderPassOutputDescriptor ( dimensions : MTITextureDimensions ( width : outputWidth , height : outputHeight , depth : 1 ) , pixelFormat : . bgra8Unorm , loadAction : . clear , storeAction : . store )
// Get the output images, the output image count is equal to the output descriptor count.
let images = MTIRenderCommand . images ( byPerforming : [ renderCommandA , renderCommandB ] , outputDescriptors : [ outputDescriptor ] )您还可以创建多个输出描述符以在一个渲染通道中输出多个图像(MRT,请参阅https://en.wikipedia.org/wiki/multiple_render_targets)。
当MTIVertex无法满足您的需求时,您可以实现MTIGeometry协议,以将自定义的顶点数据提供给命令编码器。
使用MTIRenderCommand api发出拨打电话并传递自定义MTIGeometry 。
在极少数情况下,您可能需要直接访问基础纹理,在一个渲染通道中使用多个MPS内核,进行3D渲染或自己编码渲染命令。
MTIImagePromise协议可直接访问基础纹理和渲染上下文,从而为金属斑点迈出了一步。
您可以通过实现MTIImagePromise协议来创建新的输入源或完全自定义的处理单元。您将需要导入一个额外的模块才能这样做。
Objective-C
@import MetalPetal.Extension;
迅速
// CocoaPods
import MetalPetal.Extension
// Swift Package Manager
import MetalPetalObjectiveC.Extension
例如,请参见MTIComputePipelineKernel , MTICLAHELUTRecipe或MTIImage的实现。
如果图像中使用了alpha通道,则有两种常见的表示:非企业(直/无相关)alpha和parmultiple(相关)alpha。
RGB组件使用无培养的alpha代表像素的颜色,无视其不透明度。
使用超级alpha,RGB组件代表像素的颜色,通过乘法为其不透明度调整。
Metalpetal明确处理α类型。您负责在图像创建期间提供正确的α类型。
金属斑中有三种α类型。
MTIAlphaType.nonPremultiplied :图像中的alpha值未进行体育。
MTIAlphaType.premultiplied :图像中的alpha值进行了体育锻炼。
MTIAlphaType.alphaIsOne :图像中没有α通道或图像不透明。
通常, CGImage , CVPixelBuffer和CIImage对象具有alpha频道。强烈建议使用MTIAlphaType.alphaIsOne ,如果图像不透明,例如,来自相机提要的CVPixelBuffer或jpg文件中加载的CGImage 。
您可以在MTIImage上调用unpremultiplyingAlpha()或premultiplyingAlpha()以转换图像的alpha类型。
出于绩效原因,Alpha类型验证仅发生在调试构建中。
金属中的大多数过滤器都接受无培养的α和不透明的图像,并输出未培养的α图像。
具有outputAlphaType属性的过滤器接受所有alpha类型的输入。您可以使用outputAlphaType指定输出图像的alpha类型。
例如, MTIBlendFilter , MTIMultilayerCompositingFilter , MTICoreImageUnaryFilter , MTIRGBColorSpaceConversionFilter
实际上没有修改颜色的过滤器具有传递的Alpha处理规则,这意味着输出图像的alpha类型与输入图像相同。
例如, MTITransformFilter , MTICropFilter , MTIPixellateFilter , MTIBulgeDistortionFilter
有关Alpha类型和Alpha合成的更多信息,请参阅Bartosz Ciechanowski的这篇惊人的交互式文章。
颜色空间对于图像处理至关重要。没有颜色空间,红色,绿色和蓝色组件的数字值没有意义。
在继续如何处理金属处理颜色空间之前,您可能想知道什么是颜色空间以及它如何影响颜色值的表示。网络上有许多文章解释颜色空间,为了开始,该建议是颜色空间 - Bartosz Ciechanowski。
不同的软件和框架具有不同的处理颜色空间的方式。例如,Photoshop具有默认的SRGB IEC61966-2.1工作色彩空间,而默认情况下,核心图像使用线性SRGB工作颜色空间。
金属纹理不会与它们一起存储任何颜色空间信息。金属斑点中的大多数颜色空间处理发生在图像数据的输入( MTIImage(...) )和输出( MTIContext.render... )。
为输入指定颜色空间意味着金属斑应该在创建纹理期间将源颜色值转换为指定的颜色空间。
从URL或CGImage加载时,您可以使用MTICGImageLoadingOptions指定要使用纹理数据的颜色空间。如果加载图像时未指定任何选项,则使用设备RGB颜色空间( MTICGImageLoadingOptions.default )。 nil空间禁用颜色匹配,这等同于使用输入图像的颜色空间来创建MTICGImageLoadingOptions 。如果指定的颜色空间的模型不是RGB,则将设备RGB颜色空间用作后备。
从CIImage加载时,您可以使用MTICIImageRenderingOptions指定您想要的纹理数据所需的颜色空间。如果加载CIImage时未指定任何选项,则使用设备RGB颜色空间( MTICIImageRenderingOptions.default )。 nil空间禁用颜色匹配,颜色值在CIContext的工作颜色空间中加载。
指定输出的颜色空间时,颜色空间更像是一个标签,该标签用于与系统的其余部分通信,以表示如何表示输出中的颜色值。没有进行实际的颜色空间转换。
您可以使用MTIContext.makeCGImage...或MTIContext.startTaskTo...使用colorSpace参数来指定输出CGImage的颜色空间。
您可以使用MTICIImageCreationOptions指定输出CIImage的颜色空间。
金属斑假设当未指定输出颜色空间时,输出颜色值在设备RGB颜色空间中。
CVPixelBuffer的颜色空间MetalPetal使用CVMetalTextureCache和IOSurface将CVPixelBuffer S直接映射到金属纹理。因此,您无法指定从或渲染到CVPixelBuffer颜色空间。但是,您可以指定是否使用具有SRGB像素格式的纹理进行映射。
在金属中,如果像素格式名称具有_sRGB后缀,则在读取和写入像素中的颜色值期间应用SRGB伽马压缩和解压缩。这意味着具有_sRGB像素格式的纹理假设其存储的颜色值是SRGB伽马校正的,当在着色器中读取颜色值时,SRGB将执行SRGB到线性RGB转换。当颜色值写在着色器中时,将执行线性RGB到SRGB转换。
您可以使用MTIRGBColorSpaceConversionFilter执行颜色空间转换。颜色空间转换功能也可以在MTIShaderLib.h中获得。
metalpetal::sRGBToLinear (SRGB IEC61966-2.1至线性SRGB)metalpetal::linearToSRGB (线性SRGB到SRGB IEC61966-2.1)metalpetal::linearToITUR709 (线性SRGB至itu-R 709)metalpetal::ITUR709ToLinear (iTu-r 709 to Linear srgb) 您可以使用MTISCNSceneRenderer从SCNScene生成MTIImage s。您可能需要处理场景渲染器的线性RGB颜色空间,请参见第76期,Scenekit的图像比正常情况要黑。
您可以使用MTISKSceneRenderer从SKScene生成MTIImage s。
您可以从CIImage s创建MTIImage s。
您可以使用MTIContext渲染MTIImage到CIImage 。
您可以直接与MTICoreImageKernel或MTICoreImageUnaryFilter类一起使用CIFilter 。 (仅迅速)
参见MetalPetaljs
使用MetalPetaljs,您可以使用JavaScript创建渲染管道和过滤器,从而可以从“云”下载过滤器/渲染器。
建议您使用接受MTICGImageLoadingOptions加载CGImage s和来自URL的图像的API,而不是使用接受MTKTextureLoaderOption的API。
当您使用接受MTKTextureLoaderOption的API时,默认情况下,MetalPetal会使用MTIDefaultTextureLoader加载CGImage S,来自URL的图像和命名图像。 MTIDefaultTextureLoader在内部使用MTKTextureLoader ,并为MTKTextureLoader的不一致和错误提供了一些解决方法,其性能成本很小。您还可以通过实现MTITextureLoader协议来创建自己的纹理加载程序。然后,在创建MTIContext时,将您的纹理加载程序类分配给MTIContextOptions.textureLoaderClass 。
您可以使用Cocoapods安装最新版本。
use_frameworks!
pod 'MetalPetal'
# Required if you are using Swift.
pod 'MetalPetal/Swift'
# Recommended if you'd like to run MetalPetal on Apple silicon Macs.
pod 'MetalPetal/AppleSilicon'
Swift为Objective-C API提供特定于Swift的添加和修改,以改善其映射到Swift。强烈建议您使用Swift。
AppleSilicon提供以金属阴影语言v2.3编译的默认着色器库,这是在Apple Silicon Mac上启用可编程的混合支持所需的。
将软件包依赖关系添加到您的应用
MetalPetal可以在模拟器上使用Xcode 11+和MacOS 10.15+运行。
MetalPerformanceShaders.framework在模拟器上不可用,因此依靠MetalPerformanceShaders的过滤器,例如MTIMPSGaussianBlurFilter , MTICLAHEFilter ,不起作用。
与实际的Apple GPU相比,模拟器支持更少的功能或不同的实现限制。有关详细信息,请参见开发在模拟器中运行的金属应用程序。
如果您快速查看MTIImage ,它将显示您构建的图像图形图。

为什么要进行Objective-C?
感谢您考虑为Metalpetal做出贡献。请阅读我们的贡献指南。
金属塞有缓解许可。执照
/MetalPetalExamples中的文件目录是在单独许可下许可的。许可证
文档是许可的CC-BY-4.0。