Bootstrap

display: mesa: eglapi接口:getdisplay&initialize

SPEC:https://www.khronos.org/registry/EGL/specs/eglspec.1.5.pdfhttps://www.khronos.org/registry/EGL/specs/eglspec.1.5.pdf

一共45条:

eglGetDisplay

/**
 * This is typically the first EGL function that an application calls.
 * It associates a private _EGLDisplay object to the native display.
 */
如注释,这是第一个被调用的egl函数;返回一个_EGLDisplay 指针disp

EGLDisplay EGLAPIENTRY
eglGetDisplay(EGLNativeDisplayType nativeDisplay)    
//这个nativeDisplay,在wayland client就是wl_display,而在weston(server)则是gbm_device
{
   ...
   plat = _eglGetNativePlatform(native_display_ptr); //native_display_ptr是nativeDisplay指针。
       通过传入的nativeDisplay的指针,再判断了env的native环境以后,返回egl_platforms
       _EGLPlatformType detected_platform = _eglGetNativePlatformFromEnv();//通过环境变量getenv(EGL_PLATFORM)和getenv(EGL_DISPLAY)确认 _EGLPlatformType;
       
       detected_platform = _eglNativePlatformDetectNativeDisplay(nativeDisplay);//如果没有搜索到,则再进行如下判断,也就是通过传过来的wl_display进行判断
       还没有的话,就会返回失败。disp->Platform会返回_EGL_PLATFORM_WAYLAND或者_EGL_PLATFORM_DRM
       //所以最终在我们的wayland/weston下,client会返回_EGL_PLATFORM_WAYLAND而server会返回_EGL_PLATFORM_DRM

       * Find the display corresponding to the specified native display, or create a new one.
       这里比较严苛,要保证attrib_list完全一样,才会返回一个已经存在的disp,否则都按新的创建!
       第一个plat是上一个函数得到的,第二个是wl_display指针,第三个attrib_list为NULL
   disp = _eglFindDisplay(plat, native_display_ptr, NULL);

       我们这最终创建一个新的_EGLDisplay,它的Platform指向plat,它的PlatformDisplay指向wl_display或者gbm device,为它申请attrib_list大小的Options.Attribs参数;
           最后加到这个_eglGlobal.DisplayList里面 
           /* add to the display list */
           disp->Next = _eglGlobal.DisplayList;
           _eglGlobal.DisplayList = disp;

   return _eglGetDisplayHandle(disp);  实际实现如下,返回disp地址
        return (EGLDisplay) ((disp) ? disp : EGL_NO_DISPLAY);
}

我们这最终创建一个新的_EGLDisplay,
它的Platform指向plat,client是_EGL_PLATFORM_WAYLAND而server是_EGL_PLATFORM_DRM,
它的PlatformDisplay指向wl_display或者gbm device,
为它申请attrib_list大小的Options.Attribs参数;
最后加到这个_eglGlobal.DisplayList里面 



static const struct {
   _EGLPlatformType platform;
   const char *name;
} egl_platforms[] = {
   { _EGL_PLATFORM_X11, "x11" },
   { _EGL_PLATFORM_XCB, "xcb" },
   { _EGL_PLATFORM_WAYLAND, "wayland" },
   { _EGL_PLATFORM_DRM, "drm" },
   { _EGL_PLATFORM_ANDROID, "android" },
   { _EGL_PLATFORM_HAIKU, "haiku" },
   { _EGL_PLATFORM_SURFACELESS, "surfaceless" },
   { _EGL_PLATFORM_DEVICE, "device" },
};

总体上看很简单,通过上层传递的wl_display或gbm_device这种 native window,来创建对应的_EGLDisplay对象,通过Platform和PlatformDisplay指定native和本地type。

eglInitialize

/**
 * This is typically the second EGL function that an application calls.
 * Here we load/initialize the actual hardware driver.
 */

