Android圖形系統(tǒng)系統(tǒng)篇之HWC

HWC概述

HWC(hwcomposer)是Android中進行窗口(Layer)合成和顯示的HAL層模塊欣舵,其實現(xiàn)是特定于設(shè)備的视粮,而且通常由顯示設(shè)備制造商 (OEM)完成讨永,為SurfaceFlinger服務(wù)提供硬件支持。

SurfaceFlinger可以使用OpenGL ES合成Layer,這需要占用并消耗GPU資源铣除。大多數(shù)GPU都沒有針對圖層合成進行優(yōu)化,當(dāng)SurfaceFlinger通過GPU合成圖層時鹦付,應(yīng)用程序無法使用GPU進行自己的渲染尚粘。而HWC通過硬件設(shè)備進行圖層合成,可以減輕GPU的合成壓力敲长。

顯示設(shè)備的能力千差萬別背苦,很難直接用API表示硬件設(shè)備支持合成的Layer數(shù)量互捌,Layer是否可以進行旋轉(zhuǎn)和混合模式操作,以及對圖層定位和硬件合成的限制等行剂。因此HWC描述上述信息的流程是這樣的:

  1. SurfaceFlingerHWC提供所有Layer的完整列表秕噪,讓HWC根據(jù)其硬件能力,決定如何處理這些Layer厚宰。
  2. HWC會為每個Layer標注合成方式江耀,是通過GPU還是通過HWC合成撬讽。
  3. SurfaceFlinger負責(zé)先把所有注明GPU合成的Layer合成到一個輸出Buffer智绸,然后把這個輸出Buffer和其他Layer(注明HWC合成的Layer)一起交給HWC从绘,讓HWC完成剩余Layer的合成和顯示。

雖然每個顯示設(shè)備的能力不同撵幽,但是官方要求每個HWC硬件模塊都應(yīng)該支持以下能力:

  1. 至少支持4個疊加層:狀態(tài)欄灯荧、系統(tǒng)欄、應(yīng)用本身和壁紙或者背景盐杂。
  2. 疊加層可以大于顯示屏逗载,例如:壁紙
  3. 同時支持預(yù)乘每像素(per-pixel)Alpha混合和每平面(per-plane)Alpha混合。
  4. 為了支持受保護的內(nèi)容链烈,必須提供受保護視頻播放的硬件路徑厉斟。
  5. RGBA packing order, YUV formats, and tiling, swizzling, and stride properties

Tiling:可以把Image切割成MxN個小塊,最后渲染時强衡,再將這些小塊拼接起來擦秽,就像鋪瓷磚一樣。
Swizzling:一種拌和技術(shù)漩勤,表示向量單元可以被任意地重排或重復(fù)感挥。

但是并非所有情況下HWC都比GPU更高效,例如:當(dāng)屏幕上沒有任何變化時越败,尤其是疊加層有透明像素并且需要進行圖層透明像素混合時触幼。在這種情況下,HWC可以要求部分或者全部疊加層都進行GPU合成眉尸,然后HWC持有合成的結(jié)果Buffer域蜗,如果SurfaceFlinger要求合成相同的疊加圖層列表巨双,HWC可以直接顯示之前合成的結(jié)果Buffer噪猾,這有助于提高待機設(shè)備的電池壽命。

HWC也提供了VSync事件筑累,用于管理渲染和圖層合成時機袱蜡,后續(xù)文章會進行介紹。

HWC2實現(xiàn)

Android7.0提供了HWC和HWC2兩個版本慢宗,默認使用HWC坪蚁,但是手機廠商也可以選擇HWC2奔穿,如下所示:

// frameworks\native\services\surfaceflinger\Android.mk
USE_HWC2 := false
ifeq ($(USE_HWC2),true)
    LOCAL_CFLAGS += -DUSE_HWC2
    LOCAL_SRC_FILES += \
        SurfaceFlinger.cpp \
        DisplayHardware/HWComposer.cpp
else
    LOCAL_SRC_FILES += \
        SurfaceFlinger_hwc1.cpp \
        DisplayHardware/HWComposer_hwc1.cpp
endif

