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

搞定 GPU 驅(qū)動:拆解 DRM 核心

系統(tǒng) Linux
隨著硬件技術(shù)的飛速發(fā)展,尤其是 GPU(圖形處理單元)性能的大幅提升,圖形界面對于硬件加速、多顯示器支持以及復雜圖形渲染的需求越來越迫切。

在 Linux 的世界里,圖形界面的發(fā)展可謂是一部精彩紛呈的進化史。早期的 Linux 主要以命令行界面示人,用戶通過輸入各種指令與系統(tǒng)交互,雖說高效但不夠直觀。后來,為了滿足更多用戶對于圖形化操作的需求,Linux 的圖形界面開始嶄露頭角 ,像 X Window 系統(tǒng)就為 Linux 帶來了窗口、圖標和鼠標操作等功能,讓用戶能更便捷地使用計算機。

隨著硬件技術(shù)的飛速發(fā)展,尤其是 GPU(圖形處理單元)性能的大幅提升,圖形界面對于硬件加速、多顯示器支持以及復雜圖形渲染的需求越來越迫切。這時,DRM GPU 驅(qū)動框架應運而生,它就像是 Linux 圖形系統(tǒng)的幕后英雄,為現(xiàn)代 Linux 圖形系統(tǒng)的高效運行提供了堅實的支撐。接下來,就讓我們一起揭開它神秘的面紗,看看它到底是如何運作的吧。

一、DRM GPU 驅(qū)動框架是什么?

DRM(Direct Rendering Manager)即直接渲染管理器,是 Linux 內(nèi)核中用于管理 GPU(圖形處理單元)硬件資源的關(guān)鍵子系統(tǒng) 。它就像是一個智能管家,負責協(xié)調(diào) GPU 與顯示設備之間的交互,為上層應用程序提供圖形渲染和顯示功能。從本質(zhì)上講,DRM 為用戶空間程序提供了一組接口,讓這些程序能夠直接與 GPU 通信,從而實現(xiàn)高效的圖形處理。

在 GPU 驅(qū)動的復雜體系里,DRM 堪稱是坐鎮(zhèn)中軍帳的 “幕后大佬” ,其全稱為 Direct Rendering Manager,即直接渲染管理器,是 Linux 內(nèi)核中負責管理顯卡和 GPU 的核心子系統(tǒng)。它的存在,就像是為混亂的 GPU 資源世界制定了一套嚴格且有序的規(guī)則。在 Linux 系統(tǒng)中,當多個程序都想要使用 GPU 的強大功能時,比如一個 3D 游戲在進行復雜場景渲染,同時視頻編輯軟件也在利用 GPU 加速視頻特效處理,如果沒有 DRM,這些程序?qū)?GPU 資源的爭奪就如同沒有交通規(guī)則下的車輛亂行,很容易導致系統(tǒng)的不穩(wěn)定甚至崩潰。DRM 的出現(xiàn),就為 GPU 資源分配和多進程訪問管理提供了清晰的規(guī)則,讓每個程序都能有序地使用 GPU 資源 。

從模塊上劃分,DRM可以分為三個部分:libdrm、KMS、GEM。

圖片

1.1 libdrm

libdrm 是 DRM 框架在用戶空間的重要組成部分,是一個專門為 Linux 內(nèi)核的 DRM 子系統(tǒng)提供支持的用戶空間庫。它就像是一座橋梁,一端連接著用戶空間的應用程序,另一端連接著內(nèi)核空間的 DRM 驅(qū)動,讓應用程序能夠方便地與 DRM 驅(qū)動進行交互。

libdrm 的主要作用是對底層的接口進行封裝,特別是對各種 ioctl 接口進行精心封裝,向上層應用程序提供通用的 API 接口 。這樣一來,應用程序開發(fā)者在用戶空間調(diào)用 libdrm 提供的庫函數(shù),就能輕松訪問到顯示資源,并對這些資源進行有效的管理和使用。比如在開發(fā)一個簡單的圖形顯示應用時,開發(fā)者可以通過 libdrm 提供的 API 來創(chuàng)建和管理幀緩沖區(qū),設置顯示模式等,而無需深入了解底層硬件的復雜細節(jié)。

libdrm 提供了豐富的 API,其中一些主要的 API 及其功能如下:

  • drmOpen:這個函數(shù)用于打開 DRM 設備文件,獲取一個文件描述符,后續(xù)對 DRM 設備的操作都需要通過這個文件描述符來進行,就像是拿到了進入 DRM 設備世界的鑰匙。當應用程序需要訪問 DRM 設備時,首先會調(diào)用 drmOpen 函數(shù)來建立與設備的連接。
  • drmSetClientCap:用于設置客戶端的能力標志,通過這個函數(shù)可以告知 DRM 驅(qū)動客戶端支持的功能特性,比如是否支持通用平面(DRM_CLIENT_CAP_UNIVERSAL_PLANES)、是否支持原子操作(DRM_CLIENT_CAP_ATOMIC)等,讓 DRM 驅(qū)動能夠根據(jù)客戶端的能力來進行相應的處理。
  • drmModeGetResources:該函數(shù)用于獲取 DRM 設備的資源信息,包括 CRTC、編碼器、連接器、平面等資源的相關(guān)信息,這些信息對于應用程序了解 DRM 設備的硬件配置和能力非常重要,就像了解一個工廠里有哪些機器設備以及它們的性能參數(shù)一樣。
  • drmModeGetConnector:根據(jù)給定的連接器索引,獲取連接器的詳細信息,比如連接器支持的顯示模式、連接狀態(tài)等,這對于應用程序確定顯示設備的連接情況和可用顯示模式至關(guān)重要。
  • drmModeSetCrtc:用于設置 CRTC 的參數(shù),包括選擇要顯示的幀緩沖區(qū)、設置分辨率、刷新率、顯示位置等,通過這個函數(shù)可以控制 CRTC 將指定的幀緩沖區(qū)內(nèi)容以特定的參數(shù)顯示在顯示器上,實現(xiàn)圖像的正確顯示。

1.2 KMS(內(nèi)核模式設置)

KMS(Kernel Mode Setting)即內(nèi)核模式設置,是 DRM 框架下一個極為重要的大模塊,承擔著顯示參數(shù)設置和顯示控制的關(guān)鍵職責 。在顯示參數(shù)設置方面,它就像是一個專業(yè)的畫面調(diào)節(jié)師,負責設置各種與顯示相關(guān)的參數(shù),包括分辨率、刷新率、電源狀態(tài)(如休眠喚醒)等。通過合理設置這些參數(shù),能夠讓顯示設備呈現(xiàn)出最佳的顯示效果,滿足用戶在不同場景下的需求。在觀看高清視頻時,KMS 可以將分辨率設置為視頻的原始分辨率,刷新率設置為與視頻幀率匹配的值,讓視頻播放更加流暢、清晰。

在顯示控制方面,KMS 的作用同樣不可或缺。它負責顯示 buffer 的切換,就像一個熟練的舞臺換景師,能夠在不同的顯示內(nèi)容之間快速、平穩(wěn)地切換,確保畫面的連續(xù)性和流暢性。在多任務處理場景中,當用戶從一個應用程序切換到另一個應用程序時,KMS 能夠迅速切換顯示 buffer,將新應用程序的畫面呈現(xiàn)出來。KMS 還負責多圖層的合成方式以及每個圖層的顯示位置的控制,這對于實現(xiàn)復雜的圖形界面和特效非常重要。在一個具有多個窗口和圖標的桌面環(huán)境中,KMS 會根據(jù)各個窗口和圖標的層級關(guān)系,將它們正確地合成在一起,并確定它們在屏幕上的顯示位置,讓用戶看到一個有序、美觀的桌面界面。

圖片

  • Framebuffer:與硬件無關(guān)的基礎元素,本質(zhì)是一塊內(nèi)存區(qū)域,類似畫布,驅(qū)動和應用層均可訪問。需先設置分辨率、顯示格式等參數(shù)并填充數(shù)據(jù),2D 繪制時應用程序?qū)懭雸D形數(shù)據(jù),供 CRTC 讀取顯示。
  • CRTC(顯示控制器):硬件關(guān)鍵模塊,從 framebuffer 讀取圖像并輸出給 encoder。負責配置顯示器分辨率和時序、掃描數(shù)據(jù)到顯示器、更新內(nèi)容保證實時性,還能同時控制多個顯示器。
  • ENCODER:像信號翻譯官,將 CRTC 輸出的時序轉(zhuǎn)換為外部設備所需信號,如將圖像信號編碼成 HDMI 的 TMDS 信號或 VGA 的模擬信號。
  • CONNECTOR:連接物理顯示設備的接口(如 HDMI、DP),能檢測設備熱拔插狀態(tài),讀取解析顯示器 EDID 信息(含參數(shù)和能力),供系統(tǒng)調(diào)整設置。
  • PLANE:硬件圖層,是 CRTC 和 framebuffer 的連接者,每個 CRTC 至少有一個。支持多層合成顯示,管理不同圖層(如背景、光標)的合成,可借助硬件加速提升效率,比如游戲中多圖層的合成顯示。
  • VBLANK:基于垂直消影區(qū)的軟硬件同步機制,利用 VSYNC 信號確保顯示穩(wěn)定,避免畫面撕裂。在垂直掃描間隙進行幀切換、參數(shù)更新等操作,保證視頻等內(nèi)容流暢顯示。
  • property:DRM 驅(qū)動中靈活的模式設置機制,可將需設置的參數(shù)做成 property。優(yōu)點包括:借助 Atomic 機制減少接口維護;結(jié)構(gòu)簡單(含 name、id、value,id 全局唯一);支持一次操作多個參數(shù),減少內(nèi)核用戶態(tài)切換,適配不同硬件需求,比如設置亮度、對比度等參數(shù)。

1.3 GEM(圖形執(zhí)行管理器)

