• <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>

            Raytrace world

            Chaos 的光線跟蹤世界

              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              4 隨筆 :: 0 文章 :: 63 評論 :: 0 Trackbacks

            Chaos on Graphics

            About 數(shù)學(xué)庫之一 SSE/SSE2

             

            Chaos Chiao

             

             

            毫無疑問,數(shù)學(xué)庫是圖形程序的基石,是圖形程序運(yùn)行效率的關(guān)鍵之一。一個(gè)優(yōu)秀的數(shù)學(xué)庫可以讓圖形程序運(yùn)行得更流暢,甚至要快上幾十倍上百倍。有時(shí)候替換一條除法運(yùn)算會(huì)帶來成倍的效率增長,比如用乘以 1/op 替換 vector 里的 operator /。當(dāng)然,更高級的優(yōu)化是使用 SIMD 優(yōu)化海量運(yùn)算,這就是本文的中心——SSE/SSE2 優(yōu)化。

            在描述 SSE/SSE2 優(yōu)化前,我先介紹一般的 vector/matrix 庫構(gòu)造。當(dāng)然,在 OpenEXR 里已經(jīng)有一個(gè)非常優(yōu)秀的 Imath 實(shí)現(xiàn)了,數(shù)學(xué)庫的實(shí)現(xiàn)細(xì)節(jié)可以參照它。

            在圖形程序里我們經(jīng)常會(huì)遇到向量運(yùn)算,這是標(biāo)準(zhǔn)C++編譯器所不能直接支持的,如三維空間向量。傳統(tǒng)的C圖形程序會(huì)使用“數(shù)組+宏”的實(shí)現(xiàn)方式:

             

            typedef float vector[3];

              

            到了C++時(shí)代,一般會(huì)封裝成:

             

            class Vector

            {

            private:

                float x , y , z;

            };

             

            然后加入通常的各種method,如

             

            const float& X( void ) const {

                return x;

            }

             

            標(biāo)準(zhǔn)的向量算法如內(nèi)積、外積、單位化、長度、運(yùn)算等等,都可以封裝為成員函數(shù)。

             

            Vector operator + ( const Vector& a , const float & b ) {

                return Vector( a.x + b , a.y + b , a.z + b );

            }

             

            類似的數(shù)學(xué)庫可以在Aqsis等一些開源的圖形程序里找到。不過這些結(jié)構(gòu)并不適合接下來我們要討論的SSE/SSE2優(yōu)化。

             

            SSE – Streaming SIMD Extension,是IntelPIII開始加入的一種x86擴(kuò)展指令集。在SSE以前,x86的浮點(diǎn)運(yùn)算都是以棧式FPU完成的,有一定x86匯編經(jīng)驗(yàn)的人應(yīng)該不會(huì)對那些復(fù)雜的fld、fst指令陌生吧。而SSE一方面讓浮點(diǎn)運(yùn)算可以像整數(shù)運(yùn)算的模式、如 add eax , ebx 那樣通過直接訪問寄存器完成,繞開了討厭的棧,另一方面引入了SIMD這個(gè)概念。SIMD – Single Instruction Multiply Data,顧名思義,它可以同時(shí)讓一條指令在多個(gè)數(shù)據(jù)上執(zhí)行,這種體系結(jié)構(gòu)在一度在大型機(jī)上非常流行,需要經(jīng)常進(jìn)行海量運(yùn)算的大型機(jī)器通常會(huì)通過一個(gè)數(shù)學(xué)SIMD虛擬機(jī)加快處理速度,比如同時(shí)讓一組數(shù)據(jù)執(zhí)行一個(gè)變換,數(shù)據(jù)的規(guī)模有上百萬之巨,而SIMD則可以優(yōu)化數(shù)據(jù)的存儲(chǔ)與運(yùn)算,減免某些切換Context的開銷。

            在硬件層面上面支持SIMD,某程度是因游戲需要的驅(qū)使,因?yàn)樵絹碓蕉嗟?/SPAN>3D游戲涉及大量的向量操作,一般的浮點(diǎn)運(yùn)算優(yōu)化已經(jīng)不能再適應(yīng)這種并行運(yùn)算的需要了,而直接從指令上支持SIMD操作則可以進(jìn)一步簡化向量運(yùn)算的優(yōu)化,提高指令執(zhí)行效率。像 addps 這樣的SSE指令,可以并行執(zhí)行四個(gè)32位浮點(diǎn)數(shù)的加法運(yùn)算,而延遲只有4 cycle;相比之下,原來的fadd指令光執(zhí)行一個(gè)32位單精度浮點(diǎn)數(shù)加法的延遲已經(jīng)達(dá)到了3 cycle了,還沒計(jì)算fst等存儲(chǔ)指令的延遲。(具體見后面的指令執(zhí)行單元表)

            顯然,SSE能給圖形程序帶來極大的優(yōu)化,其提高遠(yuǎn)勝于基于整數(shù)的MMX與雙單元單精度浮點(diǎn)數(shù)的3DNow!。但SSE對數(shù)據(jù)組織的要求是苛刻的,若要發(fā)揮SSE的最大威力,我們還需要進(jìn)行對齊向量數(shù)據(jù),把向量對齊到16字節(jié)。如果我們正在使用一般的三分量向量,那么就意味著有要浪費(fèi)四分之一的存儲(chǔ)空間來換取速度。當(dāng)然,這4字節(jié)還可以有很多用途,只是你必須處理得非常小心,因?yàn)槿魏芜\(yùn)算都將同時(shí)應(yīng)用到四個(gè)分量上。

            要使用SSE,必須先確認(rèn)你的編譯器是否支持新的指令集。VC6 sp6VC.net、.net 2003、ICL、GCC 、nasm 都支持SSE指令集。我推薦使用ICL,它的優(yōu)化做得最棒,生成的指令最緊湊、效率最高。使用SSE有兩種途徑,一是直接編寫匯編代碼,但難度較大,需要有一定的匯編經(jīng)驗(yàn);二是使用SSE intrinsic,一種直接在C/C++里使用SSE指令的偽函數(shù)調(diào)用。在圖形運(yùn)算的核心環(huán)節(jié)上、如raytrace核心,我建議使用匯編,這樣才能極大地體現(xiàn)出SSE的優(yōu)勢、與x86指令混合使用,并充分使用它的并行性。而在大多數(shù)場合下則推薦使用intrinsic,它的可讀性高,而且編譯器會(huì)在最后把函數(shù)調(diào)用替換成SSE指令,這樣既不需要寫內(nèi)嵌匯編代碼,又可以保證代碼的執(zhí)行效率。

             

            下面將通過幾個(gè)簡單的運(yùn)算例子介紹SSE intrinsic的使用。首先,使用SSE需要一個(gè)新的頭文件

             

            #include

             

            里面定義了一個(gè)新的數(shù)據(jù)類型,__m128,這是一個(gè)128位、4個(gè)32位單精度浮點(diǎn)數(shù)的結(jié)構(gòu),如果你正在使用VC.net,你會(huì)看到它是一個(gè)關(guān)鍵字,被當(dāng)作一種基本數(shù)據(jù)類型。要是你不打算使用匯編SSE,那么就沒必要深究編譯器在幕后到底如何處理__m128類型的數(shù)據(jù),你只需要知道里面能存放四個(gè)float,而這四個(gè)float可以進(jìn)行并行運(yùn)算。

            在定義了__m128后,文件聲明一大堆對__m128進(jìn)行運(yùn)算的函數(shù),如_mm_add_ps、_mm_sub_ps等等,這就是SSE運(yùn)算指令的聲明。使用SSE優(yōu)化在這些聲明的幫助下變得非常簡單,如計(jì)算兩個(gè)向量之和,平時(shí)需要每一個(gè)元素進(jìn)行一次加法運(yùn)算,現(xiàn)在只需要簡單地:

             

            __m128 a , b , c;

            c = _mm_add_ps( a , b );

             

            這樣等價(jià)于:

             

            float a[4] , b[4] , c[4];

            for( int i = 0 ; i < 4 ; ++ i )

                c[i] = a[i] + b[i];

             

            但前者的運(yùn)算是并行的,在一般情況下效率遠(yuǎn)比后者要高。況且在描述復(fù)雜的運(yùn)算的時(shí)候,如:

             

                   a = b * c + d / e;

             

            則可以直接寫成:

             

            __m128 a = _mm_add_ps( _mm_mul_ps( b , c ) , _mm_div_ps( d , e ) );

             

            咋看之下,很多效率至上的人馬上就會(huì)大叫“昂貴的函數(shù)調(diào)用??!Bad smell code!”。其實(shí)我正要告訴你,我也是效率至上派的。前面已經(jīng)說過了,這些看上去貌似函數(shù)的調(diào)用實(shí)際上并非函數(shù),而是所謂intrinsic,它們在編譯優(yōu)化中將被解釋為單條或多條SSE指令,而且編譯器會(huì)自動(dòng)調(diào)節(jié)調(diào)用順序以使其最大并行效率。

            不過除了直接使用這些intrinsic以外,我們還可以把它們封裝到類里面,重載運(yùn)算符,這樣就可以把運(yùn)算寫成可讀性更強(qiáng)的算術(shù)式。如果你不愿意自己動(dòng)手封裝,也可以使用Intel封裝好了的F32vec4類,它提供了完備的運(yùn)算符重載,完全使用SSE,非常方便。

            雖然Intel封裝好的類已經(jīng)很完善了,但還有一大堆數(shù)學(xué)運(yùn)算需要我們自己動(dòng)手進(jìn)行編寫,如內(nèi)積(點(diǎn)積)和外積(叉積)。

             

            首先來看一個(gè)比較實(shí)用的運(yùn)算,求倒數(shù)。求倒數(shù)在很多數(shù)學(xué)庫里都有專門的優(yōu)化,通常原理都是先求出一個(gè)近似值,然后通過Newton-Raphson逼近法求出較精確值,下面的代碼摘自NVfastmath.cpp:

             

            #define FP_ONE_BITS 0x3F800000

            // r = 1/p

            #define FP_INV(r,p)                                                   \

            {                                                                           \

                int _i = 2 * FP_ONE_BITS - *(int *)&(p);                  \

                r = *(float *)&_i;                                                \

                r = r * (2.0f - (p) * r);                                       \

            }

             

            而在SSE里也提供了兩條求倒數(shù)的指令rcpss/rcpps(對應(yīng)的intrinsic_mm_rcp_ss_mm_rcp_ps),不過這兩條指令求的并非是精確值,而是近似值,所以我們需要對它的結(jié)果進(jìn)行逼近處理。

             

            float __rcp<float>( const float& a ) {

                register float r;

                __m128 rcp = _mm_load_ss( &a );

                rcp = _mm_rcp_ss( rcp );

                _mm_store_ss( &r , rcp );

                /* [2 * rcpps(x) - (x * rcpps(x) * rcpps(x))] */

                r = 2.0f * r - ( a * r * r );

                return r;

            }

             

            原理一致,只不過我們還可以用_mm_rcp_ps并行求四分量的倒數(shù)。如果你還對SSE的威力有所保留,那我建議你設(shè)計(jì)一個(gè)測試單元測試一下使用除法求倒數(shù)與使用SSE求倒數(shù),看效率到底是誰更高、高多少。當(dāng)然,我自己已經(jīng)測試過很多次了J。

             

            然后我們把注意力放到一條非常特殊的指令shufps(對應(yīng)intrinsic_mm_shuffle_ps)上面。這是一條非常有用的指令,它可以把兩個(gè)操作數(shù)的分量以特定的順序排列并賦予給目標(biāo)數(shù)。比如

             

            __m128 b = _mm_shuffle_ps( a , a , 0 );

              

            b 的所有分量都是 a 中下標(biāo)為0的分量。第三個(gè)參數(shù)控制分量分配,是一個(gè)8bit的常量,這個(gè)常量的1~8位分別控制了從兩個(gè)操作數(shù)中選擇分量的情況,具體怎么控制將在后面討論SSE匯編中一并說明,而在使用intrinsic的時(shí)候,最好使用_MM_SHUFFLE宏,它可以定義分配情況。下面我們來復(fù)習(xí)一下叉積的求法。

            c = a x b

             

            可以寫成:

             

            Vector cross(const Vector& a , const Vector& b ) {

                return Vector(

                    ( a[1] * b[2] - a[2] * b[1] ) ,

                    ( a[2] * b[0] - a[0] * b[2] ) ,

                    ( a[0] * b[1] - a[1] * b[0] ) );

            }

             

            那么寫成SSE intrinsic形式則是:

             

            /* cross */

            __m128 _mm_cross_ps( __m128 a , __m128 b ) {

                __m128 ea , eb;

                // set to a[1][2][0][3] , b[2][0][1][3]

                ea = _mm_shuffle_ps( a , a , _MM_SHUFFLE( 3 , 0 , 2 , 1 ) );

                eb = _mm_shuffle_ps( b , b , _MM_SHUFFLE( 3 , 1 , 0 , 2 ) );

                // multiply

                __m128 xa = _mm_mul_ps( ea , eb );

                // set to a[2][0][1][3] , b[1][2][0][3]

                a = _mm_shuffle_ps( a , a , _MM_SHUFFLE( 3 , 1 , 0 , 2 ) );

                b = _mm_shuffle_ps( b , b , _MM_SHUFFLE( 3 , 0 , 2 , 1 ) );

                // multiply

                __m128 xb = _mm_mul_ps( a , b );

                // subtract

                return _mm_sub_ps( xa , xb );

            }

             

            這就是shuffle強(qiáng)大的地方,它可以直接在寄存器里直接調(diào)整分量的順序。而且配合_mm_movehl_ps,我們可以輕松解決點(diǎn)積的運(yùn)算。_mm_movehl_ps把操作數(shù)高位兩個(gè)分量賦予目標(biāo)數(shù)的低位兩分量,而目標(biāo)數(shù)的高位兩分量值不變,相當(dāng)于:

             

            a[0] = b[2];

            a[1] = b[3];

             

            三分量的向量求點(diǎn)積,可以寫成:

             

            float dot( const float& a , const float& b ) const {

                return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];

            }

             

            則用SSE intrinsic可以寫成:

             

            /* x[0] * x[1] + y[0] * y[1] + z[0] * z[1] */

            __m128 _mm_dot_ps( __m128 x , __m128 y ) {

                __m128 s , r;

                s = _mm_mul_ps( x , y );

                r = _mm_add_ss( s , _mm_movehl_ps( s , s ) );

                r = _mm_add_ss( r , _mm_shuffle_ps( r , r , 1 ) );

                return r;

            }

             

            通過這兩個(gè)例子,可以留意到向量內(nèi)元素的垂直相加一般形式,即:

             

            /* x[0] + x[1] + x[2] + x[3] */

            __m128 _mm_sum_ps( __m128 x ) {

                __m128 r;

                r = _mm_add_ps( x , _mm_movehl_ps( x , x ) );

                r = _mm_add_ss( r , _mm_shuffle_ps( r , r , 1 ) );

                return r;

            }

             

            那么通過擴(kuò)展,可以得到求向量長度的函數(shù),首先是求分量平方和函數(shù):

             

            /* x[0] * x[0] + y[0] * y[0] + z[0] * z[0] */

            __m128 _mm_square_ps( __m128 x ) {

                __m128 s , r;

                s = _mm_mul_ps( x , x );

                r = _mm_add_ss( s , _mm_movehl_ps( s , s ) );

                r = _mm_add_ss( r , _mm_shuffle_ps( r , r , 1 ) );

                return r;

            }

             

            然后就可以直接把結(jié)果求平方根,可得長度。解決了長度,接下來則是很重要的單位化了??梢哉f單位化是最重要的一個(gè)函數(shù),它經(jīng)常被調(diào)用到,而函數(shù)內(nèi)的陷阱卻又最多。求單位化其實(shí)并不難,就是分量除以向量長度,可以寫成:

             

            void normalize( const Vector& a ) {

                float len = a[0] * a[0] + a[1] * a[1] + a[2] * a[2];

                if( is_zero( len ) )

                    return;

                len = 1 / len;

                a[0] *= len;

                a[1] *= len;

                a[2] *= len;

            }

             

            我和這個(gè)家伙打交道已經(jīng)有差不多七年時(shí)間了,所以脾性非常熟悉。首先求分量的平方和,判斷是否為0(問我為什么不直接用 if( len == 0 )?好樣的,請先去復(fù)習(xí)一下浮點(diǎn)數(shù)的基本知識),然后再求倒數(shù),最后反映到分量上。在把它寫成SSE intrinsic格式前,我先引入另外一個(gè)能極大提升運(yùn)算效率的函數(shù),求平方根的倒數(shù)。有數(shù)值運(yùn)算編成經(jīng)驗(yàn)的人都知道,如果說除法是惡魔的話,那么平方根就是撒旦了,而平方根的倒數(shù)簡直就是撒旦他媽。雖然上面提供了倒數(shù)的逼近方法,但僅僅使用它還是繞不開最主要的開銷、平方根運(yùn)算。幸好,SSE提供了一個(gè)直接計(jì)算平方根倒數(shù)近似值的指令,rsqrtss/rsqrtps(即_mm_rsqrt_ss_mm_rsqrt_ps)。照搬倒數(shù)求法,可以輕松得出:

             

            /* r = 1 / sqrt(a) */

            /* 0.5 * rsqrtss * (3 - x * rsqrtss(x) * rsqrtss(x)) */

            __m128 _mm_rsqrt( __m128 a )

            {

                / divisor

                static const __m128 _05 = _mm_set1_ps( 0.5f );

                static const __m128 _3 = _mm_set1_ps( 3.f );

                __m128 rsqrt = _mm_rsqrt_ss( a );

                rsqrt =

            _mm_mul_ss(

            _mm_mul_ss( _05 , rsqrt ) ,

            _mm_sub_ss( _3 , _mm_mul_ss( a , _mm_mul_ss( rsqrt , rsqrt ) ) ) );

                return rsqrt;

            }

             

            那么就可以輕松得出單位化向量的函數(shù)了:

             

            // normalize & return value

            __m128 _mm_normalize( const __m128 a ) {

                // get length square

                __m128 l = _mm_square_ps( a );

                // test if length is zero

                if( _mm_iszero_ss( l ) )

                    return z;

                // length inverse

                l = _mm_rsqrt( l );

                // shuffle

                l = _mm_shuffle_ps( l , l , 0 );

                // multiply to vector

                return _mm_mul_ps( a , l );

            }

             

             

            SSE除了以上這些數(shù)學(xué)運(yùn)算操作外,還提供了位運(yùn)算。位運(yùn)算?想到什么了嗎?對!比較與選擇。首先來看一個(gè)最簡單的,求絕對值。通常我們會(huì)把 abs 寫成非常簡潔的形式:

             

            float abs( float a ) {

                a >= 0 ? a : -a;

            }

             

            但當(dāng)我們已經(jīng)Pack了一個(gè)向量到__m128結(jié)構(gòu)里,而又不希望Unpack他們進(jìn)行浮點(diǎn)數(shù)的比較,那么就可以使用SSE的位操作。

             

            /* abs */

            __m128 _mm_abs_ps( __m128 a )

            {

                static const union { int i[4]; __m128 m; } __mm_abs_mask_cheat_ps

            = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff};

                return _mm_and_ps( a, __mm_abs_mask_cheat_ps.m );

            }

             

            還記得單精度浮點(diǎn)數(shù)的符號存放在什么位上面嗎?我們只需把它除掉,然后就可以很輕松地得到了正值了。

             

            圖形程序很多時(shí)候會(huì)用到32位浮點(diǎn)色彩,其值域通常為[0,1],所以clamp函數(shù)出現(xiàn)的頻率也十分頻繁。要將rgba的值同時(shí)clamp到值域內(nèi),毫無疑問,SSE的并行特性又得到了發(fā)揮的機(jī)會(huì)。先來看cut函數(shù),它負(fù)責(zé)把超出值域的值干掉,但為了更靈活,我們一次只cut一邊的區(qū)間,所以cut有兩兄弟,分別是locuthicut。

             

            __m128 _mm_locut_ps( __m128 val , __m128 bound )

            {

                __m128 mask = _mm_cmplt_ps( val , bound );

                return _mm_or_ps( _mm_and_ps( mask , bound ) , _mm_andnot_ps( mask , val ) );

            }

            __m128 _mm_hicut_ps( __m128 val , __m128 bound )

            {

                __m128 mask = _mm_cmpgt_ps( val , bound );

                return _mm_or_ps( _mm_and_ps( mask , bound ) , _mm_andnot_ps( mask , val ) );

            }

             

            _mm_cmp**_ps是一系列的比較函數(shù),非常豐富,也很好用,如果替換成相應(yīng)的if-else,并行性將被大大削弱。不過_mm_cmp**_ps的最大缺點(diǎn)就是靈活度不夠,返回值是一系列位標(biāo)記,其具體的用法將在SSE匯編中討論。有了這倆哥們,clamp變得非常簡單:

             

            __m128 _mm_clamp_ps( __m128 val , __m128 min , __m128 max )

            {

                return _mm_locut_ps( _mm_hicut_ps( val , max ) , min );

            }

             

            以上只是一些很簡單的實(shí)現(xiàn),利用了SSE intrinsic對數(shù)學(xué)運(yùn)算進(jìn)行優(yōu)化,并盡可能地不分拆向量,這樣可以保證8個(gè)128位的xmm寄存器可以滿足大部分運(yùn)算。不過SSE intrinsic始終受編譯器生成代碼的質(zhì)量好壞影響,沒能真正發(fā)揮出SSE的全部威力,接下來我們將討論SSE匯編的用法與優(yōu)化。

             


            to be continued...

            posted on 2005-10-22 02:39 Chaos Chiao 閱讀(4687) 評論(7)  編輯 收藏 引用 所屬分類: Chaos on Graphics

            評論

            # re: Chaos on Graphics ~ About 數(shù)學(xué)庫之一 SSE/SSE2 2006-02-05 16:21 yyy
            看了5遍,越看越好看!  回復(fù)  更多評論
              

            # re: Chaos on Graphics ~ About 數(shù)學(xué)庫之一 SSE/SSE2 2006-04-25 03:22 信仔
            請告之 現(xiàn)在做什么??

            佩服!  回復(fù)  更多評論
              

            # re: Chaos on Graphics ~ About 數(shù)學(xué)庫之一 SSE/SSE2 2006-04-25 19:39 丁勇
            希望與博客主人及各位交流,科技創(chuàng)新,http://dycomputer.superbuckler.com/,或者http://blog.sina.com.cn/u/1224675400  回復(fù)  更多評論
              

            # re: Chaos on Graphics ~ About 數(shù)學(xué)庫之一 SSE/SSE2 2006-04-25 19:44 晴天
            2030就是歐美軟件外包專才培養(yǎng)計(jì)劃,http://www.i2030.org/  回復(fù)  更多評論
              

            # re: Chaos on Graphics ~ About 數(shù)學(xué)庫之一 SSE/SSE2 2007-02-11 13:18 pb
            寫的很好。謝謝分享。
            不知道這個(gè)系列還有沒:)  回復(fù)  更多評論
              

            # re: Chaos on Graphics ~ About 數(shù)學(xué)庫之一 SSE/SSE2 2008-04-04 20:19 Suzuki
            i think there is practical significances only in 3D program. as for general program, vector is seldom used. nonetherless it is sound for boast. i wonder if there is hardware multiplier.  回復(fù)  更多評論
              

            # re: Chaos on Graphics ~ About 數(shù)學(xué)庫之一 SSE/SSE2 2008-09-28 17:14 luguo
            那么使用AMD的是否也有類似的指令來優(yōu)化
            使用這些代碼在AMD上應(yīng)該不能運(yùn)行才對..  回復(fù)  更多評論
              

            亚洲人成精品久久久久| 久久精品毛片免费观看| 午夜精品久久久久久| 久久WWW免费人成一看片| 国产精品国色综合久久| 欧美久久久久久精选9999| 久久妇女高潮几次MBA| 精品久久久无码中文字幕| 亚洲AV日韩AV天堂久久| 久久久久国产| 亚洲人成精品久久久久| 日本高清无卡码一区二区久久| 亚洲人成精品久久久久| 亚洲欧美日韩精品久久亚洲区| 成人久久精品一区二区三区| 99久久夜色精品国产网站| 久久久久国产精品三级网| 狠狠色丁香久久婷婷综| 伊人久久大香线蕉av不卡| 久久午夜综合久久| 26uuu久久五月天| 久久线看观看精品香蕉国产| 国产精品对白刺激久久久| 人妻精品久久无码区| 综合久久精品色| 免费精品国产日韩热久久| 久久久久久久亚洲精品| 办公室久久精品| 久久综合久久综合久久| 久久精品国产只有精品2020| 久久精品中文无码资源站| 99久久99久久精品国产片果冻| 久久精品免费观看| 国产精品久久久久久久久鸭| 久久人人妻人人爽人人爽| 99久久99久久久精品齐齐| 国产精品一区二区久久 | 久久国产精品视频| 久久这里有精品视频| 亚洲欧美日韩久久精品| 亚洲国产另类久久久精品小说|