本章介绍存储无格式内存和有格式图片数据的metal resource object(MTLResource),分为2种类型:
MTLBuffer 表示无格式内存,可以包含任何类型的数据。常被用于vertex、shader、compute state data
MTLTexture 表示有格式的图片数据,包含一个特定的贴图类型和像素格式。texture被用于作为vertex、fragment、compute function的输入,也可以作为一个attachment用于存储graphic rendering ouput
本章还会讨论 MTLSamplerState,虽然sample并非resource,它们会在执行lookup texture的时候被使用
MTLBuffer 包含一块内存用来存放任意类型的数据
可以使用MTLDevice的下列方法创建MTLBuffer
以上所有的函数都包含输入参数length,来表示分配内存的尺寸,单位是bytes。所有的函数也都可以通过参数options传入一个MTLResourceOptions来修改buffer的behavior。如果options为0,则将使用默认值。
MTLBuffer 有如下函数
MTLTexture 表示有格式的图片数据,被用于作为vertex、fragment、compute function的输入,也可以作为render destination。MTLTexture 可以是如下结构体
MTLPixelFormat 用于指定 MTLTexture 的像素结构。
可以使用下列方法创建 MTLTexture
创建 MTLTexture的时候 MTLTextureDescriptor 被用于定义属性,包括图像尺寸(宽、高、深度)、pixel format、arrangement(array、cubemap)以及mipmap的数量。MTLTextureDescriptor 值在创建 MTLTexture 的时候有用,当创建完毕后, 改变 MTLTextureDescriptor 的属性将对已经创建的 texture 没有任何影响
使用 descriptor 创建一个或者多个 texture
下面代码用于创建一个texture descriptor txDesc,将其属性设置为 3D,64*64*64,并创建对应的 texture
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];
一个slice代表一个 1D、2D、3D texture image以及它关联的所有mipmaps,每个slice:
每个texture至少包含一个slice。cube、array 可能会包含多个slices。当写入或者读取textureimage data的时候,slice是从0开始计算的输入参数。1D、2D、3D texture,只有一个slice,所以slice为0.一个cube texture包含6 2D slice,slice从0到5。1DArray和2Darray,每个数组元素表示一个slice。比如,一个2DArray texture,arrayLength=10,代表有10个slice,从0-9。从一个texture structure选择一个1D、2D、3D image,首先先选择一个slice,然后再选择该slice的一个mipmap
创建普通的2D和cube texture的时候,使用下面的convenience 方法来创建 MTLTextureDescriptor,其中一些属性将被自动设置
以上两个方法都可以传入 pixelFormat 用于定义texture 的pixe format。也都可以穿入 mipmapped,用于定义texture是否被mipmap(如果mipmapped为YES,则texture被mipmapped)
下面代码使用 texture2DDescriptorWithPixelFormat:width:height:mipmapped: 方法用于创建一个64*64 的 没有mipmapped的2D texture 所对应的texture descriptor
MTLTextureDescriptor *texDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm width:64 height:64 mipmapped:NO]; id <.MTLTexture> myTexture = [device newTextureWithDescriptor:texDesc];
从一个MTLTexture中copy data或者写入data,可以使用下面方法
下面代码使用 replaceRegion:mipmapLevel:slice:withBytes:bytesPerRow:bytesPerImage: 方法使用system memory中的source data textureData 指定slice 0、mipmap 0的texture image
// 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];
MTLPixelFormat 指定 MTLTexture中一个像素的color、depth、stencil数据存储。有三种类型的pixel format:普通、packed、compressed
MTLPixelFormatGBGR422 and MTLPixelFormatBGRG422 是特殊的pixel format,被用于存放yuv color space的pixel。这种类型只用于2D texture(也不支持2D Array和cube ),不含mipmap,and an even width
一些pixel format使用sRGB color space的值存储color component(比如 MTLPixelFormatRGBA8Unorm_sRGB or MTLPixelFormatETC2_RGB8_sRGB)。当采样这个贴图的时候,metal implementation会在sample操作之前将sRGB color spacecomponent convert to a linear color space。sRGB的S convert to a linear component L的公式如下:
相应的,如果旋绕到一个sRGB pixel format的RT上时,implementation 将linear color值转为sRGB
MTLSamplerState 定义了 addressing、filter以及其他属性,用于graphics或者compute function对MTLTexture执行采样texture的操作。sampler descriptor定义了一个sampler state object的属性。创建一个sampler state object分为以下几步:
可以重复使用sampler descriptor object来创建更多的 MTLSamplerState object,可以根据需要修改descriptor 的属性。descriptor的属性只在创建的时候有用。创建完毕后,修改descriptor的属性将不会影响已经创建的sampler object
下面代码先创建 MTLSamplerDescriptor ,配置它 ,然后使用它来创建 MTLSamplerState。desriptor object的filter和address 属性没有默认值。 newSamplerStateWithDescriptor: 方法被用于通过sampler descriptor 来创建sampler state
// 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];
CPU和GPU都可以访问MTLResource的storage。然而GPU的操作与host CPU 异步,所以在host CPU访问资源storage的时候,需要注意以下几点
当执行一个 MTLCommandBuffer 的时候,host CPU对MTLResource的修改,必须要在MTLCommandBuffer被commit之前,才会被MTLDevice检测到。也就是说MTLCommandbuffer被commit后(MTLCommandBuffer 的属性为 MTLCommandBufferStatusCommitted ),host CPU对resource的修改,MTLDevice会忽略。
类似的,当MTLDevice 执行完毕一个MTLCommandBuffer后,host CPU只会检测 command buffer被执行完毕之后(MTLCommandBuffer 的属性为 MTLCommandBufferStatusCompleted),resource的修改
本节教程就到此结束,希望大家继续阅读我之后的教程。
谢谢大家,再见!
原创技术文章,撰写不易,转载请注明出处:电子设备中的画家|王烁 于 2022 年 4 月 22 日发表,原文链接(http://geekfaner.com/shineengine/blog48_MetalProgrammingGuide_3.html)