本章介绍MTLResource用于存储未格式化内存和格式化图像数据的Metal资源对象。有两种类型的MTLResource对象:
- MTLBuffer表示可以包含任何类型数据的未格式化内存的分配。缓冲区通常用于顶点,着色器和计算状态数据。
- MTLTexture表示具有指定纹理类型和像素格式的格式化图像数据的分配。纹理对象用作顶点,片段或计算函数的源纹理,以及存储图形渲染输出(即作为附件)。
MTLSamplerState本章还讨论了这些对象。虽然采样器本身不是资源,但在使用纹理对象执行查找计算时会使用它们。
缓冲区是内存的无类型分配
MTLBuffer对象表示的存储器的分配,它可以包含任何类型的数据。
创建缓冲区对象
MTLDevice方法创建并返回一个MTLBuffer对象:
newBufferWithLength:options:方法MTLBuffer使用新的存储分配创建对象。
newBufferWithBytes:length:options:方法
MTLBuffer
通过将数据从现有存储(位于CPU地址pointer
)复制到新的存储分配中来创建对象。newBufferWithBytesNoCopy:length:options:deallocator:方法创建
MTLBuffer
具有现有存储分配的对象,并且不为此对象分配任何新存储。
所有缓冲区创建方法都有输入值length
来指示存储分配的大小(以字节为单位)。所有方法也接受一个MTLResourceOptions
对象options
,可以修改创建的缓冲区的行为。如果值为options
0,则默认值用于资源选项。
缓冲方法
该MTLBuffer协议有以下方法:
该contents方法返回缓冲区存储分配的CPU地址。
该newTextureWithDescriptor:offset:bytesPerRow:方法创建一种特殊的纹理对象,引用缓冲区的数据。创建纹理对象中详细介绍了此方法。
纹理是格式化图像数据
MTLTexture对象表示可被用作用于顶点着色器资源,片段着色器,或计算功能格式化的图像数据的分配,或者作为附件被用作渲染目的地。MTLTexture对象可以有一个
以下结构:
1D,2D或3D图像
一维或一维2D图像
一个立方体六个2D图像
MTLPixelFormat
指定MTLTexture
对象中单个像素的组织。像素格式在Pixel Formats for Textures中进一步讨论。
创建纹理对象
以下方法创建并返回一个MTLTexture对象:
创建具有纹理图像数据的新存储分配的对象的newTextureWithDescriptor:方法,使用对象来描述纹理的属性。
MTLDevice``MTLTexture
MTLTextureDescriptornewTextureViewWithPixelFormat:的方法
MTLTexture
创建一个MTLTexture
共享相同的存储分配作为调用对象MTLTexture
的对象。由于它们共享相同的存储空间,因此对新纹理对象的像素的任何更改都会反映在调用纹理对象中,反之亦然。对于新创建的纹理,该newTextureViewWithPixelFormat:方法重新解释调用MTLTexture
对象的存储分配的现有纹理图像数据,就像数据以指定的像素格式存储一样。在MTLPixelFormat
新的纹理对象必须是兼容与MTLPixelFormat
原始纹理的对象。(有关普通,压缩和压缩像素格式的详细信息,请参阅纹理的像素格式。)所述newTextureWithDescriptor:offset:bytesPerRow:的方法
MTLBuffer
创建一个MTLTexture
共享主叫的存储分配对象MTLBuffer
对象作为其纹理图像数据。由于它们共享相同的存储,因此对新纹理对象的像素的任何更改都会反映在调用纹理对象中,反之亦然。在纹理和缓冲区之间共享存储可以防止使用某些纹理优化,例如像素调整或平铺。
使用纹理描述符创建纹理对象
MTLTextureDescriptor定义用于创建MTLTexture对象的属性,包括其图像大小(宽度,高度和深度),像素格式,排列(数组或多维数据集类型)和mipmap数。这些MTLTextureDescriptor
属性仅在创建MTLTexture
对象期间使用。创建MTLTexture
对象后,其MTLTextureDescriptor
对象中的属性更改不再对该纹理产生任何影响。
要从描述符创建一个或多个纹理:
-
创建一个MTLTextureDescriptor包含描述纹理数据的纹理属性的自定义对象:
textureType属性指定纹理的维度和排列(例如,数组或多维数据集)。
width,height和depth属性在基级纹理的mipmap的每个维度指定像素大小。
该pixelFormat属性指定像素在纹理中的存储方式。
arrayLength属性指定一个MTLTextureType1DArray或MTLTextureType2DArray类型纹理对象的数组元素的数量。
mipmapLevelCount属性指定mipmap级别的数量。
sampleCount属性指定每个像素中的样本数。
resourceOptions属性指定其内存分配的行为。
MTLTextureDescriptor通过调用对象的newTextureWithDescriptor:
方法从对象创建纹理
MTLDevice。纹理创建后,调用
replaceRegion:mipmapLevel:slice:withBytes:bytesPerRow:bytesPerImage:`方法来加载纹理图像数据,如将图像数据复制到纹理和从纹理复制图像数据中所述。要创建更多
MTLTexture
对象,可以重用相同的MTLTextureDescriptor
对象,根据需要修改描述符的属性值。
清单3-1显示了用于创建纹理描述符txDesc
并为3D,64x64x64纹理设置其属性的代码。
MTLTextureDescriptor * txDesc = [[MTLTextureDescriptor alloc] init];
txDesc.textureType = MTLTextureType3D;
txDesc.height = 64;
txDesc.width = 64;
txDesc.depth = 64;
txDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;
txDesc.arrayLength = 1;
txDesc.mipmapLevelCount = 1;
id aTexture = [device newTextureWithDescriptor:txDesc];
使用纹理切片 没理解
slice是单个一维,二维或三维纹理图像和其所有相关的mipmap。对于每个切片:
由指定的基级的mipmap的大小width,height和depth
所述的属性
MTLTextureDescriptor`的对象。[
我没看懂
] mipmap级别i的缩放大小由max(1,floor(width
/ 2 i))x max(1,floor(height
/ 2 i))x max(1,floor(depth
/ 2 i))指定。最大mipmap级别是第一个mipmap级别,其中实现了1 x 1 x 1的大小。[
我没看懂
] 在一个切片的mipmap级的数目可以按楼层来确定(日志2(MAX( ,width
,height
)))+depth
1。
所有纹理对象至少有一个切片; 立方体和数组纹理类型可能有多个切片。在写入和读取在将图像数据复制到纹理和从纹理复制中讨论的纹理图像数据的方法中,slice
是基于零的输入值。对于1D,2D或3D纹理,只有一个切片,因此值slice
必须为0.立方体纹理具有6个总2D切片,从0到5寻址。对于1DArray和2DArray纹理类型,每个数组元素代表一片。例如,对于arrayLength
= 10 的2DArray纹理类型,总共有10个切片,从0到9寻址。要从整体纹理结构中选择单个1D,2D或3D图像,首先选择切片,然后选择该切片中的mipmap级别。
使用便捷方法创建纹理描述符
对于常见的2D和立方体纹理,请使用以下便捷方法创建一个MTLTextureDescriptor
自动设置其若干属性值的对象:
texture2DDescriptorWithPixelFormat:width:height:mipmapped:方法
MTLTextureDescriptor
为2D纹理创建对象。的width
和height
值定义了2D纹理的尺寸。该type
属性被自动设置为MTLTextureType2D
,并且depth
和arrayLength
被设置为1。textureCubeDescriptorWithPixelFormat:size:mipmapped:
方法创建一个
MTLTextureDescriptor用于立方织构,其中所述对象
type属性设置为
MTLTextureTypeCube,
width和
height被设置为大小,
depth并且
arrayLength`被设置为1。
两种MTLTextureDescriptor
便捷方法都接受输入值,该输入值pixelFormat
定义纹理的像素格式。两种方法也接受输入值mipmapped
,该值确定纹理图像是否是mipmapped。(如果mipmapped
是YES
,纹理是mipmapped。)
清单3-2使用该texture2DDescriptorWithPixelFormat:width:height:mipmapped:
方法64x64
为未经mipmap 的2D纹理创建描述符对象。
清单3-2 使用方便的纹理描述符创建纹理对象
MTLTextureDescriptor * texDesc = [MTLTextureDescriptor
texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:64 height:64 mipmapped:NO];
id myTexture = [device newTextureWithDescriptor:texDesc];
将图像数据复制到纹理和从纹理复制
要将图像数据同步复制到MTLTexture
对象的存储分配中或从中复制数据,请使用以下方法:
replaceRegion:mipmapLevel:slice:withBytes:bytesPerRow:bytesPerImage:将来自调用者指针的像素数据区域复制到指定纹理切片的存储分配的一部分中。replaceRegion:mipmapLevel:withBytes:bytesPerRow:是一种类似的便捷方法,它将像素数据区域复制到默认切片中,假设切片相关参数的默认值(即
slice
= 0和bytesPerImage
= 0)。getBytes:bytesPerRow:bytesPerImage:fromRegion:mipmapLevel:slice:从指定的纹理切片检索像素数据的区域。getBytes:bytesPerRow:fromRegion:mipmapLevel:是一种类似的便捷方法,它从默认切片中检索像素数据的区域,假设切片相关参数的默认值(
slice
= 0和bytesPerImage
= 0)。
清单3-3显示了如何调用replaceRegion:mipmapLevel:slice:withBytes:bytesPerRow:bytesPerImage:以从textureData
切片0
和mipmap级别的系统内存中的源数据指定纹理图像0
。
清单3-3 将图像数据复制到纹理中
// pixelSize是一个像素的大小,以字节为单位
// width,height - 每个维度中的像素数
NSUInteger myRowBytes = width * pixelSize;
NSUInteger myImageBytes = rowBytes * height;
[tex replaceRegion:MTLRegionMake2D(0,0,width,height)
mipmapLevel:0 slice:0 withBytes:textureData
bytesPerRow:myRowBytes bytesPerImage:myImageBytes];
纹理的像素格式
MTLPixelFormat
指定MTLTexture
对象的各个像素中的颜色,深度和模板数据存储的组织。有三种像素格式:普通,打包和压缩。
普通格式只有常规的8位,16位或32位颜色分量。每个组件都安排在增加的内存地址中,第一个列出的组件位于最低地址。例如,MTLPixelFormatRGBA8Unorm是32位格式,每个颜色分量有8位; 最低地址包含红色,下一个地址包含绿色,依此类推。相反,对于MTLPixelFormatBGRA8Unorm,最低地址包含蓝色,下一个地址包含绿色,依此类推。
打包格式将多个组件组合成一个16位或32位值,其中组件从最低位到最高位(LSB到MSB)存储。例如,MTLPixelFormatRGB10A2Uint是一个32位打包格式,由三个10位通道(对于R,G和B)和两个用于alpha的位组成。
压缩格式以像素块排列,并且每个块的布局特定于该像素格式。压缩像素格式只能用于2D,2D阵列或立方体纹理类型。压缩格式不能用于创建1D,2DMultisample或3D纹理。
MTLPixelFormatGBGR422和MTLPixelFormatBGRG422是被用于存储像素的YUV色彩空间特殊的像素格式。这些格式仅支持2D纹理(但不包括2D数组,也不支持立方体类型),没有mipmap和偶数width
。
多种像素格式存储具有sRGB颜色空间值的颜色分量(例如,MTLPixelFormatRGBA8Unorm_sRGB或MTLPixelFormatETC2_RGB8_sRGB)。当采样操作引用具有sRGB像素格式的纹理时,Metal实现会在采样操作发生之前将sRGB颜色空间组件转换为线性颜色空间。从sRGB组件S到线性组件L的转换如下:
如果S <= 0.04045,则L = S / 12.92
如果S> 0.04045,则L =((S + 0.055)/1.055) 2.4
相反,当渲染为使用具有sRGB像素格式的纹理的颜色可渲染附件时,实现将线性颜色值转换为sRGB,如下所示:
如果L <= 0.0031308,则S = L * 12.92
如果L> 0.0031308,则S =(1.055 * L 0.41667) - 0.055
有关用于渲染的像素格式的更多信息,请参阅创建渲染通道描述符。
为纹理查找创建采样器状态对象
MTLSamplerState对象定义用于当一个图形或计算功能的上执行纹理采样操作的寻址,滤波,和其它性质MTLTexture
的对象。采样器描述符定义采样器状态对象的属性。要创建采样器状态对象:
调用对象的newSamplerStateWithDescriptor:方法MTLDevice来创建MTLSamplerDescriptor对象。
在
MTLSamplerDescriptor
对象中设置所需的值,包括过滤选项,寻址模式,最大各向异性和细节级别参数。MTLSamplerState通过调用创建描述符newSamplerStateWithDescriptor:的
MTLDevice
对象的方法,从sampler描述符创建一个对象。
您可以重用sampler描述符对象来创建更多MTLSamplerState
对象,根据需要修改描述符的属性值。描述符的属性仅在对象创建期间使用。创建采样器状态后,更改其描述符中的属性不再对该采样器状态产生影响。
清单3-4是一个代码示例,它创建MTLSamplerDescriptor
并配置它以创建一个MTLSamplerState。为描述符对象的过滤器和地址模式属性设置非默认值。然后,该newSamplerStateWithDescriptor:方法使用sampler描述符来创建采样器状态对象。
清单3-4 创建一个采样器状态对象
//创建MTLSamplerDescriptor
MTLSamplerDescriptor * desc = [[MTLSamplerDescriptor alloc] init];
desc.minFilter = MTLSamplerMinMagFilterLinear;
desc.minFilter = MTLSamplerMinMagFilterLinear;
desc.minFilter = MTLSamplerMinMagFilterLinear;
desc.tAddressMode = MTLSamplerAddressModeRepeat;
//以下所有属性都有默认值
desc.mipFilter = MTLSamplerMipFilterNotMipmapped;
desc.maxAnisotropy = 1U;
desc.normalizedCoords = YES;
desc.lodMinClamp = 0.0f;
desc.lodMaxClamp = FLT_MAX;
//创建MTLSamplerState
id sampler = [device newSamplerStateWithDescriptor:desc];
保持CPU和GPU内存之间的一致性
CPU和GPU都可以访问MTLResource对象的底层存储。但是,GPU与主机CPU异步操作,因此在使用主机CPU访问这些资源的存储时请记住以下几点。
执行MTLCommandBuffer对象时,MTLDevice只有当(并且仅当)主机CPU在提交对象之前进行了这些更改时,才能保证对象仅观察主机CPU对该MTLResource
对象引用的任何对象的存储分配所做的任何更改。也就是说,对象可能不会观察到在提交相应对象之后主机CPU所做的资源的更改(即,对象的属性是)。MTLCommandBuffer MTLCommandBuffer MTLDevice MTLCommandBuffer
status MTLCommandBuffer
MTLCommandBufferStatusCommitted
类似地,后MTLDevice
对象执行MTLCommandBuffer
对象,主机CPU只保证遵守任何更改MTLDevice
对象使得由该命令缓冲区中引用的任何资源的存储分配,如果命令缓冲区完成执行(即,status
所述的属性MTLCommandBuffer
对象是MTLCommandBufferStatusCompleted)。