SurfaceFlinger通過HWComposer使用HWC硬件能力,HWComposer構(gòu)造函數(shù)通過loadHwcModule方法加載HWC模塊敏晤,并封裝成HWC2::Device結(jié)構(gòu)贱田,如下所示:

// Load and prepare the hardware composer module,HWComposer構(gòu)造函數(shù)中通過此方法加載HWC模塊
void HWComposer::loadHwcModule()
{
    // 定義在hardware.h中,表示一個硬件模塊嘴脾,是HAL層的靈魂
    hw_module_t const* module;
    // 加載硬件廠商提供的hwcomposer模塊男摧,HWC_HARDWARE_MODULE_ID定義在hwcomposer_defs.h中,表示"hwcomposer"
    if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {
        ALOGE("%s module not found, aborting", HWC_HARDWARE_MODULE_ID);
        abort();
    }

    hw_device_t* device = nullptr;
    // 通過硬件廠商提供的open函數(shù)打開一個"composer"硬件設(shè)備译打,HWC_HARDWARE_COMPOSER也定義在hwcomposer_defs.h中耗拓,表示"composer"
    int error = module->methods->open(module, HWC_HARDWARE_COMPOSER, &device); 
    if (error != 0) {
        ALOGE("Failed to open HWC device (%s), aborting", strerror(-error));
        abort();
    }

    uint32_t majorVersion = (device->version >> 24) & 0xF;
    // mHwcDevice是HWC2.h中定義的HWC2::Device,所有與HWC的交互都通過mHwcDevice
    if (majorVersion == 2) { // HWC2奏司,hwc2_device_t是hwcomposer2.h中的結(jié)構(gòu)體
        mHwcDevice = std::make_unique<HWC2::Device>(
                reinterpret_cast<hwc2_device_t*>(device));
    } else { // 設(shè)備是基于HWC1乔询,這里用HWC2去適配,Android7.0及以前默認都是HWC1韵洋,hwc_composer_device_1_t是hwcomposer.h中的結(jié)構(gòu)體
        mAdapter = std::make_unique<HWC2On1Adapter>(
                reinterpret_cast<hwc_composer_device_1_t*>(device));
        mHwcDevice = std::make_unique<HWC2::Device>(
                static_cast<hwc2_device_t*>(mAdapter.get()));
    }
    // 獲取硬件支持的最大虛擬屏幕數(shù)量竿刁,VirtualDisplay可用于錄屏
    mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
}

先加載hwcomposer模塊得到hw_module_t,再打開composer設(shè)備得到hw_device_t麻献。hw_module_thw_device_t定義在hardware\libhardware\include\hardware\hardware.h们妥,表示一個HAL層模塊和屬于該模塊的一個實現(xiàn)設(shè)備。注意這里是先有HAL模塊勉吻,再有實現(xiàn)此模塊的硬件設(shè)備监婶。

上述通過hw_get_module方法(hardware\libhardware\hardware.c)加載hwcomposer模塊,此模塊由硬件廠商提供實現(xiàn)齿桃,例如:hardware\libhardware\modules\hwcomposer\hwcomposer.cpp是hwcomposer模塊基于HWC1的default實現(xiàn)惑惶,對應(yīng)的共享庫是hwcomposer.default.so;hardware\qcom\display\msm8994\libhwcomposer\hwc.cpp是高通MSM8994基于HWC1的實現(xiàn)短纵,對應(yīng)的共享庫是hwcomposer.msm8994.so带污。
如果是基于HWC2協(xié)議實現(xiàn),則需要實現(xiàn)hwcomposer2.h中定義的hwc2_device_t接口香到,例如:class VendorComposer : public hwc2_device_t鱼冀。Android7.0的hwcomposer模塊默認都是基于HWC1協(xié)議實現(xiàn)的。
每個HAL層模塊實現(xiàn)都要定義一個HAL_MODULE_INFO_SYM數(shù)據(jù)結(jié)構(gòu)悠就,并且該結(jié)構(gòu)的第一個字段必須是hw_module_t千绪,下面是高通MSM8994hwcomposer模塊的定義:

