顯示框架之surfaceflinger 處理事務(wù)和消費(fèi)Buffer

暫時(shí)跳過(guò)SurfaceFlinger的初始化污秆,根據(jù)systrace的函數(shù)調(diào)用來(lái)分析SurfaceFlinger運(yùn)行的原理腹泌,看SurfaceFlinger做了哪些工作授滓。


sf trace.png

前面說(shuō)的當(dāng)應(yīng)用queueBuffer過(guò)來(lái)時(shí)會(huì)請(qǐng)求vsync來(lái)刷新粒没,首先會(huì)執(zhí)行onMessageInvalidate 函數(shù)揩抡,看下這個(gè)函數(shù)做了哪些工作。

onMessageInvalidate

文件: frameworks/native/services/surfaceflinger/Surfaceflinger.cpp

void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) {
    ATRACE_CALL();
    const nsecs_t frameStart = systemTime();
    // expectedVSyncTime 是vsync回調(diào)帶過(guò)來(lái)的時(shí)間戳却邓,通過(guò)nextAnticipatedVSyncTimeFrom 計(jì)算得到
    if (expectedVSyncTime >= frameStart) {
        mExpectedPresentTime = expectedVSyncTime;
    } else {
        mExpectedPresentTime = mScheduler->getDispSyncExpectedPresentTime(frameStart);
    }
     // 存儲(chǔ)上一幀的expectedVSyncTime
    const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
    mScheduledPresentTime = expectedVSyncTime;

   ...
     // 根據(jù)上一幀的present fence判斷當(dāng)前這一幀是否pending
    const TracedOrdinal<bool> framePending = {"PrevFramePending",
                                              previousFramePending(graceTimeForPresentFenceMs)};

    // 若framePending  或者 上一幀present fence釋放的時(shí)間  > 上一幀vsync計(jì)算的時(shí)間戳 + vsync周期的一半
    // 則當(dāng)前幀要丟掉
    DisplayStatInfo stats;
    mScheduler->getDisplayStatInfo(&stats);
    const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
    const nsecs_t previousPresentTime = previousFramePresentTime();
    const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
                                             framePending ||
                                                     (previousPresentTime >= 0 &&
                                                      (lastScheduledPresentTime <
                                                       previousPresentTime - frameMissedSlop))};
    // 根據(jù)合成類(lèi)型判斷丟幀的類(lèi)型
    const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
                                                mHadDeviceComposition && frameMissed};
    const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
                                                mHadClientComposition && frameMissed};
   
    ...
    // 這部分涉及幀率切換硕糊,先是通過(guò)performSetActiveConfig 將新的幀率給到hwc,然后下一幀再更新sf這邊的狀態(tài)
    if (mSetActiveConfigPending) {
        if (framePending) {
            mEventQueue->invalidate();
            return;
        }

        // We received the present fence from the HWC, so we assume it successfully updated
        // the config, hence we update SF.
        mSetActiveConfigPending = false;
        ON_MAIN_THREAD(setActiveConfigInternal());
    }
}
  //  mPropagateBackpressure 可以通過(guò)adb shell setprop debug.sf.disable_backpressure x 來(lái)控制,表示系統(tǒng)是否允許丟幀
  //  若允許丟幀則skip這次刷幀
  if (framePending && mPropagateBackpressure) {
        if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) {
            signalLayerUpdate();
            return;
        }
    }
 ....
   bool refreshNeeded;
    {
        ConditionalLockGuard<std::mutex> lock(mTracingLock, mTracingEnabled);
        // 主要的邏輯在這兩個(gè)函數(shù)腊徙,簡(jiǎn)單理解為處理layer或者display的事務(wù)和layer的buffer
        refreshNeeded = handleMessageTransaction();
        refreshNeeded |= handleMessageInvalidate();
       ...
    }
    // 幀率切換简十,SurfaceFlinger主線程執(zhí)行
   ON_MAIN_THREAD(performSetActiveConfig());
   ...
   // 若layer的事務(wù)有變化或者有新的buffer,則觸發(fā)refresh
   signalRefresh();
}