初始化一个EGL display connection,如注释,是eglgetdisplay之后紧接着调用的函数。并且是真正初始化硬件驱动的部分,会把整个链路串起来,最终到winsys。
EGLBoolean EGLAPIENTRY
eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
函数本身会返回对应的EGL的版本号,传给major和minor
{
    _EGLDisplay *disp = _eglLockDisplay(dpy);
...
    disp->Options.ForceSoftware =
         env_var_as_boolean("LIBGL_ALWAYS_SOFTWARE", false);    //先确认下是否是做软件渲染。
...
    _eglDriver.Initialize(disp) //以硬件渲染的选项做初始化,如果初始化失败了会以软件渲染的方式做初始化。需要明确的是,一个_EGLDisplay只会有一个dri2_egl_display
        dri2_initialize(disp); //创建的就是dir2_egl_display,不同平台有差异,看后续代码块
            dri_initialize_wayland 或者 dri_initialize_drm,筛选条件就是getdisplay里面决定的Platform类型
            strcut dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
            dri2_dpy->ref_count++;

    disp->Initialized = EGL_TRUE;
    disp->Driver = &_eglDriver;  //设置_EGLDisplay的_EGLDriver,eglapi.c文件的第一层钩子函数。
...
    _eglComputeVersion(disp);   //根据_egl_extensions 判断egl版本,分别是14、15、16
    _eglCreateExtensionsString(disp);  //如果支持这些extension,把extensions 以字符串送到_EGLDisplay->ExtensionsString里面
    _eglCreateAPIsString(disp);  //根据disp->ClientAPIs设置disp->ClientAPIsString,可以是OpenGL或者Opengles或者OpenVG

}


_eglDirver实现钩子::
const _EGLDriver _eglDriver = {
   .Initialize = dri2_initialize,
   .Terminate = dri2_terminate,
   .CreateContext = dri2_create_context,
   .DestroyContext = dri2_destroy_context,
   .MakeCurrent = dri2_make_current,
   .CreateWindowSurface = dri2_create_window_surface,
   .CreatePixmapSurface = dri2_create_pixmap_surface,
   .CreatePbufferSurface = dri2_create_pbuffer_surface,
   .DestroySurface = dri2_destroy_surface,
   .GetProcAddress = dri2_get_proc_address,
   .WaitClient = dri2_wait_client,
   .WaitNative = dri2_wait_native,
   .BindTexImage = dri2_bind_tex_image,
   .ReleaseTexImage = dri2_release_tex_image,
   .SwapInterval = dri2_swap_interval,
   .SwapBuffers = dri2_swap_buffers,
   .SwapBuffersWithDamageEXT = dri2_swap_buffers_with_damage,
   .SwapBuffersRegionNOK = dri2_swap_buffers_region,
   .SetDamageRegion = dri2_set_damage_region,
   .PostSubBufferNV = dri2_post_sub_buffer,
   .CopyBuffers = dri2_copy_buffers,
   .QueryBufferAge = dri2_query_buffer_age,
   .CreateImageKHR = dri2_create_image,
   .DestroyImageKHR = dri2_destroy_image_khr,
   .CreateWaylandBufferFromImageWL = dri2_create_wayland_buffer_from_image,
   .QuerySurface = dri2_query_surface,
   .QueryDriverName = dri2_query_driver_name,
   .QueryDriverConfig = dri2_query_driver_config,
#ifdef HAVE_LIBDRM
   .CreateDRMImageMESA = dri2_create_drm_image_mesa,
   .ExportDRMImageMESA = dri2_export_drm_image_mesa,
   .ExportDMABUFImageQueryMESA = dri2_export_dma_buf_image_query_mesa,
   .ExportDMABUFImageMESA = dri2_export_dma_buf_image_mesa,
   .QueryDmaBufFormatsEXT = dri2_query_dma_buf_formats,
   .QueryDmaBufModifiersEXT = dri2_query_dma_buf_modifiers,
#endif
#ifdef HAVE_WAYLAND_PLATFORM
   .BindWaylandDisplayWL = dri2_bind_wayland_display_wl,
   .UnbindWaylandDisplayWL = dri2_unbind_wayland_display_wl,
   .QueryWaylandBufferWL = dri2_query_wayland_buffer_wl,
#endif
   .GetSyncValuesCHROMIUM = dri2_get_sync_values_chromium,
   .CreateSyncKHR = dri2_create_sync,
   .ClientWaitSyncKHR = dri2_client_wait_sync,
   .SignalSyncKHR = dri2_signal_sync,
   .WaitSyncKHR = dri2_server_wait_sync,
   .DestroySyncKHR = dri2_destroy_sync,
   .GLInteropQueryDeviceInfo = dri2_interop_query_device_info,
   .GLInteropExportObject = dri2_interop_export_object,
   .DupNativeFenceFDANDROID = dri2_dup_native_fence_fd,
   .SetBlobCacheFuncsANDROID = dri2_set_blob_cache_funcs,
};

 dri_initialize_drm::Platform_drm.c

