Metal Shading Language 語(yǔ)法規(guī)范

1. Metal Shading Language簡(jiǎn)介

Metal著色語(yǔ)言是用來(lái)編寫(xiě)3D圖形渲染邏輯并行計(jì)算核心邏輯的一門(mén)編程語(yǔ)言徙瓶,底層使用ClangLLVM進(jìn)行編譯處理慕淡。
Metal語(yǔ)言基于C++ 11.0 語(yǔ)言設(shè)計(jì)坝辫,我們主要用來(lái)編寫(xiě)在GPU上執(zhí)行的圖像渲染邏輯代碼以及通用并行計(jì)算邏輯代碼。當(dāng)我們使用Metal框架來(lái)完成APP的實(shí)現(xiàn)時(shí)則需要使用Metal編程語(yǔ)言掘譬。

2. Metal 使用常識(shí)

2.1 Metal 與 C++ 11.0

Metal Restrictions 限制 (如下的C++11.0的特性在Metal 著色語(yǔ)言中是不支持的):

  • Lambda表達(dá)式
  • 遞歸函數(shù)調(diào)?
  • 動(dòng)態(tài)轉(zhuǎn)換操作符
  • 類(lèi)型識(shí)別
  • 對(duì)象創(chuàng)建(new)和釋放(delloc)操作符
  • 操作符 noexcept
  • goto跳轉(zhuǎn)
  • 虛函數(shù)修飾符-
  • 派?類(lèi)
  • 異常處理

C++的標(biāo)準(zhǔn)庫(kù)不可以在Metal 著色語(yǔ)言中使用牵囤。

2.2 Metal 語(yǔ)言中對(duì)于指針使用的限制

  • Metal圖形和并行計(jì)算函數(shù)用到的入?yún)ⅲ绻侵羔槺仨毷褂玫刂房臻g修飾符 (device ,threadgroup ,constant)
  • 不支持函數(shù)指針
  • Metal函數(shù)名不能命名為Main函數(shù)

2.3 Metal 與OpenGL ES

  • 在OpenGL ES中饶深,圖片的紋理坐標(biāo)原點(diǎn)是基于左下角的,而在Metal中逛拱,蘋(píng)果為了統(tǒng)一敌厘,將圖片紋理的坐標(biāo)原點(diǎn)設(shè)在了左上角。
  • 在OpenGL ES中朽合,需要我們手動(dòng)去編譯頂點(diǎn)著色器和片元著色器俱两,而在Metal中,這一切是由蘋(píng)果系統(tǒng)為我們做的曹步。

3.0 Metal數(shù)據(jù)類(lèi)型

類(lèi)型 描述
bool 布爾類(lèi)型宪彩,取值范圍true,false讲婚;true可以拓展為整數(shù)常量1尿孔,false可以拓展為整數(shù)常量0
char 有符號(hào)8-bit整數(shù)
unsigned char (uchar) 無(wú)符號(hào)8-bit整數(shù)
short 有符號(hào)16-bit整數(shù)
unsigned short (ushort) 無(wú)符號(hào)16-bit整數(shù)
int 有符號(hào)32-bit整數(shù)
unsigned int (uint) 無(wú)符號(hào)32-bit整數(shù)
half 一個(gè)16-bit浮點(diǎn)數(shù)
float 一個(gè)32-bit浮點(diǎn)數(shù)
size-t 64-bit無(wú)符號(hào)整數(shù),表示sizeof操作符的結(jié)果
ptrdiff_t 64-bit有符號(hào)整數(shù)筹麸,表示2個(gè)指針的差
void 表示一個(gè)空的值集合

3.1 Metal 向量和矩陣數(shù)據(jù)類(lèi)型

3.1.1 向量

  • booln
  • charn
  • shortn
  • intn
  • ucharn
  • ushortn
  • uintn
  • halfn
  • floatn
    向量中的n,指的是維度活合,當(dāng)n=1時(shí)直接省略,n最大是4物赶。
bool2 A= {1,2};
float4 pos = float4(1.0,2.0,3.0,4.0);
float x = pos[0];
float y = pos[1];

float4 VB;
for(int i = 0; i < 4 ; i++)
    VB[i] = pos[i] * 2.0f;


//通過(guò)向量字母來(lái)獲取元素
int4 test = int4(0,1,2,3);
int a = test.x;
int b = test.y;
int c = test.z;
int d = test.w;