// 高通MSM8994
hwc_module_t HAL_MODULE_INFO_SYM = {
    // common表示hw_module_t模塊
    common: {
        tag: HARDWARE_MODULE_TAG,
        version_major: 2,
        version_minor: 0,
        id: HWC_HARDWARE_MODULE_ID, // hwcomposer
        name: "Qualcomm Hardware Composer Module",
        author: "CodeAurora Forum",
        methods: &hwc_module_methods,
        dso: 0,
        reserved: {0},
    }
};

最重要的一點:HWComposer::loadHwcModule方法最終把HWC模塊封裝成了HWC2::Device

frameworks\native\services\surfaceflinger\DisplayHardware\HWC2.h主要定義了以下三個結(jié)構(gòu)體:

  • HWC2::Device:表示硬件合成顯示設(shè)備
  • HWC2::Display:表示一個顯示屏幕梗脾,可以是物理顯示屏(可以熱插拔接入或者移除)荸型,也可以是虛擬顯示屏,現(xiàn)在的游戲錄屏一般都是基于虛擬屏幕實現(xiàn)的炸茧。
  • HWC2::Layer:表示一個疊加圖層瑞妇,對應(yīng)與應(yīng)用側(cè)的Surface稿静。

它們是對HWC硬件模塊的進一步封裝,方便進行調(diào)用辕狰。HWC2::Device持有一個hwc2_device_t改备,用于連接硬件設(shè)備,它包含了很多HWC2_PFN開頭的函數(shù)指針變量蔓倍,這些函數(shù)指針定義在hwcomposer2.h绍妨。
HWC2::Device的構(gòu)造函數(shù)中,會通過Device::loadFunctionPointers-> loadFunctionPointer(FunctionDescriptor desc, PFN& outPFN) -> hwc2_device_t::getFunction的調(diào)用鏈從硬件設(shè)備中獲取具體的函數(shù)指針實現(xiàn)柬脸。關(guān)鍵模板函數(shù)如下所示:

// 模板函數(shù)他去,用于向硬件設(shè)備查詢具體的函數(shù)指針實現(xiàn)
template <typename PFN>
[[clang::warn_unused_result]] bool loadFunctionPointer(
    FunctionDescriptor desc, PFN& outPFN) {
    // desc表示一個枚舉類型值
    auto intDesc = static_cast<int32_t>(desc);
    // mHwcDevice表示hwc2_device_t,是硬件驅(qū)動提供的實現(xiàn)
    auto pfn = mHwcDevice->getFunction(mHwcDevice, intDesc);
    if (pfn != nullptr) {
        // 強轉(zhuǎn)函數(shù)指針
        outPFN = reinterpret_cast<PFN>(pfn); 
        return true;
    } else {
        ALOGE("Failed to load function %s", to_string(desc).c_str());
        return false;
    }
}

這些函數(shù)指針主要分為三類:

  1. 硬件設(shè)備(Device)相關(guān)的函數(shù)指針
  2. 顯示屏幕(Display)相關(guān)的函數(shù)指針
  3. 疊加圖層(Layer)相關(guān)的函數(shù)指針

通過上述函數(shù)指針可以與hwc2_device_t表示的硬件合成模塊進行交互倒堕。三類指針分別選取了一個示例:

// Device方法:獲得設(shè)備支持的最大虛擬屏幕個數(shù)
uint32_t Device::getMaxVirtualDisplayCount() const
{
    return mGetMaxVirtualDisplayCount(mHwcDevice);
}
// Display方法:為指定Device的指定Display創(chuàng)建一個Layer
Error Display::createLayer(std::shared_ptr<Layer>* outLayer)
{
    // 表示創(chuàng)建的layer的唯一標識符
    hwc2_layer_t layerId = 0;
    // mDevice.mHwcDevice表示hwc2_device_t灾测,即真正的硬件設(shè)備,mId表示當(dāng)前Display的唯一標識符垦巴,即為指定Device的指定Display創(chuàng)建一個Layer
    int32_t intError = mDevice.mCreateLayer(mDevice.mHwcDevice, mId, &layerId);
    auto error = static_cast<Error>(intError);
    if (error != Error::None) {
        return error;
    }
    // 基于layerId創(chuàng)建HWC2::Layer
    auto layer = std::make_shared<Layer>(shared_from_this(), layerId);
    // 保存當(dāng)前Display所有的Layer
    mLayers.emplace(layerId, layer);
    // 返回創(chuàng)建的HWC2::Layer
    *outLayer = std::move(layer);
    return Error::None;
}
// Layer方法:為指定Device的指定Display的指定Layer指定合成方式
Error Layer::setCompositionType(Composition type)
{
    auto intType = static_cast<int32_t>(type);
    // 為指定Device的指定Display的指定Layer指定合成方式
    int32_t intError = mDevice.mSetLayerCompositionType(mDevice.mHwcDevice,
            mDisplayId, mId, intType);
    return static_cast<Error>(intError);
}