EGLBoolean
dri2_initialize_drm(_EGLDisplay *disp)
{
    _EGLDevice *dev;
    struct dri2_egl_display *dri2_dpy; //此子非常重要
    struct gbm_device *gbm;
...
    gbm = disp->PlatformDisplay;  //也就是wl_display 或者gbm_device,当然,因为我们这边是调用的dri2_initialize_drm, disp->PlatformDisplay一定是上层创建的gbm_device
    因为是第一次创建,还没有gbm对象,会调用如下:
        snprintf(buf, sizeof(buf), DRM_DEV_NAME, DRM_DIR_NAME, 0); //也就是/dev/dri/card%d
        dri2_dpy->fd = loader_open_device(buf);  //open cardX,拿到fd; dri2_dpy就是dri2_egl_display对象指针
        gbm = gbm_create_device(dri2_dpy->fd);   //创建gbm device,用于通过cardX申请buffer
            ...具体太复杂,看下段
            ...具体太复杂,看下段
            ...具体太复杂,看下段
            return gbm;

    如果不是第一次调用,gbm已经有了的情况下:
    if(gbm) 
        dri2_dpy->fd = gbm->fd; //如果gbm已经有了,直接通过gbm获取cardX的fd

    dri2_dpy->gbm_dri = gbm; //不管之前有没有gbm对象,此时已经有了,把gbm赋给dri2_dpy的gbm_dri
    这里需要明确,gbm_dri_device和gbm_device结构体是一致的。

    如果backend不是drm,这里会直接goto cleanup,也就是说,gbm_dri_backend必须是支持drm的,
这个设置是在dri_device_create里面赋值

    dev = _eglAddDevice(dri2_dpy->fd, dri2_dpy->gbm_dr->software);//通过给定的fd,通过drmGetDevice2把对应device放到 对应的_EGLDevice表里面。就在下面的dev参数内
        drmDevicePtr device;
        drmGetDevice2(fd, 0 &device);//Get information about the opened drm device
        _eglAddDRMDevice(device, &dev);
            _EGLDevice *dev;
            dev = _eglGlobal.DeviceList; //塞到这个DeviceList里面。
        return dev;
...
    disp->Device = dev;
...
    dri2_load_driver / dri2_load_driver_dri3 //区别就是看是对应的drm节点是否为render_node,如果render_node则dri3,否则dri2
        dri2_load_drvier_common(disp, dri2_driver_extensions/dri3_driver_extensions);
            __DRIextension **extensions = dri2_open_driver(disp);
                return loader_open_driver(driver_name, driver, path); //通过driver name 打开对应的 dri 驱动。如果是virtio-gpu就是dlopen(virtio_gpu_dri.so),返回的是dlsym的值。即extensions
            dri2_bind_extensions(dri2_dpy, driver_extensions, extensions, false);//加载对应的virtio_gpu_dri.so中的能力。判断so是否支持对应的driver_extensions,支持则对应挂钩
            dri2_bind_extensions(dri2_dpy, optional_drvier_extensions, extensions, true);//判断dri.so中得手支持对应的optinal_driver_extensions中的能力。支持则对应挂钩。
...给dri2_dpy赋值
...一大段的钩子函数挂钩,给dri2_dpy->gbm_dri函数挂钩
   dri2_dpy->gbm_dri->lookup_image = dri2_lookup_egl_image;
   dri2_dpy->gbm_dri->validate_image = dri2_validate_egl_image;
   dri2_dpy->gbm_dri->lookup_image_validated = dri2_lookup_egl_image_validated;
   dri2_dpy->gbm_dri->lookup_user_data = disp;
   dri2_dpy->gbm_dri->get_buffers = dri2_drm_get_buffers;
   dri2_dpy->gbm_dri->flush_front_buffer = dri2_drm_flush_front_buffer;
   dri2_dpy->gbm_dri->get_buffers_with_format = dri2_drm_get_buffers_with_format;
   dri2_dpy->gbm_dri->image_get_buffers = dri2_drm_image_get_buffers;
   dri2_dpy->gbm_dri->swrast_put_image2 = swrast_put_image2;
   dri2_dpy->gbm_dri->swrast_get_image = swrast_get_image;
   dri2_dpy->gbm_dri->base.v0.surface_lock_front_buffer = lock_front_buffer;
   dri2_dpy->gbm_dri->base.v0.surface_release_buffer = release_buffer;
   dri2_dpy->gbm_dri->base.v0.surface_has_free_buffers = has_free_buffers;
...
    dri2_setup_extensions(disp);//bind mandatory_core_extensions和optinal_core_extensions
    dri2_setup_screen(disp);//比较简单,主要是把disp->Extensions相关属性如果支持置为 true;
        disp->Extensions.xxxx = EGL_TRUE;
    drm_add_configs_for_visuals(disp)
...
    dri2_set_WL_bind_wayland_display(disp); //主要是如果是Wayland平台,需要设置disp->Extensions.WL_bind_wayland_display = EGL_TRUE;

    dri2_dpy->vtbl = &dri2_drm_display_vtbl;
    return true;
}



