Metal框架详细解析(三十九) —— Metal编程指南之资源对象:缓冲区和纹理(四)

版本记录

版本号 时间
V1.0 2018.11.04 星期日

前言

很多做视频和图像的,相信对这个框架都不是很陌生,它渲染高级3D图形,并使用GPU执行数据并行计算。接下来的几篇我们就详细的解析这个框架。感兴趣的看下面几篇文章。
1. Metal框架详细解析(一)—— 基本概览
2. Metal框架详细解析(二) —— 器件和命令(一)
3. Metal框架详细解析(三) —— 渲染简单的2D三角形(一)
4. Metal框架详细解析(四) —— 关于GPU Family 4(一)
5. Metal框架详细解析(五) —— 关于GPU Family 4之关于Imageblocks(二)
6. Metal框架详细解析(六) —— 关于GPU Family 4之关于Tile Shading(三)
7. Metal框架详细解析(七) —— 关于GPU Family 4之关于光栅顺序组(四)
8. Metal框架详细解析(八) —— 关于GPU Family 4之关于增强的MSAA和Imageblock采样覆盖控制(五)
9. Metal框架详细解析(九) —— 关于GPU Family 4之关于线程组共享(六)
10. Metal框架详细解析(十) —— 基本组件(一)
11. Metal框架详细解析(十一) —— 基本组件之器件选择 - 图形渲染的器件选择(二)
12. Metal框架详细解析(十二) —— 基本组件之器件选择 - 计算处理的设备选择(三)
13. Metal框架详细解析(十三) —— 计算处理(一)
14. Metal框架详细解析(十四) —— 计算处理之你好,计算(二)
15. Metal框架详细解析(十五) —— 计算处理之关于线程和线程组(三)
16. Metal框架详细解析(十六) —— 计算处理之计算线程组和网格大小(四)
17. Metal框架详细解析(十七) —— 工具、分析和调试(一)
18. Metal框架详细解析(十八) —— 工具、分析和调试之Metal GPU Capture(二)
19. Metal框架详细解析(十九) —— 工具、分析和调试之GPU活动监视器(三)
20. Metal框架详细解析(二十) —— 工具、分析和调试之关于Metal着色语言文件名扩展名、使用Metal的命令行工具构建库和标记Metal对象和命令(四)
21. Metal框架详细解析(二十一) —— 基本课程之基本缓冲区(一)
22. Metal框架详细解析(二十二) —— 基本课程之基本纹理(二)
23. Metal框架详细解析(二十三) —— 基本课程之CPU和GPU同步(三)
24. Metal框架详细解析(二十四) —— 基本课程之参数缓冲 - 基本参数缓冲(四)
25. Metal框架详细解析(二十五) —— 基本课程之参数缓冲 - 带有数组和资源堆的参数缓冲区(五)
26. Metal框架详细解析(二十六) —— 基本课程之参数缓冲 - 具有GPU编码的参数缓冲区(六)
27. Metal框架详细解析(二十七) —— 高级技术之图层选择的反射(一)
28. Metal框架详细解析(二十八) —— 高级技术之使用专用函数的LOD(一)
29. Metal框架详细解析(二十九) —— 高级技术之具有参数缓冲区的动态地形(一)
30. Metal框架详细解析(三十) —— 延迟照明(一)
31. Metal框架详细解析(三十一) —— 在视图中混合Metal和OpenGL渲染(一)
32. Metal框架详细解析(三十二) —— Metal渲染管道教程(一)
33. Metal框架详细解析(三十三) —— Metal渲染管道教程(二)
34. Metal框架详细解析(三十四) —— Hello Metal! 一个简单的三角形的实现(一)
35. Metal框架详细解析(三十五) —— Hello Metal! 一个简单的三角形的实现(二)
36. Metal框架详细解析(三十六) —— Metal编程指南之概览(一)
37. Metal框架详细解析(三十七) —— Metal编程指南之基本Metal概念(二)
38. Metal框架详细解析(三十八) —— Metal编程指南之命令组织和执行模型(三)

Resource Objects: Buffers and Textures - 资源对象:缓冲区和纹理

本章介绍用于存储未格式化内存和格式化图像数据的Metal资源对象(MTLResource)。 有两种类型的MTLResource对象:

  • MTLBuffer表示可以包含任何类型数据的未格式化内存的分配。 缓冲区通常用于顶点,着色器和计算状态数据。
  • MTLTexture表示具有指定纹理类型和像素格式的格式化图像数据的分配。 纹理对象用作顶点,片段或计算函数的源纹理,以及存储图形渲染输出(即作为attachment)。

本章还讨论了MTLSamplerState对象。 虽然采样器本身不是资源,但在使用纹理对象执行查找计算时会使用它们。


Buffers Are Typeless Allocations of Memory - 缓冲区是内存的无类型分配

MTLBuffer对象表示可以包含任何类型数据的内存分配。

1. Creating a Buffer Object - 创建缓冲区对象

以下MTLDevice方法创建并返回MTLBuffer对象:

所有缓冲区创建方法都具有输入值length,以指示存储分配的大小(以字节为单位)。所有方法还接受MTLResourceOptions对象,以获取可以修改创建的缓冲区行为的options。如果options的值为0,则默认值用于资源选项。