GEM(Graphic Execution Manager)即圖形執(zhí)行管理器,在 DRM GPU 驅(qū)動框架中主要負責顯示 buffer 的分配和釋放,是 GPU 顯存管理的關(guān)鍵部分 。在圖形處理過程中,需要大量的內(nèi)存來存儲圖形數(shù)據(jù),如幀緩沖區(qū)、紋理數(shù)據(jù)等,GEM 就像是一個高效的內(nèi)存管家,負責合理分配和管理這些內(nèi)存資源,確保圖形任務能夠順利進行。它的存在使得用戶空間應用程序可以更方便地管理顯存,而不需要了解底層硬件的復雜細節(jié),大大降低了開發(fā)難度。不同機制解析如下:

  • dumb:dumb 是一種簡單的內(nèi)存分配機制,它只支持連續(xù)物理內(nèi)存的 buffer 類型,基于 kernel 中通用 CMA(Contiguous Memory Allocator,連續(xù)內(nèi)存分配器)API 實現(xiàn),多用于小分辨率簡單場景 。在一些對圖形性能要求不高、分辨率較低的場景中,如簡單的嵌入式設備顯示界面、基本的文本顯示等,dumb 機制能夠滿足需求。它的優(yōu)點是實現(xiàn)簡單,直接使用 CPU 渲染即可,不需要復雜的 GPU 參與,成本較低。但由于其只支持連續(xù)物理內(nèi)存,在處理大內(nèi)存或復雜圖形場景時,會受到內(nèi)存碎片化等問題的限制,無法滿足需求。在一個簡單的電子詞典設備中,其顯示界面主要是文本內(nèi)容,分辨率較低,使用 dumb 機制來分配顯示 buffer 就非常合適。
  • prime:prime 機制相對 dumb 更為強大,它既支持連續(xù)物理內(nèi)存,也支持非連續(xù)物理內(nèi)存,基于 DMA - BUF(Direct Memory Access - Buffer,直接內(nèi)存訪問緩沖區(qū))機制實現(xiàn),可以實現(xiàn) buffer 共享,多用于大內(nèi)存復雜場景 。在現(xiàn)代圖形應用中,如 3D 游戲、高清視頻編輯等,需要處理大量的圖形數(shù)據(jù),這些數(shù)據(jù)往往需要占用大量的內(nèi)存,且對內(nèi)存的訪問效率要求很高。prime 機制能夠充分利用 DMA - BUF 機制,實現(xiàn)內(nèi)存的高效分配和共享,滿足大內(nèi)存復雜場景的需求。在運行 3D 游戲時,游戲中的各種模型、紋理、光照等數(shù)據(jù)都需要占用大量內(nèi)存,prime 機制可以為這些數(shù)據(jù)分配合適的內(nèi)存空間,并通過 buffer 共享技術(shù),讓不同的圖形模塊能夠高效地訪問這些數(shù)據(jù),提高游戲的運行性能。
  • fence:fence 是一種 buffer 同步機制,基于內(nèi)核 dma_fence 機制實現(xiàn),主要用于防止顯示內(nèi)容出現(xiàn)異步問題 。在圖形處理過程中,由于 GPU 的并行處理特性和不同硬件模塊之間的異步操作,可能會出現(xiàn)顯示內(nèi)容不同步的情況,如畫面撕裂、抖幀等,影響用戶體驗。fence 機制通過在關(guān)鍵操作點設置同步信號,確保在顯示內(nèi)容更新時,所有相關(guān)的操作都已完成,從而避免異步問題的出現(xiàn)。在進行視頻播放時,fence 可以保證視頻的每一幀都能按照正確的順序和時間顯示在屏幕上,不會出現(xiàn)畫面錯位或撕裂的現(xiàn)象,讓視頻播放更加流暢、穩(wěn)定。

二、DRM GPU 驅(qū)動框架工作原理

2.1 初始化流程

當系統(tǒng)啟動時,DRM GPU 驅(qū)動框架的初始化是一個有條不紊的過程,涉及多個關(guān)鍵步驟,確保 GPU 和顯示設備能夠正常工作并為后續(xù)的圖形處理和顯示做好準備。

在 PCI 設備探測階段,系統(tǒng)啟動后,內(nèi)核中的 PCI 子系統(tǒng)就像一個勤勞的探測器,開始對硬件設備進行全面掃描。當它檢測到顯卡設備時,就如同發(fā)現(xiàn)了寶藏一般,立即觸發(fā)對應 DRM 驅(qū)動的探測(probe)函數(shù)。以常見的 Intel i915 驅(qū)動為例,其探測函數(shù)為 i915_pci_probe,這個函數(shù)是驅(qū)動與硬件設備建立初步聯(lián)系的關(guān)鍵入口。

DRM 驅(qū)動注冊緊隨其后,顯卡驅(qū)動,如 i915、amdgpu 等,會通過 drm_driver 結(jié)構(gòu)體向 DRM 核心注冊自己 。這就像是在一個大型組織中登記自己的身份和職責,讓 DRM 核心能夠識別和管理這個驅(qū)動。注冊過程中,驅(qū)動會向 DRM 核心提供一系列的信息和功能函數(shù)指針,以便 DRM 核心在后續(xù)的工作中能夠調(diào)用驅(qū)動的功能。

接下來是 DRM 設備初始化,在探測函數(shù)中,驅(qū)動會創(chuàng)建并初始化一個 drm_device 對象,這個對象就像是 GPU 設備在軟件世界中的代表,它包含了 GPU 設備的各種信息和狀態(tài),代表一個 DRM 設備實例 。驅(qū)動會為 drm_device 對象分配內(nèi)存空間,并對其內(nèi)部的各種成員變量進行初始化,設置設備的標識、狀態(tài)、支持的功能等。

內(nèi)存管理初始化也是重要環(huán)節(jié),其中包括圖形傳輸表(GTT)的初始化 。GTT 就像是 GPU 內(nèi)存的導航圖,它記錄了 GPU 內(nèi)存中各個區(qū)域的映射關(guān)系,為 GPU 訪問內(nèi)存提供了重要的支持。在初始化過程中,會設置 GTT 的起始和結(jié)束地址等關(guān)鍵參數(shù),確保 GPU 能夠正確地訪問內(nèi)存中的圖形數(shù)據(jù)。雖然設置 GTT 范圍的 ioctl 通常由用戶空間的 X server 或 Wayland compositor 調(diào)用,但內(nèi)核驅(qū)動在初始化階段已經(jīng)準備好了 GTT 的硬件資源,就像提前搭建好了房屋的框架,只等用戶空間來裝修和布置。

如果驅(qū)動支持 KMS,那么 KMS 初始化就會啟動,這一步負責初始化顯示輸出部分 。就像是為一場演出布置舞臺,KMS 初始化會創(chuàng)建 framebuffer,為顯示內(nèi)容提供存儲空間,并設置顯示模式,確定分辨率、刷新率等參數(shù),讓顯示設備能夠以合適的方式展示圖像。

最后,DRM 核心會為每個 DRM 設備創(chuàng)建一個字符設備,如 /dev/dri/card0 。這個字符設備就像是用戶空間與 DRM 設備之間的通信橋梁,用戶空間程序通過該設備與 DRM 交互,發(fā)送各種命令和請求,實現(xiàn)對 GPU 和顯示設備的控制和管理。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 前向聲明
struct drm_device;

// 簡化的DRM驅(qū)動結(jié)構(gòu)體
struct drm_driver {
    const char *name;        // 驅(qū)動名稱
    const char *desc;        // 驅(qū)動描述
    const char *date;        // 驅(qū)動日期
    int major;               // 主版本號
    int minor;               // 次版本號

    // 函數(shù)指針
    int (*probe)(struct drm_device *dev);
    int (*load)(struct drm_device *dev);
    void (*unload)(struct drm_device *dev);
};

// 簡化的DRM設備結(jié)構(gòu)體
struct drm_device {
    struct drm_driver *driver;  // 關(guān)聯(lián)的驅(qū)動
    void *dev_private;          // 設備私有數(shù)據(jù)
    int device_id;              // 設備ID
    int status;                 // 設備狀態(tài)
    int gtt_start;              // GTT起始地址
    int gtt_end;                // GTT結(jié)束地址
    int fb_size;                // Framebuffer大小
    int crtc_count;             // CRTC數(shù)量
    int connector_count;        // 連接器數(shù)量
};

// 簡化的PCI設備結(jié)構(gòu)體
struct pci_device {
    int vendor_id;
    int device_id;
    const char *name;
};

// 模擬的Intel i915驅(qū)動
static struct drm_driver i915_driver = {
    .name = "i915",
    .desc = "Intel Graphics Driver",
    .date = "20251110",
    .major = 1,
    .minor = 6,
};

// 模擬的AMD amdgpu驅(qū)動
static struct drm_driver amdgpu_driver = {
    .name = "amdgpu",
    .desc = "AMD Graphics Driver",
    .date = "20251110",
    .major = 2,
    .minor = 3,
};

// PCI設備列表
static struct pci_device pci_devices[] = {
    {0x8086, 0x1912, "Intel HD Graphics 530"},
    {0x1002, 0x67df, "AMD Radeon RX 480"},
    {0x10de, 0x1b81, "NVIDIA GeForce GTX 1060"},
    {0, 0, NULL} // 結(jié)束標志
};

// 模擬PCI設備探測
struct pci_device* pci_probe_device() {
    printf("\n=== PCI設備探測階段 ===\n");
    printf("正在掃描PCI總線...\n");

    // 遍歷PCI設備列表
    for (int i = 0; pci_devices[i].name != NULL; i++) {
        printf("發(fā)現(xiàn)設備: %s (Vendor: 0x%04x, Device: 0x%04x)\n", 
               pci_devices[i].name, 
               pci_devices[i].vendor_id, 
               pci_devices[i].device_id);

        // 檢查是否有匹配的驅(qū)動
        if (pci_devices[i].vendor_id == 0x8086) { // Intel設備
            printf("找到匹配的Intel i915驅(qū)動\n");
            return &pci_devices[i];
        } else if (pci_devices[i].vendor_id == 0x1002) { // AMD設備
            printf("找到匹配的AMD amdgpu驅(qū)動\n");
            return &pci_devices[i];
        }
    }

