display:weston:weston-simple-egl

写在前面:

客户端渲染

在Wayland架构中,客户端UI的所有呈现均由客户端代码执行,通常由客户端使用的图形工具包执行。

图形工具箱可以使用其希望呈现UI元素的任何方法:在CPU上进行软件呈现使用GLES进行硬件呈现。 Wayland所需要做的就是将客户端渲染的每一帧和窗口的结果像素发送到合成器。 像素数据可能以几种方式传输,具体取决于渲染方式以及客户端和合成器相互支持的内容:

  •      包含实际像素数据的共享内存缓冲区。 如果没有其他机制,则支持这些备用机制。
  •      GPU缓冲区共享(DRM / DRI)。 客户端直接在GPU上渲染窗口,结果像素数据保留在GPU内存中,并将其句柄传递给合成器。 这防止了像素数据的不必要和昂贵的复制

合成器的工作

一旦合成器拥有了所有像素数据(或包含它的GPU缓冲区的句柄),它便可以合成一帧。 与客户端渲染一样,这可以通过几种方式完成:

  •      软件渲染。 CPU密集型,用作备用。 这还需要将像素数据从GPU内存中拉出,这很昂贵

  •      使用GLES进行完整的GPU渲染。 这将获取像素数据并在GPU上对其进行合成,如果动画需要的话,可能会应用着色器和3D转换。

  •      显示控制器上特定于硬件的API。 这些通常是2D合成API,与完整的3D计算相比,它们的资源占用较少,但仍在显示控制器而非CPU上进行处理,并且不需要额外的像素数据副本。[这个动作和crtc里面的合成不是一个概念]

例如,在传统的显卡上,可能有四个硬编码覆盖层:[weston的assign plane函数里面关于plane的分配基本基于下面的策略]

  •     Primary: main overlay
    • By default surfaces belong to the primary plane

    • Only surfaces on the primary plane are composited with the renderer

  •     Scanout: a single, full-screen surface
    • "Disables" composition for fullscreen clients
    • Very low overhead
  •     Sprite: typically a video overlay in a different colour space
    • Use the hardware overlay
  •     Cursor
    • Supports 64x64 surfaces
    • SHM only, contents are copied to an appropriate buffer

 

根据我从weston的assign_plane逻辑里面看到的一样,默认的view都是分给primary;cursor就是corsor,64x64;overlay就是单独的overlay;output_repaint会把所有的primary上的view交给gpu进行合成,变成scan-out;最后调用kms交给kernel

鼠标一个单独的cursor plane;video 占据sprite plane;

像素之旅

作为说明,请考虑一个UI元素从在客户端应用程序中以编程方式创建到出现在用户屏幕上的过程。假定通过将句柄传递给GPU资源(而不是客户端渲染中列出的其他方法)在客户端和合成器之间共享纹理。在本例中,我们使用Weston作为合成器。如果使用了Mutter,则将步骤5–7替换为一个步骤:使用GLES在GPU上合成所有表面,以形成最终的输出帧。

  1.     该程序使用其UI工具包创建一个新的小部件。

  2.     该工具包在其GLES上下文中设置了小部件,并将所有必要的纹理上传到GPU。

  3.     当应用程序接下来渲染帧时(例如,由于部分UI更改),它会通过GPU的GLES管道推送其GLES上下文,从而在GPU中创建一个包含整个应用程序窗口像素数据的输出纹理。
  4.     该应用程序使用Wayland协议向合成器(Weston)通知更新的窗口,并将GPU纹理句柄传递给weston。
  5.     当Weston接下来渲染框架时,它将确定是否需要对任何曲面应用GLES变换,并根据需要将曲面分配给平面和硬件叠加层。
  6.     对于每个平面,Weston会合成该平面中的所有曲面,从而创建该平面的输出像素数据。
    •  如果某个平面需要任何变换,则Weston渲染器将通过GPU GLES管道将该平面中的表面推入。
    • 如果不需要进行任何转换,或者可以使用更高效的特定于硬件的API来实现所需的转换,那么将跳过此步骤。
  7.     Weston使用特定于硬件的API来组合所有平面以形成最终的输出框架。
  8.     输出帧将发送到用户的屏幕。

