iOS metal API(一) - 在GPU上執(zhí)行計(jì)算

Metal: 使用圖形處理器渲染高級(jí)三維圖形并執(zhí)行數(shù)據(jù)并行計(jì)算

Download

編寫(xiě)一個(gè)GPU函數(shù)來(lái)執(zhí)行運(yùn)算砂心。

為了說(shuō)明GPU編程,此應(yīng)用程序?qū)蓚€(gè)數(shù)組的相應(yīng)元素添加到一起辑甜。把結(jié)果記錄在第三個(gè)數(shù)組里面,在列表1一種顯示一個(gè)用C語(yǔ)言寫(xiě)的靶端, 運(yùn)行在在CPU上的函數(shù)送爸。它通過(guò)索引循環(huán),每次循環(huán)迭代計(jì)算一個(gè)值。
列表1 數(shù)組元素相加抚官,C語(yǔ)言編寫(xiě)

void add_arrays(const float* inA,
                const float* inB,
                float* result,
                int length)
{
    for (int index = 0; index < length ; index++)
    {
        result[index] = inA[index] + inB[index];
    }
}

每個(gè)值都是獨(dú)立計(jì)算的,所以每個(gè)值會(huì)同時(shí)進(jìn)行安全的計(jì)算阶捆。
為了讓運(yùn)算執(zhí)行在GPU上凌节,需要通過(guò)MSL(Metal Shading Language),MSL是為GPU編程而設(shè)計(jì)的C++變體洒试,在Metal API 中在GPU上運(yùn)行的代碼被稱(chēng)為著色器倍奢,因?yàn)樵跉v史上他們是第一次被用來(lái)運(yùn)算3D繪圖上顏色。在表2中顯示MSL上的一個(gè)執(zhí)行和表1一樣的運(yùn)算的著色器垒棋。

列表2卒煞,數(shù)組的相加荔燎,SML編寫(xiě)

kernel void add_arrays(device const float* inA,
                       device const float* inB,
                       device float* result,
                       uint index [[thread_position_in_grid]])
{
    // the for-loop is replaced with a collection of threads, each of which
    // calls this function.
    result[index] = inA[index] + inB[index];
}

列表1和列表2 是相似的再层,但是在SML版本中有一些重要的不同點(diǎn)。我們來(lái)看一下列表2.
首先酿联,函數(shù)添加kernel關(guān)鍵字乖订,該關(guān)鍵字聲明函數(shù)為:
1.一個(gè)公共GPU函數(shù)扮饶,公共函數(shù)是在APP上能看見(jiàn)的唯一的函數(shù), 公共函數(shù)也不能被其他的著色器函數(shù)調(diào)用乍构。
2.一個(gè)運(yùn)算函數(shù)(也被稱(chēng)之為運(yùn)算內(nèi)核)甜无,是通過(guò)線程網(wǎng)絡(luò)進(jìn)行平行運(yùn)算的函數(shù)。
閱讀使用渲染管道渲染基本體去學(xué)習(xí)其他更多的公共繪圖函數(shù)的函數(shù)關(guān)鍵字哥遮。
add_arrays函數(shù)通過(guò)設(shè)備關(guān)鍵字聲明了它的三個(gè)屬性毫蚓,這說(shuō)明這些屬性的指針在設(shè)備的地址空間里。MSL為內(nèi)存定義了幾個(gè)不連續(xù)的地址空間昔善。無(wú)論何時(shí), 你在MSL上定義一個(gè)指針畔乙,你必須提供一個(gè)關(guān)鍵字來(lái)定義它的地址空間君仆。使用設(shè)備地址空間聲明GPU可以讀寫(xiě)的持久內(nèi)存。

