青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

歲月流轉(zhuǎn),往昔空明

C++博客 首頁 新隨筆 聯(lián)系 聚合 管理
  118 Posts :: 3 Stories :: 413 Comments :: 0 Trackbacks

SALVIA是從07年底開始開發(fā)的。歷經(jīng)五年,無論是設計目標,還是使用到的一些方法,都和最初差別很大。

謹以此文,紀念我在五年中作出來的各種傻逼決定。

 

1. 2007年9月 - 2007年12月:可笑的動機,可笑的雛形

動機與原型

SALVIA出現(xiàn)的原因其實很可笑。07年底的時候我正在寫一篇paper,講GP-GPU的。那個時候還沒有CUDA一類的東西,一切都要靠Shader來。本來我手上的顯卡是一塊9550的SDRAM的簡版。但是論文快結(jié)束的時候,突然這卡的風扇就罷工了。然后我降頻用了大概一個多月,卡也廢掉了。因為沒錢買新顯卡,我就打算寫一個比D3D REF快的軟件渲染器。

07年底的時候,實現(xiàn)了第一版的SALVIA,當時還叫SoftArt。第一版的SALVIA其實還算不錯,流水線的完整程度到現(xiàn)在都還沒超過,包括Cpp的Vertex Shader和Pixel Shader、紋理采樣、光照什么的一應俱全。在開發(fā)過程中,主要參考GL 2.0的Specification,也閱讀了一些同類型軟件的代碼,例如Muli3D和Mesa。

一些對管線至關重要的概念,例如透視修正、固定管線上紋理采樣的LoD Level、Clip都是借助于Spec和這些實現(xiàn)建立的。

為什么要有Shader Compiler

如果是固定管線的話,那么SALVIA做到這些特性也就足夠了。但是從SALVIA一開始,我就希望讓它成為一個Pure Shader的管線,固定管線的那些狀態(tài)實在太煩人了。本來Cpp實現(xiàn)的Shading language能滿足絕大部分的需要了,但是有一個特性徹底難倒了我:Pixel Shader的差分函數(shù)ddx/ddy。

這個東西的工作原理是這樣的:

比方說我有一段shader函數(shù):

float shading_pixel( ... ): COLOR0
{
    float x;
    // Expression for calculating x
    return ddx(x);
}

在Pixel Shader運行的時候,它一次性執(zhí)行2x2的一個小塊,所有的指令對于整個塊內(nèi)都是同步執(zhí)行的。遇到ddx(x)后,四個像素都正好執(zhí)行到這里,然后把x方向上的相鄰兩個像素的局部變量x求個差,就可以得出ddx了。

這個要求在C++中很難實現(xiàn)。

  1. 不好讓C++的四個函數(shù)都在同一個地方Join;
  2. 我不好去獲得相鄰函數(shù)的棧上的值。

其實如果要較真,當然還是有辦法的:

  1. 對于Join問題,起碼有兩種方案:
    • 自己搞一個Fiber Manager,直接控制代碼的棧的Switch。每個pixel都有一個Fiber,到了DDX/DDY就換到下一個Fiber執(zhí)行,直到所有的Fiber都執(zhí)行完畢后,計算ddx,寫入棧變量,再繼續(xù)執(zhí)行;
    • 直接用線程,Join,計算,然后繼續(xù)執(zhí)行。
  2. 對于棧變量的地址問題,也有辦法:
    • 在切換線程的時候直接保存臨時變量的地址。

但是這些實現(xiàn),要么因為切換上下文而變得奇慢無比;要么就是完全沒有平臺移植性。想來想去,還是要讓代碼按照硬件的方式SIMD執(zhí)行。

所以我最終橫下一條心:要為它做Shading Language Compiler。然后開始了漫長的Compiler開發(fā)。后來我看團長那個《漫無止境的八月》的時候,簡直就是對著鏡子照自己的傻逼。所以我才更黑團長。

2. 2008年初 - 2009年12月:黎明前的黑暗

Shader的文法

08年到09年我都在外面實習,一周上六天班,一天得干上十個多小時。從2008年初到7月份,我都一直在看編譯原理和成熟的語法庫。底子薄,看起來很吃力。到了8月份開始設計Shader的EBNF。設計語言,不外乎是三個方面:應用場景、語法和庫的支持。盡管有現(xiàn)成的HLSL和GLSL作參考,但對于我從0開始設計語言來說,這些語言的語法和語義都過于復雜了。我需要讓語言特性慢慢的添加進來。

考慮到HLSL和C比較接近,C的文法參考資料又很多,于是我選擇了從C開始裁剪語法。但是文法這個東西,并不簡簡單單是樹狀的結(jié)構,樹上的任何一個語法節(jié)點,都可能會引用到其它的文法規(guī)則。因此修改了一條規(guī)則后,你會發(fā)現(xiàn)它可能會和其它規(guī)則沖突了,二義了。于是裁剪計劃完蛋了。