    printf("未找到支持的GPU設備\n");
    return NULL;
}

// DRM驅(qū)動注冊
struct drm_driver* drm_driver_register(struct pci_device *pdev) {
    printf("\n=== DRM驅(qū)動注冊階段 ===\n");

    struct drm_driver *driver = NULL;

    if (pdev->vendor_id == 0x8086) {
        driver = &i915_driver;
    } else if (pdev->vendor_id == 0x1002) {
        driver = &amdgpu_driver;
    }

    if (driver) {
        printf("注冊DRM驅(qū)動: %s %d.%d\n", driver->name, driver->major, driver->minor);
        printf("驅(qū)動描述: %s\n", driver->desc);
        printf("驅(qū)動日期: %s\n", driver->date);
        printf("DRM驅(qū)動注冊成功\n");
    }

    return driver;
}

// DRM設備初始化
struct drm_device* drm_dev_alloc(struct drm_driver *driver, struct pci_device *pdev) {
    printf("\n=== DRM設備初始化階段 ===\n");

    struct drm_device *dev = malloc(sizeof(struct drm_device));
    if (!dev) {
        printf("DRM設備內(nèi)存分配失敗\n");
        return NULL;
    }

    memset(dev, 0, sizeof(struct drm_device));
    dev->driver = driver;
    dev->device_id = pdev->device_id;
    dev->status = 1; // 設備狀態(tài): 已初始化

    printf("創(chuàng)建DRM設備實例 (設備ID: 0x%04x)\n", dev->device_id);
    printf("DRM設備初始化完成\n");

    return dev;
}

// 內(nèi)存管理初始化
int drm_memory_manager_init(struct drm_device *dev) {
    printf("\n=== 內(nèi)存管理初始化階段 ===\n");

    // 模擬GTT初始化
    dev->gtt_start = 0xC0000000;
    dev->gtt_end = 0xD0000000;

    printf("初始化圖形傳輸表(GTT)\n");
    printf("GTT范圍: 0x%08x - 0x%08x\n", dev->gtt_start, dev->gtt_end);
    printf("GTT大小: %d MB\n", (dev->gtt_end - dev->gtt_start) / 1024 / 1024);

    // 模擬Framebuffer初始化
    dev->fb_size = 1920 * 1080 * 4; // 1920x1080 @ 32bpp
    printf("Framebuffer大小: %d KB\n", dev->fb_size / 1024);

    printf("內(nèi)存管理初始化完成\n");
    return 0;
}

// KMS初始化
int drm_kms_init(struct drm_device *dev) {
    printf("\n=== KMS初始化階段 ===\n");

    // 模擬CRTC和連接器初始化
    dev->crtc_count = 2;
    dev->connector_count = 3;

    printf("初始化Kernel Mode Setting\n");
    printf("發(fā)現(xiàn) %d 個CRTC控制器\n", dev->crtc_count);
    printf("發(fā)現(xiàn) %d 個連接器\n", dev->connector_count);

    // 模擬顯示模式設置
    printf("設置顯示模式: 1920x1080 @ 60Hz\n");
    printf("創(chuàng)建主Framebuffer\n");

    printf("KMS初始化完成\n");
    return 0;
}

// 創(chuàng)建字符設備
int drm_device_create_file(struct drm_device *dev) {
    printf("\n=== 字符設備創(chuàng)建階段 ===\n");

    static int card_number = 0;
    char dev_path[20];

    sprintf(dev_path, "/dev/dri/card%d", card_number++);

    printf("創(chuàng)建字符設備: %s\n", dev_path);
    printf("權(quán)限設置為: 0666 (rw-rw-rw-)\n");
    printf("用戶空間可以通過此設備與DRM交互\n");

    return 0;
}

// DRM設備注冊
int drm_dev_register(struct drm_device *dev) {
    printf("\n=== DRM設備注冊完成 ===\n");

    if (!dev) {
        printf("DRM設備注冊失敗: 無效設備\n");
        return -1;
    }

    printf("DRM設備 %s 注冊成功\n", dev->driver->name);
    printf("設備狀態(tài): 就緒\n");
    printf("GPU和顯示設備已準備好進行圖形處理\n");

    return 0;
}

// 模擬i915驅(qū)動探測函數(shù)
int i915_pci_probe(struct drm_device *dev) {
    printf("\n=== i915驅(qū)動探測函數(shù) ===\n");
    printf("Intel GPU設備探測中...\n");
    printf("初始化Intel特定硬件...\n");
    printf("設置PCI配置空間...\n");
    printf("使能GPU電源管理...\n");
    printf("i915驅(qū)動探測完成\n");
    return 0;
}

// 模擬amdgpu驅(qū)動探測函數(shù)
int amdgpu_pci_probe(struct drm_device *dev) {
    printf("\n=== amdgpu驅(qū)動探測函數(shù) ===\n");
    printf("AMD GPU設備探測中...\n");
    printf("初始化AMD特定硬件...\n");
    printf("設置PCIe配置...\n");
    printf("初始化Radeon電源管理...\n");
    printf("amdgpu驅(qū)動探測完成\n");
    return 0;
}

// 主函數(shù) - 模擬DRM GPU驅(qū)動初始化流程
int main() {
    struct pci_device *pdev;
    struct drm_driver *driver;
    struct drm_device *dev;

    printf("=== DRM GPU驅(qū)動框架初始化 ===\n");

    // 1. PCI設備探測
    pdev = pci_probe_device();
    if (!pdev) {
        printf("初始化失敗: 未找到支持的PCI設備\n");
        return -1;
    }

    // 2. DRM驅(qū)動注冊
    driver = drm_driver_register(pdev);
    if (!driver) {
        printf("初始化失敗: 無法注冊DRM驅(qū)動\n");
        return -1;
    }

    // 3. DRM設備初始化
    dev = drm_dev_alloc(driver, pdev);
    if (!dev) {
        printf("初始化失敗: 無法分配DRM設備\n");
        return -1;
    }

    // 4. 調(diào)用特定驅(qū)動的探測函數(shù)
    if (driver == &i915_driver) {
        i915_pci_probe(dev);
    } else if (driver == &amdgpu_driver) {
        amdgpu_pci_probe(dev);
    }

    // 5. 內(nèi)存管理初始化
    if (drm_memory_manager_init(dev) != 0) {
        printf("初始化失敗: 內(nèi)存管理初始化失敗\n");
        return -1;
    }

    // 6. KMS初始化
    if (drm_kms_init(dev) != 0) {
        printf("初始化失敗: KMS初始化失敗\n");
        return -1;
    }

    // 7. 創(chuàng)建字符設備
    if (drm_device_create_file(dev) != 0) {
        printf("初始化失敗: 無法創(chuàng)建字符設備\n");
        return -1;
    }

    // 8. DRM設備注冊完成
    if (drm_dev_register(dev) != 0) {
        printf("初始化失敗: DRM設備注冊失敗\n");
        return -1;
    }

    printf("\n=== DRM GPU驅(qū)動初始化完成 ===\n");
    printf("系統(tǒng)現(xiàn)在可以進行圖形處理和顯示輸出\n");

    // 清理資源
    free(dev);

    return 0;
}
  1. PCI 設備探測階段如何掃描硬件并匹配驅(qū)動
  2. DRM 驅(qū)動注冊過程
  3. DRM 設備初始化和私有數(shù)據(jù)結(jié)構(gòu)創(chuàng)建
  4. 內(nèi)存管理初始化(包括 GTT 設置)
  5. KMS 初始化(顯示輸出控制)
  6. 字符設備創(chuàng)建(/dev/dri/card0)

2.2 顯示流程

從應用程序產(chǎn)生圖形數(shù)據(jù)到最終顯示在屏幕上,這一過程就像是一場精心編排的表演,涉及多個組件的協(xié)同工作,數(shù)據(jù)在 libdrm、KMS、GEM 及硬件之間有序地傳遞和處理。

應用程序在運行過程中生成圖形數(shù)據(jù),這些數(shù)據(jù)就像是演出的素材 。應用程序通過調(diào)用 libdrm 提供的 API,將圖形數(shù)據(jù)相關(guān)的請求發(fā)送給內(nèi)核空間的 DRM 驅(qū)動。在渲染一個游戲場景時,游戲應用程序會調(diào)用 libdrm 的函數(shù)來創(chuàng)建和管理幀緩沖區(qū),設置顯示模式等。

libdrm 作為用戶空間與內(nèi)核空間的橋梁,收到應用程序的請求后,會對這些請求進行處理和轉(zhuǎn)換 。它會將用戶空間的函數(shù)調(diào)用轉(zhuǎn)換為對應的 ioctl 命令,然后通過設備文件(如 /dev/dri/card0)將這些命令發(fā)送給內(nèi)核空間的 DRM 驅(qū)動,就像一個翻譯官,將用戶的語言翻譯成內(nèi)核能夠理解的指令。

內(nèi)核空間的 DRM 驅(qū)動接收到 ioctl 命令后,KMS 模塊開始發(fā)揮重要作用 。KMS 會根據(jù)命令的要求,對顯示相關(guān)的參數(shù)進行設置和調(diào)整。它會配置 CRTC,讓 CRTC 從 GEM 分配的幀緩沖區(qū)中讀取待顯示的圖像數(shù)據(jù),并按照設置的分辨率、刷新率等參數(shù),將圖像數(shù)據(jù)輸出給 encoder 。在調(diào)整顯示器分辨率時,KMS 會更新 CRTC 的配置,使其能夠輸出符合新分辨率要求的圖像信號。

GEM 在這一過程中主要負責顯示 buffer 的分配和管理 。當應用程序需要顯示圖形數(shù)據(jù)時,會向 GEM 請求分配幀緩沖區(qū)。GEM 根據(jù)請求的大小和類型,從 GPU 顯存中分配合適的內(nèi)存空間,并將分配的結(jié)果返回給應用程序。在分配過程中,GEM 會記錄幀緩沖區(qū)的相關(guān)信息,如內(nèi)存地址、大小等,以便后續(xù)的管理和使用。

