精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

ByteKMP Compose ArkUI 原生渲染解決方案

開發
ArkUI 提供了一套底層 CAPI 的高性能渲染接口 Native Drawing,通過初步的實驗和測試,發現其可以在保證性能的同時避免 Graph 內存增量,并且不會帶來額外包增量。對此,我們從 25Q1 啟動了適配工作,并于近期整體適配完成。

一、背景

Compose 官方對于 native 各平臺的底層渲染接口由 Skia 提供,因此在 24 年我們率先實現了基于 Skia 的渲染鏈路。但在實際使用中,我們發現創建底層渲染通道會增加額外 Graph 內存(正比于屏幕像素,對于全屏頁面約為 55MB)。如果存在多個頁面且未在應用層進行復用,很容易會觸發 OOM,同時引入 skia 亦會帶來較大包增量。隨著業務接入 KMP 頁面的增多,skia 實現帶來的性能瓶頸愈發難以忽視。

我們注意到 ArkUI 提供了一套底層 CAPI 的高性能渲染接口 Native Drawing,通過初步的實驗和測試,發現其可以在保證性能的同時避免 Graph 內存增量,并且不會帶來額外包增量。對此,我們從 25Q1 啟動了適配工作,并于近期整體適配完成。

二、整體架構

  • Compose 作為最外層UI框架,向上提供各個UI組件及其他UI相關API(如動畫、樣式等),向內管理布局樹及更新狀態,向下封裝具體UI渲染實現。
  • Harko 作為 OHRender 的 Kotlin 封裝庫,主要用途是將 OHRender 的 C++ API 通過 cinterop 機制封裝為 Kotlin API。同時以平臺代碼的方式實現 RenderNode 和幀回調綁定。整體架構定位平行于官方 Skiko 倉庫。
  • OHRender 作為具體圖形渲染庫,大部分復用 Skia 頭文件與接口設計。通過向下封裝 Native Drawing 圖形接口,來提供渲染能力。

三、項目結構變化

3.1 Compose 項目結構變化

一個典型的 KMP 項目會通過 Target 與 SourceSet 來構建基礎的項目結構(可參考官網描述),Compose 也是如此,只是沒有使用默認的 SourceSet 依賴結構。Compose 的 SourceSet 依賴關系如下(省略非移動端目標):

在我們通過 Skiko 來實現 Compose 渲染時,基于代碼最大化復用原則,我們將 ohos 的 SourceSet 整體置于 jsNative 之下(可見上圖紅色虛線部分),即可與 iOS、WASM 等共用基于 Skiko 的接口封裝與實現。

然而當我們不再使用 Skiko 后,原本所有 Native 平臺均基于 Skiko 的設計就會出現問題。對于短期來說,我們通過將 ohos 重新置于 jb 之下并拷貝部分可復用實現來解決依賴結構問題,這是為了避免結構大量變更對后續 Compose 升級及同步造成不利影響。長期來看,需要抽出不依賴 Skiko 的統一 Native 抽象層來應對代碼可復用問題。

3.2 其他基礎庫適配

對于業務場景等上層使用場景來說,由于直接依賴的 Compose API 未發生變化,且通常不會有類似 skikoMain 等額外針對 Skiko 的 SourceSet 層級,因此無需關心底層圖形接口的變更。但對于一些依賴底層圖形接口實現的基礎庫,情況就會有所不同。

首先是一些二方基礎庫僅僅支持移動端并用到了底層圖形接口,只需修改圖形接口實現即可。 其次是 coil 、compottie 等三方庫,由于他們采用類似于 Compose SourceSet 結構存在 skiko 層,還需要像 Compose 一樣變更 SourceSet 依賴關系。

四、渲染流程

由于 Native Drawing 的原生組件載體發生變化(由 XComponent 變更為 RenderNode),需要重新處理渲染內容綁定、幀回調等部分,同時繪制過程也因切換了底層實現有些變化。下面將從這三方面出發以圖完整展示整個渲染流程。

4.1 渲染內容綁定

編譯時注冊