int e = test.r;
int f = test.g;
int g = test.b;
int h = test.a;

float4 c;
c.xyzw = float4(1.0f,2.0f,3.0f,4.0f);
c.z = 1.0f;
c.xy = float2(3.0f,4.0f);
c.xyz = float3(3.0f,4.0f,5.0f);


float4 pos = float4(1.0f,2.0f,3.0f,4.0f);
float4 swiz = pos.wxyz;  //swiz = (4.0,1.0,2.0,3.0);
float4 dup = pos.xxyy;  //dup = (1.0f,1.0f,2.0f,2.0f);

//pos = (5.0f,2.0,3.0,6.0)
pos.xw = float2(5.0f,6.0f);

//pos = (8.0f,2.0f,3.0f,7.0f)
pos.wx = float2(7.0f,8.0f);

//pos = (3.0f,5.0f,9.0f,7.0f);
pos.xyz = float3(3.0f,5.0f,9.0f);



float2 pos;
pos.x = 1.0f; //合法
pos.z = 1.0f; //非法

float3 pos2;
pos2.z = 1.0f; //合法
pos2.w = 1.0f; //非法

//非法,x出現(xiàn)2次
pos.xx = float2(3.0,4.0f);
//不合法-使用混合限定符
pos.xy = float4(1.0f,2.0,3.0,4.0);

float4 pos4 = float4(1.0f,2.0f,3.0f,4.0f);
pos4.x = 1.0f;
pos4.y = 2.0f;
//非法,.rgba與.xyzw 混合使用
pos4.xg = float2(2.0f,3.0f);
////非法,.rgba與.xyzw 混合使用
float3 coord = pos4.ryz;

float4 pos5 = float4(1.0f,2.0f,3.0f,4.0f);
//非法,使用指針來(lái)指向向量/分量
my_func(&pos5.xy);

3.1.2 矩陣

矩陣支持如下類(lèi)型:

  • halfnxm
  • floatnxm
    nxm分別指的是矩陣的?數(shù)和列數(shù)白指,最大支持4行4列
    例:
float4x4 m;
//將第二排的值設(shè)置為0
m[1] = float4(2.0f);

//設(shè)置第一行/第一列為1.0f
m[0][0] = 1.0f;

//設(shè)置第三行第四列的元素為3.0f
m[2][3] = 3.0f;

float4類(lèi)型向量的所有可能構(gòu)造方式

float4(float x);
float4(float x,float y,float z,float w);
float4(float2 a,float2 b);
float4(float2 a,float b,float c);
float4(float a,float2 b,float c);
float4(float a,float b,float2 c);
float4(float3 a,float b);
float4(float a,float3 b);
float4(float4 x);

float3類(lèi)型向量的所有可能的構(gòu)造的方式

float3(float x);
float3(float x,float y,float z);
float3(float a,float2 b);
float3(float2 a,float b);
float3(float3 x);

float2類(lèi)型向量的所有可能的構(gòu)造方式

float2(float x);
float2(float x,float y);
float2(float2 x);

4.紋理Texture類(lèi)型

紋理類(lèi)型是一個(gè)句柄,它指向一個(gè)一維/二維/三維紋理數(shù)據(jù)酵紫。
枚舉值:定義了訪(fǎng)問(wèn)權(quán)限:

enum class access (sample,read,write);

sample:紋理對(duì)象可以被采樣告嘲;
read:不使用采樣器,一個(gè)圖形渲染函數(shù)或者一個(gè)并行計(jì)算函數(shù)可以讀取紋理對(duì)象奖地;
write:一個(gè)圖形渲染函數(shù)或者一個(gè)并行計(jì)算函數(shù)可以向紋理對(duì)象寫(xiě)入數(shù)據(jù)橄唬。

texture1d<T,access a = access::sample>
texture2d<T,access a = access::sample>
texture3d<T,access a = access::sample>

T:數(shù)據(jù)類(lèi)型,設(shè)定了從紋理中讀取或者是向紋理中寫(xiě)入時(shí)的顏色類(lèi)型参歹,T可以是half仰楚,float,short泽示,int等缸血。
舉例

void foo (texture2d<float> imgA [[ texture(0) ]] ,texture2d<float, access::read> imgB [[ texture(1) ]], texture2d<float, access::write> imgC [[ texture(2) ]])
{ 
    ...
}

5. 采樣器 Samples