#################dri2和dri3的extensions有点不一样

static const struct dri2_extension_match dri3_driver_extensions[] = {
   { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) },
   { __DRI_IMAGE_DRIVER, 1, offsetof(struct dri2_egl_display, image_driver) },
   { NULL, 0, 0 }
};

static const struct dri2_extension_match dri2_driver_extensions[] = {
   { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) },
   { __DRI_DRI2, 2, offsetof(struct dri2_egl_display, dri2) },
   { NULL, 0, 0 }
};

#################
dri_device_create
主要填充gbm_dri_device 结构体
   struct gbm_dri_device *dri;

   dri->base.fd = fd;
   dri->base.bo_create = gbm_dri_bo_create;
   dri->base.bo_import = gbm_dri_bo_import;
   dri->base.bo_map = gbm_dri_bo_map;
   dri->base.bo_unmap = gbm_dri_bo_unmap;
   dri->base.is_format_supported = gbm_dri_is_format_supported;
   dri->base.get_format_modifier_plane_count =
      gbm_dri_get_format_modifier_plane_count;
   dri->base.bo_write = gbm_dri_bo_write;
   dri->base.bo_get_fd = gbm_dri_bo_get_fd;
   dri->base.bo_get_planes = gbm_dri_bo_get_planes;
   dri->base.bo_get_handle = gbm_dri_bo_get_handle_for_plane;
   dri->base.bo_get_plane_fd = gbm_dri_bo_get_plane_fd;
   dri->base.bo_get_stride = gbm_dri_bo_get_stride;
   dri->base.bo_get_offset = gbm_dri_bo_get_offset;
   dri->base.bo_get_modifier = gbm_dri_bo_get_modifier;
   dri->base.bo_destroy = gbm_dri_bo_destroy;
   dri->base.destroy = dri_destroy;
   dri->base.surface_create = gbm_dri_surface_create;
   dri->base.surface_destroy = gbm_dri_surface_destroy;

   dri->base.name = "drm";

   dri->visual_table = gbm_dri_visuals_table;
   dri->num_visuals = ARRAY_SIZE(gbm_dri_visuals_table);

   mtx_init(&dri->mutex, mtx_plain);

   force_sw = env_var_as_boolean("GBM_ALWAYS_SOFTWARE", false);
   if (!force_sw) {
      ret = dri_screen_create(dri);
      if (ret)
         ret = dri_screen_create_sw(dri);
   } else {
      ret = dri_screen_create_sw(dri);
   }