在編碼過程中,RD 可在 @Composable 方法上添加 @ArkTsExportComposable 這個自定義注解,用于表示該方法預期導出至端側使用。在 @ArkTsExportComposable 注解中,存在成員變量:id(不傳默認為包名 + 方法名),代表渲染內容類型。

在編譯時,通過 KSP 識別上述注解,自動生成注冊邏輯代碼,以 id 為 key,@Composable 方法及一些其他配置項為 value,注冊入統一全局容器對象 ComposeController (基于動態需要,也支持運行時特定條件下熱插拔)。

以下述代碼為例:

@ArkTsExportComposable(id = "hello")
@Composable
internal fun Hello() {
    Column {
        Text("hello")
    }
}

將會在 ComposeController 中生成以下鍵值對

hello

@Composable {

     hello() 

}

RenderNode 運行時綁定

ArkTS 側的運行時綁定,首先需要創建 NodeController

@Component
export struct ComposeView {
    private nodeController: HarkoNodeController | undefined
    
    aboutToAppear(): void {
        if (!this.nodeController) {
            this.nodeController = new HarkoNodeController('hello' /* 對應上節 id */, ...)
        }
    }
    
    build() {
        Stack() {
            NodeContainer(this.nodeController)
                 ...
        }
    }
}

export class HarkoNodeController extends NodeController {
    private id: string
    ...
    
    constructor(id: string, ...) {
        this.id = id
    }
}

其次在 NodeController 添加 RenderNode 節點:

export class HarkoNodeController extends NodeController {
    private rootNode: FrameNode | null = null
    private harkoNode: HarkoRenderNode | null = null
    
    ...
    
    makeNode(uiContext: UIContext): FrameNode | null {
        this.rootNode = new FrameNode(uiContext)
        const rootRenderNode = this.rootNode.getRenderNode()
        this.harkoNode = new HarkoRenderNode(this.id, ...)
        if (rootRenderNode) {
            rootRenderNode.appendChild(this.harkoNode)
        }
    }
}

export class HarkoRenderNode extends RenderNode {
    constructor(id: string, ...) {
        ...
    }
    
    ...
}

最后在 HarkoRenderNode 構造時調用 KMP 工程通過 FFI 暴露的接口并獲取返回句柄,完成整體綁定:

export class HarkoRenderNode extends RenderNode {
    private nativeView: ESObject

    constructor(id: string, ...) {
        ...
        this.nativeView = initRenderNode(id, ...) //  FFI 暴露接口
    }
    
    ...
}

Compose 綁定

在上文通過 FFI 暴露接口,我們在 Kotlin 運行環境中獲取了 ArkTS 層需要渲染的類型 id,因此我們可以通過 id 反查 ComposeController 獲取渲染內容(以及一些其他配置項)。

@ArkTsExportFunction // 此注解代表該方法需要導出至 ArkTS 使用
fun initRenderNode(id: String, ...): ShellRenderView { // 該返回接口提供了宿主需要的Compose組件能力,例如繪制
    val view = ComposeController.initRenderNode(id, ...)
    
    ...
}

在 ComposeController 中,進一步構造 ComposeScene(Compose UI 內容的抽象容器):

object ComposeController {
    ...
    
    fun initRenderNode(id: String, ...): FrameRenderView {
        // 反查渲染內容
        val content = getContent(id) ?: error("failed to get content for $id")
        return RenderingUIView(content, ...)
    }
}

/*
   UI組件接口父類,用于處理幀相關
*/
abstract class FrameRenderView {
    ...
}

/*
   Compose UI組件實現類
*/
class RenderingUIView(
    content: @Composable () -> Unit, ...
) : FrameRenderView {
    private val mediator = ComposeSceneMediator(content)
     
    ...    
}

/*
    ComposeScene 中間類
*/
class ComposeSceneMediator(
    content: @Composable () -> Unit, ...
) {
    // ComposeScene 實例
    private val scene = MultiLayerComposeScene(...)
    
    fun setContent(...) {
        scene.setContent {
            ...
            
            CompositionLocalProvider(
                ...
                content = content
            )
        }
    }
}