Encoder 收到 CRTC 輸出的圖像數(shù)據(jù)后,會將其轉(zhuǎn)換成外部設備所需要的信號 。如果是 HDMI 接口的顯示器,encoder 會將圖像數(shù)據(jù)編碼成 HDMI 接口所需的 TMDS 信號,然后通過 connector 將信號傳輸?shù)斤@示器上,最終顯示出圖像,就像一個信號轉(zhuǎn)換專家,將一種信號形式轉(zhuǎn)換為另一種適合傳輸和顯示的信號形式。

2.3 渲染流程

GPU 渲染圖形的流程就像是一場緊張刺激的賽車比賽,渲染任務的提交就像是賽車手進入賽道,GPU 執(zhí)行渲染操作如同賽車在賽道上飛馳,而渲染結(jié)果與顯示流程的協(xié)同則像是賽車比賽中的團隊協(xié)作,各個環(huán)節(jié)緊密配合,才能呈現(xiàn)出精彩的畫面。

當應用程序有渲染任務時,會通過 libdrm 將渲染任務提交給內(nèi)核空間的 DRM 驅(qū)動 。應用程序會構(gòu)建渲染命令流,這個命令流中包含了 GPU 需要執(zhí)行的各種指令,如繪制三角形、設置紋理等,以及相關(guān)的顯存地址等信息。然后,應用程序通過 DRM_IOCTL_EXECBUFFER2 等 ioctl 命令將渲染命令流提交到內(nèi)核。

內(nèi)核中的 DRM 驅(qū)動接收到渲染任務后,會對命令流進行驗證和調(diào)度 。它會檢查命令流的合法性和正確性,確保 GPU 能夠正確執(zhí)行這些指令。然后,DRM 驅(qū)動會將渲染任務調(diào)度到 GPU 硬件隊列中,等待 GPU 執(zhí)行,就像一個交通指揮員,確保渲染任務能夠有序地進入 GPU 執(zhí)行。

GPU 從硬件隊列中獲取渲染任務后,開始執(zhí)行渲染操作 。GPU 擁有強大的并行計算能力,它會按照渲染命令流中的指令,對圖形數(shù)據(jù)進行處理。在渲染 3D 模型時,GPU 會執(zhí)行頂點著色器操作,將 3D 模型的頂點數(shù)據(jù)轉(zhuǎn)換為屏幕坐標系;接著進行光柵化操作,將頂點數(shù)據(jù)轉(zhuǎn)換為片段;然后執(zhí)行片段著色器,確定每個像素的顏色,進行紋理映射、光照計算等操作,最終生成渲染結(jié)果。

渲染結(jié)果生成后,需要與顯示流程協(xié)同,將渲染好的圖像顯示在屏幕上 。通常,渲染結(jié)果會存儲在 GEM 分配的幀緩沖區(qū)中。當 KMS 進行顯示 buffer 切換時,會將包含渲染結(jié)果的幀緩沖區(qū)切換為當前顯示的緩沖區(qū),CRTC 會從這個緩沖區(qū)中讀取圖像數(shù)據(jù)并輸出到顯示器上,實現(xiàn)渲染結(jié)果的顯示。在游戲中,每一幀的渲染結(jié)果都會通過這種方式及時地顯示在屏幕上,讓玩家看到流暢的游戲畫面。

三、拆DRM核心準備工作

3.1 工具準備

在拆解 DRM 核心這場技術(shù)攻堅戰(zhàn)中,選對工具就如同戰(zhàn)士挑選趁手的武器,能讓我們事半功倍。

調(diào)試工具是我們深入探索 DRM 核心運行時狀態(tài)的 “透視鏡”。gdb(GNU Debugger)堪稱其中的佼佼者,它是 Linux 和類 Unix 系統(tǒng)下標準的調(diào)試器 ,支持多種語言和架構(gòu)。在 DRM 核心調(diào)試中,gdb 可以讓我們在 DRM 驅(qū)動代碼運行時,逐行執(zhí)行代碼,設置斷點暫停程序執(zhí)行,檢查變量的值、內(nèi)存狀態(tài)以及寄存器內(nèi)容等,從而深入了解 DRM 核心內(nèi)部的執(zhí)行邏輯,排查可能出現(xiàn)的錯誤。

比如,當 DRM 在分配顯存出現(xiàn)異常時,我們就可以借助 gdb 在相關(guān)代碼處設置斷點,查看顯存分配函數(shù)執(zhí)行過程中參數(shù)的變化以及內(nèi)存的分配情況,找出導致異常的原因。ltrace 則像是一個細心的 “記錄員”,它專注于跟蹤進程調(diào)用庫函數(shù)的情況。在 DRM 核心中,有許多對系統(tǒng)庫函數(shù)的調(diào)用,ltrace 能夠詳細記錄這些調(diào)用的順序、參數(shù)以及返回值,幫助我們了解 DRM 核心與外部庫之間的交互細節(jié),當 DRM 出現(xiàn)與庫函數(shù)相關(guān)的兼容性問題時,ltrace 的記錄就能為我們提供關(guān)鍵線索。

代碼閱讀工具則是我們解開 DRM 核心代碼奧秘的 “鑰匙”。Source Insight 以其強大的代碼瀏覽和分析功能而備受開發(fā)者青睞,它能夠快速索引代碼,方便我們在 DRM 龐大的代碼庫中查找函數(shù)定義、變量聲明以及函數(shù)調(diào)用關(guān)系等,通過其直觀的界面展示,我們可以清晰地看到 DRM 核心各個模塊之間的關(guān)聯(lián),理解代碼的整體結(jié)構(gòu)。VS Code 作為一款廣受歡迎的輕量級代碼編輯器,搭配 C/C++ 插件后,也能成為閱讀 DRM 代碼的得力助手,它支持語法高亮、代碼自動補全、代碼導航等功能,讓我們在閱讀代碼時更加高效,同時還可以通過安裝各種擴展插件,滿足不同的代碼分析需求,如代碼風格檢查、代碼重構(gòu)等,提升我們對 DRM 代碼的理解和修改能力。

3.2 知識儲備

除了工具,豐富的知識儲備也是我們拆解 DRM 核心的必備條件,它就像是構(gòu)建高樓大廈的基石,為我們的探索之旅提供堅實的支撐。

Linux 內(nèi)核機制是理解 DRM 核心運行環(huán)境的基礎。進程管理知識讓我們明白 DRM 核心在系統(tǒng)中是如何作為一個內(nèi)核模塊運行的,它如何與其他進程協(xié)調(diào)資源使用,如何響應系統(tǒng)的調(diào)度。例如,當多個進程同時請求 GPU 資源時,DRM 核心會通過進程管理機制,按照一定的優(yōu)先級和調(diào)度策略,合理地安排每個進程對 GPU 的訪問。內(nèi)存管理知識則至關(guān)重要,DRM 核心需要頻繁地進行顯存的分配、釋放和管理,了解 Linux 內(nèi)存管理機制,包括虛擬內(nèi)存、物理內(nèi)存的映射關(guān)系,內(nèi)存分配算法等,能讓我們深入理解 DRM 核心在顯存管理方面的實現(xiàn)細節(jié),以及如何優(yōu)化顯存的使用效率,避免出現(xiàn)內(nèi)存泄漏、內(nèi)存碎片等問題。

GPU 硬件架構(gòu)知識是我們理解 DRM 核心與硬件交互的橋梁。不同型號的 GPU 有著不同的硬件架構(gòu),如 NVIDIA 的 CUDA 架構(gòu)、AMD 的 GCN 架構(gòu)等,它們在顯存結(jié)構(gòu)、計算單元、指令集等方面都存在差異。了解這些硬件架構(gòu)細節(jié),我們才能明白 DRM 核心是如何根據(jù)不同的 GPU 硬件特性,進行針對性的驅(qū)動開發(fā)和優(yōu)化的。例如,知道了 GPU 顯存的層次結(jié)構(gòu),DRM 核心就能更合理地安排數(shù)據(jù)在不同層次顯存中的存儲,提高數(shù)據(jù)訪問速度;了解了 GPU 的計算單元特性,DRM 核心就能更好地將計算任務分配到合適的計算單元上,充分發(fā)揮 GPU 的計算能力。

C 語言編程知識是我們解讀 DRM 核心代碼的 “語言工具”。DRM 核心主要是用 C 語言編寫的,熟練掌握 C 語言的語法、數(shù)據(jù)結(jié)構(gòu)和算法,是我們讀懂 DRM 核心代碼邏輯的前提。我們需要能夠理解 C 語言中的指針操作、結(jié)構(gòu)體定義、函數(shù)調(diào)用等,才能深入分析 DRM 核心中復雜的數(shù)據(jù)結(jié)構(gòu)和算法實現(xiàn),如 DRM 核心中用于管理 GPU 設備的結(jié)構(gòu)體定義,以及實現(xiàn)顯存分配算法的函數(shù)邏輯等,只有這樣,我們才能在拆解 DRM 核心的過程中,準確把握代碼的意圖,進行有效的分析和修改。

四、DRM 拆解的分層步驟

4.1 進入內(nèi)核驅(qū)動代碼

當我們準備深入探索 DRM 核心時,首先要踏入的就是內(nèi)核驅(qū)動代碼這片 “迷宮” 。對于 NVIDIA GPU 驅(qū)動,其內(nèi)核驅(qū)動代碼路徑會因不同的 Linux 發(fā)行版和安裝方式有所差異。在基于 Debian 或 Ubuntu 的系統(tǒng)中,如果從官方 NVIDIA 倉庫安裝驅(qū)動,其內(nèi)核模塊通常位于/lib/modules/$(uname -r)/updates/dkms/目錄下,而核心驅(qū)動代碼文件可能在/usr/src/nvidia-<version>/中,這里的<version>代表具體的驅(qū)動版本號,例如/usr/src/nvidia-535/。在 Red Hat 或 CentOS 系統(tǒng)中,安裝的內(nèi)核驅(qū)動代碼路徑可能是/usr/src/kernels/$(uname -r)/include/drm/以及/usr/lib64/nvidia/相關(guān)目錄下 ,在這里可以找到與 DRM 核心交互的關(guān)鍵代碼文件,如nvidia_drm.ko對應的源文件。