采樣器類(lèi)型決定了如何對(duì)一個(gè)紋理進(jìn)行采樣操作蜜氨。在Metal框架中有一個(gè)采樣器對(duì)象MTLSamplerState械筛,這個(gè)對(duì)象作為圖形渲染著色器函數(shù)參數(shù)或者是并行計(jì)算函數(shù)的傳遞參數(shù),也就是在sample中我們?cè)O(shè)置紋理的采樣方式(環(huán)繞方式捎泻、過(guò)濾方式)。在Metal程序中初始化的采樣器必須使用constexpr修飾符修飾埋哟。

  • Metal支持的采樣器狀態(tài)和默認(rèn)值
枚舉名稱(chēng)(Enum Name) 有效值(Valid Value) 描述
coord normalized 從紋理中采樣時(shí)笆豁,紋理坐標(biāo)是否需要?dú)w一化
address clamp_to_edge,clamp_to_zero,mirrored_repeat,repeat 設(shè)置所有紋理坐標(biāo)的尋址模式
s_address,t_address,r_address clamp_to_edge,clamp_to_zero,mirrored_repeat,repeat 設(shè)置某個(gè)紋理坐標(biāo)的尋址模式
filter nearest,linear 設(shè)置紋理采樣的放大和縮小的過(guò)濾模式
mag_filter nearest,linear 設(shè)置紋理采樣的放大過(guò)濾模式
min_filter nearest,linear 設(shè)置紋理采樣的縮小過(guò)濾模式
mip_filter none,nearest,linear 設(shè)置紋理采樣的minmap過(guò)濾模式,如果是none,那么只有一個(gè)層紋理能生效
compare_func none,less,less_qual,greater,greater_equal,equal,not_equal 為使用r紋理坐標(biāo)做shaw map,設(shè)置比較測(cè)試邏輯赤赊,這個(gè)狀態(tài)值的設(shè)置只可以在Metal著色語(yǔ)言程序中完成

從紋理中采樣時(shí)闯狱,紋理坐標(biāo)是否需要?dú)w一化

enum class coord {noramlized,pixel };

紋理采樣過(guò)濾方式,放大/縮小過(guò)濾模式

enum class filter {nearest, linear};

設(shè)置紋理采樣縮小過(guò)濾模式

enum class min_filter {nearest, linear};

設(shè)置紋理采樣放大過(guò)濾模式

enum class mag_filter {nearest, linear};

設(shè)置s,t,r紋理坐標(biāo)的尋址模式

enum class s_address{clamp_to_zero,clamp_to_edge,repeat,mirrored_repeat };
enum class t_address{clamp_to_zero,clamp_to_edge,repeat,mirrored_repeat };
enum class r_address{clamp_to_zero,clamp_to_edge,repeat,mirrored_repeat };

設(shè)置所有紋理坐標(biāo)的尋址模式

enum class address{clamp_to_zero,clamp_to_edge,repeat,mirrored_repeat };

例:

constexpr samper s(coord::pixel,address::clamp_to_zero,filter::linear);

6. 函數(shù)修飾符

  • kernel 抛计,表示該函數(shù)是一個(gè)數(shù)據(jù)并行計(jì)算著色函數(shù),它可以被分配在一維/二維/三維線(xiàn)程組中去執(zhí)行哄孤。注意:使用kernel修飾的函數(shù),氣返回值類(lèi)型必須是void類(lèi)型吹截。
kernel void test(...)
{
    ......
}
  • vertex瘦陈,表示該函數(shù)是一個(gè)頂點(diǎn)著色函數(shù),它將為頂點(diǎn)數(shù)據(jù)流中的每個(gè)頂點(diǎn)數(shù)據(jù)執(zhí)行一次然后為每個(gè)頂點(diǎn)生成數(shù)據(jù)輸出到繪制管線(xiàn)波俄。
  • fragment晨逝,表示該函數(shù)是一個(gè)片元著色函數(shù),它將為片元數(shù)據(jù)流中的每個(gè)片元和其關(guān)聯(lián)執(zhí)行一次然后將每個(gè)片元生成的顏色數(shù)據(jù)輸出到繪制管線(xiàn)中懦铺。
    只有圖形函數(shù)才可以被vertex和fragment修飾捉貌。對(duì)于圖形修飾函數(shù),可以根據(jù)返回值判斷其是為頂點(diǎn)做計(jì)算還是為像素做計(jì)算冬念。注意:一個(gè)被函數(shù)修飾符修飾的函數(shù)不能調(diào)用另一個(gè)函數(shù)修飾符修飾的函數(shù)趁窃。