當然,如果我現(xiàn)在來設計語法,肯定會和陳漢子一樣,直接從Use Case就能把EBNF寫出來,再稍微規(guī)范一下,一門不那么復雜的語言就成了。當然像C++這種變態(tài)語言,這樣做是做不出來的。但當時我顯然不具備那樣的能力。從七月份開始就磕磕絆絆地裁剪了一些語法特性之后的語言,到了八月份才出了個千瘡百孔的方案。

神:Boost.Spirit

作為完全不懂編譯器的矬貨,設計語言一定要和編譯器的開發(fā)放在一起才能有點收獲。我用過Flex/Bison,用過ANTLR。但是當時我對編譯器特別的陌生,組織Build的能力也比較弱,因此它們在使用上繁瑣和難于調(diào)試給我?guī)砹撕艽蟮睦_。不過那時我對模板、元編程和Boost就已經(jīng)相當熟悉了,無論是開發(fā)、閱讀代碼還是Debug都能輕松應付,所以我挑了半天,選了Boost.Spirit。

Boost.Spirit是個很奇葩的東西。它想在C++里面提供一個類似于EBNF、可以定義語法分析規(guī)則的方言。要讓C++看起來像一個方言,當然是要使用神出鬼沒的操作符重載。當然,即便是修飾后的語法,看起來也還是會有點怪怪的。EBNF中的規(guī)則

Rule ::= Token SubRule0 [OptionalSubRule1]

在Cpp中最簡單可以表示成

rule = token >> subrule0 >> optional(OptionalSubRule1)

雖然看起來有點丑陋,但是它已經(jīng)完全滿足一個DSL的要求了:直觀的面向解決方案。

不過如果牽涉到實現(xiàn)細節(jié),在C++里面要寫一個又簡單、又可用Parser Generator,那幾乎是不可能完成的任務。起碼對于Combinator-based Parser來說,它夠簡單,但是沒有CPS的支持會令錯誤恢復這一類的周遭設計變得極為可怕;如果Rule只是grammar definition,不牽涉到任何Parser的構造,那解析這個definition的復雜度和調(diào)試難度又不亞于ANTLR或者Yacc這樣有單獨腳本的工具。所以這項工作,還是交給Haskell這樣的語言來完成吧。

通過使用Spirit、設計編譯器、折騰文法,讓我對Compiler和Cpp的理解都遞進了一大步。再加上08年全年都在做GUI相關的東西,也讓我對編譯器的理解有所加深。

09年下半年我一直都比較動蕩,不過到年底總算是安定了下來。

3. 2009年12月—2010年2月:長征的開始

后端與前端

09年12月份的時候,Boost升級了,Spirit也到了V2。到了2月份,我費了點功夫,把V2的Spirit折騰到SALVIA的前端上。Parser也有所變化:前一版的Parser還比較草率,這一版的Parser我?guī)缀跏峭耆凑誗pirit的Demo中的方案進行的。此時我也開始嘗試著撰寫語義分析。怎么做函數(shù)重載都是在那個時候開始點的技能樹,雖然在現(xiàn)在看來都是歪的。為了執(zhí)行生成的代碼,我設計了半個虛擬機,然后還準備寫點教程。但是我思前想后,對于Shader這樣一秒鐘要調(diào)用10M次的函數(shù),無論如何虛擬機都是不合適的。

所以我就開始籌備自己的后端。要求就是一個字:快。那個時候,陳漢子正在學怎么寫x86的JIT。但是我的語言到x86有很長的路要走。怎么去分配寄存器,怎么把類型轉(zhuǎn)換到x86的Native,怎么選擇指令,我都是一知半解的。憑我當時的知識,這一定是不可能完成的。

于是在閱讀完Intel Architecture手冊和優(yōu)化指南后,我決定去找一個合用的后端。考慮過很多可選的辦法,例如生成C++的Code然后編譯成DLL;使用Tiny C(TCC);或者是JIT。但是它們?nèi)秉c都是很明顯的。編譯成DLL必須要自己裁剪一個GCC出來;Tiny C的效率并不是很好;JIT很復雜(起碼在那個時候是這樣)。不過2月份的時候,敏敏還是誰指點了我一下,說你可以去看看LLVM。然后我去一看,牛逼,就是我要的東西!然后我就開始學LLVM。LLVM的IR很好學,一個下午就搞了個Hello world。

這個時候,minmin也在SALVIA上實現(xiàn)了Half-Space的光柵化算法。

那個時候我躊躇滿志,意氣風發(fā),三月趕英,五月超美。

可沒想著就這么掉坑里面去了。

4. 2010年2月—2011年新年:苦難的行軍

苦難:復雜的問題

