让我们来回顾一下panel surface的创建过程,panel本身是一个支持widget的window窗口类型。在使用window_create_custom创建窗口的时候,由于目前大部分设备上的cairo都是支持EGL的,因此panel surface 的buffer类型是WINDOW_BUFFER_TYPE_EGL_WINDOW,如果cairo不支持EGL绘制,那么panel surface的buffer类型就是WINDOW_BUFFER_TYPE_SHM。

cr = widget_cairo_create(panel->widget)//创建panel的surface
cairo_paint(cr)//绘制panel数据

EGL surface创建逻辑:

widget_cairo_create
	->cairo_surface = widget_get_cairo_surface(widget)
  	->surface_create_surface(surface, 0)
  		->egl_window_surface_create
  			->surface->base.prepare = egl_window_surface_prepare;
					surface->base.swap = egl_window_surface_swap;
					surface->base.acquire = egl_window_surface_acquire;
				->surface->egl_window = wl_egl_window_create(surface->surface,
						   rectangle->width,
						   rectangle->height);

					surface->egl_surface =weston_platform_create_egl_surface(display->dpy,
						   display->argb_config,
						   surface->egl_window, NULL);

					surface->cairo_surface =
									cairo_gl_surface_create_for_egl(display->argb_device,
											surface->egl_surface,
											rectangle->width,
											rectangle->height);
  		
	->cr = cairo_create(cairo_surface);

cairo_paint的简单调逻辑参考上一节,cairo_paint就直接渲染显示了。

shm surface的创建与绘制

shm类型的window渲染显示的流程总结:

1.attach

根据client端的surface resource在server端分配buffer,给pending state作为pending中的buffer,下面的操作对象均是pending中的buffer。

2.damage

新旧damage区域融合得到新的damage区域。

3.commit

​ 3.1 opengl渲染前的准备

根据shm buffer的类型、大小,设置对应的gl参数。保存到gl_surface_state结构体中。如果纹理和之前存在的不匹配,会手动生成一个合适的新纹理。

​ 3.2 正式渲染显示

weston_output_repaint使用opengl渲染surface并将framebuffer写入drm设备。repaint_flush应用pending state。渲染步骤在repaint循环中。

现在我们以shm类型的window创建为例,了解显示过程。上一节最后有一个细节,在一次idle重绘任务中,cairo绘制window结构体中的main_surface和subsurfaces,window_flush会使用surface_flush先提交所有的subsurfaces,最后再提交main_surface。surface->toysurface在创建的时候使用的是shm_surface_create。因此这里的swap将会使用shm_surface_swap。

surface->toysurface->swap(surface->toysurface,
				  surface->buffer_transform, surface->buffer_scale,
				  &surface->server_allocation);
static void
shm_surface_swap(struct toysurface *base,
		 enum wl_output_transform buffer_transform, int32_t buffer_scale,
		 struct rectangle *server_allocation)
{
	struct shm_surface *surface = to_shm_surface(base);
	struct shm_surface_leaf *leaf = surface->current;

	server_allocation->width =
		cairo_image_surface_get_width(leaf->cairo_surface);
	server_allocation->height =
		cairo_image_surface_get_height(leaf->cairo_surface);

	buffer_to_surface_size (buffer_transform, buffer_scale,
				&server_allocation->width,
				&server_allocation->height);
	//attach
	wl_surface_attach(surface->surface, leaf->data->buffer,
			  surface->dx, surface->dy);
  //dmage
	wl_surface_damage(surface->surface, 0, 0,
			  server_allocation->width, server_allocation->height);
  //commit
	wl_surface_commit(surface->surface);

	DBG_OBJ(surface->surface, "leaf %d busy\n",
		(int)(leaf - &surface->leaf[0]));

	leaf->busy = 1;
	surface->current = NULL;
}

swap的过程分为surface_attach、surface_damage、surface_commit三步。这些函数都是在compositor中定义的。libweston\compositor.c中实现了wl_surface_interface接口,所有wetson中的client调用wl_surface_xxx都会使用这里定义的这些接口。

static const struct wl_surface_interface surface_interface = {
	surface_destroy,
	surface_attach,
	surface_damage,
	surface_frame,
	surface_set_opaque_region,
	surface_set_input_region,
	surface_commit,
	surface_set_buffer_transform,
	surface_set_buffer_scale,
	surface_damage_buffer
};

surface_attach

attach函数拿到client端的buffer,并传给surface->pending state结构体。

