GacUI与设计模式(二)渲染系统

所有关于渲染的部分的代码可以在http://gac.codeplex.com下载下来之后,在\Libraries\GacUI\Source\GraphicsElement目录下面找到。

整个渲染系统的主要思想就是,图元(IGuiGraphicsElement)和渲染器(IGuiGraphicsRenderer)分开,而且粒度根据性能的要求粗细都有。为什么要这么设计呢?在前言里面说过,不同的渲染设备,譬如GDI和DirectX,需要的渲染策略和cache资源的方法都不太一样。因此为了让各个渲染设备的渲染器可以充分自定义渲染的策略,于是做出了这样的设计。

但是具体是怎么做的呢?在GacUI里面,首先可以用GetGuiGraphicsResourceManager来获取一个全局的资源管理器(GuiGraphicsResourceManager)对象。这个对象的主要作用就是注册各种创建图元和渲染器的工厂对象。为了让整个渲染系统运行起来,首先我们要把各种图元工厂(IGuiGraphicsElementFactory)注册进去。每一个图元工厂有自己的一个全局的名字。这样当你把一个图元工厂注册金资源管理器之后,从此就可以用图元的名字从资源管理器里面取出注册进去的图元工厂对象了。

其次,因为在运行的时候,每一个图元对象都会在内部保存一个专门给这个图元对象用的渲染器对象,具体的渲染设备的渲染器可以在这个渲染器对象里面cache一些资源,就可以达到为某个图元cache特殊的资源的目的了。因此为了给图元对象创建合适的渲染器对象,我们还需要将图元工厂的名字和一个渲染器工厂(IGuiGraphicsRendererFactory)关联起来。当这一步完成之后,我们就可以通过下面的代码来给一个图元关联上正确的渲染器对象:

IGuiGraphicsElement* element = xxxx;

IGuiGraphicsElementFactory* elementFactory = element->GetFactory();

IGuiGraphicsRendererFactory* rendererFactory = GetGuiGraphicsResourceManager()

->GetRendererFactory(elementFactory->GetElementTypeName());

IGuiGraphicsRenderer* renderer = rendererFactory->Create();

renderer->Initialize(element);

这样我们就从一个IGuiGraphicsElement对象构造出了对应的IGuiGraphicsRenderer对象,并且将这个渲染器对象和这个图元对象关联了起来。这一步完成之后,渲染器对象就会开始根据需要cache被关联的图元对象所需要的资源。然后我们只需要把渲染器对象的指针告诉图元对象,那么图元对象就可以在自己被更新的时候,通过调用renderer->OnELementStateChanged()适当通知一下渲染器对象,而且也可以用renderer->GetMinSize()来说的显示这个图元所需要的最小的矩形尺寸了。为什么尺寸要通过渲染器来计算呢?主要是因为具体怎么渲染是渲染器来控制的,所以尺寸当然也是需要让渲染其计算的,其中一个例子就是文字渲染了。

接下来就是如何规划图元的问题了。目前GacUI所有的图元如下所示:

Gui3DBorderElement

Gui3DSplitterElement

GuiGradientBackgroundElement

GuiImageFrameElement

GuiPolygonElement

GuiRoundBorderElement

GuiSolidBackgroundElement

GuiSolidBorderElement

GuiSolidLabelElement

GuiColorizedTextElement

我们可以看到,大部分的图元都是很简单的。GuiSolidLabelElement就稍微复杂一点,具有了一些诸如自动换行啊省略号这样的设置。而最复杂的就是GuiColorizedTextElement了,里面按行保存了文本之后,还按行给每一个字符分配了存放颜色的缓冲区,然后实现了字符串修改的时候缓冲区的分配释放更新等操作。为什么不设计一个GuiCharElement,而是做成了这两个东西呢?因为在普遍情况下,渲染器都支持对复杂的文字一次性渲染完成,如果我们把每一个字符都设计成一个图元,让排版引擎去渲染字符串的话,性能低下不说,效果可能还不如渲染器自己渲染出来的好。关于这里的一个典型的例子就是Windows所支持的可以连笔的OpenType技术了。另一个原因就是,在开发着色文本框的时候,如果所有的渲染过程不包含在一个图元,而是分散在各个字符图元的话,那更新文字和颜色的时候,无疑十分浪费内存,并且操作起来非常的麻烦,为了灵活性牺牲了太多的性能,得不偿失。