主體大人真是神,五個字就概括了我2010年一年的努力。

  • minmin做的SALVIA的Half-Space算法并不比我樸素的Top-Bottom的光柵化強;
  • 紋理上的優(yōu)化盡管使用了SSE但是仍然改進有限;
  • Shader編譯器本身的編譯時間由于Spirit的存在而實在漫長;
  • Shader編譯器和Pipeline如何關聯(lián)又無從下手;
  • LLVM的集成也因為前端而有所耽擱,另外因為各種錯誤層出不窮,讓整個開發(fā)進度變得龜速。

所以整個一年中,SALVIA的開發(fā)就是寫寫停停,停停寫寫。可以說08年初的銳氣,已經(jīng)消磨的差不多了。到了8月份的時候,我畢業(yè)了,新工作也基本上確定和熟悉了,我就和minmin說,從現(xiàn)在開始我寫半年報吧,講述一下半年來的進展。于是便有了第一篇項目簡報。

行軍:些微的進展

也正是從那個時候,我決定要把SALVIA作為一款實驗品來對待,用上所有我不會的或者新學的東西。單元測試,CMake工具鏈,為Shader設計的Pipeline,語義分析和后端的原型都在那一年加入了SALVIA。雖然從實現(xiàn)上它們已經(jīng)與現(xiàn)在相距甚遠,但是起碼一切都還是往好的方向發(fā)展。

另外,08年到09年期間在實習的時候積累的教訓開始慢慢的醞釀和發(fā)酵,敏捷也逐漸成為了我開發(fā)過程中的主要指南。

基本上,那個時候積累了很多必要的經(jīng)驗和教訓。當然絕大多數(shù)是教訓。

5. 2011年2月—2011年6月:新Shader的起點

坑神:Boost.Spirit的滅亡

在11年的春節(jié)期間,我終于無法忍受Spirit的麻煩了:

  • 一段400行不到的代碼,在我的機器上需要編譯30分鐘;
  • Object File需要占用1.9G的硬盤;
  • Mangling name輕松超過4K字符的限制;
  • 輕易撐爆obj文件的symbol table,需要用/bigobj才能夠編譯通過;
  • 甚至在編譯的時候會輕易的讓32位的MSVC CL out of memory。

要知道,以上這些還是應用了Spirit指南中的編譯速度優(yōu)化方案之后的結(jié)果。

這一切原因,都是因為Boost.Spirit對于Parser Tree,是用了完全靜態(tài)的分析樹結(jié)構。每條規(guī)則的返回值都會是完全不同的類型。這直接導致類型數(shù)量極為龐大,代碼膨脹的厲害。

于是11年的寒假我花了5天的時間重新山寨了一個文法分析器的產(chǎn)生器,并做到DSL幾乎完全和Spirit一致。只不過Parser Tree不再是靜態(tài)類型;模板的用量也減輕了很多。

Shader的階段性成果

到了四月份的時候,Shading Language Semantic/System Value已經(jīng)在語法上支持了,語義上也能分析出哪些變量是System Value,哪些變量是Uniform的。并且通過生成特殊的函數(shù)簽名,Shader滿足了以下幾個需求:

  1. Shader要返回一個函數(shù);
  2. 這個函數(shù)是可重入的(因為要并發(fā));
  3. 數(shù)據(jù)能正確的從Pipeline傳入到Shader的函數(shù)中,也能正確的返回;
  4. Shader中對于Pipeline數(shù)據(jù)引用要能正確的生成地址。

到了11年6月份的時候,終于把Shader全線貫通。雖然很多Operator和Instrinsic還不支持,但是起碼有了個可以看的Demo。

第一個版本與發(fā)布前的完善工作

LLVM用上了;VS完整了,PS也有了個雛形;預處理器什么的都有了。

Unit Test也有了原型。我為每個Stage都做了Unit test:Parser,Semantic,CodeGen和JIT。

某種意義上來說,這幾個月來在后端上順利進展,讓我多少有點得意忘形。再加上梁總的幫助,SoftArt這個名字改成SALVIA,LOGO也有了,我在部門內(nèi)部做的一些Introduction也幫助我梳理了思路。于是從4月份開始,我就籌備著要把SALVIA正式發(fā)布出去。

11年6月1號,SALVIA Milestone 1.0 發(fā)布。有Change Log,有Binary Demo,有Snapshot。

三周后,發(fā)布了第一個有Vertex Shader的Demo

6. 2011年7月—2012年1月:坂道の1.0

Pixel Shader:需求與設計

在Milestone 1.0發(fā)布后,我開始做Pixel Shader的特性。本以為半年之內(nèi)就能搞定,發(fā)個1.0揚眉吐氣一下。但是實踐證明,我真是他媽的太盲目樂觀了。

我先來說一說Pixel Shader的特點和需求。比方說我有四個pixel,每個pixel都是一個float。

struct pixel_input
{
  float data;
};

pixel_input pixel_block[4];

然后我要計算一下,這個data加上1.0之后是多少。我前面說過,我要讓指令看起來是四個像素同一時刻執(zhí)行的,那么顯然我生成的代碼就會類似于這樣:

struct pixel_input
{
  float data;
};

struct pixel_output
{
  float data;
};

void shading_pixel(pixel_input* in_data, pixel_output* out_data)
{
     // TMP = IN_DATA.DATA + 1.0
     float tmp0 = in_data[0].data + 1.0;
     float tmp1 = in_data[1].data + 1.0;
     float tmp2 = in_data[2].data + 1.0;
     float tmp3 = in_data[3].data + 1.0;

    // OUT_DATA.DATA = TMP
    out_data[0].data = tmp0;
    out_data[1].data = tmp1;
    out_data[2].data = tmp2;
    out_data[3].data = tmp3;
}

Pixel Shader:優(yōu)化與問題

顯然這里是可以優(yōu)化的:將四條指令并作一條SIMD指令。

那么這個時候,有兩個需求是要滿足的:

  1. 同樣的struct member一定要是鄰接在一起。
  2. 得根據(jù)SIMD的要求數(shù)據(jù)對齊。

只有一個域當然好辦。如果struct很復雜呢,比方說下面這樣:

struct
{
   float;
   float2;
   int3;
   struct 
   {
       float2[3];
       float;
   };
};

那就會衍生出各種問題:

  • 那要不要把每個域都展平呢?
  • 展平到什么程度?
  • 讓每個Builtin Type Member相鄰,還是讓每個Float/Int相鄰?
  • 那遇到動態(tài)尋址,怎么辦?
  • 展平后的代碼,與VS中的代碼能通用嗎?

每個方案都一定能完成,每個方案都有明顯的缺陷。最初我是想嘗試四個像素完全獨立的辦法,這樣實現(xiàn)起來最方便。但是出于對性能的追求,我又想做展平的。展平的方案做到一半,發(fā)現(xiàn)太復雜了。

坑神II:LLVM

此外,還有幾個非常嚴重的問題,發(fā)生在LLVM上。

一個是ABI。一個符合C Calling Convention的LLVM函數(shù),它對堆棧的理解與VS完全不同,特別是參數(shù)傳入或者返回Struct的時候。這樣,直接用LLVM的函數(shù)Export出來后,讓VC去Call它就一定會失敗。為了解決它,我花了近兩周的時間,設計了一個Proxy,讓函數(shù)避免用Struct來傳遞,一切數(shù)據(jù),除了和寄存器同樣大小的float和int,其余數(shù)據(jù)都通過指針來做。同時,我需要將一些函數(shù)注入到LLVM中,比方說紋理采樣,此時ABI同樣是個禍患。為了讓Code Gen正確的識別函數(shù)是LLVM的調(diào)用協(xié)議還是我自己定制的調(diào)用協(xié)議,并產(chǎn)生正確的代碼。我做了各種奇葩和傻逼的方案。有一些方案被廢棄了,但是主要的Idea,仍然沿用到現(xiàn)在。

一個是臨時變量(包括Spiller)的對齊。在Linux/GCC上,棧頂和棧基指針一定是16字節(jié)對齊的。如果編譯器需要分配一個臨時變量,那么它只要通過ESP - 0x10*n就能獲得一個對齊的地址。但是在VC中,x86下完全沒有這樣的限制(除非函數(shù)中使用了__m128,這個時候在進入Frame之后會有一個SUB/AND的指令把棧頂搞到16字節(jié)對齊。)。但LLVM生成的所有代碼,又是基于GCC的假設。SALVIA生成的局部變量,還可以控制地址,但是對于編譯器臨時生成的變量來說,就完全不可控了。在3.1之后因為引入了AVX,需要32字節(jié)對齊,這個問題就更加變本加厲了。在x86上,我還可以通過嵌入?yún)R編,來強制調(diào)整棧幀。但是在x64上,又啟動了AVX的情況下,我就徹底沒有辦法了。這個問題一直延續(xù)到現(xiàn)在,如果我不動手去Debug LLVM的話,就只能等他們什么時候想起來修復這個問題了。

SIMD執(zhí)行模型下分支的處理

Pixel Shader的執(zhí)行模型是SIMD的,這要求每個像素上同一時刻都執(zhí)行相同的指令。如果沒有分支,那自然是簡單無比。一旦有了分支就打破了這個約定。在DX9.0b及之前,這當然沒問題。

但是Shader Model 3.0正式支持Dynamic Branch開始,這個問題就凸現(xiàn)出來了:分支要怎么處理?

對于Pixel Shader來說,會面臨三種分支:靜態(tài)分支,準靜態(tài)分支(這個名字是我瞎起的)和動態(tài)分支。

float branches( uniform float udata, float vdata: POSITION): COLOR0
{
   const float zero = 0.0;
   if(zero < 1.0)
   {
     // Static branch
   }

   if(udata)
   {
      // Semi-Static Branch (我自己造的)
   }
  
   if(vdata)
   {
     // Dynamic Branch
   }
} 