################ vtbl 实现
static const struct dri2_egl_display_vtbl dri2_drm_display_vtbl = {
   .authenticate = dri2_drm_authenticate,
   .create_window_surface = dri2_drm_create_window_surface,
   .create_pixmap_surface = dri2_drm_create_pixmap_surface,
   .destroy_surface = dri2_drm_destroy_surface,
   .create_image = dri2_drm_create_image_khr,
   .swap_buffers = dri2_drm_swap_buffers,
   .query_buffer_age = dri2_drm_query_buffer_age,
   .get_dri_drawable = dri2_surface_get_dri_drawable,
};

gbm_create_device

        gbm = gbm_create_device(dri2_dpy->fd);   //创建gbm device,用于通过cardX申请buffer
            gbm = _gbm_create_device(fd);   //如果私有的,可以设置GBM_BACKEND,让libgbm去load backend,否则就是load gbm_dri.so,对应的就是dri_device_create(fd);
                dev = backend->create_device(fd);
                  dri_device_create(fd);  //就是填充gbm_dri_device结构体dri,详见下。
                    dri_screen_create(dri);  
                        driver_name = loader_get_driver_for_fd(dri->base.fd);  //获取driver name,驱动注册时候的名字。如果是virtio-gpu,那么driver_name就是virtio-gpu
                        return dri_screen_create_dri2(dri, driver_name);
                            dri_load_driver(dri);
                                extensions = dri_open_driver(dri);//加载对应的gpu驱动,并且返回__DRIextension, dri->driver = dlopen的virtio_gpu_dir.so
                                    dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL); // Temporarily work around dri driver libs that need symbols in libglapi but don't automatically link it in. 实属不理解,该函数讲打开对应的库,主要用来加载库中的符号
                                    return loader_open_driver(dri->driver_name, &dri->driver, search_path_vars);// Opens a DRI driver using its driver name, returning the __DRIextension entrypoints. 这个driver不是bsp的driver,而是用户层是so库,也就是gpu的驱动是在userspace的,加载对应的库,并加载对应的__driDriverExtensions!
                                        driver = dlopen(virtio_gpu_dri.so) //不同厂家的so不一样!
                                        extensions = dlsym(driver, );  //默认是__driDriverExtensions,__driDriverGetExtensions_virtio_gpu
                                        return extensions;
                                dri_bind_extensions(dri, gbm_dri_device_extensions, size, extensions); //将extensions 绑定到 dri 的对应的 core 和 dri2 上面, extensions是从dlopen driver,dlsym 得到的_driDriverExtensions,此时dri->dri2和dri->core已经赋值成功。见下面一部分,是core和dri2的定义
///
DRI_Core/DRI_DRI2
static struct dri_extension_match gbm_dri_device_extensions[] = {
   { __DRI_CORE, 1, offsetof(struct gbm_dri_device, core), false },
   { __DRI_DRI2, 1, offsetof(struct gbm_dri_device, dri2), false },
};
///
                                dri->driver_extensions = extensions; //最后保留下对应的地址
                            dri->loader_extensions = gbm_dri_screen_extensions; //再保留下定义的地址。
                            dri->screen = dri->dri2->createNewScreen2(0, dri->base.fd,
                                              dri->loader_extensions,
                                              dri->driver_extensions,
                                              &dri->driver_configs, dri);
                                driCreateNewScreen2  // This is the first entrypoint in the driver called by the DRI driver loader after dlopen()ing it. It's used to create global state for the driver across contexts on the same Display.
                                如注释,这是在dlopen用户态gpu驱动以后的第一个被调用的驱动函数,它用于在同一display对象上跨上下文为驱动程序创建全局状态。文件位置在src/mesa/drvier/dri/common/dri_util.c
                                    psp->driver = ((__DRIDriverVtableExtension *)driver_extensions[i])->vtable; // psp是__DRIscreen 指针,这里会失败!!不过没关系。

///
driver_extensions->name为:
DRI_Core
DRI_IMAGE_DRIVER
DRI_DRI2
DRI_ConfigOptions

