答案 0 :(得分:6)
假设你有一个像素。该像素具有颜色分量A B和C.您要绘制的表面具有颜色分量X Y和Z。
首先,您需要检查它们是否匹配。如果他们不匹配,成本会上升。假设它们匹配。
接下来,你需要进行边界检查 - 调用者是否给你一些愚蠢的东西?一些比较,添加和乘法。
接下来,您需要找到像素的位置。这是一些乘法和补充。
现在,您必须访问源数据和目标数据并进行编写。
如果您一次使用扫描线,几乎所有上述开销都可以完成一次。您可以计算扫描线的哪个部分落入边界,只需要比执行一个像素更多的开销。您可以找到扫描线在目标中写入的位置,而且只需要比一个像素更多的开销。您可以使用与一个像素相同的开销检查色彩空间转换。
最大的区别在于,您不是复制一个像素,而是复制一个块。
实际上,计算机非常擅长复制事物。某些CPU上有内置指令,有些内存系统可以在不涉及CPU的情况下执行此操作(CPU说"将X复制到Y"然后可以执行其他操作;内存到内存带宽可能高于内存到CPU到内存)。即使您通过CPU进行往返,也可以使用SIMD指令同时处理2,4,8,16甚至更多单位的数据,只要您以相同的方式处理它们即可有限的指令集。
在某些情况下,您甚至可以将工作卸载到GPU上 - 如果源和目标扫描线都在GPU上,您可以说"你的GPU,你处理它",GPU是均匀的更专业于完成这类任务。
优化的第一位 - 每个扫描线只需要检查一次而不是每个像素一次 - 可以轻松地为您提供2倍到10倍的加速。第二个 - 更有效率的blitting - 另外4倍到20倍更快。在GPU上执行所有操作可以快2到100倍。
最后一件事是实际调用函数的开销。通常这是次要的;但是当调用SetPixel 100万次(1000 x 1000图像或适度大小的屏幕)时,它会加起来。
对于具有200万像素的高清显示器,每秒60次是每秒操纵1.2亿像素。如果你想跟上屏幕的话,3 GHz机器上的单线程程序只能运行每个像素大约25条指令,并假设没有其他事情发生(这是不太可能的)。在4k显示器上,每个像素最多可以有6条指令。
在播放了许多像素的情况下,剃掉每条指令都可以带来很大的不同。
乘数无处不在。我已经将每像素操作的转换写入了每个扫描行操作,这些操作已显示出令人印象深刻的加速,然而,同样适用于CPU到GPU的加载,并且看到SIMD给出了令人印象深刻的加速。
答案 1 :(得分:3)
对SetPixelV
之类的函数的重复调用很慢,因为它必须每次都将坐标转换为内存偏移量,并且还可能在运行中进行一些颜色转换。
一个简单的"设置像素"函数可能看起来像这样(没有边界测试,颜色翻译或任何花哨的东西):
size_t offset = y * bytes_per_scanline + x * bytes_per_pixel;
for(size_t i = offset; i < offset + bytes_per_pixel; i++)
target[i] = source[i];
另一方面,位图通常通过称为 blitting 的过程绘制。这实际上是从一个内存位置到另一个内存位置的直接复制。要在Windows中实现此目的,您需要为与目标上下文兼容的位图创建设备上下文。这可确保无需翻译即可复制内存。它还可以提供更快的硬件加速副本。
一个简单的&#34;副本&#34; blit可能看起来像这样:
size_t nbytes = bytes_per_scanline * height;
for(size_t i = 0; i < nbytes; i++)
target[i] = source[i];
这不涉及协调查找,并且在内存缓存访问方面非常有效。有更快的方法来复制内存块,上面的例子只是为了说明。