我們來分情況討論一下:

  • 對于靜態(tài)分支來說,因為確定分支的是一個常量,那么顯然在編譯階段就能夠知道分支執(zhí)行與否,直接生成對應的代碼就可以了。
  • 對于uniform作為判斷條件的分支來說,在shader編譯的時候,并不知道這個分支是否會執(zhí)行。但是呢,Uniform會在Shader執(zhí)行前設置,和代碼執(zhí)行相比,Uniform設置的比例非常低。這個時候我們可以先講代碼編譯成中間表達,這個中間表達會知道一個變量是不是Uniform的。在Uniform設置好后,Shader真正執(zhí)行前,把Uniform替換成那個值,也就是把Uniform當做常量,對Shader再編譯一次,得到真正的執(zhí)行指令。所以在指令執(zhí)行的時候,準靜態(tài)分支就和靜態(tài)分支完全相同了。
  • 最后一個,動態(tài)分支。如果判斷條件就是動態(tài)的,那沒辦法,如果要支持SM3.0,就必須要能支持它。同時對于不同的Pixel,都可能有不同的分支。這對于SIMD來說,才是真正的難題。

實際上,我們真正要解決的,就是動態(tài)分支。

對于SIMD模型來說,動態(tài)分支有三種處理辦法。

  1. 跳轉(zhuǎn)執(zhí)行。像CUDA 2.0以上那樣的指令集具備有一定的跳轉(zhuǎn)執(zhí)行能力。編譯器可以把SIMD拆開,按照標量執(zhí)行。每個都執(zhí)行完了后,再繼續(xù)按照SIMD執(zhí)行其他的代碼。
  2. 條件執(zhí)行。這也是圖形硬件上最常見的執(zhí)行模式。通過一個位,就可以決定GPU中的執(zhí)行單元是否執(zhí)行一段代碼。舉個不準確的例子,如果是個4并發(fā)的執(zhí)行器,那么四個并發(fā)執(zhí)行器的執(zhí)行條件可以設置為1100,這樣就只有前兩個單元的數(shù)據(jù)執(zhí)行,后兩個不執(zhí)行了。
  3. 寫掩碼。這個辦法是沒有辦法的辦法。它的基本理念就是:只要不寫到內(nèi)存中的執(zhí)行結(jié)果,就可以認為它沒執(zhí)行過。但是寫掩碼總是浪費了指令。不過好歹它還是避免了跳轉(zhuǎn)的。所以對于早期的ARM這樣沒有分支預測的精簡體系來說,一旦有分支執(zhí)行起來就是死翹翹。所以它有類似于Select-Store這樣的指令,盡可能的避免分支的出現(xiàn)。

對于SAVLIA來說,跳轉(zhuǎn)執(zhí)行和寫掩碼是兩個可能的選擇。因為寫掩碼的代碼生成起來更加輕松一些,所以目前的SALVIA的實現(xiàn)是寫掩碼的。在x86/x64平臺上,對于AVX以上的指令,還可以用blend。但是對于其他指令而言,基本上只能是通過跳轉(zhuǎn)實現(xiàn)寫掩碼。所以這部分的開銷其實很大。等到造出了自己的SSA之后,再來考慮分支執(zhí)行的事情吧。

對于寫掩碼的掩碼要怎么計算,一開始我心里挺沒譜的。特別是有了,Continue和Break之后,情況就會變得復雜起來。一開始我沒法確信自己的方案是正確的。后來看了MESA的Gallinum以后,看見了Continue Mask和Break Mask兩個變量,瞬間就明白了。

具體怎么思考的不多說了,這里寫下幾個結(jié)論:

  1. 語言不能有Goto(有Goto會讓代碼變得非常復雜,甚至不可解);
  2. 所需要的掩碼的數(shù)量會隨著循環(huán)的嵌套層數(shù)的增加而增加;
  3. 每個循環(huán)最多有三個掩碼:Break,Continue和Mask;
  4. 程序是固定的話,掩碼的數(shù)量就一定是個常量。(要不然硬件就沒法做了)
  5. 寫掩碼的位數(shù)只和執(zhí)行單元的數(shù)量有關,和嵌套深度無關。

坂道のTest

盡管遇到了各種難處,但是很多方案還是順利的做出來了。方案和方案之間差異很大,要想順利移植,必須要有Test。

之前也說過,一開始我的Test是按照Parser,Semantic,Code Gen,JIT分開做的。但是呢,這樣一來,不同Stage之間的Test復用性非常高。而且因為Stage經(jīng)常變化,包括Stage的接口。這時候Test就完蛋了。Test本身也很枯燥(變量名都不好起),所以Test重寫起來難過的要死。

