- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我刚刚开始使用 Metal,在掌握一些基本的东西时遇到了困难。我一直在阅读大量关于 Metal 的网页,并研究了 Apple 的示例等等,但我的理解仍然存在差距。我认为我的主要困惑点是:处理顶点缓冲区的正确方法是什么,我怎么知道何时可以安全地重用它们?这种混淆以多种方式表现出来,正如我将在下面描述的那样,也许我的混淆的这些不同表现需要以不同的方式来解决。
更具体地说,我在 macOS 上的 Objective-C 中使用 MTKView 子类来显示非常简单的 2D 形状:内部具有背景颜色的 View 的整体框架,该整体框架内部具有不同背景颜色的 0+ 个矩形子框架在它们里面,然后是每个子帧内的 0+ 各种颜色的平面阴影方块。我的顶点函数只是一个简单的坐标变换,我的片段函数只是通过它接收到的颜色,基于苹果的三角形演示应用程序。对于带有单个正方形的单个子帧,我可以正常工作。到现在为止还挺好。
有几件事让我感到困惑。
一:我可以设计我的代码,用单个顶点缓冲区和对 drawPrimitives:
的单个调用来渲染整个事物。 ,在一次大爆炸中绘制所有(子)框架和正方形。然而,这不是最佳的,因为它破坏了我的代码的封装,其中每个子帧代表一个对象的状态(包含 0+ 方格的东西);我想让每个对象负责绘制自己的内容。因此,最好让每个对象都设置一个顶点缓冲区并创建自己的 drawPrimitives:
称呼。但是由于对象将按顺序绘制(这是一个单线程应用程序),我想在所有这些绘制操作中重用相同的顶点缓冲区,而不是让每个对象必须分配和拥有一个单独的顶点缓冲区。但我能做到吗?我打电话后drawPrimitives:
,我猜必须将顶点缓冲区的内容复制到 GPU,并且我假设 (?) 这不是同步完成的,因此立即开始修改下一个对象绘图的顶点缓冲区是不安全的。那么:我怎么知道 Metal 何时完成了缓冲区,我可以再次开始修改它?
二:即使#1 有一个明确定义的答案,这样我就可以阻塞直到 Metal 完成缓冲区,然后开始为下一个 drawPrimitives:
修改它打电话,这样的设计合理吗?我想这意味着我的 CPU 线程会反复阻塞以等待内存传输,这不是很好。那么这是否会促使我进行每个对象都有自己的顶点缓冲区的设计?
三:好的,假设每个对象都有自己的顶点缓冲区,或者我用一个大顶点缓冲区对整个事物进行一次“大爆炸”渲染(我认为这个问题适用于这两种设计)。我打电话后presentDrawable:
然后 commit
在我的命令缓冲区上,我的应用程序将关闭并做一些工作,然后将尝试更新显示,因此我的绘图代码现在再次执行。我想重用我之前分配的顶点缓冲区,覆盖其中的数据以进行新的、更新的显示。但同样:我怎么知道什么时候是安全的?据我了解,事实是commit
返回到我的代码并不意味着 Metal 已经完成将我的顶点缓冲区复制到 GPU,在一般情况下,我必须假设这可能需要任意长的时间,所以当我重新输入我的绘图代码。正确的说法是什么?再说一遍:我应该阻塞等待直到它们可用(但是我应该这样做),还是应该有第二组顶点缓冲区,以防 Metal 仍然忙于第一组? (这似乎只是将问题推到了顶峰,因为当我为第三次更新输入绘图代码时,之前使用的两组缓冲区可能都不可用,对吗?那么我可以添加第三组顶点缓冲区,但是第四次更新……)
四:对于绘制框架和子框架,我只想写一个每个人都可以调用的可重用的“drawFrame”类型的函数,但我对正确的设计有点困惑。使用 OpenGL 这很容易:
- (void)drawViewFrameInBounds:(NSRect)bounds
{
int ox = (int)bounds.origin.x, oy = (int)bounds.origin.y;
glColor3f(0.77f, 0.77f, 0.77f);
glRecti(ox, oy, ox + 1, oy + (int)bounds.size.height);
glRecti(ox + 1, oy, ox + (int)bounds.size.width - 1, oy + 1);
glRecti(ox + (int)bounds.size.width - 1, oy, ox + (int)bounds.size.width, oy + (int)bounds.size.height);
glRecti(ox + 1, oy + (int)bounds.size.height - 1, ox + (int)bounds.size.width - 1, oy + (int)bounds.size.height);
}
drawPrimitives:
,因为如果它被连续调用两次,当第二次调用想要修改缓冲区时,Metal 可能还没有从第一次调用中复制顶点数据。我显然不想在每次调用函数时都分配一个新的顶点缓冲区。我可以让调用者传入一个顶点缓冲区供函数使用,但这只会将问题推到一个级别;那么调用者应该如何处理这种情况呢?也许我可以让函数将新顶点附加到调用者提供的缓冲区中不断增长的顶点列表的末尾;但这似乎迫使整个渲染完全预先计划(以便我可以预先分配一个合适大小的大缓冲区以适合每个人将绘制的所有顶点 - 这需要顶级绘图代码以某种方式知道如何每个对象最终会生成许多顶点,这违反了封装),或者做一个设计,我有一个扩展的顶点缓冲区,当它的容量证明不足时,它会根据需要重新分配。我知道如何做这些事情;但他们都感觉不对。我正在为什么是正确的设计而苦苦挣扎,因为我认为我不太了解 Metal 的内存模型。有什么建议吗?对很长的多部分问题表示歉意,但我认为所有这些都归结为同样的基本缺乏理解。
最佳答案
对您潜在问题的简短回答是:在该命令缓冲区完成之前,您不应覆盖添加到命令缓冲区的命令所使用的资源。确定这一点的最佳方法是添加完成处理程序。您也可以投票 status
命令缓冲区的属性,但这并不好。
首先,在提交命令缓冲区之前,不会将任何内容复制到 GPU。此外,正如您所指出的,即使在您提交命令缓冲区之后,您也不能假设数据已完全复制到 GPU。
其次,在简单的情况下,您应该将帧的所有绘图放入单个命令缓冲区。创建和提交大量命令缓冲区(例如为每个绘制的对象创建一个缓冲区)会增加开销。
这两点结合在一起意味着您通常不能在同一帧中重用资源。基本上,您将不得不双重或三重缓冲才能同时获得正确性和良好的性能。
一种典型的技术是创建一个由信号量保护的小型缓冲区池。信号量计数最初是池中缓冲区的数量。需要缓冲区的代码在信号量上等待,当成功时,从池中取出缓冲区。它还应该向命令缓冲区添加一个完成处理程序,将缓冲区放回池中并向信号量发出信号。
您可以使用动态缓冲区池。如果代码需要一个缓冲区并且池是空的,它会创建一个缓冲区而不是阻塞。然后,当它完成时,它将缓冲区添加到池中,有效地增加了池的大小。但是,这样做通常没有意义。如果 CPU 的运行速度远远领先于 GPU,则您只需要三个以上的缓冲区,这并没有真正的好处。
至于您想让每个对象自行绘制的愿望,那当然可以做到。我会使用一个大的顶点缓冲区以及一些关于到目前为止已经使用了多少的元数据。每个需要绘制的对象都将其顶点数据附加到缓冲区,并根据该顶点数据对其绘制命令进行编码。您将使用 vertexStart
参数使绘图命令引用顶点缓冲区中的正确位置。
您还应该考虑使用图元重新启动值进行索引绘图,因此只有一个绘制命令可以绘制所有图元。每个对象将其图元添加到共享顶点数据和索引缓冲区,然后一些高级 Controller 将进行绘制。
关于objective-c - 什么时候重写并重用 MTLBuffer 或其他 Metal 顶点缓冲区是安全的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60020817/
将已完成的 MPI_Request 重新用于另一个请求是否安全?我一直在使用 MPI_Request 池来提高性能并且没有错误。但肯定知道会很好。 最佳答案 MPI_Request 类型的变量不是请求
我注意到 Qt 文档在翻译的某些方面不是很冗长。我一直在玩弄它,试图通过反复试验来弄清楚他们的行为。最终目标是在运行时更改翻译,但我很困惑 QTranslator 对象在多大程度上可以重用。 考虑一下
我有一个 UIImageView 对象,它只是一个纯黑色矩形。这是我用来选择 View 中的按钮的方法。 问题是,我的 View 中有 49 个这样的按钮,并且所有这些按钮都可以同时选择。 我用来向按
在 R 中构建模型时,如何保存模型规范以便可以在新数据上重用它?假设我根据历史数据建立逻辑回归,但直到下个月才会有新的观察结果。最好的方法是什么? 我考虑过的事情: 保存模型对象并在新 session
我是 React/Redux 的初学者。 我已经完成了一个基本组件在我的应用程序中,其操作/ reducer /商店运行良好。 我将渲染另一个 具有不同的设置( Prop )。 我想做的是分离这两个组
我正在开发 GUI 纸牌游戏,我想知道是否有办法改进我的代码。这是我的情况。 我有三张牌:A、B 和 C。玩家可以通过分别单击三个按钮之一来更换牌:分别是按钮 1、按钮 2 或按钮 3。 class
每个文本框旁边有 2 个文本框和 2 个按钮 [...]。是否可以使用一个 OpenFileDialog 并将 FilePath 传递到相应的文本框,基于单击哪个按钮?即...如果我单击第一个按钮并打
我有两个场景:第一个场景,渲染纹理平面,第二个场景,应该渲染为纹理。该纹理应用作主场景中平面的贴图。 出于某种原因,所有 THREE.WebGLRenderTarget 示例每帧都会重新绘制两个场景,
我知道 concat、StringBuffer 和 StringBuilder 之间的区别。我知道 StringBuffer.toString 支持数组的内存问题可能会导致内存爆炸。我什至知道 JDK
我有 2 个 Activity 。 A 和 B。A 有一个包含 4 个项目的操作栏。每个项目显示不同的电影列表。 B extends A 因为我希望能够使用操作栏来更改电影列表。 所以我的问题是,当我
我有一个查询,用于检查从搜索文本框中输入的每个关键字,并且必须返回最匹配的关键字。 问题是,我想排除返回行中所有值为 0 的 KW_MATCHED。 SELECT A1.*, (
当方法重用时,是否有像这样的代码可以与 UICollectionViewCell 一起使用? - (UITableViewCell *)tableView:(UITableView *)tableVi
在我的项目中,我想在可 ScrollView 中以zig-zag 模式显示图像。所以我使用 uiscrollview 子查看其中的图像。它工作正常,但它占用了太多内存,因为我将所有图像加载到 Scro
如果我有 UIViewController1 并且我让它以模态方式显示 UIViewController2,但我希望 UIViewController2 显示 UIViewController1 模式
我想在所有 CCMenuItem 中使用完全相同的标签。如果我创建相同的 CCLabelTTF 一次,那么我无法将其添加到多个 CCMenuItem 中,因为它会给出有关已添加标签的运行时错误。但是,
我正在做一个项目,我们需要显示列表与用户位置的距离。为了显示距离,当在输入中给出纬度/经度时,我们使用名为“distance”的脚本字段计算距离 "script_fields" : {
我正在尝试重用我的 UITableViewCells。目前我的应用程序运行良好,在 tableView 中显示内容。然而,当我尝试实现 - 重用 UITableViewCells 时,我的应用程序崩溃
假设我在外部样式表中定义了几个类 .b {font-weight:bold;} .c {text-align:center;} 现在我想要另一个类,它是 b 和 c 的组合 .bc 是否可以使用类 b
我目前经常分配新的协程实例(请参阅我的回答中的代码 here)。 这样做的开销并不小。 我猜想是否有某种方法可以通过重用之前分配的协程来降低成本? 虽然不确定如何实现这一点? 我可以为协程 Alloc
在我的应用程序中,我使用如下代码下载多张图片。这样做是高性能还是我可以以某种方式重用连接? for(int i = 0; i < 100; i++){ URL url = new UR
我是一名优秀的程序员,十分优秀!