大多数浏览器和
Developer App 均支持流媒体播放。
-
最大限度提升 Metal 的光线追踪性能
了解如何利用 Metal 3 的功能简化您的光线追踪代码,并提升它的性能。我们将探索 GPU 调试和分析工具,它们可以帮助您优化您的光线追踪 App。我们还将向您介绍如何利用加速架构中的每种原生数据来加快相交测试,并减少着色器代码内存访问和间接取值。此外,我们还将帮助您更快地实施加速架构构建与再拟合,以缩短加载时间并降低各帧的开销。
资源
相关视频
Tech Talks
WWDC23
WWDC22
WWDC21
WWDC20
-
下载
♪ ♪
Yi Liu: 大家好 我是 Yi 大家好 我是 Dominik 我们是 GPU 软件工程师 Yi: 今天 Dominik 和我 将和大家讨论今年我们在 Metal Ray Tracing API 中 增加的性能增强和功能 以帮助您最大限度地提高 光线追踪 App 的性能 光线追踪 App 可以模拟 在场景间反射的 单个光线 这被用于游戏和离线渲染的场景中 以产生逼真的反射 阴影 全局光照等效果 这需要模拟大量光线 因此性能对于这些 App 至关重要 幸运的是 Metal 内置了针对所有 Apple 设备优化的光线追踪支持 让我们简要回顾下光线追踪 在 Metal 中的工作原理 Metal 光线追踪 API 可用于 如计算或片元函数等 着色器函数 我们首先产生一些光线 将它们发射到场景中 接下来 我们创建一个相交处理对象 并用它来检查 我们的光线和场景中的 几何形体之间的交点 稍后 我将介绍今年 为加快相交搜索而添加的一些新功能 此过程依赖于一种特殊的数据结构 它叫做加速结构 它也表示场景中的几何形体 今天我还将谈谈几个关于加速结构的 新功能和性能改进 相交处理返回一个交点结果对象 描述每条光线所命中的图元 交点结果用于产生 写入输出图像的颜色 它也可以用来产生额外的光线 这些光线会再次经过这个过程 我们可以多次重复这个过程 只要我们想模拟 光线在场景中反射的效果 如果您想了解更多有关 Metal 光线追踪 API 的基础知识 我建议您看看我们之前的 WWDC 讲座 我们首先在 WWDC20 上引入了 Metal 光线追踪 API 去年 我们引入了 包括支持运动模糊在内的新功能 今天我们要讨论三件事 首先 我将介绍一些新功能 这些功能有助于提高您的 App 中 光线追踪的性能
接下来 我将介绍我们添加到 加速结构 API 中的改进和功能
最后 Dominik 将介绍用于光线追踪的 GPU 工具的改进 今年 我们添加了三个新功能 旨在提高光线追踪性能 或者简化您的代码 它们分别是逐图元数据 从相交函数表中 获取缓冲区的能力 以及间接指令缓冲区对 光线追踪的支持
我们先说说逐图元数据 App 通常具有与其场景中的 图元相关联的数据 例如顶点颜色 法线和纹理坐标
今年 我们添加了在加速结构中 直接为每个图元 存储少量数据的功能 访问这些数据时可以更少使用 内存间接寻址和碰到缓存缺失 从而提高性能 这也减少了存储复杂的 辅助数据结构的需要 这些数据结构通常是查找 与图元相关的数据所必需的
我们来看一个例子
Alpha 测试是一种用于在不增加 三角形计数的情况下 增加透明几何形体复杂性的技术 在这种技术中 映射到三角形上的 纹理的 alpha 通道 用于确定光线是应该 击中三角形还是继续发射
要实现这一点 就需要配置相交处理 以便在射线击中三角形时 调用自定义相交函数
最终目标是从与三角形关联的 纹理中采样 并测试 alpha 值是否允许 光线继续穿过图元 要达到这个目的 您需要两条信息 纹理对象和 UV 坐标 在 alpha 测试的典型实现中 您需要访问 Metal 设备内存中的 多个中间缓冲区才能获得此信息
首先 将与图元关联的纹理 存储在某种材质结构中
几种材料将被装入缓冲区 存储每个图元的材质结构 是不切实际的 因为它们可能非常大 并且可能有很多图元 相反 您只需要将每个图元的材料 ID 存储在缓冲区中 并使用它们查找材料 接下来 要计算 UV 您就需要从另一个缓冲区加载 每个顶点的 UV 并对其进行插值 最后 假设您使用的是 实例化几何形体 您可能希望每个实例 都有自己的材质和 UV 映射 为支持这一点 您可以将 指向 UV 和材质 ID 缓冲区的指针 存储在实例数据缓冲区中 为函数添加另一个间接级别 这种方法要求您维护一个 相当复杂的缓冲区设置 并涉及许多层的间接关系 以获得您需要的数据 这也可能导致缓存丢失 从而对性能产生负面影响 我们看看实现此图表所需的代码 然后 我将展示如何使用 逐图元数据逐步简化它 这是 alpha 测试相交函数的原始实现 当光线击中经过 alpha 测试的三角形时 该函数就会被调用 该函数从内存加载实例数据开始 这个缓冲区包含指向 UV 的指针 和实例使用的 材料缓冲区 接下来 该函数从 UV 缓冲区 加载 UV 坐标 并对其进行插值 这又是一个内存加载 然后 该函数从另一个缓冲区 加载材质索引 最后 该函数加载材质 并对相应的纹理进行采样 此时 函数得到所需的 alpha 值 可以将其与阈值进行比较 现在 我将展示如何使用逐图元数据 来简化这段代码并提高其性能 您可以直接在加速结构中只存储 相交函数需要的每个逐图元数据 而不是使用复杂的 多层间接缓冲区设置 在此示例中 您可以为每个图元 创建一个包含纹理和 UV 坐标的结构 在构建加速结构时提供该数据 当光线击中图元时 相交函数只接收指向该数据的指针 您可以在逐图元数据中 存储任何需要的内容 但较小的存储量 将有助于获得最佳性能 我将从相交函数的输入开始 可以访问所有这些数据让你在实现时 获得很大的灵活性 但也会增加 GPU 上的 寄存器使用量 有了逐图元数据 而不是整个缓冲区 您只需要访问图元数据指针 这是您直接存储在加速结构中的数据 在这种情况下 每个图元都有自己的纹理对象 和所有顶点的 UV 接下来是对全局材质缓冲区 和实例数据缓冲区的加载 这两个您都不需要 相反 您可以从每个逐图元数据指针 加载一次 这是此函数中唯一需要的 设备内存访问 接下来是 UV 您可以简单地访问嵌入在 逐图元数据结构中的数据 而不是对实例数据 获取的指针解引用 代码中的更改很细微 但对性能很重要 因为不涉及额外的内存加载 最后 还有材料属性 由于所需材质的唯一部分是纹理 因此可以在逐图元数据结构中 直接对图元的纹理进行编码 这意味着您不再需要访问 材料和材料索引缓冲区 您可以直接使用纹理 而无需支付额外的内存解引用成本 这就是使用逐图元数据时 相交代码的简单程度 所有代价高昂的内存访问 都被图元数据指针的一次加载所取代 最重要的是 代码更简单 更易理解 接下来 我将展示如何在加速结构中 存储图元数据 您需要先执行此操作 然后才能通过相交函数访问它 您需要在加速结构 几何形体描述符中设置几个字段 首先 设置存储数据的 Metal 缓冲区 接下来 指定将为每个图元 存储的数据的大小 如果数据没有在缓冲区中紧凑打包 或者没有从缓冲区的开头开始 您还可以指定步进和偏移量 否则 这些默认值为 0 因此不需要设置它们 您已经了解了如何在相交函数中 使用逐图元数据 它只是作为指针传递给函数 但这还不是全部 您可以在 任何需要的地方访问这些数据 这包括由交叉点返回的最终相交结果 如果使用相交查询 候选相交和提交相交的 图元数据也是可用的 这意味着除了相交测试之外 您还可以使用逐图元数据进行着色 通过减少内存访问和间接访问的数量 逐图元数据可以提高 相交代码和着色代码的性能 事实上 我们在自己的 一个测试 App 中发现 使用逐图元数据可以使性能 提高 10% 到 16% 我们期待您的尝试 看看您能在性能和代码质量方面 获得怎样的改进 今年 我们还为 Metal 着色语言 添加了另一个方便的功能 以帮助您简化光线追踪内核 App 通常将同一组绑定 传递给它们的相交函数 和主光线追踪内核 例如 我们的光线追踪示例代码 使用相交函数来渲染球体 此相交函数访问 包含每个球体信息的资源缓冲区 为了将此缓冲区传递给相交函数 App 将缓冲区绑定到相交函数表 然而 主光线追踪内核还需要 访问资源缓冲区 因此 App 也将缓冲区绑定到那里 今年 Metal 着色语言允许访问 绑定到相交函数表的缓冲区 有了这个新功能 您可以省去 为内核绑定缓冲区的操作 从而直接 从相交函数表中访问它 您可以通过调用相交函数表上的 get_buffer 方法 并提供其指针类型来实现这一点 您还可以通过函数类型 来访问可见的函数表 间接指令缓冲区允许您在 GPU 上 独立地对 GPU 进行编码 代表着 GPU 驱动管线的 基本元素 要想了解间接指令缓冲区 和 GPU 驱动渲染的更多信息 建议您观看 WWDC 2019 的讲座 “Modern rendering with Metal” 在间接指令缓冲区中 启用光线追踪支持很容易 您所要做的就是在描述符上 设置 supportRayTracing 标志 间接指令缓冲区发送图形和计算函数 因此您可以像往常一样 轻松地从这些函数使用光线追踪 这是我们今年添加的 所有新功能的概要 用来帮助您在 App 中 获得更好的光线追踪性能 接下来 我们来谈谈加速结构 我们已经实现了几项性能改进 并增加了专注于构建加速结构的功能 让我们回顾一下它们的用途 加速结构是加速 光线追踪过程的数据结构 它们通过递归划分空间来实现这一点 这样我们可以快速找到哪些三角形 可能与光线相交 为了支持构建复杂场景 Metal 支持两种类型的加速结构 基本加速结构和实例加速结构 单个的几何图形使用 基本加速结构来表示 它们可以是一些简单的形状 比如平面或者立方体 也可以是一些更复杂的形状 比如球体或者三角网格 您可以使用实例加速结构 来创建更复杂的场景 实例加速结构创建 基本加速结构的副本 首先 为场景中的每个对象 定义变换矩阵 然后 使用变换矩阵数组 和基本加速结构 构建实例加速结构 这就是如何使用加速结构 来构建静态场景的方法 接下来 让我们看看 像游戏这样的动态 App 将如何使用加速结构
我们从头开始 在第一次启动游戏 或加载新关卡时 您需要完成几个任务 这包括加载模型和纹理等常规任务 使用光线追踪 还需要为将要使用的 所有模型构建基本加速结构 我们建议您在加载时 构建尽可能多的 基本加速结构 以节省主渲染循环的时间 您可以根据需要使用实例加速结构 在场景中添加或移除这些对象 一旦您的 App 完成加载 就会进入主循环 每一帧 它都使用光栅化 光线追踪和后期处理的组合 来渲染场景 然而 由于游戏是动态的 您可能需要更新一些加速结构 这通常包括修整一些形变 或动画模型 如为角色蒙皮 修整现有的加速结构比完全重建 要快得多 所以我们建议在这种情况下使用它 您还应该对实例加速结构 进行完全重建 这是必要的 因为自上一帧以来 对象可能已从场景中添加或移除 或者它们可能已显著移动 在这种情况下 完全重新构建是可行的 因为只有一个实例加速结构 而且通常只包含几千个对象 今年 我们改进了 所有这些场景的性能 首先 Apple 芯片上的加速结构 构建速度现在提高了 2.3 倍 其次 修整速度也快了 38%
这意味着加载时间 和每帧开销都减少了 但还有更好的 一些 App 可以构建数百 甚至数千个 小型基本加速结构 这些小的构建 单独没有足够多的工作 来填充 GPU 会导致 GPU 利用率长期处于低水平 因此 现在只要有可能 在 Apple 芯片上就会自动 并行执行多个构建 并行运行使得构建速度 提高了 2.8 倍 这进一步减少了加载时间 这不仅仅适用于构建 它适用于所有加速结构操作 包括压缩和修整 因此您的每帧开销也会减少 您需要遵循一些指导原则 以确保您能够从这种优化中受益 下面是一个构建加速结构数组的例子 要并行执行构建 您需要确保在 多个构建操作中使用 相同的加速结构指令编码器 此外 使用相同临时缓冲区 的构建不能并行运行 因此 您需要确保依次通过 一个小的临时缓冲区池 而不是为每个构建 使用相同的临时缓冲区
这些都是我们为构建 加速结构所做的性能改进 我们还增加了三个新功能 使构建加速结构更加容易和有效
它们支持额外的顶点格式 变换矩阵 和在堆上分配的加速结构
我们从顶点格式开始 常见的性能优化是对顶点数据 使用量化或降低精度的格式 从而降低内存使用 今年 您可以从各种 顶点格式构建加速结构 这包括半精度浮点格式 平面几何图形的两分量顶点格式 以及所有常见的规格化整数格式 以前 加速度结构需要 三分量 全精度浮点顶点数据 在本例中 App 具有 半精度顶点格式的顶点数据 为了构建加速结构 需要将这些数据解包 并复制到临时缓冲区中 有了新的顶点格式功能 加速结构构建现在可以使用 任何受支持的格式的顶点数据 而不需要创建临时副本 设置顶点格式再简单不过了 您所需要做的就是 设置几何描述符上的属性 接下来 我们来谈谈变换矩阵 此功能是对新顶点格式的补充 因此您可以在构建加速结构之前 对顶点数据进行预变换 例如 您可能希望使用它们来 解包以规格化格式存储的复杂网格 让我们来看看这个场景中的 小熊猫模型 为了规范化几何图形 以使用我们的压缩格式之一 需要获取网格 计算其边界 然后将其缩放到 0 到 1 的范围 然后 您可以使用一种规范化的 整数顶点格式来存储网格 从而减少它在磁盘 和内存中占用的空间 在运行时 您可以提供一个矩阵 将每个顶点缩放和偏移到最终位置 应用该矩阵以获取原始模型 现在 我们来了解一下 如何通过变换矩阵 来建立加速结构 首先创建变换缓冲区 一种方法是创建一个 包含缩放和偏移变换矩阵的 MTLPackedFloat4x3 对象 然后 创建一个 足以容纳矩阵的 Metal Buffer 最后 将矩阵复制到缓冲区 接下来 设置加速结构 首先 创建三角形几何描述符 然后 指定变换矩阵缓冲区 最后是缓冲区偏移量 这就是设置转换矩阵所需的全部操作 这些矩阵还可以用于 组合简单的加速结构 以提高光线追踪的性能 让我们看一个示例场景 在这里 长方体和球体 都是相对简单的网格 这提供了一个在场景的前端 优化加速结构的机会 关注实例加速结构 光线击中的 每个实例都会产生间接消耗 变换光线 然后从实例 切换到基本加速结构是有成本的 这种情况更常发生在 实例重叠的情况下 若要减少实例数 可以生成同时包含长方体和球体的 单个基本加速结构 为此 可以为每个对象创建 一个几何描述符 每个对象都有自己的变换矩阵 生成的基本加速结构 是实例加速结构中的单个实例 包含长方体和球体 这会产生性能更好的加速结构 让我们看看如何在代码中设置它
首先定义球体几何描述符 接下来 像往常一样 为基本加速结构设置顶点缓冲区 索引缓冲区和其他属性 不同之处在于 您还可以指定包含 用于复制球体的变换矩阵的 变换缓冲区
对于长方体 有多个几何描述符 共享一个顶点和索引缓冲区 您只需为每个副本 指定不同的转换缓冲区 最后 在为基本加速结构 创建描述符时 添加所有的几何描述符 这将生成一个基本加速结构 您可以通过实体变换 将其实例化到场景中 与单独的加速结构相比 这种基本加速结构的 构建时间更短 并且相交速度更快
最后 加速结构的堆分配 是我们最需要的功能之一 有了这个功能 您现在可以 更好地控制加速结构分配 它还允许您在分配之间 重复使用堆内存 避免了昂贵的缓冲区分配 使用实例加速结构时 堆还可以通过减少 对 useResource: 方法的调用 来帮助提高性能 回到示例场景 实例加速结构间接引用了 基本加速结构 这意味着每次您想要使用 带有指令编码器的实例加速结构时 需要为每个基本加速结构 调用 useResource: 方法 对于大型场景 每次使用实例加速结构时 可能需要数千次调用 useResource: 面对如此多的 useResource: 调用 您可以调用 useResources: 来减少 API 调用的数量 但您仍需维护一个加速结构的数组 Metal 仍需依次通过这个数组 相反 您可以从同一个堆中 分配所有这些基本加速结构 当您想要使用实例加速结构时 您可以简单地调用 useHeap: 方法 来引用所有的基本加速结构 我们只需将对 useResource: 的调用 替换为对 useHeap: 的单个调用 就能使 App 的性能得到提升 让我们看看如何从堆中分配加速结构 您可以通过在堆上调用一个 以加速结构描述符为输入的方法 直接分配一个加速结构 如果不使用描述符进行分配 Metal 设备将确定 从堆中分配加速结构的 大小和对齐要求 通过提供描述符或加速结构大小 您可以从 Metal 设备获得此信息 一旦确定了最终大小 就可以从堆中分配加速结构 在使用堆时 有几个注意事项 首先 记住调用 useHeap: 使堆中的所有加速结构 在光线追踪过程中保持驻留 其次 默认情况下 Metal 不会 追踪您从堆中分配的资源 您可以选择加入资源风险追踪 也可以手动管理自己的同步 您可以使用 MTLFence 在指令编码器之间进行同步 使用 MTLEvent 在指令缓冲区之间进行同步 这些都是今年 Metal 光线追踪 API 中的 新功能和性能改进 接下来 Dominik 将谈一下 Xcode 的 Metal 工具的改进 它将提高您在开发 光线追踪 App 时的效率 谢谢 Yi 在 Xcode 14 中有很多 对 Metal 工具的增强 但在这里 我想强调的是一些 在开发光线追踪 App 时 特别有用的工具 从 Metal 调试器开始 我将介绍 加速结构查看器 着色器性能分析和着色器调试的改进 最后 我会介绍运行时着色器验证
首先 我们来看看 加速结构查看器 Metal 调试器中的 加速结构查看器 能够使您非常详细地检查 组成加速结构的 所有几何形体和所有网格的实例
Xcode 14 现在支持调试 带有基本或实例运动的加速结构 并支持使用检查器 对每个图元数据进行可视化的 新高亮模式 我们来看看它们的表现 如果您正在使用带有运动的加速结构 现在在底部栏中会有一个拖拽条 用于查看您在不同时间点的加速结构 在拖拽条的右边是一个“播放”按钮 您可以用它来循环播放动画 现在我将展示如何在您的加速结构中 检查单个图元 如果您正在使用新的逐图元数据 API 这将特别有用 这里有一个相应的新的高亮模式 图元高亮显示模式 允许您访问所有图元数据
并允许您选择特定的图元 进行详细检查
在左侧边栏中 可以找到数据行旁边的箭头
单击箭头将显示一个弹出窗口 其中显示了该图元的相应数据 加速结构查看器的这些新增功能 确保您可以完全访问 构成加速结构的所有组件 包括每个图元 接下来 我们来谈谈 Shader Profiler 的改进 Shader Profiler 可让您 深入了解着色器的性能 提供每条管道执行计时成本 在 Apple GPU 上 它在源代码级别提供了更高的粒度 显示了分布在指令类别中的 每行执行成本 在 Xcode 14 中 对 GPU 捕获做性能分析已更新的 支持相交函数 可见函数和动态库
这里我们有一个使用相交函数的 光线追踪内核 现在 您可以在相交函数中 查看逐行分析的结果 这包括构成成本的 指令类别的细目
分析可见函数的工作方式相同
同样 现在可以从链接的动态库中 获取着色器代码的详细性能分析信息 有了这些附加功能 您现在就可以对 管道的性能进行全面分解 直至每一行代码
接下来再来谈谈 Shader Debugger Shader Debugger 为调试 着色器代码的正确性 提供了一个独特而高效的工作流程 像 Shader Profiler 一样 我们还扩展了 对链接函数 和动态库的调试支持 这里有一个光线追踪内核 它调用了 一个通过可见函数表 传递进来的链接可见函数
现在您可以一直追踪内核的执行 直到可见函数代码 以验证代码的行为是否符合预期
同样 这也适用于调试动态库 您还可以从管线链接的 已执行动态库跳入跳出 有了这些附加功能 现在您可以 全面了解着色器在管道中的 链接函数和库的执行情况了
现在 在捕获并跳转到 Shader Debugger 之前 最好在运行时启用着色器验证
着色器验证是在 GPU 上 诊断运行时错误的一种很好的方法 可以捕获诸如超出限制的内存访问 空纹理读取等问题 要在 Xcode 中启用着色器验证 只需转到 “Edit Scheme” 对话框 选择 “Run” 操作 并在 “diagnostics” 选项卡下 勾选 “Shader Validation” 复选框 这样就可以了 在 Metal 3 中 我们添加了栈溢出检测 这将帮助您快速发现 可能导致未定义行为的问题 我将快速阐述 Metal 着色器中的 函数堆栈和栈溢出问题 调用堆栈是设备内存中的一个区域 Metal 在其中存储 着色器函数中使用的本地数据值 如果被调用的函数在编译时是未知的 Metal 需要您的帮助 来估计堆栈所需的内存量 对编译时未知函数的调用示例 可能是光线追踪相交函数 如果您在使用自定义相交函数 则应将最大调用堆栈深度设置为 1 以便为其分配空间 这是默认值 因此无需执行其他操作 但如果您使用函数表调用可见函数 这是编译时未知函数调用的 另一个示例 如果从相交函数执行这样的调用 如本例中所示 那么调用堆栈将有两层之深
另一个例子是调用动态库 和使用函数指针调用本地函数 在本例中 我们的调用堆栈有四个层级 其中包含对不同类型函数的 嵌套调用 这些函数在编译着色器时无法解析 要正确配置 Metal 以分配正确的内存量 您需要自己指定最大调用堆栈深度 4 重要的是要记住 当程序的 最大调用堆栈深度设置得太低时 就可能会发生 Stack Overflow 从而导致未定义的行为 但如果您在运行时 启用了 这种情况就会被及早发现 您会在 Xcode 中看到 栈溢出发生的信息 然后 您可以修复着色器代码 或在管道描述符中 调整最大堆栈调用深度 Xcode 14 中 Metal 工具的所有这些新改进 确保您对光线追踪 App 的 性能和正确性有更全面的了解 有关如何充分利用 Metal 工具 进行调试和分析的更多信息 请查看以下其他讲座
本期讲座旨在最大限度地 提高您的 App 的 Metal 光线追踪性能 我们讨论了如何使用 逐图元数据等新功能 来提高性能并简化代码 我们还讲述了使构建加速结构 比以往任何时候都更快 更方便的 优化技术和功能 最后 我们介绍了 Xcode 14 中 Metal 工具的所有新的增强功能 这些功能将在开发过程中 为您提供更深入的见解 齐: 感谢观看
-
-
4:04 - Alpha testing with intersection functions
float alpha = texture.sample(sampler, UV).w; return alpha >= 0.5f;
-
5:46 - Alpha testing intersection function
[[intersection(triangle, raytracing::triangle_data, raytracing::instancing)]] bool alphaTestIntersection(float2 coordinates [[barycentric_coord]], unsigned int primitiveIndex [[primitive_id]], unsigned int instanceIndex [[instance_id]], device GlobalData *globalData [[buffer(1)]], device InstanceData *instanceData [[buffer(0)]]) { device Material *materials = globalData->materials; InstanceData instance = instanceData[instanceIndex]; float2 UV = calculateSamplingCoords(coordinates, instance.uvs[primitiveIndex * 3 + 0], instance.uvs[primitiveIndex * 3 + 1], instance.uvs[primitiveIndex * 3 + 2]); int materialIndex = instance.materialIndices[primitiveIndex]; float alpha = materials[materialIndex].texture.sample(sam, UV).w; return alpha >= 0.5f; }
-
6:48 - Primitive Data
struct PrimitiveData { texture2d<float> texture; float2 uvs[3]; };
-
7:08 - Alpha testing intersection function using per-primitive data
// Alpha testing intersection function [[intersection(triangle, raytracing::triangle_data, raytracing::instancing)]] bool alphaTestIntersection(float2 coordinates [[barycentric_coord]], const device PrimitiveData *primitiveData [[primitive_data]]) { PrimitiveData ppd = *primitiveData; float2 UV = calculateSamplingCoords(coordinates, ppd.uvs[0], ppd.uvs[1], ppd.uvs[2]); float alpha = ppd.texture.sample(sam, UV).w; return alpha >= 0.5f; }
-
8:54 - Setting up per-primitive data
geometryDescriptor.primitiveDataBuffer = primitiveDataBuffer geometryDescriptor.primitiveDataElementSize = MemoryLayout<PrimitiveData>.size geometryDescriptor.primitiveDataStride = MemoryLayout<PrimitiveData>.stride geometryDescriptor.primitiveDataBufferOffset = primitiveDataOffset
-
9:18 - Using per-primitive data
// Intersection function argument: const device void *primitiveData [[primitive_data]] // Intersection result: primitiveData = intersection.primitive_data; // Intersection query: primitiveData = query.get_candidate_primitive_data(); primitiveData = query.get_committed_primitive_data();
-
11:08 - Buffers from intersection function tables
device int *buffer = intersectionFunctionTable.get_buffer<device int *>(index); visible_function_table<uint(uint)> table = intersectionFunctionTable.get_visible_function_table<uint(uint)>(index); uint result = table[0](parameter);
-
11:36 - Ray tracing from indirect command buffers
let icbDescriptor = MTLIndirectCommandBufferDescriptor() icbDescriptor.supportRayTracing = true
-
15:43 - Parallel acceleration structure builds
for (index, accelerationStructure) in accelerationStructures.enumerated() { encoder.build(accelerationStructure: accelerationStructure, descriptor: descriptors[index], scratchBuffer: scratchBuffers[index % numScratchBuffers], scratchBufferOffset: 0) }
-
17:28 - Setting vertex formats
let geometryDescriptor = MTLAccelerationStructureTriangleGeometryDescriptor() geometryDescriptor.vertexFormat = .uint1010102Normalized
-
18:29 - Creating transformation matrix buffer
var scaleTransform = MTLPackedFloat4x3(columns: ( MTLPackedFloat3Make( scale.x, 0.0, 0.0), MTLPackedFloat3Make( 0.0, scale.y, 0.0), MTLPackedFloat3Make( 0.0, 0.0, scale.z), MTLPackedFloat3Make(offset.x, offset.y, offset.z)) let transformBuffer = device.makeBuffer(length: MemoryLayout<MTLPackedFloat4x3>.size, options: .storageModeShared)! transformBuffer.contents().copyMemory(from: &scaleTransform, byteCount: MemoryLayout<MTLPackedFloat4x3>.size)
-
18:51 - Setting transformation matrix buffer on geometry descriptor
let geometryDescriptor = MTLAccelerationStructureTriangleGeometryDescriptor() geometryDescriptor.transformationMatrixBuffer = transformBuffer geometryDescriptor.transformationMatrixBufferOffset = 0
-
20:12 - Merging instances using transformation matrices
let sphereGeometryDescriptor = MTLAccelerationStructureTriangleGeometryDescriptor() sphereGeometryDescriptor.vertexBuffer = sphereVertexBuffer sphereGeometryDescriptor.indexBuffer = sphereIndexBuffer sphereGeometryDescriptor.transformationMatrixBuffer = sphereTransformBuffer let redBoxGeometryDescriptor = MTLAccelerationStructureTriangleGeometryDescriptor() redBoxGeometryDescriptor.vertexBuffer = boxVertexBuffer redBoxGeometryDescriptor.indexBuffer = boxIndexBuffer redBoxGeometryDescriptor.transformationMatrixBuffer = redBoxTransformBuffer let blueBoxGeometryDescriptor = MTLAccelerationStructureTriangleGeometryDescriptor() blueBoxGeometryDescriptor.vertexBuffer = boxVertexBuffer blueBoxGeometryDescriptor.indexBuffer = boxIndexBuffer blueBoxGeometryDescriptor.transformationMatrixBuffer = blueBoxTransformBuffer let primitiveASDescriptor = MTLPrimitiveAccelerationStructureDescriptor() primitiveASDescriptor.geometryDescriptors = [sphereGeometryDescriptor, redBoxGeometryDescriptor, blueBoxGeometryDescriptor]
-
22:33 - Heap acceleration structure allocation
let heap = device.makeHeap(descriptor: heapDescriptor)! let accelerationStructure = heap.makeAccelerationStructure(descriptor: descriptor) let sizeAndAlign = device.heapAccelerationStructureSizeAndAlign(descriptor: descriptor) let accelerationStructure = heap.makeAccelerationStructure(size: sizeAndAlign.size)
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。