列表2 移除了列表1中的for循環(huán)牲距,因?yàn)檫@個(gè)函數(shù)現(xiàn)在在運(yùn)算網(wǎng)絡(luò)中的多個(gè)線程調(diào)用返咱,此示例創(chuàng)建與數(shù)組維度完全匹配的線程的一維網(wǎng)格,因此數(shù)組中的每個(gè)條目都由不同的線程計(jì)算牍鞠。為了替換for循環(huán)提供的上一個(gè)索引咖摹,通過(guò)使用C++屬性語(yǔ)法置頂?shù)牧硪粋€(gè)MSL關(guān)鍵字(thread_position_in_grid)給函數(shù)設(shè)置一個(gè)新的索引屬性,此關(guān)鍵字聲明Metal 應(yīng)該為每一個(gè)線程計(jì)算一個(gè)唯一的索引难述,并在該參數(shù)中傳遞該索引萤晴。因?yàn)閍dd_arrays用的是一維網(wǎng)格吐句,這個(gè)索引會(huì)被定義為標(biāo)量整數(shù),即便循環(huán)被移除店读,清單1和清單2使用同一行代碼將這兩個(gè)數(shù)字相加嗦枢。如果你想從C和C++中轉(zhuǎn)換一樣的代碼到SML,用同樣的方法把循環(huán)邏輯替換為網(wǎng)格屯断。

在應(yīng)用程序中文虏,MTLDevice對(duì)象是GPU的一個(gè)抽象,用它可以和GPU交互,Metal 為每一個(gè)GPU創(chuàng)建一個(gè)MTLDevice殖演,你可以調(diào)用 MTLCreateSystemDefaultDevice()獲得默認(rèn)device 對(duì)象氧秘。在macOS 中,Mac 可以有多個(gè)GPU趴久,Metal 選擇其中一個(gè)作為默認(rèn)GPU并返回他的device 對(duì)象丸相。在macOS中,Metal還提供了其他的APIs用來(lái)檢索所有的device 對(duì)象朋鞍,但下面的例子只用于默認(rèn)的對(duì)象已添。

id<MTLDevice> device = MTLCreateSystemDefaultDevice();

初始化Metal對(duì)象

初始化做的第一件事兒是加載函數(shù)并準(zhǔn)備它在GPU 上運(yùn)行,當(dāng)你創(chuàng)建應(yīng)用滥酥,Xcode 編譯add_arrays函數(shù)并且把它添加到嵌入到應(yīng)用里面的metal庫(kù)中更舞。你可以用MTLLibrary 和 MTLFunction 對(duì)象來(lái)獲取metal庫(kù)和函數(shù)中的相關(guān)信息。為了獲取一個(gè)表示add_arrays的函數(shù)坎吻,通過(guò) MTLDevice為默認(rèn)庫(kù)創(chuàng)建一個(gè)MTLLibrary對(duì)象并向庫(kù)請(qǐng)求一個(gè)MTLFunction對(duì)象來(lái)表示著色器函數(shù)缆蝉。

- (instancetype) initWithDevice: (id<MTLDevice>) device
{
    self = [super init];
    if (self)
    {
        _mDevice = device;

        NSError* error = nil;

        // Load the shader files with a .metal file extension in the project

        id<MTLLibrary> defaultLibrary = [_mDevice newDefaultLibrary];
        if (defaultLibrary == nil)
        {
            NSLog(@"Failed to find the default library.");
            return nil;
        }

        id<MTLFunction> addFunction = [defaultLibrary newFunctionWithName:@"add_arrays"];
        if (addFunction == nil)
        {
            NSLog(@"Failed to find the adder function.");
            return nil;
        }

準(zhǔn)備一個(gè)metal管道

函數(shù)對(duì)象是MSL函數(shù)的一個(gè)代理,但是它不是一個(gè)可執(zhí)行的代碼瘦真, 通過(guò)創(chuàng)建一個(gè)管道把函數(shù)轉(zhuǎn)換成可執(zhí)行代碼刊头。管道指定了GPU完成特定任務(wù)所執(zhí)行的步驟。在Metal 中诸尽,管道由管道狀態(tài)對(duì)象表示原杂。因?yàn)檫@個(gè)例子用了一個(gè)計(jì)算函數(shù),應(yīng)用創(chuàng)建一個(gè)MTLComputePipelineState對(duì)象您机。

_mAddFunctionPSO = [_mDevice newComputePipelineStateWithFunction: addFunction error:&error];

一個(gè)計(jì)算管道運(yùn)行一個(gè)簡(jiǎn)單的計(jì)算函數(shù)穿肄,在運(yùn)行函數(shù)之前可選地操作輸入數(shù)據(jù),以及在運(yùn)行函數(shù)之后操作輸出數(shù)據(jù)际看。
當(dāng)創(chuàng)建一個(gè)管道狀態(tài)對(duì)象咸产,device對(duì)象完成這個(gè)特定GPU的函數(shù)編譯。此示例同步創(chuàng)建管道狀態(tài)對(duì)象仲闽,并將其直接返回給應(yīng)用程序脑溢。因?yàn)榫幾g確實(shí)需要一段時(shí)間,所以避免在對(duì)性能敏感的代碼中同步創(chuàng)建管道狀態(tài)對(duì)象赖欣。

