Metal 框架之同步托管资源

20 篇文章 0 订阅
19 篇文章 0 订阅

概述

托管存储(MTLStorageMode.managed)模式只在 macOS 下有效,非 macOS 下无此存储模式,请移步阅读《 Metal 框架之资源存储模式 》

该模式下的资源,CPU 和 GPU 都可以访问。 Metal 为每个处理器优化托管资源,但给定的资源内容有更改时,需要同步这些资源。同步托管资源后,CPU 和 GPU 都可以访问更新后的资源数据。

使用 CPU 或 GPU 修改托管资源后,必须先同步该资源,然后其他处理器才可以修改该资源。即使资源的 CPU 修改部分和 GPU 修改部分不重叠,也必须遵循此规则。当同步托管资源时,Metal 可能会根据需要同步比指定部分更多的资源。此外,如果 App 或系统受内存限制,Metal 可能会自动在 CPU 和 GPU 之间复制资源数据。

```

注释:

在离散内存模型中,同步速度受 PCIe 带宽限制。在统一内存模型中,Metal 可能会完全忽略同步调用,因为它只为资源创建单个内存分配。 有关 macOS 内存模型和托管资源的更多信息,请参阅 《 Metal 框架之资源存储模式 》 中的 “在 macOS 中选择资源存储模式“ 部分章节。

```

同步托管缓冲区

首先创建一个托管缓冲区

```

// Matrix buffer and matrix data structure.

id _matrixBuffer;

typedef struct

{

    matrix_float4x4 modelMatrix;

    matrix_float4x4 viewMatrix;

    matrix_float4x4 projectionMatrix;

} MatrixData;

```

接下来,通过 CPU 修改缓冲区的数据。

```

// Modify the managed buffer's data with the CPU.

MatrixData matrixData = (MatrixData)_matrixBuffer.contents;

matrixData->modelMatrix = updatedModelMatrix;

```

完成一次 CPU 修改后,调用 didModifyRange: 方法,更新特定范围的数据并保持缓冲区同步。必须调用此方法来更新 GPU 的缓冲区数据;否则,数据对于 GPU 来说是未定义的。

```

// Synchronize the managed buffer.

[_matrixBuffer didModifyRange:NSMakeRange(0, sizeof(matrixData->modelMatrix))];

```

对 GPU 修改编码后,需要调用 synchronize: 命令,来更新整个缓冲区并保持同步。必须执行此命令才能为 CPU 更新缓冲区数据;否则,CPU 的数据是未定义的。

```

// Create a command buffer for GPU work.

id commandBuffer = [_commandQueue commandBuffer];

// Encode a compute pass to modify the managed buffer's data with the GPU.

id computeCommandEncoder = [commandBuffer computeCommandEncoderWithDispatchType:MTLDispatchTypeSerial];

[computeCommandEncoder setComputePipelineState:computePipelineStateObject];

[computeCommandEncoder setBuffer:_matrixBuffer

                          offset:0

                         atIndex:0];

[computeCommandEncoder dispatchThreads:gridSize

                 threadsPerThreadgroup:threadgroupSize];

[computeCommandEncoder endEncoding];

// Synchronize the managed buffer.

id blitCommandEncoder = [commandBuffer blitCommandEncoder];

[blitCommandEncoder synchronizeResource:_matrixBuffer];

[blitCommandEncoder endEncoding];

// Add a completion handler and commit the command buffer.

[commandBuffer addCompletedHandler:^(id cb) {

    // Managed buffer is updated and synchronized.

}];

[commandBuffer commit];

```

同步托管纹理

首先创建一个托管纹理

```

// Create a managed texture.

id _imageTexture;

MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm

                                                                                             width:textureSize.width

                                                                                            height:textureSize.height

                                                                                         mipmapped:NO];

textureDescriptor.storageMode = MTLStorageModeManaged;

textureDescriptor.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;

imageTexture = [device newTextureWithDescriptor:textureDescriptor];

```

要执行 CPU 修改并同时将更改通知 Metal,需要调用 replaceRegion:mipmapLevel:withBytes:bytesPerRow: 方法,更新特定区域的数据并保持纹理同步。要更新特定的纹理切片,需调用 replaceRegion:mipmapLevel:slice:withBytes:bytesPerRow:bytesPerImage: 方法。必须调用这些方法之一来更新 GPU 的纹理数据;否则,数据对于 GPU 来说是未定义的。

```

// Simultaneously modify and synchronize the managed texture's data with the CPU.

[_imageTexture replaceRegion:MTLRegionMake2D(textureOrigin.x, textureOrigin.y, textureSize.width, textureSize.height)

                 mipmapLevel:0

                   withBytes:textureData

                 bytesPerRow:pixelSize*textureSize.width];

```

对 GPU 修改编码后, 需要调用 synchronize: 命令,来更新整个纹理并保持同步。要更新特定的纹理切片或 mipmap 级别,需要调用 synchronizeTexture:slice:level: 命令。必须执行这些命令之一来更新 CPU 的纹理数据;否则,CPU 的数据是未定义的。

```

// Create a command buffer for GPU work.

id commandBuffer = [_commandQueue commandBuffer];

// Encode a compute pass to modify the managed texture's data with the GPU.

id computeCommandEncoder = [commandBuffer computeCommandEncoderWithDispatchType:MTLDispatchTypeSerial];

[computeCommandEncoder setComputePipelineState:computePipelineStateObject];

[computeCommandEncoder setTexture:_imageTexture

                          atIndex:0];

[computeCommandEncoder dispatchThreads:gridSize

                 threadsPerThreadgroup:threadgroupSize];

[computeCommandEncoder endEncoding];

// Synchronize the managed texture.

id blitCommandEncoder = [commandBuffer blitCommandEncoder];

[blitCommandEncoder synchronizeResource:_imageTexture];

[blitCommandEncoder endEncoding];

// Add a completion handler and commit the command buffer.

[commandBuffer addCompletedHandler:^(id cb) {

    // Managed texture is updated and synchronized.

}];

[commandBuffer commit];

```

总结

本文介绍了使用 CPU 或 GPU 同步托管资源内容时需要注意的事项,并且分别给出来了创建托管缓冲区和创建托管纹理的部分代码。   

公众号:audio_video_render 会持续更新一些干货资源,欢迎大家关注。有想了解的技术也可以给我留言,我会尽量帮你解决。​​​​​​​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值