于是我重新審視了一下需求。發(fā)現(xiàn)我最終只關心JIT編譯出來的函數(shù)的運行結(jié)果,其實并不關心中間的過程。而且隨著我對編譯過程理解的逐步變化,Compiler Stages幾乎每隔兩個月就要進行比較大的修正。測試的量稍微大一點,就沒有辦法維護Test Case了。并且,對于單條語句或者非常短的函數(shù)來說,從詞法到最終JIT出來的函數(shù)所覆蓋的編譯器代碼非常之少,可能3-4個函數(shù),代碼就出來了。即便有問題,對比過去的版本輕松就能分析出來。再加上大量的Assertion,診斷起來更加容易。

因此,在這幾個月中我完全重寫了Test Case:讓JIT的測試粒度更低,測試更豐富;取消所有的中間Level的測試。新的測試回歸起來非常容易,出了問題也很好找到。在Test Case寫完后,正好看到Martin Fowler噴過度TDD的問題,真是感同身受。

測試需要嗎?當然需要。但是選擇合適的Level,做合適的測試是非常重要的。結(jié)合之前實習的時候的Unit Test經(jīng)驗,有以下幾點感受:

  1. 測試一定要選擇盡可能低的面,這樣牽涉的代碼就盡可能少;
  2. 在縱向上,粒度要細。除了單個API的Test,還要有適度的交叉,不過太綜合的測試,請讓集成測試用例來完成;
  3. 要重視代碼覆蓋率;
  4. 測試面向的API要穩(wěn)定。天天變得API會讓你徹底失去寫Test的信心。API越穩(wěn)定,在它上面出現(xiàn)問題的機會就越多,你寫的測試性價比也越高。

坡長路遠,小步快走

在完成了Test的改造后,終于有了一個合適的發(fā)布前評估。所以到了11年11月后,發(fā)布的速度就明顯變快了許多。快速的發(fā)布對于做一個長期項目來說非常重要。這也和敏捷的想法不謀而合。不管是從品質(zhì)控制上、還是進度追蹤上,或者是說對開發(fā)者自信心的增強,都需要有短平快的開發(fā)周期。11年也正好是Autodesk推行敏捷的一年。同事里面有很多的人反應說敏捷會導致軟件品質(zhì)的下降,短期目標會導致過于追逐眼前利益。

但是從我的經(jīng)驗來看,對于個人,敏捷要短平快。但對于團隊,敏捷要從長計議。不是所有的iteration都需要開發(fā)新特性,必須要保留足夠的iteration來完成重構、整理、設計方案的反省和討論。對于以年為單位的長周期產(chǎn)品來說,可以每個季度有3-5天的時間,每個人都提出對框架的改進計劃;每年有兩周的時間,完成框架的重構和修正。更小的重構,可以安排的更加短小的時間。

6. 2012年1月及以后:現(xiàn)在與未來

新特性,新思考

從11年7月份開始到現(xiàn)在,就一直在做Demo、優(yōu)化、特性的完善;以及一些新特性的思考。

總的來說,這一年半的時間里面,很多工作已經(jīng)不像早先幾年做的那么吃力,但是仍然在很多的點上有所斬獲。

  • 整個編譯器后端,包括基本的分析和優(yōu)化都已經(jīng)有所了解,LLVM也熟悉了許多;
  • 對Shader相關的API的了解也不再懵懵懂懂;
  • 對于語言機制的研究,加上陳漢子時不時拋來的一些思維發(fā)散題令我對語言有了更深入的認識;
  • 認識了RFX,在短短幾周就幫助我在閱讀V8和LLVM時積累的一些知識轉(zhuǎn)化成了有用的理解。

在2012年底為SALVIA進行了局部的重新設計,也是“學”與“習”的新一輪“習”。新的SSA及Shader優(yōu)化、JIT化的管線、對性能有要求的新前端、瞄準DX11以上Shader Model Features、JIT的調(diào)試符號,這些一定會給我?guī)碓S多絞盡腦汁想不明白的問題,但同時我也會學習到、實踐到許多新的知識。

我相信時間會教給我們一切。

posted on 2013-01-13 05:00 空明流轉(zhuǎn) 閱讀(6107) 評論(12)  編輯 收藏 引用

評論

# re: 開源光柵化渲染器SALVIA的漫長五年(準&middot;干貨) 2013-01-13 05:12 Angel.Cheung
坐個沙發(fā)~~顯然完全看不懂~~  回復  更多評論
  

# re: 開源光柵化渲染器SALVIA的漫長五年(準&middot;干貨) 2013-01-13 09:18 Scan
在最初卡ddx問題的時候,比起fiber-manager方案,可否考慮從這個方向入手:

ps的輸入都是c++的PSVector<n>類型,至于PSXXX<n>相對XXX<n>的區(qū)別在于,前者包含了4個XXX<n>,在之后進行各種計算的時候,都是對4個XXX<n>同時進行的,類似于XXX<n>和它內(nèi)部的每個分量的關系。于是用c++編寫的靜態(tài)ps中的計算都是基于PSXXX<n>的,ddx也就是在PSXXX<n>內(nèi)部各個分量之間交互。這種寫法c++編譯器能自動進行sse優(yōu)化。
當然,這遇上了后面的動態(tài)分支必然會是個死。