提示

到目前為止您看到的代碼中Metal返回的所有對(duì)象都是作為符合協(xié)議的對(duì)象返回的屑彻。Metal使用協(xié)議來(lái)抽象底層實(shí)現(xiàn)類(lèi)來(lái)定義大多數(shù)特定于gpu的對(duì)象验庙,這些實(shí)現(xiàn)類(lèi)對(duì)于不同的gpu可能會(huì)有所不同。Metal使用類(lèi)定義了與gpu無(wú)關(guān)的對(duì)象酱酬,任何給定的Metal協(xié)議的參考文檔都明確說(shuō)明了您是否可以在應(yīng)用程序中實(shí)現(xiàn)該協(xié)議壶谒。

創(chuàng)建命令隊(duì)列

為了把工作發(fā)送到GPU,你需要?jiǎng)?chuàng)建一個(gè)命令隊(duì)列膳沽。metal使用命令隊(duì)列來(lái)調(diào)度命令汗菜,通過(guò)向MTLDevice請(qǐng)求一個(gè)命令隊(duì)列來(lái)創(chuàng)建一個(gè)命令隊(duì)列。

_mCommandQueue = [_mDevice newCommandQueue];

創(chuàng)建數(shù)據(jù)緩沖區(qū)并加載數(shù)據(jù)

初始化基本metal 對(duì)象之后挑社,為GPU 加載執(zhí)行的數(shù)據(jù)陨界,這個(gè)任務(wù)對(duì)性能的影響較小,但在應(yīng)用啟動(dòng)的早期這樣做仍然很有用痛阻。

GPU有它自己的專(zhuān)用內(nèi)存菌瘪,或者它可以與操作系統(tǒng)共享內(nèi)存。Metal和操作系統(tǒng)內(nèi)核需要執(zhí)行額外的工作阱当,以便讓您在內(nèi)存中存儲(chǔ)數(shù)據(jù)俏扩,并使這些數(shù)據(jù)可供GPU使用。Metal使用資源對(duì)象抽象了這個(gè)內(nèi)存管理弊添。MTLResource是GPU在運(yùn)行命令時(shí)可以訪問(wèn)的內(nèi)存分配的資源录淡。通過(guò) MTLDevice來(lái)為GPU創(chuàng)建資源。
示例應(yīng)用創(chuàng)建三個(gè)緩沖區(qū)并用隨機(jī)數(shù)據(jù)填充前兩個(gè)油坝,第三個(gè)緩沖區(qū)是add_arrays存儲(chǔ)結(jié)果的地方嫉戚。

_mBufferA = [_mDevice newBufferWithLength:bufferSize options:MTLResourceStorageModeShared];
_mBufferB = [_mDevice newBufferWithLength:bufferSize options:MTLResourceStorageModeShared];
_mBufferResult = [_mDevice newBufferWithLength:bufferSize options:MTLResourceStorageModeShared];