onMessageInvalidate主要處理的事情1. 判斷當(dāng)前幀是否丟掉 2. handleMessageTransaction 處理layer或者display事務(wù) 3. handleMessageInvalidate 處理應(yīng)用queue過(guò)來(lái)的Buffer 4. 幀率切換 5. 觸發(fā)刷新流程

handleMessageTransaction

文件: frameworks/native/services/surfaceflinger/Surfaceflinger.cpp

bool SurfaceFlinger::handleMessageTransaction() {
    ATRACE_CALL();
    // 獲取當(dāng)前的mTransactionFlags撬腾,mTransactionFlags 由setTransactionFlags 賦值螟蝙,比如CreateLayer時(shí)會(huì)給mTransactionFlags 賦值eTransactionNeeded
    // createDisplay 時(shí)會(huì)給mTransactionFlags 賦值 eDisplayTransactionNeeded
    uint32_t transactionFlags = peekTransactionFlags();
    // flushTransactionQueues 會(huì)消費(fèi)transactionQueue,transactionQueue 是上層通過(guò)SurfaceComposerClient 設(shè)的民傻,然后再通過(guò)binder設(shè)置給sf這邊
    // setDisplayStateLocked: 處理display的事務(wù)
    // setClientStateLocked: 處理layer的事務(wù)
    // 這兩部分事務(wù)都存放在surfaceflinger的mCurrentState 里面
    bool flushedATransaction = flushTransactionQueues();
    // 有新的事務(wù)則要執(zhí)行handleTransaction
    bool runHandleTransaction =
            (transactionFlags && (transactionFlags != eTransactionFlushNeeded)) ||
            flushedATransaction ||
            mForceTraversal;
    // 處理layer和display的事務(wù)
    if (runHandleTransaction) {
        handleTransaction(eTransactionMask);
    } else {
        getTransactionFlags(eTransactionFlushNeeded);
    }

    if (transactionFlushNeeded()) {
        setTransactionFlags(eTransactionFlushNeeded);
    }
 
    return runHandleTransaction;
}

//來(lái)看下display和layer有哪些事務(wù)處理

文件: frameworks/native/services/surfaceflinger/Surfaceflinger.cpp

// display的事務(wù)胰默,包括display Surface, layerStack , Projection , viewport, Size發(fā)生變化時(shí)附上eDisplayTransactionNeeded 這個(gè)flags
uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s) {
    const ssize_t index = mCurrentState.displays.indexOfKey(s.token);
    if (index < 0) return 0;

    uint32_t flags = 0;
    DisplayDeviceState& state = mCurrentState.displays.editValueAt(index);

    const uint32_t what = s.what;
    if (what & DisplayState::eSurfaceChanged) {
        if (IInterface::asBinder(state.surface) != IInterface::asBinder(s.surface)) {
            state.surface = s.surface;
            flags |= eDisplayTransactionNeeded;
        }
    }
    if (what & DisplayState::eLayerStackChanged) {
        if (state.layerStack != s.layerStack) {
            state.layerStack = s.layerStack;
            flags |= eDisplayTransactionNeeded;
        }
    }
    if (what & DisplayState::eDisplayProjectionChanged) {
        if (state.orientation != s.orientation) {
            state.orientation = s.orientation;
            flags |= eDisplayTransactionNeeded;
        }
        if (state.frame != s.frame) {
            state.frame = s.frame;
            flags |= eDisplayTransactionNeeded;
        }
        if (state.viewport != s.viewport) {
            state.viewport = s.viewport;
            flags |= eDisplayTransactionNeeded;
        }
    }
    if (what & DisplayState::eDisplaySizeChanged) {
        if (state.width != s.width) {
            state.width = s.width;
            flags |= eDisplayTransactionNeeded;
        }
        if (state.height != s.height) {
            state.height = s.height;
            flags |= eDisplayTransactionNeeded;
        }
    }

    return flags;
}

文件: frameworks/native/services/surfaceflinger/Surfaceflinger.cpp

