display:weston渲染流程:buffer+attach+damage+frame

渲染流水線

一個Wayland client要將內存渲染到屏幕上,首先要申請一個graphic buffer,繪製完後傳給Wayland compositor並通知其重繪。Wayland compositor收集所有Wayland client的請求,然後以一定週期把所有提交的graphic buffer進行合成。合成完後進行輸出。本質上,client需要將窗口內容繪製到一個和compositor共享的buffer上。這個buffer可以是普通共享內存,也可以是DRM中的GBM或是gralloc提供的可供硬件(如GPU)操作的graphic buffer。在大多數移動平臺上,沒有專門的顯存,因此它們最終都來自系統內存,區別在於圖形加速硬件一般會要求物理連續且符合對齊要求的內存。如果是普通共享內存,一般是物理不連續的,多數情況用於軟件渲染。有些圖形驅動也支持用物理不連續內存做硬件加速,但效率相對會低一些。根據buffer類型的不同,client可以選擇自己繪製,或是通過Cairo,OpenGL繪製,或是更高層的如Qt,GTK+這些widget庫等繪製。繪製完後client將buffer的handle傳給server,以及需要重繪的區域。在server端,compositor將該buffer轉爲紋理(如果是共享內存使用glTexImage2D上傳紋理,硬件加速buffer用GL_OES_EGL_image_external擴展生成外部紋理)。最後將其與其它的窗口內容進行合成。下面是抽象的流程圖。

\

參考https://github.com/wayland-project/weston/blob/master/clients/simple-shm.c &  https://github.com/wayland-project/wayland/blob/master/protocol/wayland.xml

先來張圖:

 

1.buffer

用戶空間:
fd = os_create_anonymous_file(size);   
    用戶空間創建size大小文件,取得fd。
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    將fd用戶空間buffer映射到進程內核空間。
pool = wl_shm_create_pool(display->shm, fd, size);

對應的server端操作:
    struct wl_shm_pool *pool;
    pool->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);[server]
    創建pool。映射同一個fd;此時client與weston達成共享內存。

也通過server端的操作,爲client端的wl_shm_pool賦值。

用戶空間:
buffer->buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, format);

對應的server端操作:
    buffer->pool = pool;

通過pool獲取wl_buffer,對用戶空間的buffer進行賦值。

wl_shm_pool_destroy(pool);
    內部計數器-1,如果pool的計數爲0[destroy pool且destroy buffer之後],則銷燬pool的所有資源

buffer->shm_data = data;
    提供給client的繪畫地址。

 隨即client會對buffer->shm_data空間執行繪畫

2.attach

client:
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
    buffer->buffer是一個指向struct wl_buffer的指針
server:
surface_attach
	if (buffer_resource) {
		buffer = weston_buffer_from_resource(buffer_resource);
        實際就是weston-buffer->resource = buffer_resource;也就是在weston內部去管理這個用戶空間的buffer資源
        ...}
    weston_surface_state_set_buffer(&surface->pending, buffer);
        state->buffer = buffer;
    在通過weston_buffer_from_resource拿到用戶端的buffer以後,把weston_surface_state->buffer與weston_buffer對象綁定;
    buffer是一個指向weston_buffer的指針;實際上是:weston_surface->pending->buffer = buffer;
    /* All the pending state, that wl_surface.commit will apply. */
    對此我理解當wl_surface.commit執行時,會用到這些pending->buffer

1.attach將wl_buffer設置爲pending wl_buffer而不是currect。[wayland 0.99版本之後,都使用了double buffer state,更新的都是pending的buffer,在commit之後,纔將pending.buffer賦值給current buffer,然後clear掉pending.buffer供下次使用。然後,在服務端repaint surface時,會清理掉current.buffer供下次使用。]

本質上是需要window->surface與buffer->buffer 綁定;把用戶空間的buffer管理起來,賦值到weston裏面的weston_buffer結構體中,並且將weston_buffer與他的weston_surface->pending結構體關聯起來。

3.damage

This request is used to describe the regions where the pending buffer is different from the current surface contents, and where the surface therefore needs to be repainted. The compositor ignores the parts of the damage that fall outside of the surface.
此請求用於描述pending->buffer與current->surface->contents不同的區域,以及因此需要重新繪製的surface區域。compositor忽略掉surface以外的部分損壞。