在 ComposeScene 構造完成后,便已完成整個內容的綁定。而在后續的必要時機,即會調用 setContent 方法來完成最終的渲染內容設置。

4.2 幀回調

在上節構造 ComposeScene 時,還需傳入一個 invalidate 方法,用于在 Compose 頁面需要重新組合、重新渲染時申請下一幀:

class ComposeSceneMediator(
    content: @Composable () -> Unit,
    invalidate: () -> Unit,
    ...
) {
    private val scene = MultiLayerComposeScene(
        invalidate = invalidate,
        ...
    )
}

class RenderingUIView(...) {
    private val mediator = ComposeSceneMediator(
        content, this::invalidate
    )
    
    override fun invalidate() {
        ...
        super.invalidate()
    }
}

與 XComponent 不同,RenderNode 需要在 ArkTS 層向系統注冊幀回調,因此我們需要通過類似注入的方式,來調用幀請求,并在幀回調時讓 ArkTS 層調用對應方法延續調用鏈:

/*
    暴露給 ArkTS 側需要注入幀相關能力
*/
interface FrameImportApi {
    fun postFrame(export: FrameExportApi)
    ...
}

/*
    暴露給 ArkTS 側幀相關方法
*/
interface FrameExportApi {
    fun onFrame(...)
    ...
}

abstract class FrameRenderView(
    private val importApi: FrameImportApi,
    ...
) : FrameExportApi {
    private var needDraw = false    // 避免方法重復調用
    
    override fun invalidate() {
        if (needDraw) {
            return
        }
        needDraw = true
        importApi.postFrame(this)
    }
    
    override fun onFrame(...) {
        if (!needDraw) {
            return
        }
        needDraw = false
        
        ... // 進入到繪制過程
    }
}

在 ArkTS 側,實現幀請求并在系統回調時調用對應暴露方法,并在 4.1 節綁定時傳入該實現類的實例完成注入:

export class ShellFrameCaller implements FrameImportApi, ... {
    private uiContext: UIContext
    private frameCallback = new RenderFrameCallback()
    ...
    
    onFrame(view: ShellFrameExportApi): void {
        ...

        this.frameCallback.nativeView = view
        this.uiContext.postFrameCallback(this.frameCallback)
    }
}

export class RenderFrameCallback extends FrameCallback {
    nativeView: ShellFrameExportApi | null = null
    
    onFrame(frameTimeNanos: number) {
        ...
        this.nativeView?.onFrame(...)
    }
}

export class HarkoRenderNode extends RenderNode {
    ...

    constructor(id: string, frameCaller: ShellFrameCaller, ...) {
        ...
        this.nativeView = initRenderNode(id, frameCaller, ...)
    }
    
    ...
}

4.3 繪制過程

在上節收到系統幀回調后,在 onFrame 中會利用 CInterop 先切換至 C 層邏輯(由于 Kotlin Native 指針相關語法相當繁瑣,因此針對 ArkUI CAPI 調用盡量使用 C 實現再通過 CInterop 調用):

abstract class FrameRenderView {
    ...
    
    protected val renderNode = RenderNode()
    
    override fun onFrame(...) {
        ...
        renderNode.notifyRedraw()
    }
}

class RenderNode {
    ... 
    
    fun notifyRedraw() {
        nRenderNodeNotifyRedraw() // 外部C函數
    }
}

在 C 層,由于在幀回調內部不能直接操作 RenderNode 節點(操作的目的留到下一章敘述),因此需要先切換調用棧:

void nRenderNodeNotifyRedraw() {
    const napi_value jsNode = getJsNode();    // 在4.1節綁定時會獲取RenderNode的NAPI value
    if (jsNode == nullptr) {
        return;
    }
    OHRenderNode::RenderNodeNotifyRedraw(env, jsNode);    // 在 so 加載時可以獲得 NAPI env
}