AMD GPU 驅(qū)動的內(nèi)核驅(qū)動代碼在 Linux 內(nèi)核中相對固定,一般在drivers/gpu/drm/amd/目錄下 。這里面包含了大量與 AMD GPU 硬件交互的代碼文件,像amdgpu_drv.c是 AMDGPU 驅(qū)動的核心文件,負責設備的初始化、注冊以及與 DRM 核心的接口實現(xiàn);amdgpu_gem.c則主要處理顯存管理相關(guān)的功能,在拆解 DRM 核心時,這些文件都是我們重點關(guān)注的對象。

為了定位 DRM 核心代碼文件,我們可以利用 Linux 系統(tǒng)強大的文件搜索工具。find命令就是一個很好的幫手,例如,要在整個系統(tǒng)中查找所有與 DRM 相關(guān)的.c文件,可以使用命令find / -name "*drm*.c" ,它會遞歸地搜索根目錄下所有文件,找出文件名中包含 “drm” 的 C 語言源文件,這樣我們就能快速定位到許多 DRM 核心代碼文件。grep命令也非常實用,當我們知道某個 DRM 相關(guān)的函數(shù)名或關(guān)鍵變量名時,通過grep -r "function_name" /path/to/drm/source/code ,可以在指定的 DRM 源代碼路徑中搜索包含該函數(shù)名或變量名的文件及行號,進一步縮小我們查找核心代碼的范圍,就像在迷宮中找到了關(guān)鍵的線索。

4.2 剖析關(guān)鍵數(shù)據(jù)結(jié)構(gòu)

在 DRM 核心這片神秘領域里,關(guān)鍵數(shù)據(jù)結(jié)構(gòu)就如同構(gòu)建大廈的藍圖,決定著整個系統(tǒng)的架構(gòu)和運行邏輯。

drm_device結(jié)構(gòu)體是管理 GPU 設備的核心數(shù)據(jù)結(jié)構(gòu),它就像是一個設備的 “檔案袋”,包含了設備的各種信息和狀態(tài)。在這個結(jié)構(gòu)體中,有struct device *dev成員,它指向 Linux 內(nèi)核通用的設備結(jié)構(gòu)體,通過這個成員,DRM 設備可以與內(nèi)核的設備管理系統(tǒng)進行交互,獲取設備的基本屬性,如設備名稱、設備類型等;struct drm_minor *first_minor成員則用于管理 DRM 設備的次設備號,在一個系統(tǒng)中可能存在多個 DRM 設備實例,次設備號可以區(qū)分不同的設備實例,確保每個設備都能被正確識別和管理;還有struct drm_driver *driver成員,它指向設備所使用的 DRM 驅(qū)動結(jié)構(gòu)體,通過這個指針,設備可以調(diào)用驅(qū)動中定義的各種操作函數(shù),實現(xiàn)設備的初始化、資源分配、釋放等功能。例如,在設備初始化時,drm_device結(jié)構(gòu)體中的相關(guān)成員會被填充設備的硬件信息,然后通過drm_driver指針調(diào)用驅(qū)動中的初始化函數(shù),完成設備與 DRM 核心的連接和初始化工作。

drm_file結(jié)構(gòu)體主要用于跟蹤文件對象,它在 DRM 核心中扮演著 “連接者” 的角色,連接著用戶空間和內(nèi)核空間。其中struct drm_device *device成員指向?qū)?DRM 設備結(jié)構(gòu)體,表明這個文件對象與哪個 GPU 設備相關(guān)聯(lián);struct list_head open_nodes成員是一個鏈表頭,用于管理所有打開的節(jié)點,當用戶空間的應用程序打開 DRM 設備文件時,會創(chuàng)建一個drm_file結(jié)構(gòu)體實例,并將其添加到這個鏈表中,這樣 DRM 核心就能跟蹤所有當前打開的設備文件,實現(xiàn)對設備訪問的管理和控制,比如限制同時打開設備文件的數(shù)量,防止資源被過度占用。

在顯存管理方面,drm_gem_object結(jié)構(gòu)體是關(guān)鍵所在,它就像是顯存中的 “小管家”,負責管理顯存對象。struct drm_device *dev成員同樣指向所屬的 DRM 設備,確保顯存對象與設備的關(guān)聯(lián)性;struct drm_gem_object *next和struct drm_gem_object *prev成員用于將多個顯存對象鏈接成鏈表,方便進行統(tǒng)一管理和查找;unsigned long size成員記錄了顯存對象的大小,在分配和釋放顯存時,這個信息至關(guān)重要,DRM 核心可以根據(jù)顯存對象的大小來合理分配顯存空間,避免出現(xiàn)內(nèi)存碎片或分配不足的情況,例如在進行大型 3D 游戲場景渲染時,需要分配較大的顯存對象來存儲紋理、模型等數(shù)據(jù),drm_gem_object結(jié)構(gòu)體就會根據(jù)游戲的需求,記錄并管理相應大小的顯存對象。

4.3 跟蹤關(guān)鍵函數(shù)執(zhí)行

在拆解 DRM 核心的過程中,跟蹤關(guān)鍵函數(shù)的執(zhí)行軌跡就像是沿著線索解開謎團,能讓我們深入了解 DRM 核心的運行機制。

drm_open函數(shù)是打開 DRM 設備的關(guān)鍵入口,當用戶空間的應用程序使用open("/dev/drm/card0", O_RDWR)這樣的代碼打開 DRM 設備文件時,內(nèi)核會調(diào)用drm_open函數(shù)。在這個函數(shù)內(nèi)部,首先會根據(jù)傳入的設備文件名,查找對應的drm_device結(jié)構(gòu)體,通過設備號在系統(tǒng)維護的設備列表中定位到目標設備。然后,會創(chuàng)建一個drm_file結(jié)構(gòu)體實例,并將其與找到的drm_device關(guān)聯(lián)起來,填充相關(guān)的文件信息,如文件打開模式等。最后,返回一個文件描述符給用戶空間的應用程序,應用程序就可以通過這個文件描述符對 DRM 設備進行后續(xù)操作,比如發(fā)送控制命令、讀取設備狀態(tài)等。

drm_mode_setcrtc函數(shù)在設置顯示模式時起著關(guān)鍵作用,當我們需要調(diào)整顯示器的分辨率、刷新率等參數(shù)時,就會調(diào)用這個函數(shù)。假設我們要將顯示器分辨率設置為 1920x1080,刷新率設置為 60Hz,應用程序會構(gòu)建一個包含這些參數(shù)的結(jié)構(gòu)體,并將其作為參數(shù)傳遞給drm_mode_setcrtc函數(shù)。在函數(shù)內(nèi)部,會首先檢查傳入?yún)?shù)的合法性,確保分辨率和刷新率等參數(shù)在顯示器支持的范圍內(nèi)。然后,根據(jù)這些參數(shù)計算出相應的時鐘頻率、同步信號參數(shù)等,并將這些參數(shù)寫入到顯示器對應的寄存器中,通過控制寄存器的設置來調(diào)整顯示器的工作模式,實現(xiàn)顯示模式的切換。

drm_gem_create_object函數(shù)則負責創(chuàng)建顯存對象,以滿足圖形渲染等任務對顯存的需求。當一個 3D 游戲需要為新的場景模型分配顯存時,就會調(diào)用這個函數(shù)。函數(shù)會首先根據(jù)傳入的顯存大小參數(shù),在系統(tǒng)的顯存管理池中查找合適的空閑顯存塊。如果找到足夠大小的空閑塊,就會創(chuàng)建一個drm_gem_object結(jié)構(gòu)體實例,將其與找到的顯存塊關(guān)聯(lián)起來,并填充相關(guān)的屬性信息,如顯存對象的大小、使用狀態(tài)等。最后,返回創(chuàng)建好的顯存對象指針給調(diào)用者,調(diào)用者就可以通過這個指針來操作和使用分配到的顯存,將游戲場景模型數(shù)據(jù)存儲到這塊顯存中,為后續(xù)的圖形渲染做好準備。

五、DRM GPU 驅(qū)動框架實戰(zhàn)案例

在 Linux 桌面系統(tǒng)中,DRM GPU 驅(qū)動框架發(fā)揮著至關(guān)重要的作用,為用戶帶來了豐富而流暢的圖形體驗。以常見的 GNOME 桌面環(huán)境為例,它基于 Wayland 顯示服務器協(xié)議,與 DRM GPU 驅(qū)動框架緊密協(xié)作。在窗口管理方面,DRM GPU 驅(qū)動框架提供了高效的圖形處理能力,使得 GNOME 桌面能夠快速響應窗口的創(chuàng)建、移動、縮放等操作。當用戶打開多個應用程序窗口時,DRM 能夠合理分配 GPU 資源,確保每個窗口都能流暢地顯示內(nèi)容,不會出現(xiàn)卡頓或閃爍的情況。

在圖形特效方面,DRM GPU 驅(qū)動框架的硬件加速功能為 GNOME 桌面的各種特效提供了強大支持。像窗口的透明效果、動畫過渡效果等,都需要大量的圖形計算資源。DRM 通過充分利用 GPU 的并行計算能力,能夠快速渲染這些特效,讓用戶感受到更加美觀和流暢的桌面交互體驗。當用戶最小化或最大化窗口時,窗口的動畫過渡效果能夠平滑地呈現(xiàn),給人一種視覺上的享受。

對于多顯示器輸出,DRM GPU 驅(qū)動框架也表現(xiàn)出色。它能夠輕松識別和管理多個顯示器,實現(xiàn)不同的顯示模式,如擴展模式、復制模式等。在擴展模式下,用戶可以將不同的應用程序窗口分別顯示在不同的顯示器上,大大提高了工作效率。比如,一位程序員可以在一個顯示器上編寫代碼,在另一個顯示器上查看文檔或運行調(diào)試程序,使得工作更加便捷高效。在復制模式下,用戶可以將相同的內(nèi)容同時顯示在多個顯示器上,適用于演示、教學等場景。