说完了图元和渲染器,最后一个要介绍的就是渲染目标对象(IGuiGraphicsRenderTarget)了。尽管渲染目标可以指向很多种地方,但是在一般情况下,渲染目标所指向的都是一个窗口的客户区域(client area)。尽管在设计上这样看起来仅仅是很自然,但是实际上这么一个对象却是必须的,因为Direct2D的一个render target创建出来的画刷等资源不能直接用在另一个render target上面,而且当render target挂掉的时候,那些资源要全部干掉,重新创建render target,并且重新创建资源。这一步作为一个bug登记在了GacUI里面,还没实现,所以现在Direct2D渲染的时候,把窗口最小化再打开,有时候会变黑。

渲染目标对象的另一个功能就是计算clipping了。在形成父子关系的排版对象绑定的图元在渲染的时候,子图元是不能超出父排版对象的矩形范围的。而且鉴于大量的对象可能处于不可见的位置,所以外围的驱动渲染的代码要在渲染对象完全被clip没了的时候(譬如说在一个具有滚动条的容器里面,一个因为滚动条的关系看不见的按钮),停止渲染看不见的那颗子树,加速渲染过程。而且各个渲染设备也需要处理类似于一个文字只有上半部分能看见这样的情形。所以排版对象就可以通过提供他自己的矩形范围给渲染目标对象,从而让渲染目标对象自己计算可见的矩形范围,从而配合整个渲染流程的进行。鉴于有一部分的渲染器需要的资源是从渲染目标对象来的,因此IGuiGraphicsRenderer还有一个叫做SetRenderTarget的函数,用于在渲染对象发生变化的时候,譬如说因为窗口最小化从而造成Direct2D的render target的时效,需要重新创建的时候,通知每一个图元绑定的渲染器说,整个渲染目标对象已经换掉了,一些资源可能要重新创建。

当然在这里需要提出的就是,在GacUI的GDI和Direct2D渲染器的实现里面,是有一些依靠引用计数全局cache的资源。譬如说在同一个渲染目标对象里面渲染的两个同样颜色的矩形,他在内部使用的具体的画刷就不会真的重复创建两次。尽管GDI和Direct2D的策略不同,GDI的画刷是全局的,而Direct2D的话刷只对一个render target有效,GacUI还是提供了一个通用的资源cache算法模板,让实现类似的功能更加方便。

有关渲染系统的内容就说到这里了,下一篇文章将会具体讲排版对象的内容。

时间: 2024-08-03 23:39:56

GacUI与设计模式(二)渲染系统的相关文章

GacUI与设计模式(一)前言