[self generateRandomFloatData:_mBufferA];
[self generateRandomFloatData:_mBufferB];

這個(gè)示例中的資源是MTLBuffer對(duì)象,它是沒(méi)有預(yù)定義格式的內(nèi)存分配澈圈。Metal將每個(gè)緩沖區(qū)作為一個(gè)不透明的字節(jié)集合來(lái)管理彬檀。但是,在著色器中使用緩沖區(qū)時(shí)你來(lái)指定他的格式瞬女。這意味著你的著色器和你的應(yīng)用程序需要就任何來(lái)回傳遞的數(shù)據(jù)的格式達(dá)成一致窍帝。
當(dāng)你分配一個(gè)緩沖區(qū),你提供一個(gè)存儲(chǔ)模式來(lái)決定它的一些性能特征以及CPU或GPU是否可以訪問(wèn)它诽偷。示例應(yīng)用程序使用共享內(nèi)存storageModeShared坤学,CPU和GPU都可以訪問(wèn)該內(nèi)存。
為了用隨機(jī)數(shù)據(jù)填充緩沖區(qū)渤刃,應(yīng)用程序從緩沖區(qū)內(nèi)存獲取一個(gè)指針并在CPU上給它寫(xiě)入數(shù)據(jù)。在列表2中的add_array函數(shù)聲明了它的浮點(diǎn)數(shù)類(lèi)型的數(shù)組參數(shù)贴膘。所以你需要提供一樣格式的緩沖區(qū)卖子。

- (void) generateRandomFloatData: (id<MTLBuffer>) buffer
{
    float* dataPtr = buffer.contents;

    for (unsigned long index = 0; index < arrayLength; index++)
    {
        dataPtr[index] = (float)rand()/(float)(RAND_MAX);
    }
}

創(chuàng)建一個(gè)命令緩沖區(qū)

請(qǐng)求命令隊(duì)列創(chuàng)建一個(gè)命令緩沖區(qū)。

id<MTLCommandBuffer> commandBuffer = [_mCommandQueue commandBuffer];

創(chuàng)建一個(gè)命令編碼器

為了給命令緩沖區(qū)寫(xiě)入命令刑峡,你可以對(duì)要編碼的特定類(lèi)型的命令使用命令編碼器洋闽。這個(gè)示例創(chuàng)建一個(gè)計(jì)算命令編碼器玄柠,用來(lái)編碼計(jì)算過(guò)程。每個(gè)計(jì)算命令都會(huì)導(dǎo)致GPU創(chuàng)建線程網(wǎng)格以在GPU上執(zhí)行诫舅。

id<MTLComputeCommandEncoder> computeEncoder = [commandBuffer computeCommandEncoder];

為了編碼一個(gè)命令羽利,對(duì)編碼器進(jìn)行一系列方法調(diào)用。有些方法設(shè)置狀態(tài)信息刊懈,如管道狀態(tài)對(duì)象或傳遞給管道的參數(shù)这弧。改變這些狀態(tài)后, 你可以對(duì)命令進(jìn)行編碼以執(zhí)行管道虚汛。編碼器將所有狀態(tài)更改和命令參數(shù)寫(xiě)入命令緩沖區(qū)匾浪。

設(shè)置管道狀態(tài)和參數(shù)數(shù)據(jù)

設(shè)置要執(zhí)行命令的管道的管道狀態(tài)對(duì)象,然后為管道需要發(fā)送到add_arrays函數(shù)的任何參數(shù)設(shè)置數(shù)據(jù)卷哩。對(duì)于這個(gè)管道蛋辈,這意味著提供對(duì)三個(gè)緩沖區(qū)的引用。Metal自動(dòng)按照清單2中函數(shù)聲明中參數(shù)出現(xiàn)的順序?yàn)榫彌_區(qū)參數(shù)分配索引将谊,從0開(kāi)始冷溶。使用相同的索引提供參數(shù)。