extensions->name为:
DRI_IMAGE_LOOKUP
DRI_UseInvalidate
DRI_DRI2Loader
DRI_IMAGE_LOADER
DRI_SWRastLoader
///
                                    setupLoaderExtensions(psp, extensions); //任然是绑定一些函数。
                                    /* Option parsing before ->InitScreen(), as some options apply there. */
                                    driParseOptionInfo(&psp->optionInfo, __dri2ConfigOptions, ARRAY_SIZE(__dri2ConfigOptions));
                                    driParseConfigFiles(&psp->optionCache, &psp->optionInfo, psp->myNum, "dri2", NULL, NULL, 0, NULL, 0);

                                    *driver_configs = psp->driver->InitScreen(psp);
                                    调用的是gallium里面的dri2.c文件的galliumdrm_driver_api,具体位置是mesa/src/gallium/frontends/dri/dri2.c;returns the struct gl_config supported by this driver.
                                        ########
                                        dri2_init_screen 最终返回的是gl_config结构体。内容见下块!!!
                                        ########
                                    ...
                                    return psp;//返回的是__DRIscreen *psp;

                                    接着是基于psp,也就是Pre-screen-private driver information结构体指针,填充max_gl_es2_version,max_gl_core_version,max_gl_compat_version以及api_mask
                            extensions = dri->core->getExtensions(dri->screen);
                            dri_bind_extensions(dri, dri_core_extensions, ARRAY_SIZE(dri_core_extensions), extensions);

            return gbm;



##########关于gallium里面函数钩子。
/**
 * DRI driver virtual function table.
 *
 * DRI versions differ in their implementation of init_screen and swap_buffers.
 */
const struct __DriverAPIRec galliumdrm_driver_api = {
   .InitScreen = dri2_init_screen,
   .DestroyScreen = dri_destroy_screen,
   .CreateContext = dri_create_context,
   .DestroyContext = dri_destroy_context,
   .CreateBuffer = dri2_create_buffer,
   .DestroyBuffer = dri_destroy_buffer,
   .MakeCurrent = dri_make_current,
   .UnbindContext = dri_unbind_context,

   .AllocateBuffer = dri2_allocate_buffer,
   .ReleaseBuffer  = dri2_release_buffer,
};

dri2_init_screen

关于gallium里面的dri2_init_screen,这个是驱动部分的具体实现,是createNewScreen的具体实现,返回的是 gl_config 结构体。