说起GacUI(http://www.gaclib.net/,gac.codeplex.com),其实这个想法在我还在上大三的时候就已经有了.但是由于经验不足,在当时并没能够把这个东西给做出来,直到去年(2011)的国庆节为止.想想到现在也做了快一年了,GacUI也可以用来写一些不是特别残暴的C++GUI程序了.前几天有人问道,为什么在PC都快完蛋了并且大部分GUI都已经用C#来做的时候,我还要做这个东西呢?其实,这有两个原因:第一个我喜欢折腾C++:第二个C++好像也没什么特别好的GUI,因此

[译]OGRE3D 渲染系统线程化

译者序 偶然在网上看到这篇文章,自己很想仔细研究一下.但搜寻半天不见中文版.于是自己斗胆翻译了一下.文中不免有漏洞百出,甚至可以说有些地方不及Google翻译得好.但这样总的来说是出了一个中文版,而我自己在翻译过程中也会停下来仔细思考. OGRE这个线程化的文章很老了.因为OGRE目前已经支持多线程渲染. 这篇文章貌似是某些人研究出来的三个线程化方案,并给出了测试结果.以向OGRE社区证明线程化方案的可行性. 对于许多想研究渲染线程化的人来说,是一篇值得参考的文章.文中提出了许多在不同情况下线程

设计模式(二):自己动手使用“观察者模式”实现通知机制

在之前发布Objective-C系列博客的时候,其中提到过OC的通知机制,请参考<Objective-C中的老板是这样发通知的(Notification)>这篇博客.在之前关于Notification的博客中,只介绍了Foundation框架中的通知的使用方式.正如前面博客中提到的那样,通知是"一对多的关系",类似于广播.一个人发通知,多个人接收.这也就是设计模式中的"观察者模式".接收者的一方是Observer(观察者),而发送方是Subject(主题

2013高性能计算大会举行 揭秘天河二号系统架构

摘要: 近日,2013高性能计算用户大会在北京举行.天河二号主任设计师.国防科学技术大学卢宇彤教授在大会报告中详细揭秘了天河二号的系统架构和创新应用,披露了我国超算的自主研发实 近日,2013高性能计算用户大会在北京举行.天河二号主任设计师.国防科学技术大学卢宇彤教授在大会报告中详细揭秘了天河二号的系统架构和创新应用,披露了我国超算的自主研发实力水平,提高了大家对中国超算自主科技水平的认识. 2013高性能计算用户大会火爆举行 据大会负责人介绍:2013高性能计算用户大会备受瞩目的因素主要是大会

Oracle体系架构(二) 系统全局共享区SGA

System Global Area 是一块巨大的共享内存区域,他被看做是Oracle 数据库的一个大缓冲池,这 里的数据可以被ORACLE的各个进程共用.其大小查看语句: SQL> select * from v$sga; V$sgastat.V$buffer_pool 主要包括以下几个部分: 1.共享池(Shared pool) 共享池是SGA中最关键的内存片段,特别是在性能和可伸缩性上.太大太小都会扼杀性能,使系统 停止,将会消耗大量的CPU来管理这个共享池. 共享池可分为:Library

交互设计模式(二)-Pagination(分页,标记页数)

上期回顾:交互设计模式(一)-前言 [版权声明]:版权归作者Alite所有,转载时请以超链接形式标明文章原始出处和作者信息及本声明:http://www.alitedesign.com 模式库 在模式库里,我将列出所有电子商务网站需要的模式.以下将罗列出经典常用的模式案例,我也试图让这些模式看起来更有趣味性与实用性.(Yahoo模式库也有对Pattern的一种定义.) 模式归属类别 为了方便调用和维护模式库中的各种模式,首先将模式库中的模式分成三大类:用户需求,应用需求,语境下的设计.在用户需求

设计模式 ( 二十 ) 访问者模式Visitor(对象行为型)

特此说明:对访问者模式理解不是特别透彻,若有误,请指正,谢谢! 1.概述 在软件开发过程中,对于系统中的某些对象,它们存储在同一个集合collection中,且具有不同的类型,而且对于该集合中的对象,可以接受一类称为访问者的对象来访问,而且不同的访问者其访问方式有所不同. 例子1:顾客在超市中将选择的商品,如苹果.图书等放在购物车中,然后到收银员处付款.在购物过程中,顾客需要对这些商品进行访问,以便确认这些商品的质量,之后收银员计算价格时也需要访问购物车内顾客所选择的商品. 此时,购物车作为一个

问诊12306之二:系统不开放 3亿投资恐打水漂

中介交易 SEO诊断淘宝客 站长团购 云主机 技术大厅 9月27日凌晨消息,距离"双节"放假只剩下了最后2天,在京务工的小林(化名)一遍又一遍地刷着12306铁路购票系统,其实,她要乘坐西安的车票早在几天前已被刷成一片银灰色,连普通的站票也没有. 9月26日中午12时12306订票系统繁忙瘫痪截图 和小林同等遭遇在京打工者成千上万,是火车票没有了,还是12306根本就刷不出了车票?这个看似简单的问题却显得非常敏感.一周来,搜狐IT兵分八路,费尽周折,从不同渠道获知其中部分内幕. 在&l

.NET中的设计模式二:单件模式

设计 单件模式(Singleton)是一个非常简单的模式,这是我第一个理解并且能运用的模式.有时候模式的复杂程度并不在于本身,而是由于他的应用目的.最初的时候面对一个模式经常充满了困惑,一个简单的调用为什么要搞的如此复杂?为什么要建立这么多类,只是为了打开一个文件. 通常说来学习一个模式是一个接受.认可.领会的过程.接受:了解模式的结构,了解实例的意义:认可:认可该模式在实际工程中的作用和可行性:领会:将模式应用到开发过程中. 而模式的应用目的说到底无非是为了降低模块之间在时间和空间上的耦合程度