空明巨巨的經(jīng)歷實在是很贊的,并且居然cpu的shader涉及那么復雜的問題,可以讓人在上面浸淫那么久學到那么多!

RFX的確是神技啊!!

最后那個“6. 2011年7月及以后:現(xiàn)在與未來”該叫“7.2012年7月xxx”吧?  回復  更多評論
  

# re: 開源光柵化渲染器SALVIA的漫長五年(準&middot;干貨) 2013-01-13 10:10 穿越模式
雖然看不懂,頂一下空明大神!  回復  更多評論
  

# re: 開源光柵化渲染器SALVIA的漫長五年(準&middot;干貨) 2013-01-13 11:57 airtrack
膜拜空明巨巨!  回復  更多評論
  

# re: 開源光柵化渲染器SALVIA的漫長五年(準&middot;干貨) 2013-01-13 17:14 Richard Wei
膜拜并學習  回復  更多評論
  

# re: 開源光柵化渲染器SALVIA的漫長五年(準&middot;干貨) 2013-01-13 17:47 Edwin
弱弱的問一句:什麼是RFX ?  回復  更多評論
  

# re: 開源光柵化渲染器SALVIA的漫長五年(準&middot;干貨) 2013-01-13 17:50 空明流轉(zhuǎn)
@Edwin

人名,一個大牛
  回復  更多評論
  

# re: 開源光柵化渲染器SALVIA的漫長五年(準&middot;干貨)[未登錄] 2013-01-14 09:51 春秋十二月
長風破浪會有時 直掛云帆濟滄海 加油  回復  更多評論
  

# re: 開源光柵化渲染器SALVIA的漫長五年(準&middot;干貨) 2013-01-14 21:43 Zblc(邱震鈺)
MARK,支持~~~~~~~~~  回復  更多評論
  

# re: 開源光柵化渲染器SALVIA的漫長五年(準&middot;干貨)[未登錄] 2013-01-17 15:10 Hunter
大神,再過幾年,你對V/P/C Shader知行合一,融會貫通,國內(nèi)是容不下你了。  回復  更多評論
  

# re: 開源光柵化渲染器SALVIA的漫長五年(準&middot;干貨) 2013-03-18 11:23 hustruan
最近也在做光柵化的圖形學作業(yè),C++版的Shader實現(xiàn),確實不好做ddx,ddy這種東西,不過應該可以自己定義個包含4個Float4的ShaderFloat4,然后就可以計算各種偏導數(shù)了。  回復  更多評論
  