void OHRenderNode::RenderNodeNotifyRedraw(napi_env env, napi_value jsNode) {
    void *ptr = nullptr;
    OHRenderNode* node = nullptr;
    
    // 獲取 OHRenderNode 指針
    napi_value napi_ptr;
    napi_status status = napi_get_named_property(env, jsNode, "OHRenderNodePtr", &napi_ptr);    // 此處也在4.1節綁定
    status = napi_get_value_external(env, napi_ptr, (void **)&ptr);
    node = (OHRenderNode *)ptr;

    if (node && node->fAsyncTask != nullptr) {
        uint result = 0;
        // ensure the task can be done (keep node alive).
        napi_reference_ref(env, node->fJsObject, &result);
        // 異步執行 fAsyncTask
        // uv_async_init(loop, fAsyncTask, OHRenderNode::RenderNodeDoRedraw); 
        uv_async_send(node->fAsyncTask);
    }
    return;
}

void OHRenderNode::RenderNodeDoRedraw(uv_async_t *handle) {
    OHRenderNode *node = (OHRenderNode *)handle->data;
    if (node) {
        ...
        node->doRedraw();
    }
}

在切換調用棧后,即可通過 RenderNode 的 NapiValue 獲得 OH_Drawing_Canvas 指針,通過一定包裝將其通過回調傳回 kotlin 層進行進一步繪制。

在 Kotlin 層,我們已經獲取到了 Canvas,便可將其包裝為 ComposeCanvas 繼續后續的渲染流程:

// 對 C 層 Canvas 的 KT 包裝類
class Canvas internal constructor(ptr: NativePtr, ...) { ... }

abstract class FrameRenderView {
    ...
    
    override fun onDraw(canvasPtr: NativePtr) {
        ...
        onDraw(Canvas(canvasPtr, ...)
    }
    
    abstract fun onDraw(canvas: Canvas)
}

class RenderingUIView(...) : FrameRenderView {
    ...
    private val mediator = ComposeSceneMediator(...)
    
    override fun onDraw(canvas: Canvas) {
        ...
        mediator.onRender(canvas, ...)
    }
}

在 ComposeSceneMediator 中,只需要將 harko Canvas 包裝為 Compose Canvas,即可完成向 ComposeScene 的傳遞:

class ComposeSceneMediator(...) {
    ...
    private val scene = MultiLayerComposeScene(...)
    
    fun onRender(canvas: Canvas, ...) {
        if (needSetContent) {
            setContent()
            ...
        }
        ...
        scene.render(canvas.asComposeCanvas(), ...)
    }
}

actual typealias NativeCanvas = com.bytedance.kmp.harko.skia.Canvas

fun NativeCanvas.asComposeCanvas(): Canvas = HarkoCanvas(this)

internal class HarkoCanvas(val native: NativeCanvas) : Canvas {
    override fun save() {
        native.save()
    }
    
    ...
}

4.4 時序圖

內容綁定

幀回調及渲染

五、臟區管理

Compose 對于 native 平臺統一采用 PictureRecorder 來進行臟區管理(1.6版本),針對 ArkUI 也沿用了相同的設計。

在某個節點首次進行繪制時,會通過 PictureRecorder 進行渲染命令錄制,如果后續內容沒有發生變化,便可以復用錄制內容進行回放降低渲染耗時:

internal class RenderNodeLayer(...) : OwnedLayer {
    ...
    private val pictureRecorder = PictureRecorder()
    private var picture: Picture? = null
    
    override fun drawLayer(canvas: Canvas) {
        if (picture == null) {    // 首次或是已失效,開始錄制
            val pictureCanvas = pictureRecorder.beginRecording(...)
            performDrawLayer(pictureCanvas.asComposeCanvas())    // 實際繪制方法
            picture = pictureRecorder.finishRecordingAsPicture()
        }
    
        canvas.save()
        ...
        canvas.nativeCanvas.drawPicture(picture, null, null)
        canvas.restore()
    }
}

在節點內容發生變化后,即會調用所持有的 RenderNodeLayer  invalidate 方法,銷毀 Picture

internal class RenderNodeLayer(...) : OwnedLayer {
    ...
    private var picture: Picture? = null
    
