本章介绍如何创建和使用MTLRenderCommandEncoder以及MTLParallelRenderCommandEncoder用于将图形渲染命令编码到命令缓冲区中的对象。MTLRenderCommandEncoder命令描述了一个图形渲染管道,如图5-1所示。
一个MTLRenderCommandEncoder对象表示一个渲染命令编码器。一个MTLParallelRenderCommandEncoder对象使单个渲染遍可以分解成多个单独的MTLRenderCommandEncoder对象,每个对象可以分配给一个不同的线程。然后,来自不同渲染命令编码器的命令被链接在一起并以一致且可预测的顺序执行,如针对渲染通过的多线程所述。
创建和使用渲染命令编码器
要创建,初始化和使用单个渲染命令编码器:
1:创建一个MTLRenderPassDescriptor对象来定义一个附件集合,该附件作为渲染过程的命令缓冲区中的图形命令的渲染目的地。通常,您创建一个MTLRenderPassDescriptor对象一次,并在应用程序呈现一帧时重用该对象。请参阅创建渲染通过描述符。
2:MTLRenderCommandEncoder通过调用指定的渲染遍历描述符的renderCommandEncoderWithDescriptor:方法来创建对象MTLCommandBuffer。请参阅使用渲染通过描述符创建渲染命令编码器。
3:创建一个MTLRenderPipelineState对象以定义一个或多个绘图调用的图形渲染管线(包括着色器,混合,多采样和可见性测试)的状态。要使用此渲染流水线状态绘制图元,请调用其setRenderPipelineState:方法
MTLRenderCommandEncoder。有关详细信息,请参阅创建渲染管道状态。
4:设置渲染命令编码器使用的纹理,缓冲区和采样器,如为渲染命令编码器指定资源所述。
5:调用MTLRenderCommandEncoder方法来指定额外的固定功能状态,包括深度和模板状态,如固定功能状态操作中所述。
6:最后,调用MTLRenderCommandEncoder绘制图形基元的方法,如“ 图形几何基元”中所述。
创建渲染通过描述符
一个MTLRenderPassDescriptor对象表示编码渲染命令的目的地,它是附件的集合。渲染遍历描述符的属性可以包括用于彩色像素数据的多达四个附件的阵列,用于深度像素数据的一个附件和用于模版像素数据的一个附件。的renderPassDescriptor便利方法创建一个MTLRenderPassDescriptor带有颜色,深度,和使用默认安装状态模版附件属性对象。该visibilityResultBuffer属性指定设备可以更新的缓冲区,以指示是否有任何样品通过深度和模板测试 - 有关详细信息,请参阅固定功能状态操作。
每个单独的附件,包括将被写入的纹理由附件描述符表示。对于附件描述符,必须适当地选择关联纹理的像素格式以存储颜色,深度或模板数据。对于颜色附加描述符,请MTLRenderPassColorAttachmentDescriptor使用可呈现颜色的像素格式。对于深度附件描述符,请MTLRenderPassDepthAttachmentDescriptor使用深度可渲染像素格式,例如MTLPixelFormatDepth32Float。对于模板附件描述符,MTLRenderPassStencilAttachmentDescriptor使用模板可渲染像素格式,例如MTLPixelFormatStencil8。
纹理在设备上每个像素实际使用的内存量并不总是与Metal框架代码中纹理像素格式的大小匹配,因为设备添加了填充以进行对齐或其他目的。有关每个像素格式实际使用的内存量以及附件大小和数量的限制,请参阅“ Metal特征集表”一章。
加载和存储操作
的loadAction和storeAction附接描述符的属性指定在开始或一个渲染通道的端部执行的动作。(对于MTLParallelRenderCommandEncoder,加载和存储操作发生在整个命令的边界,而不是每个MTLRenderCommandEncoder对象的边界。有关详细信息,请参阅多个线程进行渲染传递。)
可能的loadAction值包括:
MTLLoadActionClear,它将相同的值写入指定附件描述符中的每个像素。有关此操作的更多详细信息,请参阅指定清除加载操作。
MTLLoadActionLoad,它保留纹理的现有内容。
MTLLoadActionDontCare,它允许附件中的每个像素在渲染过程开始时承担任何值。
如果您的应用程序将呈现给定帧的附件的所有像素,请使用默认加载操作MTLLoadActionDontCare。该MTLLoadActionDontCare操作允许GPU避免加载纹理的现有内容,确保最佳性能。否则,您可以使用该MTLLoadActionClear操作来清除附件的以前内容或MTLLoadActionLoad保留它们的操作。该MTLLoadActionClear动作也避免了加载现有的纹理内容,但是会导致以纯色填充目的地的费用。
可能的storeAction值包括:
MTLStoreActionStore,它将渲染过程的最终结果保存到附件中。
MTLStoreActionMultisampleResolve,它将渲染目标中的多重采样数据解析为单个样本值,将它们存储在由附件属性指定的纹理中resolveTexture,并保留附件的内容未定义。有关详细信息,请参阅示例:创建多采样渲染的渲染通过描述符。
MTLStoreActionDontCare,其在渲染通过完成之后将附件保持在未定义的状态。这可以提高性能,因为它使实现能够避免保留渲染结果所需的任何工作。
对于颜色附件,MTLStoreActionStore操作是默认存储操作,因为应用程序在渲染通过结束时几乎总是保留附件中的最终颜色值。对于深度和模板附件,MTLStoreActionDontCare是默认存储操作,因为这些附件通常在渲染遍完成后通常不需要保留。
指定清除负载动作
如果loadAction附件描述符的属性设置为MTLLoadActionClear,则在渲染过程开始时将清除值写入指定附件描述符中的每个像素。清算价值属性取决于附件的类型。
For MTLRenderPassColorAttachmentDescriptor,clearColor包含MTLClearColor由四个双精度浮点RGBA组件组成的值,用于清除颜色附件。该MTLClearColorMake功能从红色,绿色,蓝色和Alpha组件创建一个清晰的颜色值。默认的清除颜色是(0.0,0.0,0.0,1.0)或不透明的黑色。
For MTLRenderPassDepthAttachmentDescriptor,clearDepth包含用于清除深度附件的范围[0.0,1.0]中的一个双精度浮点清除值。默认值为1.0。
For MTLRenderPassStencilAttachmentDescriptor,clearStencil包含一个32位无符号整数,用于清除模板附件。默认值为0。
示例:使用加载和存储操作创建渲染传递描述符
清单5-1创建了一个带有颜色和深度附件的简单渲染遍历描述符。首先,创建两个纹理对象,一个具有可呈现颜色的像素格式,另一个具有深度像素格式。接下来renderPassDescriptor,MTLRenderPassDescriptor创建一个默认渲染传递描述符的方便方法。然后通过属性访问颜色和深度附件MTLRenderPassDescriptor。纹理和动作被设置colorAttachments[0],代表第一个颜色附件(在数组的索引0)和深度附件。
清单5-1 使用颜色和深度附件创建渲染通过描述符
MTLTextureDescriptor * colorTexDesc = [MTLTextureDescriptor
texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
width:IMAGE_WIDTH height:IMAGE_HEIGHT mipmapped:NO];
id <MTLTexture> colorTex = [device newTextureWithDescriptor:colorTexDesc];
MTLTextureDescriptor * depthTexDesc = [MTLTextureDescriptor
texture2DDescriptorWithPixelFormat:MTLPixelFormatDepth32Float
width:IMAGE_WIDTH height:IMAGE_HEIGHT mipmapped:NO];
id <MTLTexture> depthTex = [device newTextureWithDescriptor:depthTexDesc];
MTLRenderPassDescriptor * renderPassDesc = [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDesc.colorAttachments [0] .texture = colorTex;
renderPassDesc.colorAttachments [0] .loadAction = MTLLoadActionClear;
renderPassDesc.colorAttachments [0] .storeAction = MTLStoreActionStore;
renderPassDesc.colorAttachments [0] .clearColor = MTLClearColorMake(0.0,1.0,0.0,1.0);
renderPassDesc.depthAttachment.texture = depthTex;
renderPassDesc.depthAttachment.loadAction = MTLLoadActionClear;
renderPassDesc.depthAttachment.storeAction = MTLStoreActionStore;
renderPassDesc.depthAttachment.clearDepth = 1.0;
示例:创建多采样渲染的渲染通过描述符
要使用该MTLStoreActionMultisampleResolve操作,您必须将该texture属性设置为多采样类型的纹理,该resolveTexture属性将包含多重采样解析操作的结果。(如果texture不支持多重采样,然后多重采样解析操作的结果是不确定的。)的resolveLevel,resolveSlice和resolveDepthPlane性能也可以用于多重采样解析操作来指定的mipmap水平,立方体切片,和多重采样纹理的深度平面,分别。在大多数情况下,默认值resolveLevel,resolveSlice以及resolveDepthPlane是可用的。在清单5-2,初始创建附件,然后它的loadAction,storeAction,texture,和resolveTexture属性被设置为支持多重采样的决心。
清单5-2 设置具有多次采样解析的附件的属性
MTLTextureDescriptor * colorTexDesc = [MTLTextureDescriptor
texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
width:IMAGE_WIDTH height:IMAGE_HEIGHT mipmapped:NO];
id <MTLTexture> colorTex = [device newTextureWithDescriptor:colorTexDesc];
MTLTextureDescriptor * msaaTexDesc = [MTLTextureDescriptor
texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
width:IMAGE_WIDTH height:IMAGE_HEIGHT mipmapped:NO];
msaaTexDesc.textureType = MTLTextureType2DMultisample;
msaaTexDesc.sampleCount = sampleCount; //必须> 1
id <MTLTexture> msaaTex = [device newTextureWithDescriptor:msaaTexDesc];
MTLRenderPassDescriptor * renderPassDesc = [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDesc.colorAttachments [0] .texture = msaaTex;
renderPassDesc.colorAttachments [0] .resolveTexture = colorTex;
renderPassDesc.colorAttachments [0] .loadAction = MTLLoadActionClear;
renderPassDesc.colorAttachments [0] .storeAction = MTLStoreActionMultisampleResolve;
renderPassDesc.colorAttachments [0] .clearColor = MTLClearColorMake(0.0,1.0,0.0,1.0);
使用渲染通过描述符创建渲染命令编码器
创建渲染传递描述符并指定其属性后,使用对象的renderCommandEncoderWithDescriptor:方法MTLCommandBuffer创建一个render命令编码器,如清单5-3所示。
清单5-3 使用渲染通过描述符创建渲染命令编码器
id <MTLRenderCommandEncoder> renderCE = [commandBuffer
renderCommandEncoderWithDescriptor:renderPassDesc];
用核心动画显示呈现的内容
Core Animation定义了这个CAMetalLayer类,它是针对其内容使用Metal呈现的层支持视图的专门行为而设计的。甲CAMetalLayer对象表示关于内容(位置和尺寸)的几何形状的信息,它的视觉属性(背景色,边框和阴影),和由Metal所使用的资源以呈现在彩色附件的内容。它还封装了内容呈现的时间,以便内容可以在可用或在指定的时间一直显示。有关核心动画的更多信息,请参阅核心动画编程指南。
核心动画还定义了CAMetalDrawable可显示资源的对象的协议。该CAMetalDrawable协议扩展MTLDrawable并提供符合MTLTexture协议的对象,因此可以将其用作渲染命令的目的地。要渲染成一个
CAMetalLayer对象,您应该CAMetalDrawable为每个渲染遍获取一个新对象,获取MTLTexture它提供的对象,并使用该纹理创建颜色附件。与颜色附件不同,深度或模板附件的创建和销毁是昂贵的。如果您需要深度或模板附件,请创建它们一次,然后在每次渲染框架时重用它们。
通常,您可以使用该layerClass方法将其指定CAMetalLayer为您自己的自定义UIView子类的后盾层类型,如清单5-4所示。否则,您可以CAMetalLayer使用其init方法创建一个,并将图层包含在现有视图中。
清单5-4 使用CAMetalLayer作为UIView子类的后盾层
+ (id) layerClass {
return [CAMetalLayer class];
}
要显示由Metal在图层中呈现的内容,您必须CAMetalDrawable从CAMetalLayer对象获取可显示的资源(对象),然后通过将其附加到MTLRenderPassDescriptor对象来渲染到该资源中的纹理。为此,您首先设置CAMetalLayer描述其提供的可绘制资源的对象的属性,然后在nextDrawable每次开始渲染新框架时调用其方法。如果CAMetalLayer属性未设置,nextDrawable方法调用失败。以下CAMetalLayer属性描述可绘制对象:
该pixelFormat属性声明纹理的像素格式。支持的值是MTLPixelFormatBGRA8Unorm(默认值)和MTLPixelFormatBGRA8Unorm_sRGB。
该drawableSize属性声明了设备像素中纹理的尺寸。为了确保您的应用程序以显示的精确尺寸呈现内容(无需在某些设备上进行附加采样阶段),请在计算图层所需的大小时,将目标屏幕nativeScale或nativeBounds属性考虑在内。
该framebufferOnly属性声明纹理是否只能用作附件(YES),或者它是否也可以用于纹理采样和像素读取/写入操(NO)。如果YES,层对象可以优化显示纹理。对于大多数应用,建议的值为YES。
该presentsWithTransaction属性声明是否使用标准的Core Animation事务机制(YES)更新了层渲染资源的更改,或者与异常更新(NO默认值)异常更新。
如果nextDrawable方法成功,它返回一个CAMetalDrawable具有以下只读属性的对象:
- 该texture属性保存纹理对象。在创建渲染管道(MTLRenderPipelineColorAttachmentDescriptor对象)时,将其用作附件。
- 该layer属性指向CAMetalLayer该负责显示绘制对象。
重要提示: 只有一小部分可绘制的资源,因此长帧渲染时间可能暂时耗尽这些资源,并导致nextDrawable方法调用阻止其CPU线程直到该方法完成。为了避免昂贵的CPU停止,请在调用对象的nextDrawable方法之前执行不需要可绘制资源的所有每帧操作CAMetalLayer。 |
---|
要在渲染完成后显示可绘制对象的内容,您必须通过调用可绘制对象的present方法将其提交给Core Animation 。要同步与负责其呈现命令缓冲区完成一个绘制的介绍,你可以调用的presentDrawable:还是
presentDrawable:atTime:一个上方便的方法MTLCommandBuffer对象。这些方法使用调度处理程序(请参阅注册处理程序块进行命令缓冲区执行)来调用drawable的present方法,该方法涵盖大多数场景。该presentDrawable:atTime:方法进一步控制何时显示可绘制。
创建渲染管道状态
要使用MTLRenderCommandEncoder对象编码渲染命令,必须首先指定一个MTLRenderPipelineState对象来定义任何绘图调用的图形状态。渲染流水线状态对象是一个长寿命的持久对象,可以在渲染命令编码器之外创建,提前高速缓存,并重用于多个渲染命令编码器。当描述相同的图形状态集时,重新使用先前创建的渲染流水线状态对象可以避免重新评估并将指定状态转换为GPU命令的昂贵的操作。
渲染管道状态是一个不可变的对象。要创建渲染流水线状态,您首先创建并配置一个MTLRenderPipelineDescriptor描述呈现管道状态的属性的可变对象。然后,使用描述符创建一个MTLRenderPipelineState对象。
创建和配置渲染流水线描述符
要创建一个渲染流水线状态,首先创建一个MTLRenderPipelineDescriptor对象,该对象具有描述在渲染过程中要使用的图形渲染流水线状态的属性,如图5-2所示。colorAttachments新MTLRenderPipelineDescriptor对象的属性包含一个对象数组MTLRenderPipelineColorAttachmentDescriptor,每个描述符表示一个颜色附加状态,它指定该附件的混合操作和因子,如“ 渲染流水线附件描述符中的配置混合”中所详述。附件描述符还指定附件的像素格式,它必须与渲染流水线描述符的纹理与相应附件索引的像素格式匹配,否则发生错误。
除了配置颜色附件之外,还要为MTLRenderPipelineDescriptor对象设置这些属性:
- 设置depthAttachmentPixelFormat属性以匹配的像素格式的纹理depthAttachment在MTLRenderPassDescriptor。
- 设置stencilAttachmentPixelFormat属性以匹配的像素格式的纹理stencilAttachment在MTLRenderPassDescriptor。
- 要在渲染流水线状态中指定顶点或片段着色器,请分别设置vertexFunction或fragmentFunction属性。设置fragmentFunction于nil像素的光栅化禁用到指定颜色的附件,其通常用于深度只呈现或用于输出数据到从顶点着色器的缓冲器的对象。
- 如果顶点着色器具有每顶点输入属性的参数,请设置该vertexDescriptor属性以描述该参数中顶点数据的组织,如数据组织顶点描述符中所述。
- 对于大多数典型的渲染任务YES,该rasterizationEnabled属性的默认值就足够了。要仅使用图形流水线的顶点阶段(例如,收集在顶点着色器中转换的数据),请将此属性设置为NO。
- 如果附件支持多重采样(即附件是MTLTextureType2DMultisample
类型纹理),则可以为每个像素创建多个样本。要确定片段如何组合以提供像素覆盖,请使用以下MTLRenderPipelineDescriptor属性。- 该sampleCount属性确定每个像素的样本数。当MTLRenderCommandEncoder被创建时,sampleCount对于纹理所有附件都必须符合这一sampleCount特性。如果附件不支持多重采样,sampleCount则为1,也是默认值。
- 如果alphaToCoverageEnabled设置为YES,则colorAttachments[0]读取用于输出的alpha通道片段,并用于确定覆盖掩码。
- 如果alphaToOneEnabled设置为YES,那么alpha通道片段值colorAttachments[0]被强制为1.0,这是最大的可表示值。(其他附件不受影响。)
从描述符创建渲染管道状态
创建渲染流水线描述符并指定其属性后,使用它创建MTLRenderPipelineState对象。因为创建渲染流水线状态可能需要昂贵的图形状态评估和可能的指定图形着色器的编译,所以您可以使用阻塞或异步方法来以最适合您应用程序设计的方式安排此类工作。
要同步创建渲染流水线状态对象,请调用对象的方法newRenderPipelineStateWithDescriptor:error:或newRenderPipelineStateWithDescriptor:options:reflection:error:方法MTLDevice。当Metal评估描述符的图形状态信息并编译着色器代码以创建管道状态对象时,这些方法将阻塞当前线程。
要异步创建渲染流水线状态对象,请调用对象的方法newRenderPipelineStateWithDescriptor:completionHandler:或newRenderPipelineStateWithDescriptor:options:completionHandler:方法MTLDevice。这些方法立即返回 - Metal异步地评估描述符的图形状态信息,并编译着色器代码来创建管道状态对象,然后调用完成处理程序来提供新MTLRenderPipelineState对象。
创建MTLRenderPipelineState对象时,您还可以选择创建反映数据,以显示管道的着色器函数及其参数的详细信息。该newRenderPipelineStateWithDescriptor:options:reflection:error:和newRenderPipelineStateWithDescriptor:options:completionHandler:方法提供这些数据。如果不使用反射数据,请避免使用。有关如何分析反射数据的更多信息,请参阅在运行时确定功能详细信息。
创建后MTLRenderPipelineState的对象,调用setRenderPipelineState:
的方法MTLRenderCommandEncoder来渲染管线状态在渲染使用命令编码器相关联。
清单5-5演示了创建一个调用的渲染管道状态对象pipeline。
MTLRenderPipelineDescriptor * renderPipelineDesc =
[[MTLRenderPipelineDescriptor alloc] init];
renderPipelineDesc.vertexFunction = vertFunc;
renderPipelineDesc.fragmentFunction = fragFunc;
renderPipelineDesc.colorAttachments [0] .pixelFormat = MTLPixelFormatRGBA8Unorm;
//从MTLRenderPipelineDescriptor创建MTLRenderPipelineState
NSError * errors = nil;
id <MTLRenderPipelineState> pipeline = [device
newRenderPipelineStateWithDescriptor:
renderPipelineDesc error:&errors];
assert(pipeline &&!errors);
//设置MTLRenderCommandEncoder的流水线状态
[renderCE setRenderPipelineState:pipeline];
变量vertFunc和fragFunc是被指定为称为渲染流水线状态描述符的性质着色器的功能renderPipelineDesc。同时调用对象的newRenderPipelineStateWithDescriptor:error:方法MTLDevice使用流水线状态描述符来创建渲染管道状态对象。调用指定与render命令编码器一起使用的对象的setRenderPipelineState:方法。MTLRenderCommandEncoderMTLRenderPipelineState
注意: 因为一个MTLRenderPipelineState对象创建成本很高,所以当你想要使用相同的图形状态时,应该重用它。 |
---|
在渲染管道附件描述符中配置混合
混合使用高度可配置的混合操作来将片段功能(源)返回的输出与附件(目标)中的像素值进行混合。混合操作决定了源和目标值如何与混合因子相结合。
要为颜色附件配置混合,请设置以下MTLRenderPipelineColorAttachmentDescriptor属性:
- 要启用混合,请设置blendingEnabled为YES。默认情况下禁用混合。
- writeMask识别哪些颜色通道被混合。默认值MTLColorWriteMaskAll允许混合所有颜色通道。
- rgbBlendOperation并alphaBlendOperation分别为RGB和Alpha片段数据分配一个MTLBlendOperation值。两个属性的默认值为MTLBlendOperationAdd。
- sourceRGBBlendFactor,sourceAlphaBlendFactor,destinationRGBBlendFactor,和destinationAlphaBlendFactor分配源和目标混合因子。
了解混合因素和操作
四个交融因素是指恒定混合颜色值:MTLBlendFactorBlendColor,
MTLBlendFactorOneMinusBlendColor,MTLBlendFactorBlendAlpha,和MTLBlendFactorOneMinusBlendAlpha。调用setBlendColorRed:green:blue:alpha:的方法MTLRenderCommandEncoder,以指定与这些混合因子使用的恒定的颜色和α值,如在固定功能状态的操作。
一些混合操作通过将源值乘以源MTLBlendFactor值(缩写为SBF),将目标值乘以目标混合因子(DBF),并使用由该MTLBlendOperation值表示的算术来组合结果来组合分段值。(如果该混合操作是任一MTLBlendOperationMin或MTLBlendOperationMax下,SBF和DBF混合因子被忽略。)例如,MTLBlendOperationAdd对于既rgbBlendOperation和alphaBlendOperation性质定义了用于RGB和α值以下添加剂混合操作:
- RGB =(Source.rgb * sourceRGBBlendFactor)+(Dest.rgb * destinationRGBBlendFactor)
- Alpha =(Source.a * sourceAlphaBlendFactor)+(Dest.a * destinationAlphaBlendFactor)
在默认混合行为中,源会完全覆盖目的地。这种行为等同于同时设置sourceRGBBlendFactor并sourceAlphaBlendFactor以MTLBlendFactorOne和destinationRGBBlendFactor和destinationAlphaBlendFactor到MTLBlendFactorZero。这种行为在数学上表示为:
- RGB =(Source.rgb * 1.0)+(Dest.rgb * 0.0
- A =(Source.a * 1.0)+(Dest.a * 0.0)
另一种常用的混合操作,其中源alpha定义剩余多少目的地颜色,可以用数学表达为:
- RGB =(Source.rgb * 1.0)+(Dest.rgb *(1 - Source.a))
- A =(Source.a * 1.0)+(Dest.a *(1 - Source.a))
使用自定义混合配置
清单5-6显示了使用混合操作MTLBlendOperationAdd,源混合因子MTLBlendFactorOne和目标混合因子的自定义混合配置的代码MTLBlendFactorOneMinusSourceAlpha。
colorAttachments[0]是具有MTLRenderPipelineColorAttachmentDescriptor指定混合配置的属性的对象。
清单5-6 指定自定义混合配置
MTLRenderPipelineDescriptor * renderPipelineDesc =
[[MTLRenderPipelineDescriptor alloc] init];
renderPipelineDesc.colorAttachments [0] .blendingEnabled = YES;
renderPipelineDesc.colorAttachments [0] .rgbBlendOperation = MTLBlendOperationAdd;
renderPipelineDesc.colorAttachments [0] .alphaBlendOperation = MTLBlendOperationAdd;
renderPipelineDesc.colorAttachments [0] .sourceRGBBlendFactor = MTLBlendFactorOne;
renderPipelineDesc.colorAttachments [0] .sourceAlphaBlendFactor = MTLBlendFactorOne;
renderPipelineDesc.colorAttachments [0] .destinationRGBBlendFactor =
MTLBlendFactorOneMinusSourceAlpha;
renderPipelineDesc.colorAttachments [0] .destinationAlphaBlendFactor =
MTLBlendFactorOneMinusSourceAlpha;
NSError * errors = nil;
id <MTLRenderPipelineState> pipeline = [device
newRenderPipelineStateWithDescriptor:renderPipelineDesc error:&errors];
为渲染命令编码器指定资源
MTLRenderCommandEncoder本节讨论的方法指定用作顶点和片段着色器函数的参数的资源,它们由对象中的属性vertexFunction和fragmentFunction属性指定MTLRenderPipelineState。这些方法将渲染资源(缓冲区,纹理和采样器)分配给atInderender命令编码器中的相应参数表index(),如图5-3所示。
以下setVertex*方法将一个或多个资源分配给顶点着色器函数的相应参数。
- setVertexBuffer:offset:atIndex:
- setVertexBuffers:offsets:withRange:
- setVertexTexture:atIndex:
- setVertexTextures:withRange:
- setVertexSamplerState:atIndex:
- setVertexSamplerState:lodMinClamp:lodMaxClamp:atIndex:
- setVertexSamplerStates:withRange:
- setVertexSamplerStates:lodMinClamps:lodMaxClamps:withRange:
这些setFragment*方法类似地将一个或多个资源分配给片段着色器函数的相应参数。
- setFragmentBuffer:offset:atIndex:
- setFragmentBuffers:offsets:withRange:
- setFragmentTexture:atIndex:
- setFragmentTextures:withRange:
- setFragmentSamplerState:atIndex:
- setFragmentSamplerState:lodMinClamp:lodMaxClamp:atIndex:
- setFragmentSamplerStates:withRange:
-
setFragmentSamplerStates:lodMinClamps:lodMaxClamps:withRange:
缓冲区参数表最多有31个条目,纹理参数表中有31个条目,采样器状态参数表中有16个条目。
在Metal着色语言源代码中指定资源位置的属性限定词必须与Metal框架方法中的参数表索引相匹配。在清单5-7中,分别为顶点着色器定义了两个分别为0和1的缓冲区(posBuf和texCoordBuf)。
列表5-7 Metal框架:为顶点函数指定资源
[renderEnc setVertexBuffer:posBuf offset:0 atIndex:0];
[renderEnc setVertexBuffer:texCoordBuf offset:0 atIndex:1];
在清单5-8中,函数签名具有与属性相应的预选赛参数buffer(0)和buffer(1)。
清单5-8 Metal着色语言:顶点函数参数匹配框架参数表指数
vertex VertexOutput metal_vert(float4 *posData [[ buffer(0) ]],
float2 *texCoordData [[ buffer(1) ]])
类似地,在清单5-9,(缓冲器,纹理和一个采样器fragmentColorBuf,shadeTex以及sampler,分别地),所有索引为0,针对片段着色器定义。
清单5-9 Metal框架:为片段函数指定资源
[renderEnc setFragmentBuffer:fragmentColorBuf offset:0 atIndex:0];
[renderEnc setFragmentTexture:shadeTex atIndex:0];
[renderEnc setFragmentSamplerState:sampler atIndex:0];
在清单5-10,函数签名具有与属性限定符相应参数buffer(0),texture(0)以及sampler(0)分别。
清单5-10 Metal着色语言:片段函数参数匹配框架参数表指数
fragment float4 metal_frag(VertexOutput in [[stage_in]], float4 *fragColorData [[ buffer(0) ]], texture2d<float> shadeTexValues [[ texture(0) ]], sampler samplerValues [[ sampler(0) ]] )
数据组织的顶点描述符
在Metal框架代码中,MTLVertexDescriptor每个流水线状态可以有一个描述输入到顶点着色器功能的数据的组织,并在着色语言和框架代码之间共享资源位置信息。
在Metal着色语言代码中,每顶点输入(例如标量或整数或浮点值向量)可以组织在一个结构中,可以在一个使用[[ stage_in ]]属性限定符声明的参数中传递,如VertexInput结构为例如顶点功能vertexMath在清单5-11。每顶点输入结构的每个字段都有[[ attribute(index) ]]限定符,它指定了顶点属性参数表中的索引。
清单5-11 Metal着色语言:带有属性指数的顶点函数输入
struct VertexInput {
float2 position [[ attribute(0) ]];
float4 color [[ attribute(1) ]];
float2 uv1 [[ attribute(2) ]];
float2 uv2 [[ attribute(3) ]];
};
struct VertexOutput {
float4 pos [[ position ]];
float4 color;
};
vertex VertexOutput vertexMath(VertexInput in [[ stage_in ]])
{
VertexOutput out;
out.pos = float4(in.position.x, in.position.y, 0.0, 1.0);
float sum1 = in.uv1.x + in.uv2.x;
float sum2 = in.uv1.y + in.uv2.y;
out.color = in.color + float4(sum1, sum2, 0.0f, 0.0f);
return out;
}
要使用[[ stage_in ]]限定符引用着色器函数输入,请描述一个MTLVertexDescriptor对象,然后将其设置为其 vertexDescriptor属性MTLRenderPipelineState。MTLVertexDescriptor有两个属性:attributes和layouts。
所述attributes的属性MTLVertexDescriptor是一个MTLVertexAttributeDescriptorArray用于定义每一顶点属性如何在被映射到一个顶点函数参数的缓冲器组织对象。该attributes属性可以支持访问在同一缓冲区内交错的多个属性(如顶点坐标,表面法线和纹理坐标)。阴影语言代码中成员的顺序不必在框架代码的缓冲区中保留。数组中的每个顶点属性描述符具有以下属性,它们提供了一个顶点着色器功能信息来定位和加载参数数据:
bufferIndex,它是指定MTLBuffer访问的缓冲区参数表的索引。在指定用于渲染命令编码器的资源中讨论缓冲区参数表。
format,其中规定了如何在框架代码中解释数据。如果数据类型不是精确类型匹配,则可能会转换或扩展。例如,如果着色语言类型是half4与框架format是[MTLVertexFormatFloat2(https://developer.apple.com/documentation/metal/mtlvertexformat/1515535-float2),则当数据被用作一个参数到顶点的功能,它可以被从浮到一半转换并从两个到四个元件膨胀(具有在0.0,1.0最后两个要素)。
offset,它指定从顶点开始可以找到数据的位置。
图5-4显示了一个MTLVertexAttributeDescriptorArrayMetal框架代码,它实现了与清单5-11中vertexMath着色语言代码中顶点函数的输入相对应的交错缓冲区。
清单5-12显示了对应于图5-4所示的交错缓冲区的Metal框架代码。
清单5-12 Metal框架:使用顶点描述符来访问交织数据
id <MTLFunction> vertexFunc = [library newFunctionWithName:@“vertexMath”];
MTLRenderPipelineDescriptor * pipelineDesc =
[[MTLRenderPipelineDescriptor alloc] init];
MTLVertexDescriptor * vertexDesc = [[MTLVertexDescriptor alloc] init];
vertexDesc.attributes [0] .format = MTLVertexFormatFloat2;
vertexDesc.attributes [0] .bufferIndex = 0;
vertexDesc.attributes [0] .offset = 0;
vertexDesc.attributes [1] .format = MTLVertexFormatFloat4;
vertexDesc.attributes [1] .bufferIndex = 0;
vertexDesc.attributes [1] .offset = 2 * sizeof(float); // 8 bytes
vertexDesc.attributes [2] .format = MTLVertexFormatFloat2;
vertexDesc.attributes [2] .bufferIndex = 0;
vertexDesc.attributes [2] .offset = 8 * sizeof(float); // 32字节
vertexDesc.attributes [3] .format = MTLVertexFormatFloat2;
vertexDesc.attributes [3] .bufferIndex = 0;
vertexDesc.attributes [3] .offset = 6 * sizeof(float); // 24 bytes
vertexDesc.layouts [0] .stride = 10 * sizeof(float); // 40 bytes
vertexDesc.layouts [0] .stepFunction = MTLVertexStepFunctionPerVertex;
pipelineDesc.vertexDescriptor = vertexDesc;
pipelineDesc.vertexFunction = vertFunc;
MTLVertexAttributeDescriptor对象attributes数组中的每个对象都MTLVertexDescriptor对应VertexInput于着色器函数中的索引结构成员。attributes[1].bufferIndex = 0指定参数表中索引号为0的缓冲区的使用。(在这个例子中,每个MTLVertexAttributeDescriptor都有相同的bufferIndex,所以每个引用在参数表中的索引0处的相同的顶点缓冲区。)offset值指定顶点内的数据的位置,因此attributes[1].offset = 2 * sizeof(float)将相应数据的起始位置从8启动缓冲区。format选择这些值以匹配着色器函数中的数据类型,因此attributes[1].format = MTLVertexFormatFloat4指定使用四个浮点值。
该layouts物业MTLVertexDescriptor是一个MTLVertexBufferLayoutDescriptorArray。对于每一个MTLVertexBufferLayoutDescriptor在layouts,属性指定如何顶点和属性数据是从对应的取出MTLBuffer时Metal绘制图元在参数表中。(有关绘图图元,看到绘制几何图元。)的stepFunction属性的MTLVertexBufferLayoutDescriptor判断是否获取属性数据的每个顶点,对于一些数量的实例,或者仅仅一次。如果stepFunction设置为获取某些数量的实例的属性数据,则stepRate属性MTLVertexBufferLayoutDescriptor决定了多少个实例。该stride属性指定两个顶点的数据之间的距离(以字节为单位)。
图5-5描述了MTLVertexBufferLayoutDescriptor对应于清单5-12中的代码。layouts[0]指定如何从缓冲区参数表中的相应索引0获取顶点数据。layouts[0].stride指定两个顶点的数据之间的40个字节的距离。的值layouts[0].stepFunction,MTLVertexStepFunctionPerVertex时,指定绘图时的属性数据被取为每个顶点。如果值stepFunction是MTLVertexStepFunctionPerInstance,所述stepRate属性确定多久属性数据被取出。例如,如果stepRate为1,则为每个实例获取数据; 如果stepRate是2,对于每两个实例,等等。