static void
surface_attach(struct wl_client *client,
	       struct wl_resource *resource,
	       struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
{
	struct weston_surface *surface = wl_resource_get_user_data(resource);
	struct weston_buffer *buffer = NULL;

	if (buffer_resource) {
    //核心功能:根据client端的resource在server分配buffer
		buffer = weston_buffer_from_resource(buffer_resource);
		if (buffer == NULL) {
			wl_client_post_no_memory(client);
			return;
		}
	}
  //将分配的buffer设置为pending,待更新到current
	weston_surface_state_set_buffer(&surface->pending, buffer);

	surface->pending.sx = sx;
	surface->pending.sy = sy;
	surface->pending.newly_attached = 1;
}

surface_damage

static void
surface_damage(struct wl_client *client,
	       struct wl_resource *resource,
	       int32_t x, int32_t y, int32_t width, int32_t height)
{
	struct weston_surface *surface = wl_resource_get_user_data(resource);

	if (width <= 0 || height <= 0)
		return;

	pixman_region32_union_rect(&surface->pending.damage_surface,
				   &surface->pending.damage_surface,
				   x, y, width, height);
}

pixman_region32_union_rect将原有的damage区域与新的damage区域进行组合,得到新的damage区域。

surface_commit

先对pending中的buffer进行缩放、旋转、长宽有效性等一系列验证,然后weston_surface_commit函数将surface提交给opengl渲染并合成显示。下面会详细讲解surface的渲染显示过程。

->surface_commit
    ->1. weston_surface_is_pending_viewport_source_valid//对pending buffer进行缩放、旋转等数据校验操作
    ->2. weston_surface_is_pending_viewport_dst_size_int//校验surface长宽的有效性
    ->3. surface->pending.acquire_fence_fd//fence_fd有效
  		->wl_shm_buffer_get(surface->pending.buffer->resource)//检查shm buffer
    ->4.weston_surface_commit(surface)//commit surface
    	->4.1 weston_surface_commit_state(surface, &surface->pending)//重点函数
  		->4.2 weston_surface_commit_subsurface_order(surface)
    	->4.3 weston_surface_schedule_repaint//重绘定时器
  
    ->5. weston_subsurface_commit(surface)//subsurface commit

weston_surface_commit_state

先将pending中的buffer提取到surface中。attach的时候需要判断weston_buffer的类型,shm、egl、dmabuf三种类型对应了三种gl render attach函数。

weston_surface_commit_state(操作的是pending buffer)
    1.->weston_surface_attach(surface, state->buffer)
    	weston_buffer_reference(&surface->buffer_ref, buffer)//关联weston_surface和weston_buffer
		surface->compositor->renderer->attach(surface, buffer)//使用合成器里的渲染器gl_renderer_attach
    		gl_renderer_attach_shm//
    		gl_renderer_attach_egl//创建对应surface buffer的egl_image,激活绑定纹理。
    		gl_renderer_attach_dmabuf//使用dmabuf时的函数
    2.//渲染结束清理pending buffer
    3.-> weston_surface_build_buffer_matrix(surface, &surface->surface_to_buffer_matrix)//对surface进行旋转,裁剪,缩放等矩阵操作
    4.->weston_surface_update_size(surface)//设置surface大小
    5.->surface->committed(surface, state->sx, state->sy)
    //若desktop,则surface.c:: weston_desktop_surface_committed, 看上去是设置surface在屏幕上的位置,也就是显示位置,窗口管理一部分;主要是update了size,需要做对应的改动
    6.->apply_damage_buffer//dmage 区域合并
    7.->wl_surface.set_input_region
    8.->wl_surface.frame//插入frame_callbake_list链表
    9.->wl_signal_emit(&surface->commit_signal, surface)
		//!最后发出commit_signal,将output从pending list移除掉并添加进合成器的output_list

这里以shm为例进行深入分析。在repaint之前gl_renderer_attach_shm需要为渲染做一些准备,获取shm buffer,绑定到传入的surface buffer,根据shm buffer的参数设置gl相关的参数(像素格式、大小),纹理不匹配时ensure_textures生成新纹理,gl_surface_state表示这个surface的渲染状态。

static void
gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer,
		       struct wl_shm_buffer *shm_buffer)
{
	struct weston_compositor *ec = es->compositor;
	struct gl_renderer *gr = get_renderer(ec);
	struct gl_surface_state *gs = get_surface_state(es);
	GLenum gl_format[3] = {0, 0, 0};
	GLenum gl_pixel_type;
	int pitch;
	int num_planes;

	buffer->shm_buffer = shm_buffer;
	buffer->width = wl_shm_buffer_get_width(shm_buffer);
	buffer->height = wl_shm_buffer_get_height(shm_buffer);

	num_planes = 1;
	gs->offset[0] = 0;
	gs->hsub[0] = 1;
	gs->vsub[0] = 1;

