
上節(jié)總結(jié)
前幾天我們大體上介紹完成了osg的事件循環(huán)的介紹,總結(jié)一下osg的時(shí)間循環(huán)主要就是得到平臺(tái)(windows)的所有消息,并遍歷所有的node的eventCallback,并對(duì)他們進(jìn)行處理。接下來(lái)我們就要進(jìn)入osg的另一個(gè)維持生命的循環(huán)---更新循環(huán)。
更新循環(huán)
OSG 更新循環(huán)的作用與事件回調(diào)有類(lèi)似之處:由專(zhuān)門(mén)的訪問(wèn)器對(duì)象_updateVisitor 的負(fù) 責(zé)場(chǎng)景圖形更新遍歷;所有的節(jié)點(diǎn)和 Drawable 幾何體對(duì)象都可以使用 setUpdateCallback 設(shè) 置更新回調(diào);通過(guò)具現(xiàn) NodeCallback::operator()或者 Drawable::UpdateCallback::update 函數(shù), 可以在回調(diào)對(duì)象中添加自定義的工作。
但是,更新回調(diào)與事件回調(diào)不同之處在于:事件循環(huán)是在當(dāng)一個(gè)用戶交互動(dòng)作或系統(tǒng)事件產(chǎn)生時(shí),每個(gè)節(jié)點(diǎn)(以及 Drawable 對(duì)象)的事件回調(diào)才會(huì)被調(diào)用一次;而節(jié)點(diǎn)(以及 Drawable 對(duì)象)的更新回調(diào)只會(huì)在每幀中被調(diào)用一次。這一區(qū)別決定了我們應(yīng)當(dāng)在什么時(shí)候使用事件回調(diào), 以及在什么時(shí)候使用更新回調(diào)。
osgViewer::Viewer::updateTraversal()
那我們就開(kāi)始進(jìn)入osgViewer::Viewer::updateTraversal(),updateTraversal和eventTraversal一樣首先都要定義目前處在的時(shí)間以及幀數(shù),并進(jìn)行記錄,這樣有利于進(jìn)行統(tǒng)計(jì)分析。下面我們就要進(jìn)入osgViewer::Viewer::updateTraversal()里最重要的函數(shù)osgViewer::Scene::updateSceneGraph()函數(shù)。
這個(gè)函數(shù)中我們先介紹一下它的主體功能,再去介紹這里遇到的一些新的概念。主要功能:
1、使用DatabasePager::updateSceneGraph函數(shù)更新場(chǎng)景的分頁(yè)數(shù)據(jù)庫(kù),異步處理在分頁(yè)數(shù)據(jù)庫(kù)處理線程中。
2、ImagePager::updateSceneGraph函數(shù), 更新場(chǎng)景的分頁(yè)圖像庫(kù),異步處理在分頁(yè)數(shù)據(jù)庫(kù)處理線程中。
3、設(shè)置圖片請(qǐng)求的處理器。
我們先介紹一下DatabasePager和ImagePager
DatabasePager:分頁(yè)數(shù)據(jù)庫(kù)。在大型三維場(chǎng)景中采用數(shù)據(jù)分頁(yè)的方式進(jìn)行動(dòng)態(tài)調(diào)度。這里“分頁(yè)”的意思是隨著視口范圍的變化,場(chǎng)景只加載和渲染當(dāng)前視口范圍內(nèi)數(shù)據(jù),并將離開(kāi)視口范圍內(nèi)的數(shù)據(jù)清除內(nèi)存(可以設(shè)定不同的數(shù)據(jù)卸載策略),不再渲染。保證內(nèi)存中只有有限的數(shù)據(jù)量,場(chǎng)景的每一幀也只有有限的數(shù)據(jù)被送到圖形渲染管道,從而提高渲染性能。
ImagePager: 分頁(yè)圖像庫(kù)。查看ImagePager 的相關(guān)內(nèi)容了。這個(gè)類(lèi)的工作 性質(zhì)與 DatabasePager 沒(méi)什么大的區(qū)別,它主要負(fù)責(zé)的是紋理圖片文件的運(yùn)行時(shí)加載工作。
DatabasePager和ImagePager都會(huì)用到獨(dú)立的線程進(jìn)行他們自己的工作。我們想要進(jìn)入讀懂他們代碼的內(nèi)容,首先我們得具備openThread的基本知識(shí)。
openThread的基本知識(shí)
面向?qū)ο蟮目缙脚_(tái)線程庫(kù) OpenThreads 原本是獨(dú)立的開(kāi)源工程,OSG 2.x 以后的版本將 其納入了自己的體系結(jié)構(gòu)當(dāng)中,成為 OSG 基本庫(kù)的一份子。 OpenThreads 庫(kù)包含了以下幾個(gè)主要的線程處理類(lèi):?Thread 類(lèi):線程實(shí)現(xiàn)類(lèi)。它是一個(gè)面向?qū)ο蟮木€程實(shí)現(xiàn)接口,每定義一個(gè) Thread 類(lèi), 就相當(dāng)于定義了一個(gè)共享進(jìn)程資源,但是可以獨(dú)立調(diào)度的線程。通過(guò)重寫(xiě) run()和 cancel()這 兩個(gè)成員函數(shù),即可實(shí)現(xiàn)線程運(yùn)行時(shí)和取消時(shí)的操作;通過(guò)調(diào)用 start()和 cancel(),可以啟 動(dòng)或中止已經(jīng)定義的進(jìn)程對(duì)象。?Mutex 類(lèi):互斥體接口類(lèi)。如同 pthread 等常用的線程庫(kù)那樣,OpenThreads 也提供了互 斥體操作的機(jī)制,它有效地避免了各個(gè)線程對(duì)同一資源的相互競(jìng)爭(zhēng),即,某一線程欲操作某 一共享資源時(shí),首先使用互斥體成員的 lock()函數(shù)加鎖,操作完成之后再使用 unlock 函數(shù)解鎖。一個(gè)線程類(lèi)中可以存在多個(gè) Mutex 成員,用于在不同的地點(diǎn)或情形下為共享區(qū)域加鎖; 但是一定要在適當(dāng)?shù)臅r(shí)候解鎖,以免造成線程的共享數(shù)據(jù)無(wú)法再訪問(wèn)。?Condition 類(lèi):條件量接口類(lèi)。它依賴(lài)于某個(gè) Mutex 互斥體,互斥體加鎖時(shí)阻塞所在的 線程,解鎖或者超過(guò)時(shí)限則釋放此線程,允許其繼續(xù)運(yùn)行。 這里涉及了幾個(gè)線程操作中重要的概念:同步,阻塞以及條件變量。線程同步,簡(jiǎn)單來(lái) 說(shuō)就是使同一進(jìn)程的多個(gè)線程可以協(xié)調(diào)工作,例如讓它們都在指定的執(zhí)行點(diǎn)等待對(duì)方,直到 全員到期之后才開(kāi)始同步運(yùn)行;擁塞,即強(qiáng)制一個(gè)線程在某個(gè)執(zhí)行點(diǎn)上等待,直到滿足繼續(xù) 運(yùn)行的條件為止。例如其它的線程到達(dá)同一執(zhí)行點(diǎn),某個(gè)變量初始化完成等等,可以通過(guò)條 件變量來(lái)設(shè)計(jì)各種條件。?Block 類(lèi):阻塞器類(lèi)。顧名思義,這個(gè)類(lèi)的作用就是阻塞線程的執(zhí)行,使用 block()阻塞 執(zhí)行它的線程(注意,不一定是定義它的 Thread 線程,而是當(dāng)前執(zhí)行了 block 函數(shù)的線程, 包括系統(tǒng)主進(jìn)程),并使用 release()釋放之前被阻塞的線程。 下圖所示的代碼實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的線程,并演示了 Block 類(lèi)的使用方法。運(yùn)行程序后 可以發(fā)現(xiàn),Block::block()函數(shù)將首先阻塞主進(jìn)程,被釋放后再次阻塞的是 TestThread 線程, 這與它是誰(shuí)的成員變量并無(wú)關(guān)系。BlockCount 類(lèi):計(jì)數(shù)阻塞器類(lèi)。它與阻塞器類(lèi)的使用方法基本相同:block()阻塞線程, release()釋放線程;不過(guò)除此之外,BlockCount 的構(gòu)造函數(shù)還可以設(shè)置一個(gè)阻塞計(jì)數(shù)值。計(jì) 數(shù)的作用是:每當(dāng)阻塞器對(duì)象的 completed()函數(shù)被執(zhí)行一次,計(jì)數(shù)器就減一,直至減到零 就釋放被阻塞的線程。?Barrier 類(lèi):線程?hào)艡陬?lèi)。這是一個(gè)對(duì)于線程同步頗為重要的阻塞器接口,它的構(gòu)造函 數(shù)與 BlockCount 類(lèi)似,可以設(shè)置一個(gè)整數(shù)值,我們可以把這個(gè)值理解成柵欄的“強(qiáng)度”。每 個(gè)執(zhí)行了 Barrier::block()函數(shù)的線程都將被阻塞;當(dāng)被阻塞在柵欄處的線程達(dá)到指定的數(shù)目時(shí),就好比柵欄無(wú)法支撐那么大的強(qiáng)度一樣,柵欄將被沖開(kāi),所有的線程將被釋放。重要的 是,這些線程是幾乎同時(shí)釋放的,也就保證了線程執(zhí)行的同步性。 注意 BlockCount 與 Barrier 的區(qū)別,前者是由其它任意線程執(zhí)行指定次數(shù)的 completed() 函數(shù),即可釋放被阻塞的線程;而后者則是必須阻塞指定個(gè)數(shù)的線程之后,所有的線程才會(huì) 同時(shí)被釋放。 ScopedLock 模板:這個(gè)模板是與 Mutex 配合出現(xiàn)的,它的作用域之內(nèi)將對(duì)共享資源進(jìn) 行加鎖,作用域之外則自動(dòng)解鎖。
原文 ,原文鏈接??http://www.3wwang.cn/blog/article.ftl?id=22