我使用glsl作为GPGPU的框架进行实时图像处理。我目前正在尝试“刮掉”几毫秒来使我的应用程序实时。这是基本设置:
我拍摄输入图像,计算它的几个变换,然后输出结果图像。例如,让输入图像为I
。然后,一个片段着色器计算f(I);
,第二个计算g(I);
,最后一个计算h(f(I),g(I))
。
我的问题是关于有效计算f(I),g(I)
:如果我使用2个单独的片段着色器(因此2个渲染通道),或者如果我使用具有2个输出的单个片段着色器,这是否重要?后者会跑得更快吗?我大多发现了关于“如何做”的讨论;不是表现。
感谢目前为止的回复。下面是几个评论,下面是我的用例示例,其中包含更多细节:
我希望使用1-d滤镜过滤图像I
的行;并且还过滤平方图像的行(每个像素的平方)。 f(I) = filter rows
和g(I) = square and filter rows
:
shader1: (input image) I --> filter rows --> I_rows (output image)
shader2: (input image) I --> square pixels and filter rows--> I^2_rows (output image)
问题是:会写一个着色器,这两个操作比一个接一个地运行这两个着色器要快吗? @derhass建议答案是肯定的,因为访问相同的纹理地点和享受当地。但如果它不适合纹理局部:我还能享受性能提升吗?或者是一个着色器渲染到两个输出基本上相当于两个渲染过程?
答案 0 :(得分:5)
使用多次渲染过程通常比使用MRT输出的一次过程要慢,但这也取决于您的情况。
根据我的理解,f(I)
和g(I)
都会对输入图像I
进行采样,如果每个采样相同(或紧密相邻)的参与,您可以从纹理中获益匪浅缓存之间不同的操作 - 您只需对输入纹理进行一次采样,而不是使用多通道方法采样两次。
进一步采用这种方法:您是否需要单独使用中间结果f(I)
和g(I)
?也许您可以直接将h(f(I),g(I))
放到一个着色器上,这样就不需要多次传递和MRT。如果您希望能够动态地组合您的操作,您仍然可以使用该apporach并以编程方式动态组合不同的着色器代码部分以实现操作(如果可能),并且仅在绝对必要时使用多个传递。
修改强>
由于问题在此期间已经更新,我想我可以提供一些更具体的答案:
到目前为止我所说的,特别是关于将h(f(I),g(f(I))放入一个着色器只是一个好主意,如果h(或f和g)不需要任何相邻的像素。 h是一个nxn滤波器内核,你必须访问nxn个不同的输入纹理元素,并且由于这些输入不是直接知道的,你必须为每个输入计算f和g。如果f和h都是滤波器内核,则有效复合操作的过滤器大小会更大,最好先计算中间结果并使用多次传递。
查看您描述的具体问题,可以归结为此。
如果您以最天真的方式使用两个单独的着色器,则渲染将如下所示。
每次抽奖都有其开销。 GL将不得不做一些额外的验证。与组合着色器方法相比,切换着色器可能是此处最昂贵的额外步骤,因为它可能会强制GPU管道刷新。 Als,对于每个绘制调用,您都有顶点处理,栅格化和每个片段属性的相互关联操作。只有一个着色器,这个开销很多就会消失,到目前为止所描述的每片段计算可以被共享"对于两个过滤器。
但如果不是纹理局部性:我还会喜欢吗? 性能提升?
由于我到目前为止所说的内容,以及您提供的着色器的具体内容,我倾向于说:是的。但是如果我们忽略这里的纹理凹陷,效果将是非常小的可忽略的,特别是如果我们假设合理的高分辨率图像,使得相对于工作总量的相对开销很小。我至少会说使用单次通过MRT设置不会慢。但是,只有对特定GPU上的特定实现进行基准测试/分析才能给出明确的答案。
为什么我说"你呈现的着色器"。因为在这两种情况下,您都可以在一个着色器中进行图像平方。您可以将其拆分为两个不同的着色器和渲染通道。在这种情况下,您将获得额外的开销(对于已经提到的)来编写中间结果,并且必须阅读它。但是,由于您在中间结果上运行过滤器,因此您不必多次对任何输入纹理元素进行平方,但在组合方法中,您可以执行此操作。如果平方操作足够昂贵,并且您的滤波器尺寸足够大,理论上可以节省比多次传递开销所引入的更多的时间。同样,只有基准测试/分析能够告诉你收支平衡的位置。
我过去曾经使用MRT和多个渲染通道进行了一些基准测试,尽管我感兴趣的图像处理操作与您的有点不同。我发现在这种情况下,纹理访问是关键因素,您可以在纹理访问延迟中隐藏许多其他计算(如平方颜色值)。我认为你的"但如果它不是纹理局部"有点不切实际,因为它是对整体运行时间的主要贡献。并且它不仅仅是局部性,它也是纹理访问的总数:使用多重着色器方法,大小为w*h
,大小为n
的1D过滤器,你最终将获得2*w*h*n
纹理访问,而使用组合方法,你只会减少到*w*h*n
,这将在过去产生巨大的差异。
对于AMD FirePro V9800,图像尺寸为1920x1080,只是通过渲染纹理quds将像素复制到两个输出缓冲区,我得到了两次传递:~0,320ms(即使没有切换着色器)vs 1次传递MRT:~0,230女士。因此,执行时间减少了#34;仅#34; 30%,但这只是每个着色器调用一次texutre获取。使用滤波器内核,我希望看到这个数字随着内核尺寸的增加而减少50%(但我还没有测量到这一点)。
答案 1 :(得分:1)
让我们忽略硬件特定事物的任何潜在好处,例如数据缓存,寄存器重用等,如果您在单个着色器调用中执行整个算法并且完全专注于算法复杂性一分钟,则可能会发生这些好处。 / p>
2D图像上的Gaussian Blur是separable filter(X和Y可以模糊为更简单的一维模糊系列),如果你拆分水平和垂直申请分两次通过。
延迟着色是另一个例子。许多实现不是在单遍中对所有灯进行一次大规模循环,而是仅对每个单独的灯实际覆盖的屏幕区域进行一次通过每光遮蔽。
多次通过并不总是一件坏事,当它简化你的算法时,就像可分离的过滤器或轻度覆盖一样,它通常 好 。< / p>
您的结果可能会有所不同,但如果您使用一种方法比另一种方法在Big O表示法中的算法复杂性有显着差异,则值得探索两种实现的运行时性能。