# re: 開源光柵化渲染器SALVIA的漫長五年(準&middot;干貨) 2016-04-08 23:42 bitzhuwei
博主你好,我現(xiàn)在在做一個GLSL的編譯器(前端),但是我找到的GLSL文法是個病態(tài)的文法,我對GLSL本身用的還不夠多,沒辦法自己修改出一個正常的文法。求博主提供一個GLSL的文法?(bitzhuwei@qq.com)  回復  更多評論
  

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久久999| 午夜视频在线观看一区| 久久精品成人一区二区三区蜜臀| 国产精品久久一卡二卡| 亚洲一区三区视频在线观看| 日韩亚洲欧美中文三级| 欧美日韩一区在线视频| 午夜视频一区| 久久免费观看视频| 夜夜爽99久久国产综合精品女不卡| 亚洲精品美女久久久久| 欧美va天堂| 亚洲欧美另类在线| 久久看片网站| 亚洲午夜av电影| 欧美自拍丝袜亚洲| 亚洲天堂免费观看| 欧美韩日一区二区| 亚洲欧美国产三级| 久久久久9999亚洲精品| 日韩亚洲欧美综合| 亚洲一区区二区| 亚洲高清电影| 中文无字幕一区二区三区| 国产综合色精品一区二区三区| 欧美aa在线视频| 国产精品盗摄久久久| 美女成人午夜| 国产精品毛片va一区二区三区 | 久久精品国亚洲| 欧美国产1区2区| 久久精品1区| 欧美精品一区二区精品网| 久久精品亚洲一区二区三区浴池| 欧美激情视频一区二区三区不卡| 久久精品免费电影| 久久激情五月丁香伊人| 99re6这里只有精品视频在线观看| 亚洲免费视频一区二区| 99pao成人国产永久免费视频| 亚洲欧美资源在线| 亚洲一区久久久| 免费亚洲一区二区| 久久综合图片| 国产精品国产三级国产专播精品人 | 午夜视黄欧洲亚洲| 99国内精品久久久久久久软件| 久久国产精品网站| 亚洲欧美日韩中文视频| 欧美区日韩区| 亚洲人被黑人高潮完整版| 激情av一区二区| 欧美亚洲一区| 久久成人精品电影| 国产精品视频xxxx| 亚洲午夜视频在线观看| 一区二区免费在线观看| 欧美不卡高清| 亚洲国产精品va在线看黑人动漫| 激情丁香综合| 久久精品亚洲精品| 久久网站热最新地址| 国产一区二区三区四区在线观看 | 免费精品视频| 欧美激情免费在线| 亚洲欧洲在线一区| 欧美凹凸一区二区三区视频| 欧美va亚洲va国产综合| 精品91视频| 欧美高清视频www夜色资源网| 一本大道久久精品懂色aⅴ| 欧美一区二区视频观看视频| 国产精品成人在线| 亚洲一区二区在线免费观看视频| 99精品国产福利在线观看免费 | 亚洲精品国产精品乱码不99| 91久久在线播放| 欧美精品v国产精品v日韩精品| 亚洲国产日韩欧美| 亚洲一区免费视频| 国产午夜精品福利| 久久乐国产精品| 欧美激情一区二区三区四区| 99这里只有精品| 国产精品蜜臀在线观看| 亚洲欧美日韩一区| 蜜臀a∨国产成人精品| 在线免费精品视频| 欧美日本中文字幕| 亚洲一级二级| 免费不卡中文字幕视频| 99热这里只有精品8| 国产毛片一区二区| 噜噜噜久久亚洲精品国产品小说| 亚洲另类春色国产| 久久精品欧美日韩精品| 亚洲国产精品成人久久综合一区| 欧美精品一区二| 亚洲欧美成人一区二区在线电影| 久久综合色播五月| 亚洲一区国产精品| 国内精品写真在线观看| 欧美精品一区二| 中文在线不卡视频| 欧美成年视频| 亚洲欧美日韩成人| 亚洲国产综合在线| 国产精品色网| 欧美国产第一页| 欧美中文在线观看| 9l国产精品久久久久麻豆| 久久人人97超碰精品888| 一区二区三区黄色| 亚洲国产三级| 国产老肥熟一区二区三区| 麻豆精品一区二区综合av| 欧美日韩成人激情| 榴莲视频成人在线观看| 亚洲一区二区综合| 99热在这里有精品免费| 影音先锋亚洲精品| 国产欧美一区二区三区久久人妖| 免费在线观看精品| 久久国产福利| 午夜激情综合网| 亚洲视频综合| 99精品热视频| 亚洲精品国产拍免费91在线| 性欧美videos另类喷潮| 一区二区三区高清视频在线观看| 1000部国产精品成人观看| 国产欧美日本| 国产农村妇女精品| 国产精品久久国产愉拍| 欧美视频一区二区在线观看| 欧美激情aaaa| 免费在线观看精品| 久久一区二区三区四区五区| 久久久久久噜噜噜久久久精品| 亚洲欧洲av一区二区| 亚洲一区二区三区影院| 亚洲小少妇裸体bbw| 亚洲高清123| 欧美肉体xxxx裸体137大胆| 欧美肥婆在线| 欧美日韩精品久久久| 欧美激情中文不卡| 欧美激情日韩| 欧美午夜不卡在线观看免费| 欧美日韩在线亚洲一区蜜芽| 欧美视频在线观看免费网址| 欧美午夜视频一区二区| 国产精品久久久一区二区| 国产麻豆精品theporn| 国产精品羞羞答答xxdd| 国产资源精品在线观看| 精品成人一区二区三区| 亚洲福利视频在线| 亚洲精选中文字幕| 亚洲一区中文| 久久久综合激的五月天| 欧美高清视频一区二区三区在线观看| 免费亚洲网站| 亚洲精品国产精品乱码不99按摩| 亚洲午夜电影| 久久精品水蜜桃av综合天堂| 欧美α欧美αv大片| 久久精品官网| 国产免费观看久久| 黑人巨大精品欧美黑白配亚洲| 亚洲国产精品成人综合| 亚洲乱码一区二区| 亚洲一区二区精品在线观看| 久久精品视频在线| 亚洲成人直播| 亚洲一区二区在线看| 久久中文字幕一区| 国产精品成人一区二区艾草| 国产午夜精品一区二区三区视频 | 国产日韩欧美一区二区| 亚洲国产精品久久久久| 亚洲一品av免费观看| 欧美在线观看一区二区| 欧美激情成人在线视频| 一个色综合av| 亚洲欧美日韩国产| 欧美国产精品一区| 国产自产v一区二区三区c| 亚洲九九精品| 麻豆9191精品国产| 亚洲天堂av电影| 欧美岛国在线观看| 国内偷自视频区视频综合| 亚洲精品欧洲| 久久久夜夜夜| 亚洲午夜视频在线| 久久亚洲精品一区二区| 国产日产精品一区二区三区四区的观看方式 | 久久亚洲国产成人| 国产亚洲欧美激情|