// layer的事務(wù),包括ePositionChanged, eLayerChanged, eAlphaChanged 等等漓踢,跟上層的surface設(shè)置一樣牵署,同樣的事務(wù)同步給layer
uint32_t SurfaceFlinger::setClientStateLocked(
        const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
        bool privileged,
        std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
...  
   sp<Layer> layer = nullptr;
    if (s.surface) {
        layer = fromHandleLocked(s.surface).promote();
    ...

    if (what & layer_state_t::eDeferTransaction_legacy) {
        layer->pushPendingState();
    }

    // Only set by BLAST adapter layers
    if (what & layer_state_t::eProducerDisconnect) {
        layer->onDisconnect();
    }

    if (what & layer_state_t::ePositionChanged) {
        if (layer->setPosition(s.x, s.y)) {
            flags |= eTraversalNeeded;
        }
    }
...
}

通過(guò)setDisplayStateLocked 和 setClientStateLocked 獲取了display和layer的事務(wù)變化的flag,并且setClientStateLocked還將layer與surface進(jìn)行了事務(wù)的同步喧半。

文件:  frameworks/native/services/surfaceflinger/Surfaceflinger.cpp

void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
{
    ...
    if ((transactionFlags & eTraversalNeeded) || mForceTraversal) {
        mForceTraversal = false;
         // 遍歷mCurrentState 里面的layer碟刺,對(duì)有事務(wù)變化的layer進(jìn)行處理,doTransaction 主要的處理邏輯是對(duì)sync ponit的處理薯酝,
        // sync ponit用于延遲顯示的一些layer半沽,根據(jù)FrameNumber進(jìn)行同步爽柒,如果當(dāng)前幀數(shù)達(dá)到了設(shè)定值,latchBuffer就可以消費(fèi)這個(gè)layer
        mCurrentState.traverse([&](Layer* layer) {
            uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
            if (!trFlags) return;

            const uint32_t flags = layer->doTransaction(0);
            if (flags & Layer::eVisibleRegion)
                mVisibleRegionsDirty = true;

            if (flags & Layer::eInputInfoChanged) {
                mInputInfoChanged = true;
            }
        });
    }

    // 處理 display的 事務(wù)邏輯
    if (transactionFlags & eDisplayTransactionNeeded) {
        processDisplayChangesLocked();
        processDisplayHotplugEventsLocked();
    }
   ...
    // SurfaceFlinger維持mCurrentState 和 mDrawingState 兩個(gè)狀態(tài)者填,是個(gè)大的結(jié)構(gòu)體浩村,mCurrentState 可以理解為下一幀的
    // layer和display的狀態(tài), mDrawingState 可以理解為當(dāng)前幀的狀態(tài)占哟,commitTransaction 將 mCurrentState 更新到這一幀的狀態(tài)
    commitTransaction();
}

handleMessageTransaction 主要的作用是處理display和layer的事務(wù)心墅,將上層的surface和底層的layer屬性做個(gè)同步,涉及到很多的細(xì)節(jié)以后遇到具體場(chǎng)景來(lái)分析榨乎,最后將mCurrentState 賦給 mDrawingState 更新到當(dāng)前這一幀的狀態(tài)怎燥。

handleMessageInvalidate

文件:frameworks/native/services/surfaceflinger/Surfaceflinger.cpp

bool SurfaceFlinger::handleMessageInvalidate() {
    ATRACE_CALL();
    // 處理queue過(guò)來(lái)的Buffer
    bool refreshNeeded = handlePageFlip();

    if (mVisibleRegionsDirty) {
        // 如果可見(jiàn)區(qū)域有變化,則重新計(jì)算layer的范圍
        computeLayerBounds();
    }

    for (auto& layer : mLayersPendingRefresh) {
        Region visibleReg;
        visibleReg.set(layer->getScreenBounds());
        invalidateLayerStack(layer, visibleReg);
    }
    mLayersPendingRefresh.clear();
    return refreshNeeded;
}

bool SurfaceFlinger::handlePageFlip()
{
   ...
    // 遍歷 mDrawingState里面的layer蜜暑,判斷該layer是否可在當(dāng)前vsync內(nèi)顯示铐姚,如果queueBuffer帶過(guò)來(lái)的時(shí)間戳大于Vsync的時(shí)間戳,則表示該layer不能在當(dāng)前vsync內(nèi)顯示
    // 能夠顯示的layer放到mLayersWithQueuedFrames 里面
    mDrawingState.traverse([&](Layer* layer) {
        if (layer->hasReadyFrame()) {
            frameQueued = true;
            if (layer->shouldPresentNow(expectedPresentTime)) {
                mLayersWithQueuedFrames.push_back(layer);
            } else {
                ATRACE_NAME("!layer->shouldPresentNow()");
                layer->useEmptyDamage();
            }
        } else {
            layer->useEmptyDamage();
        }
    });
   ...
    // 遍歷mLayersWithQueuedFrames 里面的layer肛捍,執(zhí)行l(wèi)atchBuffer隐绵,在latchBuffer里面消費(fèi)Buffer
    // 成功消費(fèi)的layer放到mLayersPendingRefresh 里面
     for (auto& layer : mLayersWithQueuedFrames) {
            if (layer->latchBuffer(visibleRegions, latchTime, expectedPresentTime)) {
                mLayersPendingRefresh.push_back(layer);
            }
            layer->useSurfaceDamage();
            if (layer->isBufferLatched()) {
                newDataLatched = true;
            }
     ...
    // 當(dāng)有需要消費(fèi)Buffer的layer則返回true
   return !mLayersWithQueuedFrames.empty() && newDataLatched;

文件: frameworks/native/services/surfaceflinger/BufferLayer.cpp

bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
                              nsecs_t expectedPresentTime) {
   ...
   // 執(zhí)行順序BufferQueueLayer-> updateTexImage ==> BufferLayerConsumer-> updateTexImage,具體邏輯在
   // BufferLayerConsumer 里面
   status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime);
   // 更新mBufferInfo里的buffer,這個(gè)變量是在BufferQueueLayer里面維護(hù)
    err = updateActiveBuffer();
    if (err != NO_ERROR) {
        return false;
    }
    //更新mBufferInfo的FrameNumber
    err = updateFrameNumber(latchTime);
    if (err != NO_ERROR) {
        return false;
    }
    // 更新到mBufferInfo
    gatherBufferInfo();
    ...
}

文件: frameworks/native/services/surfaceflinger/BufferLayerConsumer.cpp

status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
                                             bool* autoRefresh, bool* queuedBuffer,
                                             uint64_t maxFrameNumber) {
  ...
    BufferItem item;

   // acquireBuffer:消費(fèi)queue過(guò)來(lái)的Buffer,放到item里面
    status_t err = acquireBufferLocked(&item, expectedPresentTime, maxFrameNumber);
    ...
    // 更新BufferLayerConsumer 狀態(tài)拙毫,都是從queueBuffer設(shè)置而來(lái)
    err = updateAndReleaseLocked(item, &mPendingRelease);
    ...
}