    override fun invalidate() {
        if (!isDestroyed && picture != null) {
            picture?.close()
            picture = null
        }
        invalidateParentLayer()    // 使父節點失效
    }
}

然而在 Native Drawing 中,PictureRecorder 對應的接口 OH_Drawing_RecordCmdUtilsBeginRecording 并不支持嵌套使用,且官方并不會在短期支持其嵌套調用,因此需另辟蹊徑來解決此問題。考慮到 ND 的宿主是 RenderNode,且 RenderNode 本身支持嵌套使用。故而可以將問題轉換為通過嵌套 RenderNode 來進行臟區管理。在繪制過程當中,若遇到 beginRecording 調用,便會創建一個子 RenderNode 至節點樹中,并保存其位置和寬高等屬性信息(通過 FFI 存儲在 ArkTS 層 NodeStatusModify 中),并在后續渲染命令提交時進行還原,整體結構如下圖所示:

值得關注的是,由于 RenderNode 的創建和上樹命令均沒有 CAPI,因此這些操作都需要通過 FFI 調用 ArkTS 接口完成,對此會造成額外渲染耗時以及 ArkTS 堆內存增量。

六、性能與展望

切換 Native Drawing 后能夠如預期般很好地解決原Skia實現的幾個痛點問題:減少約3MB包增量,以及降低整體內存57.5MB(以實際線上業務頁面為例,主要得益于 GL 和 Graph 內存分項),可以基本解決多 KMP 頁面造成的 OOM 問題。

但目前 Native Drawing 實現仍存在一些性能問題。其中內存方面,在 ArkTS 堆內存分項會有24MB左右的劣化;而在 FPS 方面,120 FPS 上限情況下依 Compose 頁面復雜程度不同會有10%-15%的劣化(90或60 FPS 上限時可以基本對齊)。經分析其主要原因正是上節提到的部分 CAPI 缺失。

通過和 ArkUI 官方技術團隊的交流和探討,我們很高興地看到缺失的 CAPI 將會在下次的系統大版本升級時補齊。通過其官方技術團隊提供的實驗室數據及本地驗證,可以確認 ArkTS 堆內存劣化問題可被解決,同時 FPS 也能對齊 skia 版本。

責任編輯:龐桂玉 來源: 字節跳動技術團隊
相關推薦

2023-09-11 07:11:04

CSSNesting

2009-05-05 13:56:59

五舟intel四核

2021-10-18 07:58:33

MyBatis Plu數據庫批量插入

2012-05-27 16:21:31

IDC華為

2018-12-03 12:17:27

Semptian解決方案

2018-12-03 12:26:30

YADRO解決方案

2018-12-03 11:59:42

Inventec解決方案

2018-12-03 12:13:21

Mellanox解決方案

2016-03-13 17:58:57

2021-01-12 10:43:22

數字化轉型IT云原生

2018-12-03 12:04:10

Kyligence解決方案

2012-05-27 18:09:33

NAG Cache華為

2010-12-21 17:38:12

2018-12-03 12:09:39

時速云解決方案

2018-12-03 12:23:45

IBMMCM解決方案

2010-12-21 17:20:01

2010-12-21 17:28:58

2017-08-02 17:23:22

AzureIoTAWS

2009-07-15 17:09:32

Swing線程
點贊
收藏

51CTO技術棧公眾號

亚洲国产精选| 亚洲女同志亚洲女同女播放| 国产欧美一区二区精品久久久| 黑人巨大精品欧美一区二区三区| 蜜桃成人在线| 亚洲图片中文字幕| 激情丁香综合| 亚洲午夜性刺激影院| 中文字幕66页| 98色花堂精品视频在线观看| 国产午夜精品一区二区| 亚洲aa在线观看| 国产成人无码精品| 色狮一区二区三区四区视频| 精品国产自在久精品国产| 无码无遮挡又大又爽又黄的视频| 黄色在线播放网站| 99久久国产综合色|国产精品| 国产精品久久久久久久9999| 亚洲熟女www一区二区三区| 亚洲三级网页| 欧美成人乱码一区二区三区| 国产一区视频免费观看| 国产极品人妖在线观看| 国产精品久久久久久久久晋中 | 久久久无码中文字幕久...| 午夜视频在线免费播放| 国产精品一卡二| 国产精品视频地址| 日本午夜视频在线观看| 欧美xxx在线观看| 国产一区二区三区在线看| 国产精品亚洲一区二区无码| 台湾天天综合人成在线| 日韩欧美综合在线视频| 91.com在线| 大片免费在线看视频| 久久精品欧美一区二区三区不卡| 国产精品久久久久久久久婷婷 | 国产精品免费在线视频| 欧美电影免费网站| 日韩欧美国产三级| 日韩av加勒比| 精品三级在线| 在线欧美日韩国产| 日日橹狠狠爱欧美超碰| 爱情岛亚洲播放路线| 亚洲激情第一区| 中文字幕综合在线观看| 成黄免费在线| 欧美高清在线精品一区| 日韩免费中文专区| 国产区视频在线| 国产网站一区二区三区| 欧美一区二区三区四区在线观看地址| 婷婷综合激情网| www.一区二区| 国产一区精品在线| 天天干天天草天天射| 成人久久久精品乱码一区二区三区 | av蜜臀在线| 亚洲福利一区二区| 国产精品久久久久久久久电影网| 2024最新电影在线免费观看| 亚洲美女屁股眼交3| 先锋影音男人资源| 亚洲羞羞网站| 亚洲伊人伊色伊影伊综合网| 久久亚洲中文字幕无码| 日韩伦理精品| 色呦呦网站一区| 日本新janpanese乱熟| 国产精品久久亚洲不卡| 欧美日韩免费在线视频| 天天影视色综合| 深夜福利一区| 亚洲第一黄色网| 丰满少妇在线观看资源站| 一区二区美女| 日韩一区二区三区国产| 九九视频免费在线观看| 国产欧美日韩亚洲一区二区三区| 欧美在线视频免费观看| 做爰视频毛片视频| 国产一区二区在线观看视频| 国产免费一区| 九色蝌蚪在线| 自拍av一区二区三区| 996这里只有精品| 日本不卡1234视频| 欧美日韩国产影片| wwwww在线观看| 免费成人高清在线视频theav| 一本色道久久88综合日韩精品| 国产精品国产精品88| 亚洲美洲欧洲综合国产一区| 国产精品自拍视频| 亚洲精品久久久蜜桃动漫| 久久久综合视频| 国产日韩欧美大片| 午夜av不卡| 7878成人国产在线观看| 精品人妻一区二区三区日产乱码卜| 国内精品久久久久久久久电影网| 操日韩av在线电影| www.毛片.com| 国产一区免费电影| 你懂的视频在线一区二区| 国产精品实拍| 日本韩国欧美在线| 女女调教被c哭捆绑喷水百合| 久久99高清| 欧美激情精品在线| 中文天堂在线视频| 91香蕉视频污在线| 国产日产欧美一区二区| 国产精品伦理| 亚洲第一福利视频| 精品无码久久久久成人漫画| 久久精品成人| 成人国产一区二区| 91xxx在线观看| 欧美视频一区二区三区…| 免费国偷自产拍精品视频| 欧美一级本道电影免费专区| 26uuu日韩精品一区二区| 国产美女明星三级做爰| 国产女人aaa级久久久级| 99视频在线免费播放| 日韩精品中文字幕吗一区二区| 亚洲新声在线观看| 欧美三级午夜理伦| 粉嫩欧美一区二区三区高清影视| 一区二区免费在线视频| free欧美| 亚洲人成啪啪网站| 丁香六月婷婷综合| caoporn国产精品| 男人添女人下部视频免费| 亚洲综合伊人| 色系列之999| 国产精品sm调教免费专区| 久久久综合精品| 日本精品www| 日韩mv欧美mv国产网站| 97久久精品国产| 成人毛片在线精品国产| 一区二区免费在线| 丰满少妇中文字幕| 欧美精品福利| av观看久久| 亚洲小说区图片区都市| 欧美成人一区二区| 青草草在线视频| 国产成人av电影| 福利在线一区二区| 国产伦精品一区二区三区在线播放 | 亚洲av片不卡无码久久| 亚洲综合另类| 欧美xxxx黑人又粗又长密月| 男人最爱成人网| 国产亚洲在线播放| 中文字幕一区二区在线视频| 国产精品理论片| 久久综合在线观看| 欧美日韩mv| 国产91精品入口17c| 国产精品69xx| 亚洲乱亚洲乱妇无码| 精品国产乱子伦| 国产精品视频第一区| 亚洲精品20p| 欧美精品不卡| 久久国产精品高清| 国产一区二区主播在线| 久久久国产精品一区| 亚洲国产精彩视频| 精品美女久久久久久免费| 精品无人区无码乱码毛片国产 | 手机av在线免费观看| 日韩欧美中文在线| 亚洲欧洲综合网| 国产精品一二三区在线| 日韩在线综合网| 精品国产乱码| 亚洲自拍偷拍网址| 一级毛片久久久| 久久久av电影| 天天操天天干天天爽| 欧美专区亚洲专区| 久久久夜色精品| 国产亚洲欧美日韩在线一区| 午夜免费视频网站| 亚洲免费网址| 中文字幕在线中文字幕日亚韩一区| 欧洲一区在线| 日本精品在线视频| 亚洲男同gay网站| 亚洲欧美激情四射在线日| 国产喷水福利在线视频| 精品国产精品自拍| 少妇人妻丰满做爰xxx| 93久久精品日日躁夜夜躁欧美| 2025韩国理伦片在线观看| 亚洲视频一二| 在线看无码的免费网站| 日韩精品福利一区二区三区| 成人看片人aa| 影视一区二区三区| 久久久久国产精品一区| 日韩在线免费电影| 精品在线小视频| 精品国产九九九| 欧美色偷偷大香| 欧美日韩精品区| 亚洲三级在线播放| 亚洲精品成人av久久| av亚洲产国偷v产偷v自拍| 一区二区三区四区毛片| 首页国产欧美日韩丝袜| 欧美不卡在线播放| 欧美黄色精品| 最新精品视频| 不卡中文字幕| 久久久久无码国产精品一区| av在线亚洲色图| 91麻豆桃色免费看| 日本欧美一区| 日本久久久久久| 国产乱码精品一区二三赶尸艳谈| 久久国产精品久久精品| www.亚洲免费| 国产亚洲美女久久| 欧美日本韩国一区二区| 精品捆绑美女sm三区| 国产农村妇女毛片精品久久| 欧美视频一区二区| 蜜臀99久久精品久久久久小说| 亚洲成人第一页| 精品小视频在线观看| 亚洲另类色综合网站| 日韩欧美国产成人精品免费| 国产精品五月天| 黄免费在线观看| 久久久电影一区二区三区| 中文字幕丰满孑伦无码专区| 99久久精品国产毛片| 国产精品成人无码专区| 成人精品免费网站| 无码任你躁久久久久久老妇| 丁香五精品蜜臀久久久久99网站 | 国产亚洲精品日韩| 国产色a在线| 伊人av综合网| 夜级特黄日本大片_在线| 最近的2019中文字幕免费一页| аⅴ资源新版在线天堂| 在线视频免费一区二区| 日本三级在线视频| 久久精品福利视频| av大全在线| 国内伊人久久久久久网站视频| 福利网站在线观看| 欧美孕妇性xx| av成人免费看| 亚洲jizzjizz日本少妇| 日韩精品视频一区二区三区| 99久久精品无码一区二区毛片| 97超碰成人| 久久超碰亚洲| 不卡中文一二三区| 男人草女人视频| 中文国产一区| 国产视频一区二区三区在线播放| 秋霞电影一区二区| 丰满少妇中文字幕| 91蜜桃传媒精品久久久一区二区| 免费看91的网站| 亚洲男人的天堂网| 亚洲视频免费播放| 欧美亚日韩国产aⅴ精品中极品| 97人妻精品一区二区三区视频| 欧美电影免费提供在线观看| 午夜成人免费影院| 这里只有精品视频| 污影院在线观看| 欧美专区在线视频| 99热这里有精品| 久久爱av电影| 91亚洲国产| av高清在线免费观看| 日韩精品乱码免费| 午夜免费视频网站| 久久久久99精品国产片| 欧美日韩在线观看成人| 色综合久久88色综合天天6| 91国偷自产中文字幕久久| 亚洲国产欧美日韩精品| 无遮挡动作视频在线观看免费入口| 欧美黑人一级爽快片淫片高清| 欧美福利在线播放| 99理论电影网| 成人一二三区| 欧美午夜小视频| 国产一区二区剧情av在线| 少妇真人直播免费视频| 一区二区三区 在线观看视频| 精品国产青草久久久久96| 亚洲的天堂在线中文字幕| 麻豆传媒在线完整视频| 欧美极品美女电影一区| 自拍偷拍亚洲图片| 欧美日韩精品免费在线观看视频| 欧美激情91| 天天干天天干天天干天天干天天干| 成人午夜又粗又硬又大| 欧洲美女女同性互添| 欧美午夜性色大片在线观看| 午夜精品久久久久久久99老熟妇| 在线视频免费一区二区| 成人欧美magnet| 国产伦精品一区二区三区免 | 久久爱www.| 亚洲欧洲国产日韩精品| 销魂美女一区二区三区视频在线| 亚洲av午夜精品一区二区三区| 自拍偷拍亚洲激情| 中文字幕乱码人妻无码久久| 亚洲免费人成在线视频观看| 国产极品人妖在线观看| 91九色蝌蚪嫩草| 91精品国产麻豆国产在线观看| 国内自拍视频一区| 久久欧美一区二区| 日韩欧美一区二区一幕| 精品久久久久久久人人人人传媒 | 少妇精品久久久| 欧美日韩一道本| 成人在线综合网| 免费在线观看亚洲| 日韩一区二区免费电影| 黄色小网站在线观看| 国产日韩欧美中文| 日韩欧美网址| 99re精彩视频| 中文字幕在线观看不卡| 亚洲网站免费观看| 少妇高潮久久77777| 日韩一级特黄| 在线视频一二三区| 国产一区二区三区蝌蚪| 男人操女人的视频网站| 欧美一区二区三区免费视频 | 992tv成人免费视频| 国产香蕉精品| 欧美二区在线视频| 久久久国产精品麻豆| 中文字幕有码无码人妻av蜜桃| 中文字幕欧美精品日韩中文字幕| av免费在线一区| 中文字幕一区二区三区有限公司| 久久99国产精品久久99果冻传媒| 国产精品免费在线视频| 日韩女优电影在线观看| 不卡一本毛片| 欧美xxxx黑人又粗又长密月| 蜜芽一区二区三区| 国产黄色小视频网站| 精品裸体舞一区二区三区| 亚洲美女炮图| 亚洲春色在线视频| 国产麻豆精品视频| 国产精品日日夜夜| 国产丝袜一区二区三区免费视频| 中文字幕日本一区二区| 老司机午夜网站| 91亚洲国产成人精品一区二区三| 免费无码国产精品| 久久精品中文字幕电影| 99久久香蕉| 久久综合伊人77777麻豆最新章节| 中文字幕视频一区| 神马午夜电影一区二区三区在线观看| 日本久久久久久久久久久| 2023国产精品久久久精品双| 精品人妻在线视频| 在线观看一区日韩| 污污视频在线看| 欧美成人免费在线| 国产一区不卡在线| 中文字幕第15页| 久久精品国产欧美激情| 久久精品色综合| 岛国av在线免费| 午夜精品福利视频网站| 日本视频在线| 久久福利电影| 国产成人免费在线视频| 99久久久无码国产精品免费蜜柚| 欧美精品情趣视频|