Urho3D 1.7.1 源代码分析(一)

2019-11-30  本文已影响0人  RonZheng2010

1. 概述

Urho3D依据功能划分成若干子系统,如:

最重要的三个子系统是Graphics、UI和Renderer。

Context是各子系统运行的上下文环境。子系统都必须向它注册,通过这种方式,Context将各子系统粘合在一起,使它们能互相发现、互相发送通知(事件)、互相调用。这就如同设计模式中的中介者(Mediator)模式。

Engine也是一个子系统。没有将它列在上述的列表中,是因为它其实是其他子系统的组织者,由它负责创建和驱动其他子系统。Engine也要向Context注册,以便与其他子系统通信。

Application是应用程序类。 使用者应该从它派生自己的类。这里的HelloWorld是Urho3D的HelloWorld例子中定义的派生类。

Object类的成员context_是对Context的引用。实际上系统只有唯一的Context实例,构造Object对象时,将context_指向这个实例。需要与其他子系统的类都从Object派生,以便与其他子系统交互。

2. Object注册与创建

2.1 Object Factory

所有可以从Context创建的对象,除了从Object派生,还得有自己的ObjectFactory类,也就是ObjectFactoryImpl<T>。Context的成员factories_保存了对象类工厂的实例。

TypeInfo保存类的标识信息,也就是成员type_,这是一个根据类名称生成的全局唯一的Hash值。TypeInfo还保存指向父类的链接,也就是成员baseTypeInfo_。

每个类都要包括宏URHO3D_OBJECT(typename, baseTypeName)。 如下是宏展开后的部分代码。其中的函数GetTypeStatic()可以获得类的StringHash值,这个值是GetTypeInfoStatic()中定义的一个TypeInfo类型的静态变量typeInfoStatic。

static Urho3D::StringHash GetTypeStatic()
{
  return GetTypeInfoStatic()->GetType();
}

static const Urho3D::TypeInfo* GetTypeInfoStatic()
{
  static const Urho3D::TypeInfo typeInfoStatic(#typeName, BaseClassName::GetTypeInfoStatic()); 
  return &typeInfoStatic;
}

2.2 Object注册

Context可创建的对象类应该提供静态函数RegisterObject()。

RegisterObject()的工作如下:

2.3 Object创建

Context::CreateObject<T>()的工作如下:

3. 事件处理机制

3.1 Event与EventHandler

每个event有自己唯一的StringHash值。EventNameRegistrar::RegiseterName()根据event的名字生成这个值。EventNameRegistrar的成员eventNames_保存了所有从event的StringHash值到名字的映射。

Object在成员eventHandlers_中保存自己的事件处理器EventHandler。

EventHandler抽象了事件处理器的接口。它的成员eventType_是event的StringHash值,成员receiver_是event的接收函数。

EventHandlerImpl<T>实现EventHandler。在EventHandleImpl<T>中,一般将receiver_设置成模板参数类T的成员函数,这样事件处理请求就被重定向了。

Context的成员eventReceivers_保存了从event的StringHash值到一组Object的映射,这组Object是该事件的接收者。

3.2 SubscribeToEvent()

SubscribeToEvent()负责订阅事件,如下的例子调用SubscribeToEvent()订阅E_SCREENMODE事件。

Object::SubscribeToEvent(E_SCREENMODE, URHO3D_HANDLER(Renderer, HandleScreenMode));

E_SCREENMODE是一个代表event type的StringHash值。它由URHO3D_EVENT()宏定义如下:

URHO3D_EVENT(E_SCREENMODE, ScreenMode)
{
  URHO3D_PARAM(P_WIDTH, Width);
}

以上宏定义展开如下:

static const Urho3D::StringHash
E_SCREENMODE(Urho3D::EventNameRegistrar::RegisterEventName(“ScreenMode”));

namespace ScreenMode
{
  static const Urho3D::StringHash P_WIDTH(“Width”);
}

RegisterEventName()从event名字”ScreenMode”计算出StringHash值。E_SCREENMODE被设置成该值。该事件还带参数P_WIDTH。

URHO3D_HANDLER()创建一个Event handler的实例。

#define URHO3D_HANDLER(className, function) \
(new Urho3D::EventHandlerImpl<className>(this, &className::function))

展开URHO3D_HANDLER(Renderer, HandleScreenMode) 的结果如下:

new Urho3D::EventHandlerImpl<Renderer>(this, &Renderer::HandleScreenMode)

EventHandlerImpl<Renderer>保存Renderer的实例,和event处理函数Render::HandlerScreenMode()。这样收到该event时,就调用EventHandlerImpl::Invoke()处理。

以上的步骤如下图所示。

3.3 SendEvent()

SendEvent()负责发送事件。如下的例子发送E_SCREENMODE事件。

SendEvent(E_SCREENMODE, eventData);

如下是SendEvent()的工作。

可以看出,这是一个从Context 到Object的两级派发机制。

Object的成员eventHanders_允许为同一个事件指定两个处理函数,一个绑定了特定的事件发送对象,一个不绑定。前一个优先级更高。在Object::OnEvent()中,会先查找前一个。

4. 工作队列

4.1 WorkThread 与 WorkQueue

Thread是封装线程的类。WorkThread是Thread的派生类,它专门配合工作队列WorkQueue使用。WorkQueue的成员threads_保存了一组WorkThread实例。

WorkItem是工作队列中的任务,成员workFunction_是任务的回调函数。WorkQueue的成员queue_保存了当前要执行的任务列表。

WorkQueue::CreateThreads()创建WorkThread实例,并调用Thread::Run()创建实际的线程,线程句柄保存在Thread的成员handle_中。

线程的执行函数是ThreadFunctionStatic()。它调用Thread的虚拟函数ThreadFunction(),Thread的派生类应实现这个函数。

WorkThread的成员owner_指向它所属的WorkQueue实例。WorkerThread的ThreadFunction()调用这个实例的ProcessItems()。后者持续从成员queue_取出任务并执行(也就是取出WorkItem,并调用WorkItem::workFunction_)。

WorkQueue的多个线程可以并行地执行queue_中的任务。从queue_中取出任务时,使用Mutex作互斥。

除了成员queue_之外,WorkQueue的成员poolItems_保存空闲的WorkItem实例,成员workItems_保存正在使用中的WorkItem实例。使用workItems_和queue_两个列表保存工作队列,是为了尽量减低互斥对访问效率的影响。

4.2 使用WorkQueue

WorkQueue的一般的使用过程如下。

这里使用OcclusionBuffer:DrawTrianles()作为例子,说明WorkQueue的一般使用过程。

Octree的成员batches_是一组OcclusionBatch实例。Octree需要在一组工作线程中对它们执行OcclusionBatch::DrawBatch(),并等待所有任务完成。

相关链接
Urho3D 1.7.1 源代码分析 (一)
Urho3D 1.7.1 源代码分析 (二)
Urho3D 1.7.1 源代码分析 (三)
Urho3D 1.7.1 源代码分析 (四)
Urho3D 1.7.1 源代码分析 (五)

上一篇 下一篇

猜你喜欢

热点阅读