client的设置方法:
region = wl_compositor_create_region(window->display->compositor);
wl_region_add(region, 0, 0,
window->geometry.width,
window->geometry.height);
wl_surface_set_opaque_region(window->surface, region);
wl_region_destroy(region);
server的对应操作:
static void
compositor_create_region(struct wl_client *client,
struct wl_resource *resource, uint32_t id)
{
struct weston_region *region;
//创建region对象
region = malloc(sizeof *region);
...
pixman_region32_init(®ion->region);
//通过pixman对region进行初始化
region->resource =
wl_resource_create(client, &wl_region_interface, 1, id);
//真正创建weston里面的object,初始化。
...
wl_resource_set_implementation(region->resource, ®ion_interface,
region, destroy_region);
//任然是初始化的一部分。
}
初始化weston_region以及他的resource
static void
region_add(struct wl_client *client, struct wl_resource *resource,
int32_t x, int32_t y, int32_t width, int32_t height)
{
struct weston_region *region = wl_resource_get_user_data(resource);
pixman_region32_union_rect(®ion->region, ®ion->region,
x, y, width, height);
}
//其实就是通过pixman对region创建一个矩形区域。
static void
surface_set_opaque_region(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource)
{
struct weston_surface *surface = wl_resource_get_user_data(resource);
struct weston_region *region;
if (region_resource) {
region = wl_resource_get_user_data(region_resource);
pixman_region32_copy(&surface->pending.opaque,
®ion->region);
} else {
pixman_region32_clear(&surface->pending.opaque);
}
}
//把surface与region进行一个绑定,如果region参数为null。则清空surface的region区域;
当然现在还是更新的pending部分,没有实际应用。不commit都是pending
理论上这个值只设置一次就可以。但是建议在不性能考虑的时候每次commit都设置一下。
static void
region_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
//释放resource
总的来说很简单,client遵循完全一致的操作逻辑,server这边也是中规中矩的简单操作。后面就要看commit以及后续怎么处理region,如下是commit以后server对pending state的处理
/* wl_surface.set_opaque_region */
pixman_region32_init(&opaque);
pixman_region32_intersect_rect(&opaque, &state->opaque,
0, 0, surface->width, surface->height);
//主要是和surface的区域做一个交集的动作,取交集部分作为不透明区域
if (!pixman_region32_equal(&opaque, &surface->opaque)) {
pixman_region32_copy(&surface->opaque, &opaque);
//把对应的区域拷贝到surface里面,而不是pending的了。
wl_list_for_each(view, &surface->views, surface_link)
weston_view_geometry_dirty(view);
//简单也简单,就是view->transform.dirty = 1;属性
}
pixman_region32_fini(&opaque);
pending 的不透明区域并不会因为一帧完整的commit而消失,所以理论上设置一次即可。
关于region这块区域要从头开始,先是在view的创建过程中创建region:
WL_EXPORT struct weston_view *
weston_view_create(struct weston_surface *surface)
{
struct weston_view *view;
...
view->surface = surface;
view->plane = &surface->compositor->primary_plane;
/* Assign to surface */
wl_list_insert(&surface->views, &view->surface_link);
wl_signal_init(&view->destroy_signal);
wl_list_init(&view->link);
wl_list_init(&view->layer_link.link);
pixman_region32_init(&view->clip);
view->alpha = 1.0;
pixman_region32_init(&view->transform.opaque);
wl_list_init(&view->geometry.transformation_list);
wl_list_insert(&view->geometry.transformation_list,
&view->transform.position.link);
weston_matrix_init(&view->transform.position.matrix);
wl_list_init(&view->geometry.child_list);
pixman_region32_init(&view->geometry.scissor);
pixman_region32_init(&view->transform.boundingbox);
view->transform.dirty = 1;
return view;
}
boundingbox区域(view的全局坐标):
weston_view_update_transform_enable:
pixman_region32_init_rect(&surfregion, 0, 0,
view->surface->width, view->surface->height);
weston_view_update_transform_scissor(view, &surfregion);
surfbox = pixman_region32_extents(&surfregion);
view_compute_bbox(view, surfbox, &view->transform.boundingbox);
pixman_region32_fini(&surfregion);
先创建一个完整的surface区域:0,0,w,h。
如果有裁剪,则对surface区域做一个裁剪,此时任然是局部的。
static void
view_compute_bbox(struct weston_view *view, const pixman_box32_t *inbox,
pixman_region32_t *bbox)
{
float min_x = HUGE_VALF, min_y = HUGE_VALF;
float max_x = -HUGE_VALF, max_y = -HUGE_VALF;
int32_t s[4][2] = {
{ inbox->x1, inbox->y1 },
{ inbox->x1, inbox->y2 },
{ inbox->x2, inbox->y1 },
{ inbox->x2, inbox->y2 },
};
//确立矩形区域
float int_x, int_y;
int i;
if (inbox->x1 == inbox->x2 || inbox->y1 == inbox->y2) {
/* avoid rounding empty bbox to 1x1 */
pixman_region32_init(bbox);
return;
}
//如果x1与x2相等,就是没宽度,如果y1与y2相等,就是没长度,也就是个0
for (i = 0; i < 4; ++i) {
float x, y;
weston_view_to_global_float(view, s[i][0], s[i][1], &x, &y);
if (x < min_x)
min_x = x;
if (x > max_x)
max_x = x;
if (y < min_y)
min_y = y;
if (y > max_y)
max_y = y;
}
int_x = floorf(min_x);
int_y = floorf(min_y);
pixman_region32_init_rect(bbox, int_x, int_y,
ceilf(max_x) - int_x, ceilf(max_y) - int_y);
//基本上就是 surface.x + view->geomerty.x 与 surface.y + view->geomerty.y和对应的w,h。
}
WL_EXPORT void
weston_view_to_global_float(struct weston_view *view,
float sx, float sy, float *x, float *y)
{
if (view->transform.enabled) {
struct weston_vector v = { { sx, sy, 0.0f, 1.0f } };
weston_matrix_transform(&view->transform.matrix, &v);
if (fabsf(v.f[3]) < 1e-6) {
weston_log("warning: numerical instability in "
"%s(), divisor = %g\n", __func__,
v.f[3]);
*x = 0;
*y = 0;
return;
}
*x = v.f[0] / v.f[3];
*y = v.f[1] / v.f[3];
} else {
*x = sx + view->geometry.x;
*y = sy + view->geometry.y;
}
//如果有变化,就要做对应的变化,如果没有,就是surface的x或y + view->geomerty,x或y。
}
weston_view_from_global_float
正好和上面的相反。
weston_view_set_position:设置view的绝对坐标。
weston_view_damage_below
view->transform.boundingbox - view->clip = damage
计算全局的view坐标减去clip的坐标,得到真正需要处理的属于此view的坐标大小,送给plane->damage
clip坐标为其他覆盖在此view上的不透明区域。
weston_surface_assign_output
view->transform,boundingbox & view->output->region
让view的output坐标和 boundingbox坐标取交集。
说是重新计算surface的view在哪些output上面。注意,是通过view所在的outputmask来得到surface所在的output。
weston_view_assign_output
view->transform.boundingbox & output->region
让view的boundingbox坐标和 output坐标取交集,注意这部分是通过output做的遍历。
重新计算view在哪些output上面。
一般是先执行weston_view_assign_output决定了view在哪些output上,然后再调用
weston_surface_assgin_output
weston_view_update_transform_disable
weston_view_update_transform_enable
暂时还不明确用途。
weston_view_update_transform
weston_view_matches_output_entirely
确认view的boundingbox是否覆盖了对应的output的所有区域,如果是返回true,否则返回false。
weston_compositor_pick_view
weston_view_destroy
如名字
view_accumulate_damage
讲述一下gl_renderer_repaint_output
/* NOTE: We now allow falling back to ARGB gl visuals when XRGB is
* unavailable, so we're assuming the background has no transparency
* and that everything with a blend, like drop shadows, will have something
* opaque (like the background) drawn underneath it.
*
* Depending on the underlying hardware, violating that assumption could
* result in seeing through to another display plane.
*/
static void
gl_renderer_repaint_output(struct weston_output *output,
pixman_region32_t *output_damage)
{
weston_log("%s+++\n", __func__);
struct gl_output_state *go = get_output_state(output);
struct weston_compositor *compositor = output->compositor;
struct gl_renderer *gr = get_renderer(compositor);
EGLBoolean ret;
static int errored;
/* areas we've damaged since we last used this buffer */
pixman_region32_t previous_damage;
/* total area we need to repaint this time */
pixman_region32_t total_damage;
enum gl_border_status border_status = BORDER_STATUS_CLEAN;
struct weston_view *view;
if (use_output(output) < 0) //本质就是调用eglMakeCurrent,详细见:https://www.zybuluo.com/SR1s/note/650099
return;
/* Clear the used_in_output_repaint flag, so that we can properly track
* which surfaces were used in this output repaint. */
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;
}
}
//设置这个used_in_output_repaint标志位。这个标志位在调用gpu函数进行绘画时置为true。
而绘画完成后置为false。主要用于说明对应surface所处状态。正在被output_repaint所用。
if (go->begin_render_sync != EGL_NO_SYNC_KHR)
gr->destroy_sync(gr->egl_display, go->begin_render_sync);
if (go->end_render_sync != EGL_NO_SYNC_KHR)
gr->destroy_sync(gr->egl_display, go->end_render_sync);
//名副其实的render sync机制,后面需要学习一下,看上去是用来保证个glrender的开始和结束状态。
go->begin_render_sync = create_render_sync(gr);
/* Calculate the global GL matrix */
go->output_matrix = output->matrix;
weston_matrix_translate(&go->output_matrix,
-(output->current_mode->width / 2.0),
-(output->current_mode->height / 2.0), 0);
weston_matrix_scale(&go->output_matrix,
2.0 / output->current_mode->width,
-2.0 / output->current_mode->height, 1);
/* If using shadow, redirect all drawing to it first. */
if (shadow_exists(go)) {
/* XXX: Shadow code does not support resizing. */
assert(output->current_mode->width == go->shadow.width);
assert(output->current_mode->height == go->shadow.height);
glBindFramebuffer(GL_FRAMEBUFFER, go->shadow.fbo);
glViewport(0, 0, go->shadow.width, go->shadow.height);
} else {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width,
go->borders[GL_RENDERER_BORDER_BOTTOM].height,
output->current_mode->width,
output->current_mode->height);
}
/* In fan debug mode, redraw everything to make sure that we clear any
* fans left over from previous draws on this buffer.
* This precludes the use of EGL_EXT_swap_buffers_with_damage and
* EGL_KHR_partial_update, since we damage the whole area. */
if (gr->fan_debug) {
pixman_region32_t undamaged;
pixman_region32_init(&undamaged);
pixman_region32_subtract(&undamaged, &output->region,
output_damage);
gr->fan_debug = false;
repaint_views(output, &undamaged);
gr->fan_debug = true;
pixman_region32_fini(&undamaged);
}
/* previous_damage covers regions damaged in previous paints since we
* last used this buffer */
pixman_region32_init(&previous_damage);
pixman_region32_init(&total_damage); /* total area to redraw */
/* Update previous_damage using buffer_age (if available), and store
* current damaged region for future use. */
output_get_damage(output, &previous_damage, &border_status);
output_rotate_damage(output, output_damage, go->border_status);
/* Redraw both areas which have changed since we last used this buffer,
* as well as the areas we now want to repaint, to make sure the
* buffer is up to date. */
pixman_region32_union(&total_damage, &previous_damage, output_damage);
border_status |= go->border_status;
if (gr->has_egl_partial_update && !gr->fan_debug) {
int n_egl_rects;
EGLint *egl_rects;
/* For partial_update, we need to pass the region which has
* changed since we last rendered into this specific buffer;
* this is total_damage. */
pixman_region_to_egl_y_invert(output, &total_damage,
&egl_rects, &n_egl_rects);
gr->set_damage_region(gr->egl_display, go->egl_surface,
egl_rects, n_egl_rects);
free(egl_rects);
}
if (shadow_exists(go)) {
/* Repaint into shadow. */
if (compositor->test_data.test_quirks.gl_force_full_redraw_of_shadow_fb)
repaint_views(output, &output->region);
else
repaint_views(output, output_damage);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width,
go->borders[GL_RENDERER_BORDER_BOTTOM].height,
output->current_mode->width,
output->current_mode->height);
blit_shadow_to_output(output, &total_damage);
} else {
repaint_views(output, &total_damage);
}
pixman_region32_fini(&total_damage);
pixman_region32_fini(&previous_damage);
draw_output_borders(output, border_status);
wl_signal_emit(&output->frame_signal, output_damage);
go->end_render_sync = create_render_sync(gr);
if (gr->swap_buffers_with_damage && !gr->fan_debug) {
int n_egl_rects;
EGLint *egl_rects;
/* For swap_buffers_with_damage, we need to pass the region
* which has changed since the previous SwapBuffers on this
* surface - this is output_damage. */
pixman_region_to_egl_y_invert(output, output_damage,
&egl_rects, &n_egl_rects);
ret = gr->swap_buffers_with_damage(gr->egl_display,
go->egl_surface,
egl_rects, n_egl_rects);
free(egl_rects);
} else {
ret = eglSwapBuffers(gr->egl_display, go->egl_surface);
}
if (ret == EGL_FALSE && !errored) {
errored = 1;
weston_log("Failed in eglSwapBuffers.\n");
gl_renderer_print_egl_error_state();
}
go->border_status = BORDER_STATUS_CLEAN;
/* We have to submit the render sync objects after swap buffers, since
* the objects get assigned a valid sync file fd only after a gl flush.
*/
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);
weston_log("%s---\n", __func__);
}
随后的调用就在draw_view也就是合成的地方了
/* repaint bounding region in global coordinates: */
pixman_region32_t repaint;
/* opaque region in surface coordinates: */
pixman_region32_t surface_opaque;
/* non-opaque region in surface coordinates: */
pixman_region32_t surface_blend;
//总共三个区域,一个是全局的repaint。
//另外两个则是相对surface的 surface opaque和surface blend
pixman_region32_init(&repaint);
pixman_region32_intersect(&repaint,
&ev->transform.boundingbox, damage);
pixman_region32_subtract(&repaint, &repaint, &ev->clip);
//以上是以全局坐标去考虑区域的。也就是repaint的区域是全局中的damage区域。再减去一个view的clip区域,
//注意这个是绝对坐标哦!也就是全局坐标
/* blended region is whole surface minus opaque region: */
pixman_region32_init_rect(&surface_blend, 0, 0,
ev->surface->width, ev->surface->height);
//先假设全部区域都是可混合的,再找到最终和混合区域
if (ev->geometry.scissor_enabled)
pixman_region32_intersect(&surface_blend, &surface_blend,
&ev->geometry.scissor);
pixman_region32_subtract(&surface_blend, &surface_blend,
&ev->surface->opaque);
//合成的时候是从下往上画的。上面用到opaque region也很简单,这个view哪些部分是blend区域,
//哪部分是opaque区域。注意这个坐标是对应surface自己的相对坐标哦!
/* XXX: Should we be using ev->transform.opaque here? */
pixman_region32_init(&surface_opaque);
if (ev->geometry.scissor_enabled)
pixman_region32_intersect(&surface_opaque,
&ev->surface->opaque,
&ev->geometry.scissor);
else
pixman_region32_copy(&surface_opaque, &ev->surface->opaque);
//同样的,找到相对应surface的相对的不透明区域
....
....
if (pixman_region32_not_empty(&surface_opaque)) {
struct gl_shader_config alt = sconf;
if (alt.req.variant == SHADER_VARIANT_RGBA) {
/* Special case for RGBA textures with possibly
* bad data in alpha channel: use the shader
* that forces texture alpha = 1.0.
* Xwayland surfaces need this.
*/
alt.req.variant = SHADER_VARIANT_RGBX;
}
if (ev->alpha < 1.0)
glEnable(GL_BLEND);
else
glDisable(GL_BLEND);
repaint_region(gr, ev, &repaint, &surface_opaque, &alt);
gs->used_in_output_repaint = true;
}
if (pixman_region32_not_empty(&surface_blend)) {
glEnable(GL_BLEND);
repaint_region(gr, ev, &repaint, &surface_blend, &sconf);
gs->used_in_output_repaint = true;
}
//先画不透明的区域,然后画除此之外的blending区域,那传入的一个是全局坐标,
//一个是相对surface的坐标,是如何真正完成绘画的呢?
要绘制的最后一个区域是“region”和“surf_region”的交集。 但是,'region' 在全局坐标中,'surf_region' 在表面局部坐标中。 texture_region() 将迭代来自两个区域的所有矩形对,计算每对矩形的交集多边形,如果它具有非零区域(实际上至少有 3 个顶点),则将其存储为三角形扇形。
static void
repaint_region(struct gl_renderer *gr,
struct weston_view *ev,
pixman_region32_t *region,
pixman_region32_t *surf_region,
const struct gl_shader_config *sconf)
{
GLfloat *v;
unsigned int *vtxcnt;
int i, first, nfans;
/* The final region to be painted is the intersection of
* 'region' and 'surf_region'. However, 'region' is in the global
* coordinates, and 'surf_region' is in the surface-local
* coordinates. texture_region() will iterate over all pairs of
* rectangles from both regions, compute the intersection
* polygon for each pair, and store it as a triangle fan if
* it has a non-zero area (at least 3 vertices, actually).
*/
nfans = texture_region(ev, region, surf_region);
v = gr->vertices.data;
vtxcnt = gr->vtxcnt.data;
/* position: */
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[0]);
glEnableVertexAttribArray(0);
/* texcoord: */
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[2]);
glEnableVertexAttribArray(1);
if (!gl_renderer_use_program(gr, sconf)) {
gl_renderer_send_shader_error(ev);
/* continue drawing with the fallback shader */
}
for (i = 0, first = 0; i < nfans; i++) {
glDrawArrays(GL_TRIANGLE_FAN, first, vtxcnt[i]);
if (gr->fan_debug)
triangle_fan_debug(gr, sconf, first, vtxcnt[i]);
first += vtxcnt[i];
}
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);
gr->vertices.size = 0;
gr->vtxcnt.size = 0;
}
NOTE:
build view list的时候是从上到下的顺序(先塞入的在view list的最下面)。draw view的时候,是从下到上。
下,即离我们最远的,如bakcground开始。
上,即离我们最近的,如focus。
[21:27:53.413] weston_compositor_build_view_list : ++++
[21:27:53.413] view_list_add : view:0x5619cd4097c0++++
[21:27:53.413] weston_view_update_transform : view:0x5619cd4097c0++++
[21:27:53.413] view_list_add : view:0x5619cd2149d0++++
[21:27:53.413] weston_view_update_transform : view:0x5619cd2149d0++++
[21:27:53.413] view_list_add : view:0x5619cd401d80++++
[21:27:53.413] weston_view_update_transform : view:0x5619cd401d80++++
[21:27:53.413] weston_view_damage_below : view:0x5619cd401d80++++
[21:27:53.413] weston_view_damage_below : view:0x5619cd401d80----
[21:27:53.413] weston_view_update_transform_enable : view:0x5619cd401d80++++
[21:27:53.413] view_compute_bbox++++
[21:27:53.413] view_compute_bbox----
[21:27:53.413] weston_view_update_transform_enable : view:0x5619cd401d80----
[21:27:53.413] weston_view_damage_below : view:0x5619cd401d80++++
[21:27:53.413] weston_view_damage_below : view:0x5619cd401d80----
[21:27:53.413] weston_view_assign_output : view:0x5619cd401d80++++
[21:27:53.413] weston_surface_assign_output : surface:0x5619cd365ae0++++
[21:27:53.413] weston_surface_assign_output : surface:0x5619cd365ae0----
[21:27:53.413] weston_view_assign_output : view:0x5619cd401d80----
[21:27:53.413] weston_view_update_transform : view:0x5619cd401d80----
[21:27:53.413] view_list_add : view:0x5619cd235680++++
[21:27:53.413] weston_view_update_transform : view:0x5619cd235680++++
[21:27:53.413] weston_compositor_build_view_list : ----
[21:27:53.413] output_accumulate_damage : ++++
[21:27:53.413] view_accumulate_damage : view:0x5619cd4097c0++++
[21:27:53.414] view_accumulate_damage : view:0x5619cd4097c0----
[21:27:53.414] view_accumulate_damage : view:0x5619cd2149d0++++
[21:27:53.414] view_accumulate_damage : view:0x5619cd2149d0----
[21:27:53.414] view_accumulate_damage : view:0x5619cd401d80++++
[21:27:53.414] view_compute_bbox++++
[21:27:53.414] view_accumulate_damage : view:0x5619cd401d80----
[21:27:53.414] view_accumulate_damage : view:0x5619cd235680++++
[21:27:53.414] view_accumulate_damage : view:0x5619cd235680----
[21:27:53.414] output_accumulate_damage : ---
[21:27:53.414] gl_renderer_repaint_output+++
[21:27:53.414] repaint_views
[21:27:53.414] repaint_views:plane 0 0
[21:27:53.414] draw_view : view:0x5619cd235680++++
[21:27:53.414] damage region 0 0 1024 640
[21:27:53.414] boundingbox region 0 0 1024 640
[21:27:53.414] repaint region= boundingbox & damage 0 0 1024 640
[21:27:53.414] surface blend region 0 0 0 0
[21:27:53.414] surface opaque region 0 0 1024 640
[21:27:53.414] repaint opaque region++
[21:27:53.414] repaint_region:region 0 0 1024 640
[21:27:53.414] repaint_region:surf region 0 0 1024 640
[21:27:53.414] repaint opaque region--
[21:27:53.414] draw_view : view:0x5619cd235680----
[21:27:53.414] repaint_views:plane 0 0
[21:27:53.414] draw_view : view:0x5619cd401d80++++
[21:27:53.414] damage region 0 0 1024 640
[21:27:53.414] boundingbox region 175 93 981 584
[21:27:53.414] repaint region= boundingbox & damage 175 93 981 584
[21:27:53.414] surface blend region 0 0 806 491
[21:27:53.414] surface opaque region 35 35 771 456
[21:27:53.414] repaint opaque region++
[21:27:53.414] repaint_region:region 175 93 981 584
[21:27:53.414] repaint_region:surf region 35 35 771 456
[21:27:53.414] repaint opaque region--
[21:27:53.414] repaint blend region++
[21:27:53.414] repaint_region:region 175 93 981 584
[21:27:53.414] repaint_region:surf region 0 0 806 491
[21:27:53.414] repaint blend region--
[21:27:53.414] draw_view : view:0x5619cd401d80----
[21:27:53.414] repaint_views:plane 0 0
[21:27:53.414] draw_view : view:0x5619cd2149d0++++
[21:27:53.414] damage region 0 0 1024 640
[21:27:53.414] boundingbox region 0 0 1024 32
[21:27:53.414] repaint region= boundingbox & damage 0 0 1024 32
[21:27:53.414] surface blend region 0 0 1024 32
[21:27:53.414] surface opaque region 0 0 0 0
[21:27:53.414] repaint blend region++
[21:27:53.414] repaint_region:region 0 0 1024 32
[21:27:53.414] repaint_region:surf region 0 0 1024 32
[21:27:53.414] repaint blend region--
[21:27:53.414] draw_view : view:0x5619cd2149d0----
[21:27:53.414] repaint_views:plane 0 0
[21:27:53.414] draw_view : view:0x5619cd4097c0++++
[21:27:53.414] damage region 0 0 1024 640
[21:27:53.414] boundingbox region 1003 -3 1035 29
[21:27:53.414] repaint region= boundingbox & damage 1003 0 1024 29
[21:27:53.414] surface blend region 0 0 32 32
[21:27:53.414] surface opaque region 0 0 0 0
[21:27:53.414] repaint blend region++
[21:27:53.414] repaint_region:region 1003 0 1024 29
[21:27:53.414] repaint_region:surf region 0 0 32 32
[21:27:53.414] repaint blend region--
[21:27:53.414] draw_view : view:0x5619cd4097c0----
[21:27:53.421] gl_renderer_repaint_output---