//mesa/src/gallium/frontends/dri/dri2.c
static const __DRIconfig **
dri2_init_screen(__DRIscreen * sPriv)
{
   const __DRIconfig **configs;
   struct dri_screen *screen;
   struct pipe_screen *pscreen = NULL;
...
   if (pipe_loader_drm_probe_fd(&screen->dev, screen->fd)) { //主要填充screen->dev,见下文
      dri_init_options(screen);
          pipe_loader_load_options(screen->dev);

          dri_fill_st_options(screen);//判断驱动是否支持这些功能,如这个application是否支持disable_blend_func_extended, force_glsl_version, allow_glsl_relaxed_es,force_gl_vendor等等,并且返回唯一hash来指定。代码是dri_screen.c
              driComputeOptionsSha1(optionCache, options->config_options_sha1);

      pscreen = pipe_loader_create_screen(screen->dev);  //create_screen继续跟踪
          util_cpu_detect()
              call_once(&cpu_once_flag, util_cpu_detect_once);// check cpu特性?
          pipe_loader_load_options(dev); //同样的参数又来了一遍,感觉不太重要
          return dev->ops->create_screen(dev,&config);   //pipe_loader_drm_create_screen,见下文
              pipe_loader_drm_create_screen
                  return ddev->dd->create_screen(ddev->fd, config);
   }
...此时就已经有了pscreen,整个通路已经打通。screen 是 dri_screen指针, pscreen是pipe_screen指针。

   screen->throttle = pscreen->get_param(pscreen, PIPE_CAP_THROTTLE);

   /* set up the dri extension list for this screen based on its underlying gallium screen;s capabilities;根据pscreen设置screen的一些extensions。包括最重要的dmabuf,还有对应prime共享机制。createImageFromFs, createImageFormDmaBufs 等等。
   dri2_init_screen_extensions(screen, pscreen, false);

   configs = dri_init_screen_helper(screen, pscreen);
       screen ->base.screen = pscreen;
       ...
       screen ->st_api = st_gl_api_create(); //对应实现在st_manager.c
       ...
       dri_postprocessing_init(screen); //
       screen->st_api->query_versions(...); //设置screen的一些version,gl的,gles的。
       return dir_fill_in_modes(screen); //返回_DRIconfig

   screen->can_share_buffer = tru
   screen->auto_fake_front = dri_with_format(sPriv);
   screen->broken_invalidate = !sPriv->dri2.useInvalidate;
   screen->lookup_egl_image = dri2_lookup_egl_image;

   return configs; //最终返回给是一个configs
}


######
pipe_loader_drm_probe_fd
    pipe_loader_drm_probe_fd_nodup(dev, new_fd);
        struct pipe_loader_drm_device *ddev;
            loader_get_pci_id_for_fd  //就是拿vendor_id和chip_id
                drm_get_pci_id_for_fd
                    drmGetDevice2(fd, 0 ,&device)
                        *vendor_id = device->deviceinfo.pci->vendor_id;
                        *chip_id = device->deviceinfo.pci->device_id;
                    drmFreeDevice(device);
            
            ddev->base.ops = &pipe_loader_drm_ops; //详见下
            ddev->base.driver_name = loader_get_driver_for_fd(fd);//通过fd获取drver的名字。kernel驱动里面设置的。modetest -M xxx这个name
            ddev->dd = get_driver_descriptor(ddev->base.driver_name, plib);//返回的是drm_driver_descriptor,硬件强相关。具体驱动实现?
                if (strcmp(driver_descriptors[i]->driver_name, driver_name) == 0)
                    return driver_descriptors[i]; //具体看驱动是哪个,我们是virtio_gpu_driver_descriptor. 定义在drm_helper_public.h,具体实现可看到drm_help.h里面的#define DEFINE_DRM_DRIVER_DESCRIPTOR
            
            *dev = &ddev->base; //此时dev有了ops,drvier_name,type, vendor_id, chip_id等硬件信息。
            

#####
//pipe_loader_drm.c
static const struct pipe_loader_ops pipe_loader_drm_ops = {
   .create_screen = pipe_loader_drm_create_screen,
   .get_driconf = pipe_loader_drm_get_driconf,
   .release = pipe_loader_drm_release
};

pipe_loader_drm_create_screen(pipe_loader_device *dev, pipe_screen_config *config)
    return ddev->dd->create_screen(ddev->fd,config);
        pipe_vritio_gpu_create_screen(fd, *config);//对应实现在drm_helper.h
            struct pipe_screen *screen;
            screen = virgl_drm_screen_create(fd, config);//对应实现在virgl_drm_winsys.c 终于到了winsys层!
                ...创建一个pipe_screen指针 和 fd之间的hash表关系,后面就可以通过fd找到对应的pipe_screen
                ...如果不是第一次创建,则会对virgl_screen的refcnt++;否则如下
                strcut virgl_winsys *vws;
                vws = virgl_drm_winsys_create(fd); //首先是通过drmioctl对param功能的确认,见下文
                    strcut virgl_drm_winsys *qdws;
                    ret = drmIoctl(drmFD, DRM_IOCTL_VIRTGPU_GETPARAM, &getparam); //确认这些功能支持与否
                    drm_version = virgl_drm_get_version(fd); //就是drmGetVersion再加点自己的东西,就是个版本设置。
                    接下来就是初始化qdws,这个东西很重要,就是最最最hal的地方!直接调用的就是ioctl
                    return &qdws->base;           
                pscreen = virgl_create_screen(vws, config);
                    完整的初始化virgl_screen,并把vws,也就是上面的qdws->base放入screen->vws
                    ...
                    vwm->get_caps();
                    fixup_formats();
                    ...
                    slab_create_parent()
                    virgl_disk_cache_create();
                    return &screen->base;

                ...会把这个pscreen塞入hash,方便后续找到,复用。当然也绑定了pscreen->destroy函数为virgl_drm_screen_destroy;
                最终返回的是pscreen
            return screen ? debug_screen_wrap(screen) : NULL;
                又对这个screen进行了四次封装,这个screen真的波折太多:
                screen = ddebug_screen_create(screen);
                screen = rbug_screen_create(screen);
                screen = trace_screen_create(screen);
                screen = noop_screen_create(screen);
                return screen //总算结束了!


// virgl_drm_winsys.h params
struct param params[] = { PARAM(VIRTGPU_PARAM_3D_FEATURES),
                          PARAM(VIRTGPU_PARAM_CAPSET_QUERY_FIX),
                          PARAM(VIRTGPU_PARAM_RESOURCE_BLOB),
                          PARAM(VIRTGPU_PARAM_HOST_VISIBLE),
                          PARAM(VIRTGPU_PARAM_CROSS_DEVICE)
};




########st_api
//st_manager.c
static const struct st_api st_gl_api = {
   .name = "Mesa " PACKAGE_VERSION,
   .api = ST_API_OPENGL,
   .profile_mask = ST_PROFILE_DEFAULT_MASK |
                   ST_PROFILE_OPENGL_CORE_MASK |
                   ST_PROFILE_OPENGL_ES1_MASK |
                   ST_PROFILE_OPENGL_ES2_MASK |
                   0,
   .feature_mask = ST_API_FEATURE_MS_VISUALS_MASK,
   .destroy = st_api_destroy,
   .query_versions = st_api_query_versions,
   .create_context = st_api_create_context,
   .make_current = st_api_make_current,
   .get_current = st_api_get_current,
   .destroy_drawable = st_api_destroy_drawable,
};

上面这段大致1.初始化vws和pscreen,也就是真正的hal接口了。  pscreen拥有一切对下的接口。牛

 core和dir2的实现在dri_util.c里面

/** Core interface */
const __DRIcoreExtension driCoreExtension = {
    .base = { __DRI_CORE, 2 },

    .createNewScreen            = NULL,
    .destroyScreen              = driDestroyScreen,
    .getExtensions              = driGetExtensions,
    .getConfigAttrib            = driGetConfigAttrib,
    .indexConfigAttrib          = driIndexConfigAttrib,
    .createNewDrawable          = NULL,
    .destroyDrawable            = driDestroyDrawable,
    .swapBuffers                = driSwapBuffers, /* swrast */
    .createNewContext           = driCreateNewContext, /* swrast */
    .copyContext                = driCopyContext,
    .destroyContext             = driDestroyContext,
    .bindContext                = driBindContext,
    .unbindContext              = driUnbindContext
};

/** DRI2 interface */
const __DRIdri2Extension driDRI2Extension = {
    .base = { __DRI_DRI2, 4 },

    .createNewScreen            = dri2CreateNewScreen,
    .createNewDrawable          = driCreateNewDrawable,
    .createNewContext           = driCreateNewContext,
    .getAPIMask                 = driGetAPIMask,
    .createNewContextForAPI     = driCreateNewContextForAPI,
    .allocateBuffer             = dri2AllocateBuffer,
    .releaseBuffer              = dri2ReleaseBuffer,
    .createContextAttribs       = driCreateContextAttribs,
    .createNewScreen2           = driCreateNewScreen2,
};

 dri_initialize_wayland

EGLBoolean
dri2_initialize_wayland(_EGLDisplay *disp)
{
   if (disp->Options.ForceSoftware)
      return dri2_initialize_wayland_swrast(disp);
   else
      return dri2_initialize_wayland_drm(disp);
      ...
      ...wayland client的标准调用相关就写了
          dri2_dpy -> fd = loader_get_user_preferred_fd(dri2_dpy->fd, &dri2_dpy->is_different_gpu);
          //目的就是返回fd,但是如果有多个支持渲染的节点,貌似会和weston用到的node有区别。看样子是合成和渲染分card执行,换句话也就是支持多card。
          dev = _eglAddDevice(dri2_dpy->fd , false);
      ...剩下这段和dri2_initialize_drm一致...
          dri_wl_setup_swap_interval(disp); //设置interval,也就是多少个vsync做绘画。
}
;