可以通過類圖媳搪,直觀感受下引用關(guān)系。


HWC類圖

HWC2::Device構(gòu)造函數(shù)除了完成獲取函數(shù)指針實現(xiàn)以外骤宣,還會通過Device::registerCallbacks向硬件設(shè)備注冊三個Display的回調(diào):熱插拔秦爆,刷新和VSync信號,如下所示:

// HWC硬件的幾種回調(diào)描述符
// hardware\libhardware\include\hardware\hwcomposer2.h
enum class Callback : int32_t {
    Invalid = HWC2_CALLBACK_INVALID,
    // 顯示屏幕(Display)的熱插拔
    Hotplug = HWC2_CALLBACK_HOTPLUG,
    // 顯示屏幕(Display)的刷新
    Refresh = HWC2_CALLBACK_REFRESH,
    // 顯示屏幕(Display)的VSync信號
    Vsync = HWC2_CALLBACK_VSYNC,
};
// 注冊熱插拔/刷新/VSync回調(diào)
void Device::registerCallbacks()
{   // Callback枚舉類型如上所示
    registerCallback<HWC2_PFN_HOTPLUG>(Callback::Hotplug, hotplug_hook);
    registerCallback<HWC2_PFN_REFRESH>(Callback::Refresh, refresh_hook);
    registerCallback<HWC2_PFN_VSYNC>(Callback::Vsync, vsync_hook);
}

// 模板函數(shù)憔披,用于向硬件設(shè)備(hwc2_device_t)注冊函數(shù)回調(diào)
template <typename PFN, typename HOOK>
void registerCallback(Callback callback, HOOK hook) {
    // Callback枚舉類型如上所示
    auto intCallback = static_cast<int32_t>(callback);
    // this表示HWC2::Device, hwc2_callback_data_t表示void指針類型
    auto callbackData = static_cast<hwc2_callback_data_t>(this);
    // 把函數(shù)指針強轉(zhuǎn)成統(tǒng)一的void (*)() 函數(shù)指針類型
    auto pfn = reinterpret_cast<hwc2_function_pointer_t>(hook);
    // 向hwc2_device_t注冊函數(shù)回調(diào)等限,callbackData表示透傳的資源
    mRegisterCallback(mHwcDevice, intCallback, callbackData, pfn);
}
    
// 以VSync的靜態(tài)函數(shù)為例
static void vsync_hook(hwc2_callback_data_t callbackData,
            hwc2_display_t displayId, int64_t timestamp) {
    // callbackData表示透傳的void指針,實際指HWC2::Device        
    auto device = static_cast<HWC2::Device*>(callbackData);
    // 通過displayId獲取對應(yīng)的HWC2::Display
    auto display = device->getDisplayById(displayId);
    if (display) {
        // 向外回調(diào)
        device->callVsync(std::move(display), timestamp);
    } else {
        ALOGE("Vsync callback called with unknown display %" PRIu64, displayId);
    }
}

void Device::callVsync(std::shared_ptr<Display> display, nsecs_t timestamp)
{
    // 通過std::function可調(diào)用對象mVsync向外回調(diào)芬膝,該可調(diào)用對象實際是HWComposer通過Device::registerVsyncCallback方法注冊的
    if (mVsync) {
        mVsync(std::move(display), timestamp);
    } else {
        mPendingVsyncs.emplace_back(std::move(display), timestamp);
    }
}   