上面的图很好理解,如果是window composition,势必会跟在primary的plane上,那么就需要gpu把这些view变成一张full-screen的图,另外需要记得,合成完以后,像kernel传递的是这个gpu合成后的fd,而不是之前的多个view的fd;而如果本身就是full-screen或者overlay的,则并不需要这个合成的过程,所以两者在合成方面是有差异的

EGL 使用实践

EGL 的使用要遵循一些固定的步骤,按照这些步骤去配置、创建、渲染、释放。

其中:
Display(EGLDisplay) 是对实际显示设备的抽象
Surface(EGLSurface)是对用来存储图像的内存区域 FrameBuffer 的抽象,包括 Color Buffer, Stencil Buffer ,Depth Buffer
Context (EGLContext) 存储 OpenGL ES绘图的一些状态信息

1.创建与本地窗口系统的连接
    调用 eglGetDisplay 方法得到 EGLDisplay
2.初始化 EGL 方法
    调用 eglInitialize 方法初始化
3.确定渲染表面的配置信息
    调用 eglChooseConfig 方法得到 EGLConfig
4.创建渲染上下文
    通过 EGLDisplay 和 EGLConfig ,调用 eglCreateContext 方法创建渲染上下文,得到 EGLContext
5.创建渲染表面
    通过 EGLDisplay 和 EGLConfig ,调用 eglCreateWindowSurface 方法创建渲染表面,得到 EGLSurface
6.绘制表面
    使用 OpenGL ES API 绘制图形:gl_*()
7.绑定上下文
    通过 eglMakeCurrent 方法将 EGLSurface、EGLContext、EGLDisplay 三者绑定,接下来就可以使用 OpenGL 进行绘制了。
8.交换缓冲
    当用 OpenGL 绘制结束后,使用 eglSwapBuffers 方法交换前后缓冲,将绘制内容显示到屏幕上
9.释放 EGL 环境
    绘制结束,不再需要使用 EGL 时,取消 eglMakeCurrent 的绑定,销毁 EGLDisplay、EGLSurface、EGLContext。



egl相关可以参考

https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglIntro.xhtml
https://glumes.com/post/opengl/opengl-egl-usage/
https://cloud.tencent.com/developer/article/1472434

如果对 EGLDisplay、EGLSurface 、EGLContext 这些抽象概念傻傻分不清楚,可以参考这幅图:

 

Wayland Requirements for OpenGL/ES

  1. Must support the following Native Display Types for eglGetDisplay():
    1. –wl_display for clients
    2. –gbm handle for Weston
  2. Must support the following EGL_EXTENSIONs:
    1. –EGL_KHR_image_pixmap
    2. –EGL_WL_bind_wayland_display
  3. Must support the following Native Pixmap Type for eglCreateImageKHR():
    1. –EGL_WAYLAND_BUFFER_WL
  4. Must support the following Wayland extension APIs:
    1. –eglBindWaylandDisplayWL
    2. –eglUnbindWaylandDisplayWL
    3. –eglQueryWaylandBufferWL

 

  1. Use eglGetPlatformDisplayEXT in concert with EGL_PLATFORM_WAYLAND_KHR to create an EGLdisplay.
  2. Configure the display normally, choosing a config appropriate to your circumstances with EGL_SURFACE_TYPE set to EGL_WINDOW_BIT .
  3. Use wl_egl_window_create to create a wl_egl_window for a given wl_surface .
  4. Use eglCreatePlatformWindowSurfaceEXT to create an EGLSurface for a wl_egl_window .
  5. Proceed using EGL normally, e.g. eglMakeCurrent to make current the EGL context for your surface and eglSwapBuffers to send an up-to-date buffer to the compositor and commit the surface.