kernel void test1(...)
{
}
vertex float4 test2(...)
{
  test1(...); // 這個(gè)是錯(cuò)誤的調(diào)用
}

7. 地址空間修飾符

地址空間修飾符,是Metal著色語(yǔ)言用來(lái)給參數(shù)變量或者函數(shù)變量分配內(nèi)存使用的急前。所有的著色函數(shù)(kernel,vertex,fragment)的參數(shù)棚菊,如果是指針或者是引用,都必須帶有地址空間修飾符叔汁。

  • device(設(shè)備地址空間)
    在設(shè)備地址空間(device)指向設(shè)備內(nèi)存池(這里指的是顯存)分配出來(lái)的緩存對(duì)象统求,既是可讀也是可寫(xiě)的。
device float4 *color;
struct test{
 float a[2];
int b[1];
}
device test *info;

注意:紋理對(duì)象總是在設(shè)備地址空間分配內(nèi)存据块,device地址空間修飾符不必出現(xiàn)在紋理類(lèi)型定義中码邻,一個(gè)紋理對(duì)象的內(nèi)容無(wú)法直接訪(fǎng)問(wèn),可使用Metal自身的內(nèi)建函數(shù)另假。

  • threadgroup(線(xiàn)程組地址空間)
    線(xiàn)程組地址空間用于為并行計(jì)算著色函數(shù)分配內(nèi)存變量像屋,這些變量被一個(gè)線(xiàn)程組的所有線(xiàn)程共享。在線(xiàn)程組地址空間分配的變量不能被用于圖形繪制著色函數(shù)边篮。
    在并行計(jì)算周瑟函數(shù)中己莺,在線(xiàn)程組地址空間分配的變量為一共線(xiàn)程組共同使用奏甫,生命周期和線(xiàn)程組相同。
kernel void func(threadgroup float *a[[threadgroup(0)]])
{
    ....
}
  • constant(常量地址空間)
    常量地址空間指向的緩存對(duì)象也是從設(shè)備內(nèi)存池分配存儲(chǔ)凌受,但是它是只讀的阵子。
    在程序域的變量的生命周期和程序一樣,在程序中的并行計(jì)算著色函數(shù)或者圖形繪制著色函數(shù)使用胜蛉,但是constant的值是不會(huì)變的挠进。
    注意:常量地址空間的指針或是引用可以作為函數(shù)的參數(shù)。向聲明為常量的變量賦值會(huì)編譯失敗誊册,聲明常量不賦值也會(huì)編譯失敗领突。
constant float samples[] = {1.0f,2.0f,3.0f,4.0f};
samples[4] = {3,2,3,2}; //編譯失敗,因?yàn)閏onstant是只讀的
constant float a; // 編譯失敗案怯,因?yàn)闆](méi)有賦值
  • thread(線(xiàn)程地址空間)
    thread地址空間指向每個(gè)線(xiàn)程準(zhǔn)備的地址空間君旦,這個(gè)線(xiàn)程的地址空間定義的變量在其他線(xiàn)程不可見(jiàn),在圖形繪制著色函數(shù)或者并行計(jì)算著色函數(shù)中可使用thread聲明嘲碱。
kernel void func(...)
{
  float x;
  thread float p = &x;
  ...
}

對(duì)于圖形著色函數(shù)金砍,其指針或是引用類(lèi)型的參數(shù)必須定義為device或者是constant地址空間,對(duì)于并行計(jì)算著色函數(shù)悍汛,其指針或是引用類(lèi)型的參數(shù)必須定義為device或者是constant或者是threadgroup地址空間

8. 函數(shù)參數(shù)與變量

圖形繪制或者并行計(jì)算著色器函數(shù)的輸入輸出都是通過(guò)參數(shù)傳遞捞魁,除了常量地址空間變量和程序域定義的采樣器除外。

  • device buffer - 設(shè)備緩存离咐,一個(gè)指向設(shè)備地址空間的任意數(shù)據(jù)類(lèi)型的指針或者引用谱俭;
  • constant buffer - 常量緩存區(qū),一個(gè)指向常量地址空間的任意數(shù)據(jù)類(lèi)型的指針或者引用宵蛀;
  • texture - 紋理對(duì)象昆著;
  • sampler - 采樣器對(duì)象;
  • threadgroup - 在線(xiàn)程組中供各線(xiàn)程共享的緩存术陶。
    注意:被著色器函數(shù)的緩存(device和constant)不能重名凑懂。