	switch (wl_shm_buffer_get_format(shm_buffer)) {
	case WL_SHM_FORMAT_XRGB8888:
	...
	case WL_SHM_FORMAT_ARGB8888:
		gs->shader_variant = SHADER_VARIANT_RGBA;
		pitch = wl_shm_buffer_get_stride(shm_buffer) / 4;
		gl_format[0] = GL_BGRA_EXT;
		gl_pixel_type = GL_UNSIGNED_BYTE;
		es->is_opaque = false;
		break;
	case WL_SHM_FORMAT_RGB565:
	...
	case WL_SHM_FORMAT_YUV420:
	...
	case WL_SHM_FORMAT_NV12:
	...
	case WL_SHM_FORMAT_YUYV:
	...
	default:
		weston_log("warning: unknown shm buffer format: %08x\n",
			   wl_shm_buffer_get_format(shm_buffer));
		return;
	}

	//纹理不匹配的时候,生成新纹理
	if (pitch != gs->pitch ||
	    buffer->height != gs->height ||
	    gl_format[0] != gs->gl_format[0] ||
	    gl_format[1] != gs->gl_format[1] ||
	    gl_format[2] != gs->gl_format[2] ||
	    gl_pixel_type != gs->gl_pixel_type ||
	    gs->buffer_type != BUFFER_TYPE_SHM) {
		...
		...
    
		ensure_textures(gs, GL_TEXTURE_2D, num_planes);
	}
}

weston_surface_schedule_repaint(surface)

渲染前的准备工作完成之后,下面就是渲染并显示这个surface,这里才开始真正调用到opengl和drm。

weston_output_schedule_repaint
    ->idle_repaint
    	->start_repaint_loop
    		->drm_output_start_repaint_loop//libweston/backend-drm/drm.c
drmVBlank vbl = {
		.request.type = DRM_VBLANK_RELATIVE,
		.request.sequence = 0,
		.request.signal = 0,
	};
//设置用于输出的vblank同步的类型,传入crtc的pipeline,返回当前的显示器是主显示器,还是
//副显示器
vbl.request.type |= drm_waitvblank_pipe(output->crtc);
//等待vblank事件触发,成功则返回0
ret = drmWaitVBlank(backend->drm.fd, &vbl);

/* Error ret or zero timestamp means failure to get valid timestamp */
if ((ret == 0) && (vbl.reply.tval_sec > 0 || vbl.reply.tval_usec > 0)) {
		//屏幕刷新率计算
		weston_compositor_read_presentation_clock(backend->compositor,
							  &tnow);
		drm_output_update_msc(output, vbl.reply.sequence);
		weston_output_finish_frame(output_base, &ts,
						WP_PRESENTATION_FEEDBACK_INVALID);
}
//第一次启动不会走到这里,在weston_output_finish_frame中启动repaint循环,repaint得到数据后才会走到这里。
pending_state = drm_pending_state_alloc(backend);
//复制pending state到current state
drm_output_state_duplicate(output->state_cur, pending_state,
				   DRM_OUTPUT_STATE_PRESERVE_PLANES);
//在pending状态的时候,更新所有drm output的状态
ret = drm_pending_state_apply(pending_state);

指定了drmvblank的类型为DRM_VBLANK_RELATIVE,设置sequence为当前的vblank计数。

  • DRM_VBLANK_ABSOLUTE: request.sequence是过去某个时间点以来的 vblank 计数,例如系统启动。
  • DRM_VBLANK_RELATIVE: request.sequence是当前值的 vblank 计数。例如 1 指定下一个 vblank。该值可以与这些值的任意组合进行按位或运算:
  • DRM_VBLANK_SECONDARY: 使用第二显示器的 vblank。
  • DRM_VBLANK_EVENT: 立即返回并触发事件回调而不是等待指定的 vblank。

weston_output_finish_frame

output_repaint_timer_handler函数最后会开启一轮新的repaint定时,repaint从而一直循环下去。repaint_begin创建一个合成器的drm_pending_state repaint_data,每一次repaint都要分配一个新的drm_pending_state给drm使用。

weston_output_repaint使用opengl渲染surface并将framebuffer写入drm设备。repaint_flush应用pending state。

weston_output_finish_frame//计算帧率
	->output_repaint_timer_arm//启动repaint定时器
		->output_repaint_timer_handler//定时器溢出处理函数,最后再次启动repaint定时器,从而实现repaint循环。
