From: http://www.garagegames.com/community/blogs/view/14899
多线程网格皮肤化显示约定
网格皮肤化, 是对象渲染过程中的一部分, 这一过程由 TSSkinMesh::render() 完成。TSSkinMesh::render() 会调用 UpdateSkin() , 然后再调用 Parent::render() 。Parent::render() 会创建一个渲染实例。然而, 顶点缓存(vertex buffer) 被创建后, 并不会被立即使用。据与此, 使用一个"Promise" 来表示一个尚未准备好但最终会准备好的东西的想法诞生了。与许多网络案例相似, 网格的肤化也是如此。
#ifndef _PROMISE_H_ #define _PROMISE_H_ // This is a rough draft template <typename T> class Promise { protected: // Return the promised data. Undefined results if isReady() is false. virtual T* get() = 0; public: virtual ~Promise() { } // Returns true if the promised data is ready. vritual bool isReady() const = 0; // Blocks until the promised data is ready, and returns it. virtual T* resolve() { while(!isReady()) { return get(); } } }; #endif
然后, 在渲染实例对象中存储一个 Promise* 指针 而不在是一个顶点缓存(vertex buffer) 指针。在向设备发送绘制数据之前, 调用某个 promise 实例的 resolve() 方法, 可达到在未准备好之前的等待操作。
#ifndef _SIMPLE_PROMISE_H #define _SIMPLE_PROMISE_H template <typename T> class SimplePromise : public Promise<T> { protected: T *mPtr; virtual T *get() { return mPtr; } public: SimplePromise(T *ptr) : mPtr = ptr { } virtual bool isReady() const { return true; } virtual boid set(T *ptr) { mPtr = ptr; } }; #endif
于是, 可以创建 ThreadedPromise 类来表示使用一个线程来完成某个约定(promise)。然后, 在创建一个 PromisedFulfilerThread 类, 该类可以操作一个ThreadedPromise 类实例的集合。由于加锁、释放锁也是需要时间的,不适合对一个集合进行简单操作, 也就是说, 将锁置于 PromisedFulfilerThread 类中是不合适。通过重新对问题进行考虑, 将互斥锁(mutex)置于 PromisedPromise 类中更为合适。通过测试发现, 主线程(Thread main) 会对渲染实例进行批处理操作, 开始绘制, 当进入到一个尚未准备就绪的 Promise 中时,停下来空转。如果主线程可以在需要时选取若干 promise 实例, 然后完成它,还是可以提升渲染性能的。
TSSkinBatcher -- Threaded Skin Promise Metrics - Promises batched to threads: 134050 - Fulfilled before resolve: 91382 - Promises blocked on resolve: 42668 [31.83%] + Worker thread processed 15065 [35.31%] + Main thread processed 27603 [64.69%]
可以发现,134k 次皮肤请求中,91k 次在主线程请求之前在Worker线程种得到处理;42k次的 promise 阻塞了主线程。在这42k次请求中, 在主线程准备访问它们之前, 15k个 promise在Worker线程种得到处理...
#ifndef _THREADEDPROMISE_H #define _THREADEDPROMISE_H template<typename T> class ThreadedPromise : public Promise<T> { public: virtual T* resolve() { PROFILE_SCOPE(ThreadedPromise_Resolve); #ifdef ENABLE_THREADED_PROMISE_METRICS if(_isReady()) { Atomic::Value32::Increment(&smNoBlockResolves); return get(); } #else if(_isReady()) { returng get(); } #endif // Interlock op, take control of promise if(mInterlockState == ilsPromiseNoState && (Atomic::Value32::Increment(&mInterlockState) == ilsPromiseAcquired)) { #ifdef ENABLE_THREADED_PROMISE_METRICS Atomic::Value32::Increment(&smMainThreadResolveBlocks); #endif return set(reinterpret_cast<T*>(mFulfilerFunction(mFunctionData))); } while(!_isReady()) { Platform::sleep(0); } #ifdef ENABLE_THREADED_PROMISE_METRICS Atomic::Value32::Increment(&smWorkerThreadResolveBlocks); #endif return get(); } // ...