對(duì)于每個(gè)著色器函數(shù)來(lái)說(shuō),一個(gè)修飾符是必須指定的梧宫,他用來(lái)設(shè)定一個(gè)緩存接谨,紋理,采樣器的位置塘匣。

  • device buffer/constant buffer → [[buffer(index)]]
  • texture → [[texture(index)]]
  • sampler → [[sampler(index)]]
  • threadgroup → [[threadgroup(index)]]
    index是一個(gè)unsigned integer類(lèi)型的值脓豪,它表示了一個(gè)緩存、紋理忌卤、采樣器的位置扫夜。
kernel void add_vectros(
                const device float4 *inA [[buffer(0)]],
                const device float4 *inB [[buffer(1)]],
                device float4 *out [[buffer(2)]]
                uint id[[thread_position_in_grid]])
{
    out[id] = inA[id] + inB[id];
}
thread_position_in_grid:用于表示當(dāng)前節(jié)點(diǎn)在多線(xiàn)程網(wǎng)格中的位置;

內(nèi)建變量屬性修飾符

  • [[vertex_id]]: 頂點(diǎn)id標(biāo)識(shí)符
  • [[position]]: 頂點(diǎn)信息(float4)表示片元的窗口相對(duì)坐標(biāo)
  • [[point_size]]: 點(diǎn)的大小(float)
  • [[color(m)]]:顏色笤闯,m編譯前必須要確定
struct myFragmentOutput{
    float4 cl_f[[color(0)]];
    float4 cl_i[[color(1)]];
    float4 cl_u[[color(2)]];
};

fragment myFragmentOutput shader()
{
    myFragmentOutput f;
    f.cl_f = ...;
    ...
    return f;
}
  • [[stage_in]]:片元著色函數(shù)使用的單個(gè)片元輸入數(shù)據(jù)是由頂點(diǎn)著色函數(shù)輸出然后經(jīng)過(guò)光柵化生成的堕阔。頂點(diǎn)和片元著色函數(shù)都是只能有一個(gè)參數(shù)被聲明為使用“stage_in”修飾符,對(duì)于一個(gè)使用了stage_in修飾符的自定義的結(jié)構(gòu)體颗味,期結(jié)構(gòu)體內(nèi)部成員可以是整形超陆、浮點(diǎn)型標(biāo)量、浮點(diǎn)型向量脱衙。

屬性修飾符目的:

  1. 參數(shù)表示資源如何定位? 可以理解為端口侥猬;
  2. 在固定管線(xiàn)和可編程管線(xiàn)進(jìn)行內(nèi)建變量的傳遞例驹;
  3. 將數(shù)據(jù)沿著渲染管線(xiàn)從頂點(diǎn)函數(shù)傳遞片元函數(shù)捐韩。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鹃锈,隨后出現(xiàn)的幾起案子荤胁,更是在濱河造成了極大的恐慌,老刑警劉巖屎债,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仅政,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡盆驹,警方通過(guò)查閱死者的電腦和手機(jī)圆丹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)躯喇,“玉大人,你說(shuō)我怎么就攤上這事「忍叮” “怎么了弄兜?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)正压。 經(jīng)常有香客問(wèn)我欣福,道長(zhǎng),這世上最難降的妖魔是什么焦履? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任拓劝,我火速辦了婚禮,結(jié)果婚禮上嘉裤,老公的妹妹穿的比我還像新娘郑临。我一直安慰自己,他們只是感情好价脾,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布牧抵。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪犀变。 梳的紋絲不亂的頭發(fā)上妹孙,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音获枝,去河邊找鬼蠢正。 笑死,一個(gè)胖子當(dāng)著我的面吹牛省店,可吹牛的內(nèi)容都是我干的嚣崭。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼懦傍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼雹舀!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起粗俱,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤说榆,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后寸认,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體签财,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年偏塞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了唱蒸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡灸叼,死狀恐怖神汹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情怜姿,我是刑警寧澤慎冤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站沧卢,受9級(jí)特大地震影響蚁堤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜但狭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一披诗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧立磁,春花似錦呈队、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)粒竖。三九已至,卻和暖如春几于,著一層夾襖步出監(jiān)牢的瞬間蕊苗,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工沿彭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留朽砰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓喉刘,卻偏偏與公主長(zhǎng)得像瞧柔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子睦裳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容