https://ppaalanen.blogspot.com/2012/03/what-does-egl-do-in-wayland-stack.html : pq 是weston/wayland开发者一员,非常资深。看最近博客貌似打算在Wayland上面支持HDR

Wayland server


 The Wayland server in the diagram is Weston with the DRM backend. The server does its 
rendering using GL ES 2, which it initialises by calling EGL. Since the server runs on 
"bare KMS", it uses the EGL DRM platform, which could really be called as the GBM 
platform, since it relies on the Mesa GBM interface. Mesa GBM is an abstraction of the 
graphics driver specific buffer management APIs (for instance the various libdrm_* 
libraries), implemented internally by calling into the Mesa GPU drivers.

 Mesa GBM provides graphics memory buffers to Weston. Weston then uses EGL calls to bind
 them into GL objects, and renders into them with GL ES 2. A rendered buffer is shown on 
an output (monitor) by queuing a page flip via the libdrm KMS API.

 If the EGL implementation offers the extension EGL_WL_bind_wayland_display, Weston will 
use it to register its wl_display object (facing the clients) to EGL. In practice, the 
Mesa EGL then adds a new global Wayland object to the wl_display. That object (or 
interface) is called wl_drm, and the server will automatically advertise that to all 
clients. Clients will use wl_drm for DRM authentication, getting the right DRM device 
node, and sharing graphics buffers with the server without copying pixels
    
Wayland client


 A Wayland client, naturally, connects to a Wayland server, and gets the main Wayland 
protocol object wl_display. The client creates a window, which is a Wayland object of 
type wl_surface. All what follows is enabled by the Wayland platform support in Mesa EGL.

 The client passes the wl_display object to eglGetDisplay() and receives an EGLDisplay to 
be used with EGL calls. Then comes the trick that is denoted by the double-arrowed blue 
line from Wayland client to Mesa EGL in the diagram. The client calls the wayland-egl API 
(implemented in Mesa) function wl_egl_window_create() to get the native window handle. 
Normally you would just use the "real" native window object wl_surface (or an X11 Window 
if you were using X). The native window handle is used to create the EGLSurface EGL 
handle. Wayland has this extra step and the wayland-egl API because a wl_surface carries 
no information of its size. When the EGL library allocates buffers, it needs to know the 
size, and wayland-egl API is the only way to tell that.

 Once EGL Wayland platform knows the size, it can allocate a graphics buffer by calling 
the Mesa GPU driver. Then this graphics buffer needs to be mapped into a Wayland protocol 
object wl_buffer. A wl_buffer object is created by sending a request through the wl_drm 
interface carrying the name of the (DRM) graphics buffer. In the server side, wl_drm 
requests are handled in the Mesa EGL library, where the corresponding server side part of 
the wl_buffer object is created. In the diagram this is shown as the blue dotted arrow 
from EGL Wayland platform to itself. Now, whenever the wl_buffer object is referenced in 
the Wayland protocol, the server knows exactly what it is.
    
The client now has an EGLSurface ready, and renders into it by using one of the GL APIs 
or OpenVG offered by Mesa. Finally, the client calls eglSwapBuffers() to show the result 
in its Wayland window.

 The buffer swap in Mesa EGL Wayland platform uses the Wayland core protocol and sends an 
attach request to the wl_surface, with the wl_buffer as an argument. This is the blue 
dotted arrow from EGL Wayland platform to Wayland server.

 Weston itself processes the attach request. It knows the buffer is not a shm buffer, so 
it passes the wl_buffer object to the Mesa EGL library in an eglCreateImageKHR() function 
call. In return Weston gets an EGLImage handle, which is then turned into a 2D texture, 
and used in drawing the surface (window). This operation is enabled by 
EGL_WL_bind_wayland_display extension.
    
Summary


 The important facts, that should be apparent in the diagram, are:
•There are two different EGL platforms in play: one for the server, and one for the 
clients.
•A Wayland server does not contain any graphics hardware or driver specific code, it is 
all in the generic libraries of DRM, EGL and GL (libdrm and Mesa).
•Everything about wl_drm is an implementation detail internal to the EGL library in use. 
The system dependent part of Weston is the backend, which somehow must be able to drive 
the outputs. The new abstractions in Mesa (GBM API) make it completely hardware agnostic 
on standard Linux systems. Therefore every Wayland server implementation does not need 
its own set of graphics drivers, like X does.

 It is also worth to note, that 3D graphics on X uses very much the same drivers as 
Wayland. However, due to the Wayland requirements from the EGL framework (extensions, EGL 
Wayland platform), proprietary driver stacks need to specifically implement Wayland 
support, or they need to be wrapped into a meta-EGL-library, that glues Wayland support 
on top. Proprietary drivers also need to provide a way to use accelerated graphics 
without X, for a Wayland server to run without X beneath. Therefore the desktop 
proprietary drivers like Nvidia's have a long way to go, as currently nvidia does not 
implement EGL at all, no support for Wayland, and no support for running without X, or 
even setting a video mode without X.

 Due to the way wl_drm is totally encapsulated into Mesa EGL and how the interfaces are 
defined for the EGL Wayland platform and the EGL extension, another EGL implementor can 
choose their very own way of sharing graphics buffers instead of using wl_drm.

 There are already plans to change to some of the architecture described in this article, 
so it is possible that details in the diagram become outdated fairly soon. This article 
also does not consider a purely software rendered Wayland stack, which certainly would 
lift all these requirements, but quite likely be too slow in practice for the desktop.

客户机将wl_display对象传递给eglGetDisplay(),并接收EGLDisplay,以便与EGL调用一起使用。然后是图中从Wayland客户端到Mesa EGL的双箭头蓝线所表示的技巧。客户端调用wayland-egl API(在Mesa中实现)函数wl_egl_window_create()来获得本机窗口句柄。通常,你只需要使用“真实的”本机窗口对象wl_surface(或者使用X的X11窗口)。本机窗口句柄用于创建EGLSurface EGL句柄。Wayland有这个额外的步骤和Wayland -egl API,因为wl_surface不携带它的大小信息。当EGL库分配缓冲区时,它需要知道缓冲区的大小,而wayland-egl API是告诉这个的唯一方法。一旦EGL Wayland平台知道了大小,它就可以通过调用Mesa GPU驱动程序来分配图形缓冲区。然后这个图形缓冲区需要映射到一个Wayland协议对象wl_buffer。通过携带(DRM)图形缓冲区名称的wl_drm接口发送请求来创建wl_buffer对象。在服务器端,wl_drm请求在Mesa EGL库中处理,在该库中创建wl_buffer对象的相应服务器端部分。在图中,这显示为从EGL Wayland平台到它自身的蓝色虚线箭头。现在,只要wl_buffer对象在Wayland协议中被引用,服务器就会确切地知道它是什么。客户端现在已经准备好了一个EGLSurface,并通过使用Mesa提供的GL api或OpenVG来呈现它。最后,客户端调用eglSwapBuffers()在其Wayland窗口中显示结果。Mesa EGL Wayland平台中的缓冲区交换使用Wayland核心协议,并以wl_buffer作为参数向wl_surface发送attch请求。这是从EGL Wayland平台到Wayland服务器的蓝色虚线箭头。Weston本身处理attch请求。它知道缓冲区不是shm缓冲区,所以它通过eglCreateImageKHR()函数调用将wl_buffer对象传递给Mesa EGL库。作为回报,Weston获得一个EGLImage手柄,然后将其转换为2D纹理,并用于绘制表面(窗口)。该操作由EGL_WL_bind_wayland_display扩展名启用。