status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
                                                  uint64_t maxFrameNumber) {
    status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
    ...
    if (item->mGraphicBuffer != nullptr) {
        std::lock_guard<std::mutex> lock(mImagesMutex);
        if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->graphicBuffer() == nullptr ||
            mImages[item->mSlot]->graphicBuffer()->getId() != item->mGraphicBuffer->getId()) {
            // 將acquire出來(lái)的Buffer做成EGLImage依许,為了后面GPU合成
            mImages[item->mSlot] = std::make_shared<Image>(item->mGraphicBuffer, mRE);
        }
    }

    return NO_ERROR;
}

文件: frameworks/native/libs/gui/ConsumerBase.cpp

status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
        nsecs_t presentWhen, uint64_t maxFrameNumber) {
     ...
    // 實(shí)現(xiàn)在BufferQueueConsumer的 acquireBuffer
    status_t err = mConsumer->acquireBuffer(item, presentWhen, maxFrameNumber);
    if (err != NO_ERROR) {
        return err;
    }

    if (item->mGraphicBuffer != nullptr) {
        if (mSlots[item->mSlot].mGraphicBuffer != nullptr) {
            freeBufferLocked(item->mSlot);
        }
        //更新Bufferslot里面的GraphicsBuffer,這個(gè)Buffer的owner是GPU在處理
        mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer;
    }
    // 更新Bufferslot里面的frameNumber和fence缀蹄,與queueBuffer設(shè)置保持一致峭跳,這里的fence為acquireFence
    mSlots[item->mSlot].mFrameNumber = item->mFrameNumber;
    mSlots[item->mSlot].mFence = item->mFence;

    CB_LOGV("acquireBufferLocked: -> slot=%d/%" PRIu64,
            item->mSlot, item->mFrameNumber);

    return OK;
}

