当前位置:C++技术网 > 精选软件 > 绘图技术的闪烁原因探究5:双缓冲绘图技术延伸--局部刷新多缓冲绘图技术模型

绘图技术的闪烁原因探究5:双缓冲绘图技术延伸--局部刷新多缓冲绘图技术模型

更新时间:2016-04-23 23:01:38浏览次数:1+次

    当你看完绘图技术闪烁原因探究的4篇文章后,对于普通的客户端开发的界面绘制消除闪烁是不成问题的了。在《绘图技术的闪烁原因探究4:详细分析编程实现双缓冲绘图技术》结合了代码做了实践与理论的结合。应该来说,非常完整的讲述了绘图闪烁问题。是不是学完这些,绘图就再也不会闪烁了呢?当然不是。
    前面我们讲过了,闪烁的原因还是因为计算机绘制两次有反差的画面间隔时间长了,导致人眼识别到两次绘图过程,产生闪烁感。所以,计算机的绘图速度始终都是关键。即使我们使用了双缓冲技术,也不一定能够满足所有的需求。
    我们既要不闪烁,还要绘制高性能,如果只简单使用双缓冲技术,也就是要将所有的东西画到一个整体的画面,然后一次性拷贝给显卡缓冲。如果频率太高,或者计算机性能还是不够好,虽然整体上不闪烁了,毕竟整体上的画面是一样的。然而局部的却是有变化的画面。如果局部的画面有颜色反差,此时计算机绘图变慢,就会导致局部的闪烁。
    所以说,双缓冲不是最终的解决方案,只是一个基础的方案,可以满足大多数需求。进一步的提高绘图性能,需要在双缓冲技术上发展延伸。
    在双缓冲技术中,我们所有的绘图都绘制到同一个内存的位图中存储。如果一个画面完成成千上万次绘图才能完成,那么是非常耗时的,加上刷新频率的提高,绘图任务非常重。此时的双缓冲是完全满足不了要求的。那么如果是超大的高分辨率的屏幕(你可以想象一下41英寸的高清显示屏),也就更加导致了绘制量的剧增。我们不能依赖一个单一的位图存储所有的绘图数据。
    一个屏幕显示的数据都很大。如果我们让一个线程来绘制,好多秒才更新一次画面,这个如果是电影,还让别人怎么看呢?而且整个画面的变化其实只有局部几个位置的变化,我们从头到尾绘制,也是很浪费资源的。
    那么现在的问题是:
    1.一个线程绘制大量的数据,势单力薄,导致绘制时间很长,虽然画面整体上不会闪烁了,如果处理的不够快,在局部上会闪烁。
    2.本来势单力薄的单线程绘制已经不堪重负了,我们还让他绘制很多可以不用绘制的数据,这也是做了很多无用功。如果能从这些不必重新画的地方节省出时间来,绘图的速度也会变化。
    3.因为一个画面的数据存放在一个内存位图中,就算此时有多个线程来帮忙,一次也只能有一个线程能够写内存,其他的线程需要等待。这样也是再增加绘图需要的时间。
    三个问题都反映出绘图的速度,一个是劳动力不够,一个是工作量太大(含大量的不必要的工作量,而且占大多数),一个劳动力增加了,但是却不能高效的利用。
    知道了问题所在,我们还是好找到解决办法的。劳动力不够,我们可以加劳动力。增加线程就和去人才市场捞人一样的,非常容易。将这些人招进公司了,如何让他们高效的工作才是真正的问题。那么第一个问题就这样轻松解决了。也就是使用多线程绘图。人多了,工作量还在,虽然人多了做事快,但是工作量也多,我们把不必要的工作量减掉,这样也就更快了。我们可以针对变化的部分进行重新绘制,不变化的地方就不绘制。我们可以根据场景来判断需要重画的地方和不需要重画的地方,然后只重绘需要绘制的地方。绘图的工作量瞬间就减少了很多了,这样也就给绘图减负了,自然也可以快很多。
    那么人多了,而且绘制更加精准了,如何分配任务,如何协作就是关键问题了。这些也正是多线程编程需要解决的问题,也不是我们这里绘图的难事。
    为了消除局部闪烁问题,动用的资源也多了,架势还不小。所以在编程上也就变得复杂了,这也是在特别的场合需要,一般情况还用不了这么高级的技术。

    我们针对需要重绘的区域组织成一个列表,然后将他们分配给多个线程去绘制。每一个线程只绘制一个局部,绘制的结果保存到位图中。然后位图再复制到显卡缓存中显示。这个结构可以叫做局部刷新的双缓冲技术。下面是局部刷新双缓冲绘图技术模型图:

局部刷新双缓冲绘图技术模型图

【局部刷新双缓冲绘图技术模型图】
    因为局部刷新的双缓冲技术只绘制了局部的画面,所以第一次绘制的完整画面一直要留存,局部的画面画好后直接复制到完整的画面的对应的位置。这样多个线程的绘制都完成后,就形成了一个完整的画面,这样不变的部分一直存储在位图中,变化的部分由多个线程分别处理了。这样速度确实快了不少。
    这样就结束了吗?当然不是。还有提升的地方哦。因为线程都是直接去操作内存的位图的,所以这个内存位图是临界资源,也就是说,每次只能有一个去写入。那么每次在将局部的画面信息写入到位图时,需要锁定位图,以免位图被其他线程破坏了。如果多个部分是完全不想交的位置,其实一般也可以不加保护的读写。理论上是可以的,就好像一个场地有多个不想交的位置,各画各的。然而实际的情况是,可能有重叠的时候,只是先画的被后画的覆盖,如果不加保护,那么后画的可能被先画的覆盖,因为线程可能随时切换,这是多线程里的特性,不多说。对位图加锁是推荐的做法。
    加锁了之后,多个线程如果都同时向内存位图写入数据,自然是只有排队等了。这样就降低了效率。那么解决的办法就是,再给每一个线程分配一个局部位图,线程直接将局部的很多的画面数据写入到局部位图中,这样多个线程可以同时工作。如果局部是一个精细的画面,可能绘制时间也是蛮多的,这样效率提升就更加明显了。

    当多个局部的绘图完成后,就有多个局部位图数据了,那么此时再将多个局部位图按照逻辑一次性的复制到完整的位图对应的局部位置,从而形成一个完整的位图,然后再将完整的位图复制到显卡缓存实现界面输出。我们叫这个为局部刷新三缓冲绘图技术,这个原理模型如下图所示:

局部刷新三缓冲绘图技术模型图

【局部刷新三缓冲绘图技术模型图】
    从图中可以看到,我们自己添加了两级缓冲,完整位图一级,局部位图一级,然后显卡缓冲一级,一共就是三级。这个技术用到了多线程,多局部和一次性贴图显示。这个技术就比较典型了,可以继续延伸为四缓冲、五缓冲、甚至是分布式绘图结构。因为局部的局部,局部的局部的局部都可能需要很多时间来绘制,这样就可以利用多个计算机分布式执行,最后汇总结果进一步汇总处理,知道最后一级显示输出。

    如果搞明白了局部刷新三缓冲绘图技术的原理,其他的也都好理解了。理解了这个技术模型,对于绘图也就有了更加深入的见解了。我就完全分享给大家了,不做任何保留。然后再延伸就是分布式的结构模型了。模型图如下所示:

分布式结构绘图模型

【分布式结构绘图模型】

    有了这些延伸之后,可以解决各种层次的绘图问题。这是高性能无闪烁绘图了。这些写代码起来就超级复杂了。不过我们可以借助双缓冲绘图技术来理解,其实原理也就很简单了。而越延伸通用性也就越高了,分布式不仅可以用来绘图,还可以处理各种技术。比如可以计算,可以预测等等。