2. Buffer Methods - 缓冲方法

MTLBuffer协议具有以下方法:


Textures Are Formatted Image Data - 纹理是格式化图像数据

MTLTexture对象表示格式化图像数据的分配,可以将其用作顶点着色器,片段着色器或计算函数的资源,或者用作要用作渲染目标的attachmentMTLTexture对象可以具有以下结构之一:

  • 1D,2D或3D图像
  • 一维或一维2D图像
  • 六个2D图像的立方体

MTLPixelFormat指定MTLTexture对象中各个像素的组织。像素格式在Pixel Formats for Textures中进一步讨论。

1. Creating a Texture Object - 创建纹理对象

以下方法创建并返回MTLTexture对象:

  • newTextureWithDescriptor:方法使用MTLTextureDescriptor对象创建一个具有纹理图像数据的新存储分配的MTLTexture对象来描述纹理的属性。
  • MTLTexturenewTextureViewWithPixelFormat:方法创建一个MTLTexture对象,该对象与调用MTLTexture对象共享相同的存储分配。由于它们共享相同的存储空间,因此对新纹理对象的像素的任何更改都会反映在调用纹理对象中,反之亦然。对于新创建的纹理,newTextureViewWithPixelFormat:方法重新解释调用MTLTexture对象的存储分配的现有纹理图像数据,就好像数据以指定的像素格式存储一样。新纹理对象的MTLPixelFormat必须与原始纹理对象的MTLPixelFormat兼容。 (有关普通,压缩和压缩像素格式的详细信息,请参阅Pixel Formats for Textures。)
  • MTLBuffernewTextureWithDescriptor:offset:bytesPerRow:方法创建一个MTLTexture对象,该对象共享调用MTLBuffer对象的存储分配作为其纹理图像数据。由于它们共享相同的存储,因此对新纹理对象的像素的任何更改都会反映在调用纹理对象中,反之亦然。在纹理和缓冲区之间共享存储可以防止使用某些纹理优化,例如像素调整或平铺。

2. Creating a Texture Object with a Texture Descriptor - 使用纹理描述符创建纹理对象

MTLTextureDescriptor定义用于创建MTLTexture对象的属性,包括其图像大小(宽度,高度和深度),像素格式,排列(数组或多维数据集类型)和mipmap数。 MTLTextureDescriptor属性仅在创建MTLTexture对象期间使用。创建MTLTexture对象后,其MTLTextureDescriptor对象中的属性更改不再对该纹理产生任何影响。

要从描述符创建一个或多个纹理:

Listing 3-1显示了用于创建纹理描述符txDesc并为3D64x64x64纹理设置其属性的代码。

// Listing 3-1  Creating a Texture Object with a Custom Texture Descriptor

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 <MTLTexture> aTexture = [device newTextureWithDescriptor:txDesc];

3. Working with Texture Slices - 使用纹理切片

切片(slice)是单个1D,2D或3D纹理图像及其所有关联的mipmap。对于每个切片:

  • 基准级别mipmap的大小由MTLTextureDescriptor对象的widthheightdepth属性指定。
  • mipmap级别i的缩放尺寸由max(1,floor(width / 2i))x max(1,floor(height / 2i))x max(1,floor(depth / 2i))指定。最大mipmap级别是第一个mipmap级别,其中实现了1 x 1 x 1的大小。
  • 一个切片中的mipmap级别的数量可以由floor(log2(max(width,height,depth)))+ 1确定。

所有纹理对象至少有一个切片;立方体和数组纹理类型可能有多个切片。在写入和读取在纹理中复制图像数据和Copying Image Data to and from a Texture中讨论的纹理图像数据的方法中,slice是从零开始的输入值。对于1D,2D或3D纹理,只有一个切片,因此切片的值必须为0。立方体纹理具有6个总2D切片,从0到5寻址。对于1DArray2DArray纹理类型,每个数组element代表一个切片。例如,对于arrayLength = 102DArray纹理类型,总共有10个切片,从0到9寻址。要从整体纹理结构中选择单个1D,2D或3D图像,首先选择切片,然后选择该切片中的mipmap级别。

4. Creating a Texture Descriptor with Convenience Methods - 使用便捷方法创建纹理描述符

对于常见的2D和立方体纹理,请使用以下便捷方法创建MTLTextureDescriptor对象,并自动设置其若干属性值:

两种MTLTextureDescriptor便捷方法都接受输入值pixelFormat,它定义纹理的像素格式。两种方法也接受mipmapped的输入值,该值确定纹理图像是否是mipmapped。 (如果mipmapped为YES,则纹理被mipmap处理。)

Listing 3-2使用texture2DDescriptorWithPixelFormat:width:height:mipmapped:方法为未mipmapped64x64 2D纹理创建描述符对象。

Listing 3-2  Creating a Texture Object with a Convenience Texture Descriptor

MTLTextureDescriptor *texDesc = [MTLTextureDescriptor 
         texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm 
         width:64 height:64 mipmapped:NO];
id <MTLTexture> myTexture = [device newTextureWithDescriptor:texDesc];