[computeEncoder setComputePipelineState:_mAddFunctionPSO];
[computeEncoder setBuffer:_mBufferA offset:0 atIndex:0];
[computeEncoder setBuffer:_mBufferB offset:0 atIndex:1];
[computeEncoder setBuffer:_mBufferResult offset:0 atIndex:2];

還可以為每個(gè)參數(shù)指定偏移量尊浓。偏移量為0表示命令將從緩沖區(qū)開(kāi)始訪問(wèn)數(shù)據(jù)逞频。不管怎么樣,你可以使用一個(gè)緩沖區(qū)存儲(chǔ)多個(gè)參數(shù)眠砾,為每個(gè)參數(shù)指定偏移量虏劲。

你沒(méi)有為索引參數(shù)提供任何參數(shù),因?yàn)閍dd_arrays函數(shù)定義它的值由GPU提供褒颈。

指定線程數(shù)和組織

接下來(lái)柒巫,決定要?jiǎng)?chuàng)建多少線程以及如何組織這些線程。Metal 可以創(chuàng)建1維谷丸,2維或3維網(wǎng)格堡掏。add_arrays函數(shù)使用一維數(shù)組,因此該示例創(chuàng)建了一個(gè)大小為(dataSize x 1 x 1)的一維網(wǎng)格刨疼,Metal從中生成0到dataSize-1之間的索引泉唁。

MTLSize gridSize = MTLSizeMake(arrayLength, 1, 1);

指定線程組大小

Metal 將網(wǎng)格細(xì)分為更小的網(wǎng)格,成為線程組揩慕。每個(gè)線程組單獨(dú)運(yùn)算亭畜。Metal 可以將線程組分配給GPU上的不同的處理元素以加快處理速度。您還需要確定為命令創(chuàng)建線程組的大小迎卤。

NSUInteger threadGroupSize = _mAddFunctionPSO.maxTotalThreadsPerThreadgroup;
if (threadGroupSize > arrayLength)
{
    threadGroupSize = arrayLength;
}
MTLSize threadgroupSize = MTLSizeMake(threadGroupSize, 1, 1);

應(yīng)用程序向管道狀態(tài)對(duì)象請(qǐng)求最大可能的線程組拴鸵,如果該大小大于數(shù)據(jù)集的大小,則會(huì)收縮該線程組。 maxTotalThreadsPerThreadgrou屬性給一個(gè)線程組允許的最大的值劲藐,它根據(jù)用于創(chuàng)建管道狀態(tài)對(duì)象的函數(shù)的復(fù)雜性的不同而不同八堡。

編碼運(yùn)算命令以執(zhí)行線程

最后,對(duì)命令進(jìn)行編碼以調(diào)度線程網(wǎng)格聘芜。

[computeEncoder dispatchThreads:gridSize
          threadsPerThreadgroup:threadgroupSize];

當(dāng)GPU執(zhí)行此命令時(shí)兄渺,它使用你先前設(shè)置的狀態(tài)和命令的參數(shù)來(lái)調(diào)度線程來(lái)執(zhí)行計(jì)算。你可以按照相同的步驟使用編碼器將多個(gè)計(jì)算命令編碼到計(jì)算過(guò)程中汰现,而無(wú)需執(zhí)行任何冗余步驟挂谍。例如,可以設(shè)置管道狀態(tài)對(duì)象一次服鹅,然后為要處理的每個(gè)緩沖區(qū)集合設(shè)置參數(shù)并編碼一個(gè)命令凳兵。

結(jié)束計(jì)算過(guò)程

當(dāng)沒(méi)有更多的命令添加到計(jì)算過(guò)程時(shí),結(jié)束編碼過(guò)程以結(jié)束計(jì)算過(guò)程企软。

[computeEncoder endEncoding];

提交命令緩沖區(qū)以執(zhí)行其命令

通過(guò)將命令緩沖區(qū)提交到隊(duì)列來(lái)執(zhí)行命令緩沖區(qū)中的命令庐扫。