DRM GPU 驅(qū)動框架在Linux 桌面系統(tǒng)中的應用代碼示例如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

// 簡化的DRM數(shù)據(jù)結(jié)構(gòu)
struct drm_device {
    int fd;                     // DRM設備文件描述符
    char *device_path;          // 設備路徑
    int crtc_count;             // CRTC數(shù)量
    int connector_count;        // 連接器數(shù)量
    int plane_count;            // Plane數(shù)量
};

// 簡化的顯示器結(jié)構(gòu)體
struct display {
    int id;                     // 顯示器ID
    char *name;                 // 顯示器名稱
    int width;                  // 寬度
    int height;                 // 高度
    int refresh_rate;           // 刷新率
    int x;                      // 位置X坐標
    int y;                      // 位置Y坐標
};

// 簡化的窗口結(jié)構(gòu)體
struct window {
    int id;                     // 窗口ID
    char *title;                // 窗口標題
    int x;                      // 位置X坐標
    int y;                      // 位置Y坐標
    int width;                  // 寬度
    int height;                 // 高度
    int z_order;                // Z軸順序
    float opacity;              // 透明度
    int display_id;             // 顯示的顯示器ID
};

// 簡化的DRM compositor結(jié)構(gòu)體
struct drm_compositor {
    struct drm_device *drm_dev; // DRM設備
    struct display *displays;   // 顯示器列表
    int display_count;          // 顯示器數(shù)量
    struct window *windows;     // 窗口列表
    int window_count;           // 窗口數(shù)量
};

// 模擬打開DRM設備
struct drm_device* drm_open_device(const char *path) {
    printf("[DRM] 打開DRM設備: %s\n", path);

    struct drm_device *dev = malloc(sizeof(struct drm_device));
    if (!dev) {
        printf("[DRM] 內(nèi)存分配失敗\n");
        return NULL;
    }

    dev->device_path = strdup(path);
    dev->fd = 3; // 模擬文件描述符
    dev->crtc_count = 4;
    dev->connector_count = 4;
    dev->plane_count = 8;

    printf("[DRM] 成功打開DRM設備,CRTC: %d, 連接器: %d, Plane: %d\n",
           dev->crtc_count, dev->connector_count, dev->plane_count);

    return dev;
}

// 模擬檢測顯示器
int drm_detect_displays(struct drm_compositor *compositor) {
    printf("\n[DRM] 檢測顯示器...\n");

    // 模擬檢測到兩個顯示器
    compositor->display_count = 2;
    compositor->displays = malloc(sizeof(struct display) * compositor->display_count);

    // 第一個顯示器 (主顯示器)
    compositor->displays[0].id = 0;
    compositor->displays[0].name = "HDMI-1";
    compositor->displays[0].width = 1920;
    compositor->displays[0].height = 1080;
    compositor->displays[0].refresh_rate = 60;
    compositor->displays[0].x = 0;
    compositor->displays[0].y = 0;

    // 第二個顯示器 (擴展顯示器)
    compositor->displays[1].id = 1;
    compositor->displays[1].name = "DP-1";
    compositor->displays[1].width = 1920;
    compositor->displays[1].height = 1080;
    compositor->displays[1].refresh_rate = 60;
    compositor->displays[1].x = 1920;
    compositor->displays[1].y = 0;

    printf("[DRM] 檢測到 %d 個顯示器:\n", compositor->display_count);
    for (int i = 0; i < compositor->display_count; i++) {
        struct display *disp = &compositor->displays[i];
        printf("  顯示器 %d: %s, 分辨率: %dx%d@%dHz, 位置: (%d, %d)\n",
               disp->id, disp->name, disp->width, disp->height, 
               disp->refresh_rate, disp->x, disp->y);
    }

    return compositor->display_count;
}

// 模擬初始化KMS
int drm_init_kms(struct drm_device *dev) {
    printf("\n[DRM] 初始化KMS...\n");

    // 模擬設置顯示模式
    printf("[DRM] 設置CRTC模式...\n");
    printf("[DRM] 初始化Plane硬件圖層...\n");
    printf("[DRM] 配置顯示管道...\n");
    printf("[DRM] KMS初始化完成\n");

    return 0;
}

// 模擬創(chuàng)建窗口
struct window* drm_create_window(struct drm_compositor *compositor, 
                               const char *title, int x, int y, 
                               int width, int height, int display_id) {
    printf("\n[窗口管理] 創(chuàng)建窗口: %s\n", title);

    struct window *win = malloc(sizeof(struct window));
    if (!win) {
        printf("[窗口管理] 內(nèi)存分配失敗\n");
        return NULL;
    }

    win->id = compositor->window_count++;
    win->title = strdup(title);
    win->x = x;
    win->y = y;
    win->width = width;
    win->height = height;
    win->z_order = compositor->window_count;
    win->opacity = 1.0f;
    win->display_id = display_id;

    // 重新分配窗口列表
    compositor->windows = realloc(compositor->windows, 
                                 sizeof(struct window) * compositor->window_count);
    compositor->windows[compositor->window_count - 1] = *win;

    printf("[窗口管理] 窗口 %d 創(chuàng)建成功: %s (%dx%d) 在顯示器 %d\n",
           win->id, win->title, win->width, win->height, win->display_id);

    return win;
}

// 模擬移動窗口
int drm_move_window(struct drm_compositor *compositor, int window_id, int x, int y) {
    printf("\n[窗口管理] 移動窗口 %d 到位置 (%d, %d)\n", window_id, x, y);

    for (int i = 0; i < compositor->window_count; i++) {
        if (compositor->windows[i].id == window_id) {
            struct window *win = &compositor->windows[i];

            // 檢查是否跨顯示器移動
            int old_display_id = win->display_id;
            int new_display_id = -1;

            // 查找新位置所在的顯示器
            for (int j = 0; j < compositor->display_count; j++) {
                struct display *disp = &compositor->displays[j];
                if (x >= disp->x && x < disp->x + disp->width &&
                    y >= disp->y && y < disp->y + disp->height) {
                    new_display_id = j;
                    break;
                }
            }

            // 更新窗口位置
            win->x = x;
            win->y = y;

            // 如果跨顯示器移動,更新顯示器ID
            if (new_display_id != -1 && new_display_id != old_display_id) {
                win->display_id = new_display_id;
                printf("[窗口管理] 窗口 %d 移動到顯示器 %d (%s)\n",
                       window_id, new_display_id, 
                       compositor->displays[new_display_id].name);
            }

            printf("[窗口管理] 窗口 %d 移動完成\n", window_id);
            return 0;
        }
    }

    printf("[窗口管理] 未找到窗口 %d\n", window_id);
    return -1;
}

// 模擬設置窗口透明度
int drm_set_window_opacity(struct drm_compositor *compositor, int window_id, float opacity) {
    printf("\n[圖形特效] 設置窗口 %d 透明度為 %.2f\n", window_id, opacity);

    for (int i = 0; i < compositor->window_count; i++) {
        if (compositor->windows[i].id == window_id) {
            compositor->windows[i].opacity = opacity;

            // 模擬硬件加速的透明度設置
            printf("[圖形特效] 使用DRM Plane alpha混合功能\n");
            printf("[圖形特效] 窗口透明度設置完成\n");
            return 0;
        }
    }

    printf("[圖形特效] 未找到窗口 %d\n", window_id);
    return -1;
}

// 模擬窗口動畫效果
int drm_animate_window(struct drm_compositor *compositor, int window_id, 
                      int target_x, int target_y, int duration) {
    printf("\n[圖形特效] 窗口 %d 動畫: 從當前位置移動到 (%d, %d),持續(xù) %d 毫秒\n",
           window_id, target_x, target_y, duration);

    for (int i = 0; i < compositor->window_count; i++) {
        if (compositor->windows[i].id == window_id) {
            struct window *win = &compositor->windows[i];

            int start_x = win->x;
            int start_y = win->y;
            int steps = 20; // 動畫步數(shù)
            int step_delay = duration / steps;

            printf("[圖形特效] 開始窗口動畫...\n");

            // 模擬動畫幀
            for (int j = 0; j <= steps; j++) {
                float progress = (float)j / steps;
                win->x = start_x + (target_x - start_x) * progress;
                win->y = start_y + (target_y - start_y) * progress;

                // 模擬VSYNC等待
                usleep(step_delay * 1000);

                // 模擬頁面翻轉(zhuǎn)
                if (j % 5 == 0) { // 每5步顯示一次進度
                    printf("[圖形特效] 動畫進度: %.0f%%, 位置: (%d, %d)\n",
                           progress * 100, win->x, win->y);
                }
            }

            printf("[圖形特效] 窗口動畫完成\n");
            return 0;
        }
    }

    printf("[圖形特效] 未找到窗口 %d\n", window_id);
    return -1;
}

// 模擬設置多顯示器模式
int drm_set_display_mode(struct drm_compositor *compositor, const char *mode) {
    printf("\n[多顯示器] 設置顯示模式: %s\n", mode);

    if (strcmp(mode, "extend") == 0) {
        // 擴展模式
        compositor->displays[0].x = 0;
        compositor->displays[0].y = 0;
        compositor->displays[1].x = 1920;
        compositor->displays[1].y = 0;

        printf("[多顯示器] 設置為擴展模式:\n");
        printf("  顯示器 0: %s, 位置: (0, 0)\n", compositor->displays[0].name);
        printf("  顯示器 1: %s, 位置: (1920, 0)\n", compositor->displays[1].name);

    } else if (strcmp(mode, "mirror") == 0) {
        // 復制模式
        compositor->displays[0].x = 0;
        compositor->displays[0].y = 0;
        compositor->displays[1].x = 0;
        compositor->displays[1].y = 0;

        printf("[多顯示器] 設置為復制模式:\n");
        printf("  兩個顯示器顯示相同內(nèi)容\n");

    } else if (strcmp(mode, "single") == 0) {
        // 單顯示器模式
        compositor->displays[0].x = 0;
        compositor->displays[0].y = 0;
        compositor->displays[1].x = -1920; // 移出屏幕
        compositor->displays[1].y = 0;

        printf("[多顯示器] 設置為單顯示器模式:\n");
        printf("  僅使用顯示器 0: %s\n", compositor->displays[0].name);

    } else {
        printf("[多顯示器] 未知模式: %s\n", mode);
        return -1;
    }

    // 模擬DRM模式設置
    printf("[多顯示器] 使用drmModeSetCrtc配置顯示模式\n");
    printf("[多顯示器] 模式設置完成\n");

    return 0;
}