5. Copying Image Data to and from a Texture - 将图像数据复制到纹理和从纹理复制

要将图像数据同步复制到MTLTexture对象的存储分配中或从中复制数据,请使用以下方法:

Listing 3-3显示了如何调用replaceRegion:mipmapLevel:slice:withBytes:bytesPerRow:bytesPerImage:从系统内存中的源数据指定纹理图像,textureData,在切片0mipmap级别0。

// Listing 3-3  Copying Image Data into the Texture

//  pixelSize is the size of one pixel, in bytes
//  width, height - number of pixels in each dimension
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];

6. Pixel Formats for Textures - 纹理的像素格式

MTLPixelFormat指定MTLTexture对象的各个像素中的颜色,深度和模板数据存储的组织。有三种像素格式:ordinary, packed, and compressed

  • Ordinary格式只有常规的8位,16位或32位颜色分量。每个组件都安排在增加的内存地址中,第一个列出的组件位于最低地址。例如,MTLPixelFormatRGBA8Unorm是32位格式,每个颜色分量有8位;最低地址包含红色,下一个地址包含绿色,依此类推。相反,对于MTLPixelFormatBGRA8Unorm,最低地址包含蓝色,下一个地址包含绿色,依此类推。
  • Packed格式将多个组件组合成一个16位或32位值,其中组件从最低位到最高位(LSB到MSB)存储。例如,MTLPixelFormatRGB10A2Uint是一种32位打包格式,由三个10位通道(R,G和B)和两位alpha组成。
  • Compressed格式以像素块排列,每个块的布局特定于该像素格式。压缩像素格式只能用于2D,2D阵列或立方体纹理类型。压缩格式不能用于创建1D2DMultisample3D纹理。

MTLPixelFormatGBGR422MTLPixelFormatBGRG422是特殊的像素格式,用于存储YUV颜色空间中的像素。这些格式仅支持2D纹理(但不包括2D数组,也不支持立方体类型),没有mipmaps和均匀width

多种像素格式存储具有sRGB颜色空间值的颜色分量(例如,MTLPixelFormatRGBA8Unorm_sRGBMTLPixelFormatETC2_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 * L0.41667) - 0.055

有关用于渲染的像素格式的更多信息,请参阅Creating a Render Pass Descriptor


Creating a Sampler States Object for Texture Lookup - 为纹理查找创建采样器状态对象

MTLSamplerState对象定义在图形或计算函数对MTLTexture对象执行纹理采样操作时使用的寻址,过滤和其他属性。采样器描述符定义采样器状态对象的属性。要创建采样器状态对象:

您可以重用sampler描述符对象来创建更多MTLSamplerState对象,根据需要修改描述符的属性值。描述符的属性仅在对象创建期间使用。创建采样器状态后,更改其描述符中的属性不再对该采样器状态产生影响。

Listing 3-4是一个代码示例,它创建MTLSamplerDescriptor并对其进行配置以创建MTLSamplerState。为描述符对象的过滤器和地址模式属性设置非默认值。然后newSamplerStateWithDescriptor:方法使用sampler描述符来创建一个采样器状态对象

Listing 3-4  Creating a Sampler State Object

// create MTLSamplerDescriptor
MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
desc.minFilter = MTLSamplerMinMagFilterLinear;
desc.magFilter = MTLSamplerMinMagFilterLinear;
desc.sAddressMode = MTLSamplerAddressModeRepeat;
desc.tAddressMode = MTLSamplerAddressModeRepeat;
//  all properties below have default values
desc.mipFilter        = MTLSamplerMipFilterNotMipmapped;
desc.maxAnisotropy    = 1U;
desc.normalizedCoords = YES;
desc.lodMinClamp      = 0.0f;
desc.lodMaxClamp      = FLT_MAX;
// create MTLSamplerState
id <MTLSamplerState> sampler = [device newSamplerStateWithDescriptor:desc];

Maintaining Coherency Between CPU and GPU Memory - 保持CPU和GPU内存之间的一致性

CPU和GPU都可以访问MTLResource对象的底层存储。但是,GPU与主机CPU异步操作,因此在使用主机CPU访问这些资源的存储时请记住以下几点。

执行MTLCommandBuffer对象时,MTLDevice对象仅保证观察主机CPU对该MTLCommandBuffer对象引用的任何MTLResource对象的存储分配所做的任何更改,如果(并且仅当)主机CPU在MTLCommandBuffer对象已提交之前进行了这些更改。也就是说,MTLDevice对象可能不会观察到在提交相应的MTLCommandBuffer对象之后主机CPU所做的资源的更改(即,MTLCommandBuffer对象的status属性是MTLCommandBufferStatusCommitted)。

类似地,在MTLDevice对象执行MTLCommandBuffer对象之后,如果命令缓冲区已完成执行(即MTLCommandBuffer对象的status属性是MTLCommandBufferStatusCompleted),则主机CPU仅保证观察MTLDevice对象对该命令缓冲区引用的任何资源的存储分配所做的任何更改 )。

后记

本篇主要讲述了Metal编程指南之资源对象:缓冲区和纹理,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容