總結(jié)一下望门,HWC2::Device構(gòu)造函數(shù)向硬件設(shè)備注冊三個Display回調(diào):熱插拔,刷新和VSync信號锰霜。當(dāng)HWC2::Device收到這些回調(diào)時筹误,會通過監(jiān)聽器向外回調(diào)到對應(yīng)的HWComposer函數(shù):HWComposer::hotplug/HWComposer::invalidate/HWComposer::vsync。HWComposer再通過這些信息驅(qū)動對應(yīng)工作癣缅,后續(xù)文章進行介紹厨剪。

HWC Layer合成方式

上文提到HWC2::Device中的函數(shù)指針是hardware\libhardware\include\hardware\hwcomposer2.h中定義的,除此之外友存,該頭文件還定義了一些重要的結(jié)構(gòu)體祷膳,這里介紹兩個比較重要的:

// 顯示屏類型
enum class DisplayType : int32_t {
    Invalid = HWC2_DISPLAY_TYPE_INVALID,
    // 物理顯示屏,顯示設(shè)備有一個主屏幕爬立,然后可以通過熱插拔添加或者刪除外接顯示屏
    Physical = HWC2_DISPLAY_TYPE_PHYSICAL,
    // 虛擬顯示屏钾唬,內(nèi)容會渲染到離屏緩沖區(qū)万哪,Android錄屏功能就是基于虛擬屏實現(xiàn)的
    Virtual = HWC2_DISPLAY_TYPE_VIRTUAL,
};

// Layer合成類型侠驯,HWC2_COMPOSITION_XX取自hwc2_composition_t枚舉
enum class Composition : int32_t {
    Invalid = HWC2_COMPOSITION_INVALID,
    Client = HWC2_COMPOSITION_CLIENT,
    Device = HWC2_COMPOSITION_DEVICE,
    SolidColor = HWC2_COMPOSITION_SOLID_COLOR,
    Cursor = HWC2_COMPOSITION_CURSOR,
    Sideband = HWC2_COMPOSITION_SIDEBAND,
};

DisplayType表示顯示屏類型抡秆,上面注釋已經(jīng)介紹,重點看下Layer合成類型:

  1. Client:這里的Client是相對于HWC硬件設(shè)備來說的吟策,即不通過HWC來合成圖層儒士,而是通過GPU先把所有的這類圖層合成到client target buffer(一個離屏的圖形緩沖區(qū),buffer_handle_t表示指向這塊顯存的指針檩坚,顯存由Gralloc模塊分配)着撩,然后再通過Display::setClientTarget把這塊圖形Buffer的地址傳遞給HWC設(shè)備,最后由HWC設(shè)備把其他Layer和這個圖形Buffer進一步合成匾委,并最終展示在Display上拖叙。
  2. Device:通過HWC硬件來合成圖層,默認情況下赂乐,SurfaceFlinger會配置每個Layer都通過Device方式合成薯鳍,但是HWC設(shè)備會根據(jù)硬件設(shè)備的性能改變某些圖層的合成方式。
  3. SolidColor:HWC設(shè)備將通過Layer::setColor設(shè)置的顏色渲染這個圖層挨措,如果HWC設(shè)備不支持這種合成方式挖滤,那么將會請求SurfaceFlinger改變合成方式為Client。
  4. Cursor:與Device類似浅役,但是這個圖層的位置可以通過setCursorPosition異步設(shè)置斩松。如果HWC設(shè)備不支持這種合成方式,那么將會請求SurfaceFlinger改變合成方式為Client或者Device觉既。
  5. Sideband:HWC硬件會處理該類圖層的合成惧盹,以及它的緩沖區(qū)更新和內(nèi)容同步,但是只有擁有HWC2_CAPABILITY_SIDEBAND_STREAM能力的設(shè)備才支持這種圖層瞪讼,若設(shè)備不支持岭参,那么將會請求SurfaceFlinger改變合成方式為Client或者Device。

那么一個Layer的合成方式是怎么確定的那尝艘?大致流程如下所示:

Layer合成方式

  1. 當(dāng)VSync信號到來時演侯,SurfaceFlinger被喚醒,處理Layer的新建背亥,銷毀和更新秒际,并且為相應(yīng)Layer設(shè)置期望的合成方式。
  2. 所有Layer更新后狡汉,SurfaceFlinger調(diào)用validateDisplay娄徊,讓HWC決定每個Layer的合成方式。
  3. SurfaceFlinger調(diào)用getChangedCompositionTypes檢查HWC是否對任何Layer的合成方式做出了改變盾戴,若是寄锐,那么SurfaceFlinger則調(diào)整對應(yīng)Layer的合成方式,并且調(diào)用acceptDisplayChanges通知HWC。
  4. SurfaceFlinger把所有Client類型的Layer合成到Target圖形緩沖區(qū)橄仆,然后調(diào)用setClientTarget把Target Buffer設(shè)置給HWC剩膘。(如果沒有Client類型的Layer,則可以跳過該方法)
  5. 最后盆顾,SurfaceFlinger調(diào)用presentDisplay怠褐,讓HWC完成剩余Layer的合成,并且在顯示屏上展示出最終的合成結(jié)果您宪。

總結(jié)

本篇文章只是簡單介紹了HWC模塊的相關(guān)類:HWComposer奈懒、HWC2::DeviceHWC2::DisplayHWC2::Layer宪巨,以及它們的關(guān)系磷杏。此外,還著重介紹了Layer的合成方式和合成流程捏卓。后續(xù)文章會更加全面的介紹SurfaceFlinger是如何通過HWC模塊完成Layer合成和上屏的(虛擬屏幕是到離屏緩沖區(qū))茴丰。

相關(guān)源碼

  1. hardware\libhardware\include\hardware\hardware.h
  2. hardware\libhardware\hardware.c
  3. hardware\libhardware\include\hardware\hwcomposer_defs.h
  4. hardware\libhardware\include\hardware\hwcomposer.h
  5. hardware\libhardware\include\hardware\hwcomposer2.h
  6. frameworks\native\services\surfaceflinger\DisplayHardware\HWC2.h
  7. frameworks\native\services\surfaceflinger\DisplayHardware\HWC2.cpp
  8. frameworks\native\services\surfaceflinger\DisplayHardware\HWComposer.h
  9. frameworks\native\services\surfaceflinger\DisplayHardware\HWComposer.cpp
  10. frameworks\native\services\surfaceflinger\SurfaceFlinger.h
  11. frameworks\native\services\surfaceflinger\SurfaceFlinger.cpp
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市天吓,隨后出現(xiàn)的幾起案子贿肩,更是在濱河造成了極大的恐慌,老刑警劉巖龄寞,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汰规,死亡現(xiàn)場離奇詭異,居然都是意外死亡溜哮,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進店門锣笨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來入撒,“玉大人璃赡,你說我怎么就攤上這事碉考〕土穑” “怎么了瞒渠?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵剿吻,是天一觀的道長椰棘。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任拄查,我火速辦了婚禮腺毫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己淤击,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布印机。 她就那樣靜靜地躺著,像睡著了一般门驾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奶是,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天楣责,我揣著相機與錄音,去河邊找鬼聂沙。 笑死秆麸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的及汉。 我是一名探鬼主播蛔屹,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼豁生!你這毒婦竟也來了兔毒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤甸箱,失蹤者是張志新(化名)和其女友劉穎育叁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芍殖,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡豪嗽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了豌骏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片龟梦。...
    茶點故事閱讀 38,687評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖窃躲,靈堂內(nèi)的尸體忽然破棺而出计贰,到底是詐尸還是另有隱情,我是刑警寧澤蒂窒,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布躁倒,位于F島的核電站荞怒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏秧秉。R本人自食惡果不足惜褐桌,卻給世界環(huán)境...
    茶點故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望象迎。 院中可真熱鬧荧嵌,春花似錦、人聲如沸砾淌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拇舀。三九已至逻族,卻和暖如春蜻底,著一層夾襖步出監(jiān)牢的瞬間骄崩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工薄辅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留要拂,地道東北人。 一個月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓站楚,卻偏偏與公主長得像脱惰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子窿春,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,576評論 2 349

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