// 模擬合成一幀畫面
int drm_composite_frame(struct drm_compositor *compositor) {
    static int frame_count = 0;

    printf("\n[合成器] 合成第 %d 幀畫面\n", ++frame_count);

    // 模擬收集所有窗口
    printf("[合成器] 收集 %d 個窗口\n", compositor->window_count);

    // 模擬按Z軸順序排序窗口
    printf("[合成器] 按Z軸順序排序窗口\n");

    // 模擬渲染每個窗口
    for (int i = 0; i < compositor->window_count; i++) {
        struct window *win = &compositor->windows[i];
        struct display *disp = &compositor->displays[win->display_id];

        printf("[合成器] 渲染窗口 %d: %s, 位置: (%d, %d), 透明度: %.2f, 顯示器: %s\n",
               win->id, win->title, win->x - disp->x, win->y - disp->y, 
               win->opacity, disp->name);
    }

    // 模擬頁面翻轉(zhuǎn)
    printf("[合成器] 使用DRM頁面翻轉(zhuǎn)機制更新顯示\n");
    printf("[合成器] 幀合成完成\n");

    return 0;
}

// 模擬DRM compositor初始化
struct drm_compositor* drm_compositor_init() {
    printf("=== DRM GPU驅(qū)動框架桌面應用示例 ===\n\n");

    struct drm_compositor *compositor = malloc(sizeof(struct drm_compositor));
    if (!compositor) {
        printf("內(nèi)存分配失敗\n");
        return NULL;
    }

    memset(compositor, 0, sizeof(struct drm_compositor));

    // 1. 打開DRM設備
    compositor->drm_dev = drm_open_device("/dev/dri/card0");
    if (!compositor->drm_dev) {
        free(compositor);
        return NULL;
    }

    // 2. 初始化KMS
    if (drm_init_kms(compositor->drm_dev) != 0) {
        drm_compositor_destroy(compositor);
        return NULL;
    }

    // 3. 檢測顯示器
    if (drm_detect_displays(compositor) == 0) {
        printf("未檢測到顯示器\n");
        drm_compositor_destroy(compositor);
        return NULL;
    }

    printf("\n[合成器] DRM compositor初始化完成\n");
    return compositor;
}

// 模擬DRM compositor銷毀
void drm_compositor_destroy(struct drm_compositor *compositor) {
    if (!compositor) return;

    printf("\n=== 清理資源 ===\n");

    // 釋放窗口
    for (int i = 0; i < compositor->window_count; i++) {
        free(compositor->windows[i].title);
    }
    free(compositor->windows);

    // 釋放顯示器
    for (int i = 0; i < compositor->display_count; i++) {
        free(compositor->displays[i].name);
    }
    free(compositor->displays);

    // 關(guān)閉DRM設備
    if (compositor->drm_dev) {
        printf("[DRM] 關(guān)閉DRM設備: %s\n", compositor->drm_dev->device_path);
        free(compositor->drm_dev->device_path);
        free(compositor->drm_dev);
    }

    free(compositor);
    printf("資源清理完成\n");
}

// 主函數(shù) - 模擬DRM桌面應用
int main() {
    struct drm_compositor *compositor;
    struct window *win1, *win2, *win3;

    // 初始化DRM compositor
    compositor = drm_compositor_init();
    if (!compositor) {
        printf("初始化失敗\n");
        return -1;
    }

    // 1. 窗口管理示例
    printf("\n=== 窗口管理示例 ===\n");

    // 創(chuàng)建窗口
    win1 = drm_create_window(compositor, "終端", 100, 100, 800, 600, 0);
    win2 = drm_create_window(compositor, "瀏覽器", 200, 200, 1024, 768, 0);
    win3 = drm_create_window(compositor, "文檔編輯器", 300, 300, 900, 600, 1);

    // 合成幀
    drm_composite_frame(compositor);

    // 移動窗口
    drm_move_window(compositor, win2->id, 400, 300);

    // 合成幀
    drm_composite_frame(compositor);

    // 2. 圖形特效示例
    printf("\n=== 圖形特效示例 ===\n");

    // 設置窗口透明度
    drm_set_window_opacity(compositor, win1->id, 0.8f);

    // 窗口動畫
    drm_animate_window(compositor, win3->id, 500, 200, 1000);

    // 合成幀
    drm_composite_frame(compositor);

    // 3. 多顯示器示例
    printf("\n=== 多顯示器示例 ===\n");

    // 設置擴展模式
    drm_set_display_mode(compositor, "extend");

    // 將窗口移動到第二個顯示器
    drm_move_window(compositor, win2->id, 2000, 100);

    // 合成幀
    drm_composite_frame(compositor);

    // 設置復制模式
    drm_set_display_mode(compositor, "mirror");

    // 合成幀
    drm_composite_frame(compositor);

    // 4. 性能測試
    printf("\n=== 性能測試 ===\n");

    // 模擬10幀合成
    printf("模擬10幀合成...\n");
    for (int i = 0; i < 10; i++) {
        drm_composite_frame(compositor);
        usleep(16666); // 約60fps
    }

    // 清理資源
    drm_compositor_destroy(compositor);

    printf("\n=== 示例完成 ===\n");

    return 0;
}
  1. 窗口管理:創(chuàng)建、移動和管理多個應用窗口,支持窗口 Z 軸排序和跨顯示器移動。
  2. 圖形特效:實現(xiàn)窗口透明度設置和動畫效果,展示了 DRM 硬件加速的圖形渲染能力。
  3. 多顯示器輸出:支持擴展模式、復制模式和單顯示器模式,展示了 DRM 對多顯示器的管理能力。
責任編輯:武曉燕 來源: 深度Linux
相關(guān)推薦

2024-08-16 18:42:23

2023-03-23 16:02:07

樹莓派4GPU調(diào)試

2025-11-07 04:00:00

2013-11-14 16:50:08

2024-01-09 08:24:47

JMM核心線程

2009-05-14 18:33:33

intelNehalem服務器

2021-06-10 10:39:14

Linux 5.14驅(qū)動程序Hyper-V DRM

2019-07-22 15:37:56

CPU核心GPU

2021-09-19 10:49:04

LinuxAI處理單元DRM

2024-05-09 09:59:09

Elasticsea搜索數(shù)據(jù)

2011-05-17 14:01:46

DRMGameloftAndroid

2023-03-06 16:11:00

設備移植開源GPU驅(qū)動

2025-08-05 01:15:00

DSLANTLR 4語言

2019-06-11 09:02:22

2023-03-23 08:37:23

Linux

2015-06-03 14:40:04

大數(shù)據(jù)數(shù)據(jù)挖掘

2025-05-26 09:05:00

2023-09-01 18:18:32

2021-01-27 05:19:41

Mycat模塊t中間件
點贊
收藏

51CTO技術(shù)棧公眾號