output_repaint_timer_handler
  //创建一个合成器的drm_pending_state repaint_data,每一次repaint都要分配一个
  //新的drm_pending_state给drm使用。
 ->repaint_data =compositor->backend->repaint_begin
 //数据绘制,渲染完的数据保存在output
 ->weston_output_maybe_repaint(output, &now, repaint_data)
    -->weston_output_repaint(output, repaint_data);
 //送显
 ->compositor->backend->repaint_flush(compositor,
							 repaint_data);

weston_output_repaint

weston_compositor_build_view_list(ec);
if (output->assign_planes && !output->disable_planes) {
    	//assign planes分配planes
		output->assign_planes(output, repaint_data);
	} else {
		wl_list_for_each(ev, &ec->view_list, link) {
			weston_view_move_to_plane(ev, &ec->primary_plane);
	}
//计算damage区域
output_accumulate_damage(output);

pixman_region32_init(&output_damage);
pixman_region32_intersect(&output_damage,
				  &ec->primary_plane.damage, &output->region);
pixman_region32_subtract(&output_damage,
				 &output_damage, &ec->primary_plane.clip);
//repaint渲染出结果
output->repaint(output, &output_damage, repaint_data);
pixman_region32_fini(&output_damage);
//向client端发送done消息
wl_list_for_each_safe(cb, cnext, &frame_callback_list, link) {
		wl_callback_send_done(cb->resource, frame_time_msec);
		wl_resource_destroy(cb->resource);
	}

drm_assign_planes
drm_pending_state *pending_state = repaint_data;
*primary = &output_base->compositor->primary_plane//primary走GPU渲染
drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY//默认使用overlay模式
    
state = drm_output_propose_state(output_base, pending_state, mode); 
//为每个output中的view分配对应的plane,并且分配plane对应的合成器,默认是GPU合成。
//如果这里overlay失败,就会尝试使用mix模式;再失败,就是尝试GPU-ONLY的合成模式
weston_view_move_to_plane(ev, primary);//如果没有assign_planes,直接全部交给primary_plane

drm_output_repaint

output->repaint最终指向的是drm_output_render_gl函数,走到gl_renderer_repaint_output这里,我们终于来到了最核心的opengl渲染部分。通过访问drm compositor的drm_fd来访问drm设备。在opengl画完数据后,drm_fb_get_from_bo将fb写入drm设备(最重要的函数)。

drm_output_render(state, damage)
	->drm_output_render_gl(state, damage)
  			//第一步 gl渲染
        ->gl_renderer_repaint_output
        	->repaint_views
        		->draw_view //opengl渲染过程
  			//第二步 拿到gbm buffer
        ->bo = gbm_surface_lock_front_buffer(output->gbm_surface);//opengl画好以后从gbm_surface中拿到gbm_buffer,gbm buffer object

				//第三步,将buffer写入drm设备。
				//************送显最最核心的函数************
				//通过gbm_bo拿到drm frame buffer
				//ret作为返回值,是drm_fb类型,drm_fb_get_from_bo设置了drm_fb中的drm_fd,以及						buffer的实际数据与大小。
				//其中drm_fb_addfb函数通过drm api(drmModeAddFB2WithModifiers,		
				//drmModeAddFB2,drmModeAddFB)将framebuffer写入进drm设备。
        ->ret = drm_fb_get_from_bo(bo, b, true, BUFFER_GBM_SURFACE);
				
				//返回给scanout_state,为了后续尝试计算新damage区域,如果失败就是0,那么也就是全图
				->ret->gbm_surface = output->gbm_surface;
  ->drmModeCreatePropertyBlob(b->drm.fd, rects, sizeof(*rects) * n_rects, &scanout_state->damage_blob_id);

gl_renderer_repaint_output

gl_renderer_repaint_output中仍然有缩放、旋转操作,设置视图边界的操作。

wl_list_for_each_reverse(view, &compositor->view_list, link) {
		if (view->plane == &compositor->primary_plane) {
			struct gl_surface_state *gs =
				get_surface_state(view->surface);
			gs->used_in_output_repaint = false;
		}
	}
get_surface_state->gl_renderer_create_surface(surface)
  ->
  //将buffer引用和gl surface中的buffer引用关联起来
  weston_buffer_reference(&gs->buffer_ref, buffer);
	weston_buffer_release_reference(&gs->buffer_release_ref,
					es->buffer_release_ref.buffer_release);  

  if (surface->buffer_ref.buffer) {
    //attach到buffer_ref.buffer
    //其中gl_renderer_attach_shm将共享缓存中的内容读到surface中,这里涉及大量的gl参数
    //有gl_renderer_attach_egl、gl_renderer_attach_dmabuf
		gl_renderer_attach(surface, surface->buffer_ref.buffer);
    //将buffer_ref.buffer
    //刷新damage区域
		gl_renderer_flush_damage(surface);
	}


//gr->create_sync(gr->egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID,attribs)

//调用EGL的"eglCreateSyncKHR"创建一个sync对象
go->begin_render_sync = create_render_sync(gr);
//渲染damage区域
repaint_views(output, &total_damage);

wl_signal_emit(&output->frame_signal, output_damage);
//
go->end_render_sync = create_render_sync(gr);
//swapSwapBuffers的作用
ret = eglSwapBuffers(gr->egl_display, go->egl_surface);
//在swap buffer之后必须提交render sync 对象,只有在flush之后, render sync
//对象才真正拥有一个有效的sync file fd
timeline_submit_render_sync(gr, output, go->begin_render_sync,
				    TIMELINE_RENDER_POINT_TYPE_BEGIN);
timeline_submit_render_sync(gr, output, go->end_render_sync,
				    TIMELINE_RENDER_POINT_TYPE_END);

update_buffer_release_fences(compositor, output);

gl_renderer_garbage_collect_programs(gr);

repaint_views

static void
repaint_views(struct weston_output *output, pixman_region32_t *damage)
{
	struct weston_compositor *compositor = output->compositor;
	struct weston_view *view;

	wl_list_for_each_reverse(view, &compositor->view_list, link)
		if (view->plane == &compositor->primary_plane)
      //!
			draw_view(view, output, damage);
  			->ensure_surface_buffer_is_ready//等待EGLSyncKHR object
          ->attribs[1] = dup(surface->acquire_fence_fd);//复制文件描述符
  				->sync = gr->create_sync(gr->egl_display,
			       EGL_SYNC_NATIVE_FENCE_ANDROID,
			       attribs);//根据复制的fd创建sync对象
  				->wait_ret = gr->wait_sync(gr->egl_display, sync, 0);//阻塞等待egl完成fence
  				->destroy_ret = gr->destroy_sync(gr->egl_display, sync);//完成后摧毁sync对象
        ->gl_shader_config_init_for_view//配置输入纹理的着色器,surface的旋转矩阵、边缘滤波器种类(linear/nearest)
          //blend之后,opengl进行渲染
        ->	if (pixman_region32_not_empty(&surface_blend)) {
									glEnable(GL_BLEND);
          				//GL渲染
									repaint_region(gr, ev, &repaint, &surface_blend, &sconf);
									gs->used_in_output_repaint = true;
						}
}

实际渲染过程repaint_region,使能顶点着色器/片段着色器,打开shader program,glDrawArrays绘制三角形(一个四边形,由两个三角形组成,六个顶点数据)。

drm_repaint_flush

应用pending buffer

 drm_pending_state_apply
 	->drm_pending_state_apply_atomic
        ->drm_output_apply_state_atomic//注意是以plane_list遍历
            ->drmModeAtomicCommit //这个就是commit,没什么好讲的;

将pending state切到current后,终于完成了渲染显示整个流程。

drm_output_enable

drm函数的初始化过程。

static int
drm_output_enable(struct weston_output *base)
{
	struct drm_output *output = to_drm_output(base);
	struct drm_backend *b = to_drm_backend(base->compositor);
	int ret;

	assert(!output->virtual);

	ret = drm_output_attach_crtc(output);
	if (ret < 0)
		return -1;

	ret = drm_output_init_planes(output);
	if (ret < 0)
		goto err_crtc;

	if (drm_output_init_gamma_size(output) < 0)
		goto err_planes;

	if (b->pageflip_timeout)
		drm_output_pageflip_timer_create(output);

	if (b->use_pixman) {
		if (drm_output_init_pixman(output, b) < 0) {
			weston_log("Failed to init output pixman state\n");
			goto err_planes;
		}
	} else if (drm_output_init_egl(output, b) < 0) {
		weston_log("Failed to init output gl state\n");
		goto err_planes;
	}

	drm_output_init_backlight(output);

	output->base.start_repaint_loop = drm_output_start_repaint_loop;
	output->base.repaint = drm_output_repaint;
	output->base.assign_planes = drm_assign_planes;
	output->base.set_dpms = drm_set_dpms;
	output->base.switch_mode = drm_output_switch_mode;
	output->base.set_gamma = drm_output_set_gamma;

	weston_log("Output %s (crtc %d) video modes:\n",
		   output->base.name, output->crtc->crtc_id);
	drm_output_print_modes(output);

	return 0;

err_planes:
	drm_output_deinit_planes(output);
err_crtc:
	drm_output_detach_crtc(output);
	return -1;
}

欢迎关注【求密勒实验室】
weston中panel数据合成commit_缩放