文件: frameworks/native/libs/gui/BufferQueueConsumer.cpp

//跳過(guò)一些特殊情況代碼細(xì)節(jié),把主線code拎出來(lái)分析
status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
        nsecs_t expectedPresent, uint64_t maxFrameNumber) {
   ...
   // queueBuffer時(shí)入的隊(duì)列
   BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
   ...
   // 拿到queueBuffer對(duì)應(yīng)的slot和BufferItem
   slot = front->mSlot;
   *outBuffer = *front;
    ...
   if (!outBuffer->mIsStale) {
            mSlots[slot].mAcquireCalled = true;
            // Don't decrease the queue count if the BufferItem wasn't
            // previously in the queue. This happens in shared buffer mode when
            // the queue is empty and the BufferItem is created above.
            if (mCore->mQueue.empty()) {
                mSlots[slot].mBufferState.acquireNotInQueue();
            } else {
                 // 更新?tīng)顟B(tài)為acquire
                mSlots[slot].mBufferState.acquire();
            }
      // queueBuffer入隊(duì)缺前,acquireBuffer出隊(duì)
     mCore->mQueue.erase(front);
     ...
}

handleMessageInvalidate 主要作用是執(zhí)行 latchBuffer 去 acquire 應(yīng)用queue過(guò)來(lái)的Buffer蛀醉,然后拿到queueBuffer時(shí)設(shè)的Bufferslot一些狀態(tài)屬性給到BufferQueueLayer的mBufferInfo,同時(shí)還把這個(gè)Buffer做成EGLImage為后面的GPU合成做準(zhǔn)備诡延,期間都是數(shù)據(jù)之間的傳遞滞欠,所以說(shuō)SurfaceFlinger并未真正觸碰Buffer的內(nèi)容古胆。


Buffer消費(fèi)流程.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肆良,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子逸绎,更是在濱河造成了極大的恐慌惹恃,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棺牧,死亡現(xiàn)場(chǎng)離奇詭異巫糙,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)颊乘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)参淹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)醉锄,“玉大人,你說(shuō)我怎么就攤上這事浙值】也唬” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵开呐,是天一觀的道長(zhǎng)烟勋。 經(jīng)常有香客問(wèn)我,道長(zhǎng)筐付,這世上最難降的妖魔是什么卵惦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮瓦戚,結(jié)果婚禮上沮尿,老公的妹妹穿的比我還像新娘。我一直安慰自己伤极,他們只是感情好蛹找,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著哨坪,像睡著了一般庸疾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上当编,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天届慈,我揣著相機(jī)與錄音,去河邊找鬼忿偷。 笑死金顿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鲤桥。 我是一名探鬼主播揍拆,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼茶凳!你這毒婦竟也來(lái)了嫂拴?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤贮喧,失蹤者是張志新(化名)和其女友劉穎筒狠,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體箱沦,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辩恼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灶伊。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疆前,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出聘萨,到底是詐尸還是另有隱情峡继,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布匈挖,位于F島的核電站碾牌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏儡循。R本人自食惡果不足惜舶吗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望择膝。 院中可真熱鬧誓琼,春花似錦、人聲如沸肴捉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)齿穗。三九已至傲隶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窃页,已是汗流浹背跺株。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脖卖,地道東北人乒省。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像畦木,于是被迫代替她去往敵國(guó)和親袖扛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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