国产精品一区二区无线| 久久99视频| 亚洲午夜激情av| 精品久久久久亚洲| 久久国产香蕉视频| 精品久久sese| www.午夜色| 国产视频第一页| 亚洲欧洲午夜| 亚洲天堂av女优| 涩多多在线观看| 欧美调教sm| 国产精品久线观看视频| 国产精品区一区二区三含羞草| 黄色在线免费观看| 忘忧草精品久久久久久久高清| 精品电影一区二区| 色悠悠久久综合网| √天堂8资源中文在线| 国产三级一区二区| 国产精品免费一区二区| 亚洲天堂中文网| 国产日本精品| www.欧美三级电影.com| aa一级黄色片| 国产精品毛片无码| 色婷婷国产精品综合在线观看| 日本免费黄色小视频| 可以免费看污视频的网站在线| 国产精品一级二级三级| 国产精品视频中文字幕91| 国产精品第九页| 亚洲精品久久| 伊人久久久久久久久久| 精品无码国产一区二区三区51安| 伊人亚洲精品| 欧美日韩视频在线一区二区| 日韩av在线播放不卡| 动漫一区在线| 国产精品狼人久久影院观看方式| 欧美大香线蕉线伊人久久国产精品| 国产免费叼嘿网站免费| 免费高清在线一区| 欧美在线播放视频| 天天操天天干视频| 亚洲国产国产亚洲一二三| 久久视频国产精品免费视频在线 | 日日夜夜天天综合入口| 国产精品久久久久三级| 视频一区二区在线| 久久av少妇| 久久蜜桃一区二区| 久久综合九色99| 污污视频在线免费看| 精品亚洲国内自在自线福利| 国产精品免费一区二区三区都可以| 亚洲欧美在线观看视频| 日韩一级不卡| 91国内在线视频| 免费看日韩毛片| 国产情侣久久| 国产不卡av在线免费观看| 男女视频免费看| 免费看黄裸体一级大秀欧美| 8x海外华人永久免费日韩内陆视频| 久久草视频在线| 亚洲黄色在线| 国产91精品不卡视频| 国产免费av一区| 久久看片网站| 国产精品成人久久久久| 中文字幕视频在线播放| 极品美女销魂一区二区三区| 91天堂在线视频| 成人久久久精品国产乱码一区二区 | 精品视频免费在线播放| 国产精品vvv| 色综合天天性综合| 久久精品影视大全| av在线国产精品| 精品国产欧美一区二区| 亚洲国产精品成人综合久久久| 亚洲综合小说图片| 日韩中文在线不卡| 久久精品www人人爽人人| 亚洲国内欧美| 国产精品久久久久久久av大片 | 欧美成欧美va| 日韩天堂av| 国产精品久久不能| 国产高清第一页| 97精品国产97久久久久久久久久久久| 欧美日韩喷水| www在线观看播放免费视频日本| 亚洲一区在线观看免费观看电影高清| 久久国产成人精品国产成人亚洲| av在线不卡精品| 日韩一区二区高清| 少妇精品一区二区| 国产精品国产三级国产在线观看| 久久久久久久久久婷婷| 亚洲av无码不卡| 国产成人精品亚洲日本在线桃色 | 日韩欧美在线中字| 久久久久久综合网天天| 自拍偷拍福利视频| 成人一区二区三区| 亚洲福利av| 电影在线观看一区| 欧美精品 国产精品| 中文字幕 日本| 亚洲综合自拍| 国产成人精品国内自产拍免费看| 99久久精品国产一区色| 91亚洲精品乱码久久久久久蜜桃| 婷婷视频在线播放| 91av亚洲| 亚洲精品在线免费播放| 欧美激情精品久久久久久免费 | 禁断一区二区三区在线| 久久久久国色av免费观看性色 | 亚洲欧美日韩精品久久久 | 国产经典自拍视频在线观看| 一区二区三区蜜桃网| 亚洲一区二区三区四区五区xx| 哺乳一区二区三区中文视频| 日韩亚洲欧美成人| 黄色污污网站在线观看| a亚洲天堂av| 黄黄视频在线观看| 欧美啪啪网站| 亚洲天堂第二页| 婷婷激情五月网| 成人精品视频一区二区三区 | 天天躁日日躁aaaa视频| 精品动漫3d一区二区三区免费| 成人激情春色网| 999国产在线视频| 色成年激情久久综合| 亚洲国产精品无码久久久久高潮 | 欧美激情资源网| 免费av网址在线| 精品福利一区| 欧美精品久久久久久久久| 国产精品久久久久精| 国产精品久久久久一区| 我看黄色一级片| 成人综合久久| 国产精品网站大全| av在线免费观看网| 欧美少妇一区二区| 欧洲性xxxx| 毛片av中文字幕一区二区| 日韩av在线电影观看| 姬川优奈av一区二区在线电影| 亚洲精品综合精品自拍| www.国产一区二区| 国产视频一区二区在线| 99久久国产宗和精品1上映| 伊人久久大香线蕉av不卡| 日韩美女在线看| 国产日本在线| 欧美日本韩国一区二区三区视频 | 性做久久久久久久免费看| 中文字幕在线视频播放| 国产一区二区三区的电影| 久久99精品国产99久久| 欧美自拍电影| 中日韩午夜理伦电影免费| 亚洲视频中文字幕在线观看| 最好看的中文字幕久久| gogo亚洲国模私拍人体| 影音先锋久久久| 精品国产一区二区三区麻豆小说 | 亚洲欧洲一区二区| 久久九九精品视频| 国内精品视频一区| 黄色软件在线观看| 欧美裸体bbwbbwbbw| 久久久精品人妻一区二区三区四| av午夜一区麻豆| 久久久久免费精品| 欧美日本一区二区视频在线观看| 国产在线一区二区三区四区| 粉嫩一区二区三区| 欧美人在线视频| 国产最新视频在线| 337p亚洲精品色噜噜狠狠| 国产精品99无码一区二区| 久久先锋影音av| 欧洲美女亚洲激情| 国产精品三上| av电影一区二区三区| 六月丁香久久丫| 国产热re99久久6国产精品| 日本高清成人vr专区| 亚洲欧美三级在线| 国产婷婷在线视频| 日韩欧美亚洲成人| 青青草在线观看视频| 国产喂奶挤奶一区二区三区| 日本一本在线视频| 日韩高清在线电影| 欧美国产日韩激情| 久久电影院7| 韩国一区二区三区美女美女秀| 99久久精品一区二区成人| 久久久久日韩精品久久久男男| 高清在线观看av| 精品国产百合女同互慰| 在线视频免费观看一区| 精品成人av一区| 麻豆精品一区二区三区视频| 国产欧美精品区一区二区三区 | 国产综合久久久| 国产伦理久久久| 四虎精品一区二区免费| 日本aⅴ大伊香蕉精品视频| 日本在线观看高清完整版| 中文字幕精品网| 手机看片1024日韩| 日韩一级精品视频在线观看| 在线免费观看av片| 91成人国产精品| 亚洲精品视频在线观看免费视频| 中文字幕亚洲一区二区av在线| 日韩人妻一区二区三区| 成人av先锋影音| 免费黄视频在线观看| 激情综合网天天干| av污在线观看| 日韩电影在线免费看| 久久精品国产精品亚洲色婷婷| 欧美特黄一区| 2022中文字幕| 亚洲欧美亚洲| 永久免费网站视频在线观看| 91精品精品| 色撸撸在线观看| 国产韩国精品一区二区三区| 亚洲精品中文字幕乱码三区不卡| 免费av一区二区三区四区| 鲁鲁狠狠狠7777一区二区| 美女午夜精品| 国产一区二区不卡视频在线观看| 一区二区日韩| 国产精品裸体一区二区三区| 国产精品115| 国产一区福利视频| 日韩欧美ww| 免费日韩av电影| 国产成人3p视频免费观看| 免费看国产精品一二区视频| 国产在线观看91一区二区三区| 日韩少妇中文字幕| 波多野结衣一区| 五月天色婷婷综合| 亚洲欧美文学| 伊人成色综合网| 久久精品人人做人人爽电影蜜月| 日韩av在线综合| 麻豆精品精品国产自在97香蕉| 天天干天天玩天天操| 国产最新精品免费| 中文字幕一区二区三区人妻在线视频 | 天天色综合色| a级黄色片免费| 亚洲看片一区| 欧美精品无码一区二区三区| 另类综合日韩欧美亚洲| 美女被艹视频网站| 成人午夜又粗又硬又大| 网站免费在线观看| 亚洲国产精品传媒在线观看| 亚洲xxxx3d动漫| 懂色av中文一区二区三区天美| 久久久精品毛片| 在线成人高清不卡| 日本人妻熟妇久久久久久| 亚洲美女动态图120秒| 日本高清在线观看wwwww色| 米奇精品一区二区三区在线观看| av资源在线看片| 国产精品露脸av在线| 久久久精品区| 久久精品日产第一区二区三区精品版 | 日韩精品一区二区三区电影| 日韩午夜av| 中文字幕在线综合| 成人av在线资源网站| 亚洲综合欧美综合| 一区二区三区视频在线看| 五月婷婷视频在线| 91精品国产综合久久久久久久久久| 隣の若妻さん波多野结衣| 国产午夜精品全部视频在线播放| 超碰最新在线| 日韩美女视频免费看| 视频一区视频二区欧美| 日本一区二区三不卡| 中文在线播放一区二区| 熟女性饥渴一区二区三区| 国产一区二区三区蝌蚪| 欧美图片第一页| 亚洲一区二区三区四区在线免费观看| 天干夜夜爽爽日日日日| 日韩视频一区在线观看| 888av在线| 欧美中文字幕精品| 91精品导航| 一区二区三区不卡在线| 久久高清免费观看| 极品白嫩少妇无套内谢| 国产精品不卡在线| 免费av中文字幕| 国产视频精品xxxx| 丰满诱人av在线播放| 91九色视频在线| 成人免费a**址| 国产精品wwwww| 99国产精品一区| 激情四射综合网| 69堂成人精品免费视频| 国产美女视频一区二区三区 | 中文字幕亚洲情99在线| 欧美日韩国产观看视频| 国产精品白丝jk白祙| 欧美激情五月| 久久久精品视频国产| 国产精品久久久久久久久搜平片| 天堂а√在线中文在线新版| 亚洲国产精品久久91精品| 欧美黄色视屏| 亚洲已满18点击进入在线看片| 久久综合av| 亚洲人辣妹窥探嘘嘘| 国产欧美一区二区在线观看| 特黄视频免费看| 亚洲美女在线视频| 日本黄色免费在线| 精品日产一区2区三区黄免费| 极品中文字幕一区| 波多野吉衣在线视频| 一区二区三区毛片| 国产成人三级一区二区在线观看一 | 性色av蜜臀av色欲av| 精品日本高清在线播放| 天堂在线视频观看| 欧美亚洲国产视频小说| 任我爽精品视频在线播放| 国产二级片在线观看| 97se亚洲国产综合自在线观| 91porny在线| 亚洲久久久久久久久久| 欧美日韩精品一区二区三区视频| 欧美一区二区综合| 日本不卡免费在线视频| 国产黄a三级三级| 91精品久久久久久久99蜜桃| 超碰在线最新| 国偷自产av一区二区三区小尤奈| 亚洲国产91| 国产成人av一区二区三区不卡| 在线一区二区观看| 日本中文字幕在线看| 亚洲一区二区三| 一区免费在线| 丰满大乳奶做爰ⅹxx视频| 欧美亚洲愉拍一区二区| 国产黄色在线网站| 国内不卡一区二区三区| 久久欧美肥婆一二区| 欧美a级片免费看| 日韩精品一区二区三区在线观看| 国产v日韩v欧美v| 日本成人三级电影网站| 久久 天天综合| 国产极品美女高潮无套嗷嗷叫酒店| 日韩精品在线视频| 99热播精品免费| 亚洲理论电影在线观看| 久久亚洲二区三区| 一级α片免费看刺激高潮视频| 久久99热这里只有精品国产| 婷婷亚洲精品| www.com污| 午夜精品久久久| 91社区在线观看播放| www 成人av com| 美女被久久久| 午夜69成人做爰视频| 亚洲女人天堂视频| 国产高清亚洲| 69堂免费视频| 亚洲精品少妇30p| 撸视在线观看免费视频| 99视频免费观看| 日韩国产精品久久久久久亚洲| 欧美国产在线看| 一区三区二区视频|