[commandBuffer commit];

命令隊(duì)列創(chuàng)建了命令緩沖區(qū),因此提交緩沖區(qū)時(shí)總是將其放在該隊(duì)列上仗哨。提交命令緩沖區(qū)后形庭,Metal 異步的準(zhǔn)備要執(zhí)行的命令,然后調(diào)度命令緩沖區(qū)在GPU上執(zhí)行厌漂。在GPU執(zhí)行完命令緩沖區(qū)的所有命令后萨醒,Metal將命令緩沖區(qū)標(biāo)記為已完成。

等待運(yùn)算完成

當(dāng)GPU處理命令時(shí)苇倡,應(yīng)用可以做其他的工作富纸。這個(gè)樣本不需要做任何額外的工作,所以它只需要等待 命令緩沖區(qū)完成旨椒。

[commandBuffer waitUntilCompleted];

或者晓褪,為了在Metal 處理完所有命令時(shí)得到通知,向命令緩沖區(qū)添加一個(gè)完成處理程序(addCompletedHandler(_:))综慎,或者通過(guò)讀取命令緩沖區(qū)的狀態(tài)status屬性來(lái)檢查它的狀態(tài)涣仿。

從緩沖區(qū)讀取結(jié)果

命令緩沖區(qū)完成后,GPU的運(yùn)算結(jié)果被保存在輸出緩沖區(qū)中示惊,并且Metal 執(zhí)行任何必要的步驟以確保CPU可以看到他們好港。在真實(shí)的應(yīng)用中,可以從緩沖區(qū)中讀取結(jié)果并對(duì)其進(jìn)行處理米罚,例如在屏幕上顯示結(jié)果或?qū)⒔Y(jié)果寫(xiě)入文件钧汹。因?yàn)橛?jì)算只是用來(lái)說(shuō)明創(chuàng)建一個(gè)Metal 應(yīng)用程序的過(guò)程,該示例讀取存儲(chǔ)在輸出緩沖區(qū)中的值并進(jìn)行測(cè)試录择,以確保CPU和GPU計(jì)算的結(jié)果相同

- (void) verifyResults
{
    float* a = _mBufferA.contents;
    float* b = _mBufferB.contents;
    float* result = _mBufferResult.contents;

    for (unsigned long index = 0; index < arrayLength; index++)
    {
        if (result[index] != (a[index] + b[index]))
        {
            printf("Compute ERROR: index=%lu result=%g vs %g=a+b\n",
                   index, result[index], a[index] + b[index]);
            assert(result[index] == (a[index] + b[index]));
        }
    }
    printf("Compute results as expected\n");
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拔莱,一起剝皮案震驚了整個(gè)濱河市类嗤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辨宠,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件货裹,死亡現(xiàn)場(chǎng)離奇詭異嗤形,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)弧圆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)赋兵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人搔预,你說(shuō)我怎么就攤上這事霹期。” “怎么了拯田?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵历造,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我船庇,道長(zhǎng)吭产,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任鸭轮,我火速辦了婚禮臣淤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘窃爷。我一直安慰自己邑蒋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布按厘。 她就那樣靜靜地躺著医吊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刻剥。 梳的紋絲不亂的頭發(fā)上遮咖,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天造虏,我揣著相機(jī)與錄音御吞,去河邊找鬼。 笑死漓藕,一個(gè)胖子當(dāng)著我的面吹牛陶珠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播享钞,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼揍诽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诀蓉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起暑脆,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤渠啤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后添吗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體沥曹,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年碟联,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妓美。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鲤孵,死狀恐怖壶栋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情普监,我是刑警寧澤贵试,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站凯正,受9級(jí)特大地震影響锡移,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜漆际,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一淆珊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奸汇,春花似錦施符、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至贯涎,卻和暖如春听哭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背塘雳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工陆盘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人败明。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓隘马,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親妻顶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酸员,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355