根据pq的文章可以知道,buffer是在weston_platform_create_egl_surface里面实现的。可以看到这个函数用到了window->native,于pq的文章说明一致。
eglswapbuffers会调用surface_attch.
如上所述也就和我们通过weston-debug观察到的现象一致。
另外,看样子simple-egl的最终绘图结果只是texture。

simple-egl.c

关于EXT扩展egl函数说明:https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt
main
{
a..	init_egl(&display, &window);
        weston_platform_get_egl_display//基于wayland的平台,获取egl display对象 [egl实践1.]
            eglGetDisplay / eglGetPlatformDisplayEXT             //get EGLDisplay
                return eglGetDisplay((EGLNativeDisplayType) native_display);//使用EGL中的函数eglGetDisplay(),并向EGL提供强制转换为NativeDisplayType指针的wl_display指针

        eglInitialize                                                       //[egl实践2.]
        eglBindAPI(EGL_OPENGL_ES_API);
        eglGetConfigs  //获取属性
        eglChooseConfig//选择属性      //get EGLConfig                      //[egl实践3.]
        eglGetConfigAttrib//获取属性资源,获取color buffer位宽
        eglCreateContext              //get EGLContext                     //[egl实践4.]
        eglQueryString
        weston_check_egl_extension(extensions, "EGL_EXT_buffer_age")//判断是否支持
        weston_check_egl_extension(extensions, "EGL_EXT_swap_buffers_with_damage")//判断是否支持
        weston_check_egl_extension(extensions, "EGL_KHR_swap_buffers_with_damage")//判断是否支持


b..	create_surface(&window);
        wl_compositor_create_surface  //get wl_surface
        wl_egl_window_create          //get wl_egl_window
            window->native =
		            wl_egl_window_create(window->surface,
				             window->geometry.width,
				             window->geometry.height);//调用要wl_egl_window_create()使用的surface以及其大小来获取wl_egl_window指针。

        weston_platform_create_egl_surface //get EGLSurface
            eglCreateWindowSurface   / eglCreatePlatformWindowSurfaceEXT    //[egl实践5.],这里会把实际的buffer和wl_buffer绑定
            	return eglCreateWindowSurface(dpy, config,
				      (EGLNativeWindowType) native_window,
				      attrib_list);//它将wl_egl_window指针转换为NativeWindowType指针,并将其传递给eglCreateWindowSurface(); wl_egl_window的相关处理函数在wayland-egl-core.c中实现,改变窗口大小等操作

        xdg_wm_base_get_xdg_surface   //get xdg_surface
        xdg_surface_get_toplevel      //get xdg_toplevel
        wl_surface_commit             //一次commit。把其他相关属性除了buffer以外的内容commit一次
        eglMakeCurrent                                                     //[egl实践7.]
        if (!window->frame_sync)
		    eglSwapInterval(display->egl.dpy, 0);                        //eglSwapInterval用于设置buffer交换时的最小帧数,默认为1;如果interval设置为0,则缓冲区交换不会同步到视频帧,只要渲染完成,交换就会发生。
所以跑weston-simple-egl -b 0;意味着,不等weston的vsync同步,纯gpu绘画,绘画完成就交换前端和后端缓冲区


c..	init_gl(&window);
	    frag = create_shader(window, frag_shader_text, GL_FRAGMENT_SHADER);// 创建片元着色器
	    vert = create_shader(window, vert_shader_text, GL_VERTEX_SHADER);  // 创建顶点着色器
        program = glCreateProgram();         // 创建着色器程序
	    glAttachShader(program, frag);       // 向程序中加入片元着色器
	    glAttachShader(program, vert);       // 向程序中加入顶点着色器
	    glLinkProgram(program);              // 链接程序,使上述操作生效  
        glGetProgramiv                       // 获取program的链接情况
        glUseProgram(program);               //加载并使用链接好的程序
        glBindAttribLocation(program, window->gl.pos, "pos");//手动为name为pos的attribute变量设置地址索引window->gl.pos
	    glBindAttribLocation(program, window->gl.col, "color");//手动为name为color的attribute变量设置地址索引window->gl.col
	    glLinkProgram(program);              // 链接程序,使上述操作生效  
        window->gl.rotation_uniform = glGetUniformLocation(program, "rotation");//获取一致变量rotation在着色器程序中的位置序号,通过该序号可以设置一致变量的值,如果没有该变量则返回-1


	/* The mainloop here is a little subtle.  Redrawing will cause
	 * EGL to read events so we can just call
	 * wl_display_dispatch_pending() to handle any events that got
	 * queued up as a side effect. */
	while (running && ret != -1) {
		if (window.wait_for_configure) {
			ret = wl_display_dispatch(display.display);      //https://www.systutorials.com/docs/linux/man/3-wl_display_dispatch/
		} else {
			ret = wl_display_dispatch_pending(display.display);//处理完server过来的排在队列的消息。随后重画
d..			redraw(&window, NULL, 0);                                   //[egl实践6.绘画]
	            glViewport(0, 0, window->geometry.width, window->geometry.height);
	            glUniformMatrix4fv(window->gl.rotation_uniform, 1, GL_FALSE,
			               (GLfloat *) rotation);
	            glClearColor(0.0, 0.0, 0.0, 0.5);
	            glClear(GL_COLOR_BUFFER_BIT);
	            glVertexAttribPointer(window->gl.pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
	            glVertexAttribPointer(window->gl.col, 3, GL_FLOAT, GL_FALSE, 0, colors);
	            glEnableVertexAttribArray(window->gl.pos);
	            glEnableVertexAttribArray(window->gl.col);
	            glDrawArrays(GL_TRIANGLES, 0, 3);
	            glDisableVertexAttribArray(window->gl.pos);
	            glDisableVertexAttribArray(window->gl.col);                
                display->swap_buffers_with_damage / eglSwapBuffers        //[egl实践8.交换缓冲];会做attch/damage/commit
		}
	}
}
因为最开始在创建egldisplay以及eglsurface的时候和wayland的display以及surface关联,故eglSwapBuffer应该会让weston有所动作。

我还没找到gpu如何把handler传给weston的地方

 

https://www.apertis.org/waylandevaluation/compositors/

https://events.static.linuxfound.org/sites/events/files/slides/als2015_wayland_weston_v2.pdf

https://blog.csdn.net/u012839187/article/details/107403974

接下篇:https://blog.csdn.net/u012839187/article/details/113104654

======

drm_assign_planes

drm_output_propose_state   设置plane和view之间的关系,并且调用drm_pending_state_test[atomic_test]确认这个状态是否能atomic

drm_output_prepare_plane_view       

drm_fb_get_from_view     //重要,如果是wl_shm_buffer则跳过;否则获取drm_fd;从view中拿到drm_fd并且将drm_fd和buffer关联;返回drm_fd

drm_output_try_view_on_plane

drm_pending_state_test

 

 

 

drm_pending_state_apply_atomic         // enable or disable crtc active状态  &  atomic commit在调用完下面函数以后

        drm_output_apply_state_atomic    //setprop for output

 

 

int wl_display_roundtrip (struct wl_display *display)

Block until all pending request are processed by the server

The main queue is dispatched by calling wl_display_dispatch(). This will dispatch any events queued on the main queue and attempt to read from the display fd if its empty. Events read are then queued on the appropriate queues according to the proxy assignment. Calling that function makes the calling thread the main thread.

wl_display_dispatch实现

1.wl_display_flush
2.wl_display_poll
3.wl_display_read_events

1.dispatch把对应的消息通过socket发给server

2.dispatch拿到server发来的消息,分发出去

3.frame_listener拿到对应的frame_done消息,执行redraw

4.redraw把对应的消息打包好,通过dispatch发给server

如果不在这个规定的心跳里面更新呢?会造成显示不稳定。我尝试在simple-shm的dispatch里面更新buffer,显示闪烁(我画了一个白屏)

循环

  • 3
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值