client:
wl_surface_damage(window->surface, x, y, window->width - 40, window->height - 40);
此請求用於描述掛起緩衝區[attach操作中的pending->buffer]與當前表面內容不同的區域,以及表面因此需要重新繪製的位置。合成器忽略掉落在表面之外的部分。
損壞是雙緩衝狀態,其中x和y指定損壞矩形的左上角。
server:
surface_damage
    struct weston_surface *surface = wl_resource_get_user_data(resource);
    pixman_region32_union_rect(&surface->pending.damage_surface,
 				   &surface->pending.damage_surface,
 				   x, y, width, height);
    獲取surface後,將新的damage區域與原有的damage區域進行組合,得到新的damage區域。

4.frame

 Request a notification when it is a good time to start drawing a new frame, by creating a frame callback. This is useful for throttling redrawing operations, and driving animations.

如果客戶端提交的更新時間早於某個更新,則某些更新可能無法顯示,並且客戶端通過過於頻繁的繪製而浪費資源。【callback的作用就是解決上述問題】

    A server should avoid signaling the frame callbacks if the surface is not visible in any way, e.g. the surface is off-screen, or completely obscured by other opaque surfaces.

client:
window->callback = wl_surface_frame(window->surface);
    申請幀繪製回調,每當繪製完一幀就發送wl_callback::done消息給用戶client
server:
surface_frame
    wl_list_insert(surface->pending.frame_callback_list.prev, &cb->link);
    在Server端創建Frame callback,它會被放在該surface下的frame_callback_list列表中。返回它的代理對象wl_callback

 

接下一篇

display:weston渲染流程:commit

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

 

NOTE:

Q: Weston will never use HW planes for wl_shm_buffer, only for GBM or dmabuf type buffer it will be used.

A: That's correct. The reason is that we need to be able to get a KMS framebuffer object with the pixel content from the client buffer in it. Effectively, the only way to import client content into a KMS framebuffer is via dmabuf; KMS has no method of creating framebuffers from an arbitrary pointer to user memory. And we need a framebuffer object in order to display anything on a plane.

 

Direct Memory Access (DMA)

This is the most complex of the data-transfer systems as both the host and the slave can access each others memory in any way they chose. As most !CS or !CprE people can quickly see, that only leads to more problems. So, DMA programming is the most complex of the three normal types of memory access. With DMA, an external chip/processor (let's be honest, a 3D processor counts as an external processor in its own right) can see anything in out memory space and we can see anything in its memory space -- barring limitations from things like the GART. With DMA, a typical interaction with an external chip/ processor is "here's an address at which you can find a complex data structure in my memory space in which you can find the commands and data to describe the operation which I wish you to perform. Have fun. Oh, tell me when you're done." DMA will normally allow the highest level of performance, but it does require some compromises that may sacrifice some latency for bandwidth.

2.commit將pending wl_buffer設置爲currect surface的buffer。

3.surface的大小由buffer決定。

wl_surface.attach assigns the given wl_buffer as the pending wl_buffer. wl_surface.commit makes the pending wl_buffer the new surface contents, and the size of the surface becomes the size calculated from the wl_buffer, as described above. After commit, there is no pending buffer until the next attach.

    Committing a pending wl_buffer allows the compositor to read the pixels in the wl_buffer. The compositor may access the pixels at any time after the wl_surface.commit request. When the compositor will not access the pixels anymore, it will send the wl_buffer.release event. Only after receiving wl_buffer.release, the client may reuse the wl_buffer. A wl_buffer that has been attached and then replaced by another attach instead of committed will not receive a release event, and is not used by the compositor.

    If wl_surface.attach is sent with a NULL wl_buffer, the following wl_surface.commit will remove the surface content. 

    看wayland官方文檔的內容,是允許attch一個NULLbuffer隨後commit的。但是我測試下來client會死等callback消息【卡死】;猜測是否爲client端自己定義timer進行commit的提交能夠實現透明效果,不應該等待server的callback消息。

 

Note:

如何確認client端應用的函數對應的server端的函數:

查看client裏面bind的服務,如:

wl_registry_bind(registry, id, &wl_shm_interface, 1);

同樣的在server端會註冊這個interface,如:

wl_global_create(display, &wl_shm_interface, 1, NULL, bind_shm))

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章