static void visual_2d_fill_path(GF_VisualManager *visual, DrawableContext *ctx, GF_STENCIL stencil, GF_TraverseState *tr_state, Bool is_erase) { Bool has_modif = GF_FALSE; GF_IRect clip; GF_Raster2D *raster = visual->compositor->rasterizer; /*background & direct drawing : use ctx clip*/ if ((ctx->flags & CTX_IS_BACKGROUND) || tr_state->immediate_draw) { if (ctx->bi->clip.width && ctx->bi->clip.height) { GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s[%s] (direct draw)\n", gf_node_get_log_name(ctx->drawable->node), gf_node_get_class_name(ctx->drawable->node) )); if (stencil) { raster->surface_set_clipper(visual->raster_surface, &ctx->bi->clip); raster->surface_fill(visual->raster_surface, stencil); } else { raster->surface_clear(visual->raster_surface, &ctx->bi->clip, 0); } has_modif = GF_TRUE; } } /*indirect drawing, draw path in all dirty areas*/ else { u32 i; for (i=0; i<visual->to_redraw.count; i++) { /*there's an opaque region above, don't draw*/ #ifdef TRACK_OPAQUE_REGIONS if (!is_erase && (visual->draw_node_index<visual->to_redraw.list[i].opaque_node_index)) continue; #endif clip = ctx->bi->clip; gf_irect_intersect(&clip, &visual->to_redraw.list[i].rect); if (clip.width && clip.height) { GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s[%s] (indirect draw @ dirty rect idx %d)\n", gf_node_get_log_name(ctx->drawable->node), gf_node_get_class_name(ctx->drawable->node), i)); if (stencil) { raster->surface_set_clipper(visual->raster_surface, &clip); raster->surface_fill(visual->raster_surface, stencil); } else { raster->surface_clear(visual->raster_surface, &clip, 0); } has_modif = 1; } } } #ifndef GPAC_DISABLE_3D if (!is_erase) visual->nb_objects_on_canvas_since_last_ogl_flush++; #endif if (has_modif) { visual->has_modif = 1; #ifndef GPAC_DISABLE_3D if (!visual->offscreen && visual->compositor->hybrid_opengl && !is_erase) ra_union_rect(&visual->hybgl_drawn, &ctx->bi->clip); #endif } }
Bool group_cache_compute_stats(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state, DrawableContext *first_child, Bool skip_first_child) { GF_Rect group_bounds; DrawableContext *ctx; u32 nb_segments, nb_objects; u32 alpha_pixels, opaque_pixels, area_world; u32 video_cache_max_size, cache_size, prev_cache_size; u32 i; GF_RectArray ra; /*compute stats*/ nb_objects = 0; nb_segments = 0; alpha_pixels = opaque_pixels = 0; prev_cache_size = group->cached_size; /*reset bounds*/ group_bounds.width = group_bounds.height = 0; video_cache_max_size = tr_state->visual->compositor->video_cache_max_size; /*never cache root node - this should be refined*/ if (gf_node_get_parent(node, 0) == NULL) goto group_reject; if (!group->traverse_time) goto group_reject; ra_init(&ra); ctx = first_child; if (!first_child) ctx = tr_state->visual->context; if (skip_first_child) ctx = ctx->next; /*compute properties for the sub display list*/ while (ctx && ctx->drawable) { //Fixed area; u32 alpha_comp; /*get area and compute alpha/opaque coverage*/ alpha_comp = GF_COL_A(ctx->aspect.fill_color); /*add to group area*/ gf_rect_union(&group_bounds, &ctx->bi->unclip); nb_objects++; /*no alpha*/ if ((alpha_comp==0xFF) /*no transparent texture*/ && (!ctx->aspect.fill_texture || !ctx->aspect.fill_texture->transparent) ) { ra_union_rect(&ra, &ctx->bi->clip); } nb_segments += ctx->drawable->path->n_points; ctx = ctx->next; } if ( /*TEST 1: discard visually empty groups*/ (!group_bounds.width || !group_bounds.height) || /*TEST 2: discard small groups*/ (nb_objects<MIN_OBJECTS_IN_CACHE) || /*TEST 3: low complexity group*/ (nb_segments && (nb_segments<10)) ) { ra_del(&ra); goto group_reject; } ra_refresh(&ra); opaque_pixels = 0; for (i=0; i<ra.count; i++) { opaque_pixels += ra.list[i].width * ra.list[i].height; } ra_del(&ra); /*get coverage in world coords*/ area_world = FIX2INT(group_bounds.width) * FIX2INT(group_bounds.height); /*TEST 4: discard low coverage groups in world coords (plenty of space wasted) we consider that this % of the area is actually drawn - this is of course wrong, we would need to compute each path coverage in local coords then get the ratio */ if (10*opaque_pixels < 7*area_world) goto group_reject; /*the memory size allocated for the cache - cache is drawn in final coordinate system !!*/ group_bounds.width = tr_state->visual->compositor->cache_scale * group_bounds.width / 100; group_bounds.height = tr_state->visual->compositor->cache_scale * group_bounds.height / 100; cache_size = FIX2INT(group_bounds.width) * FIX2INT(group_bounds.height) * 4 /* pixelFormat is ARGB*/; /*TEST 5: cache is less than 10x10 pixels: discard*/ if (cache_size < 400) goto group_reject; /*TEST 6: cache is larger than our allowed memory: discard*/ if (cache_size>=video_cache_max_size) { tr_state->cache_too_small = 1; goto group_reject; } /*compute the delta value for measuring the group importance for later discard (avg_time - Tcache) / (size_cache - drawable_gain) */ group->priority = INT2FIX(nb_objects*1024*group->traverse_time) / cache_size / group->nb_stats_frame; /*OK, group is a good candidate for caching*/ group->nb_objects = nb_objects; group->cached_size = cache_size; /*we're moving from non-cached to cached*/ if (!(group->flags & GROUP_IS_CACHABLE)) { group->flags |= GROUP_IS_CACHABLE; tr_state->visual->compositor->draw_next_frame = 1; /*insert the candidate and then update the list in order*/ group_cache_insert_entry(node, group, tr_state); /*keep track of this cache object for later removal*/ gf_list_add(tr_state->visual->compositor->cached_groups_queue, group); GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning cache on during stat pass for node %s - %d kb used in all caches\n", gf_node_get_log_name(node), tr_state->visual->compositor->video_cache_current_size )); } /*update memory occupation*/ else { tr_state->visual->compositor->video_cache_current_size -= prev_cache_size; tr_state->visual->compositor->video_cache_current_size += group->cached_size; if (group->cache) group->cache->force_recompute = 1; } return 1; group_reject: group->nb_objects = nb_objects; if ((group->flags & GROUP_IS_CACHABLE) || group->cache) { group->flags &= ~GROUP_IS_CACHABLE; if (group->cache) { group_cache_del(group->cache); group->cache = NULL; group->flags &= ~GROUP_IS_CACHED; } gf_list_del_item(tr_state->visual->compositor->cached_groups, group); tr_state->visual->compositor->video_cache_current_size -= cache_size; } #if 0 GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] REJECT %s\tObjects: %d\tSlope: %g\tBytes: %d\tTime: %d\n", gf_node_get_log_name(node), group->nb_objects, FIX2FLT(group->priority), group->cached_size, group->traverse_time )); GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Status (B): Max: %d\tUsed: %d\tNb Groups: %d\n", tr_state->visual->compositor->video_cache_max_size, tr_state->visual->compositor->video_cache_current_size, gf_list_count(tr_state->visual->compositor->cached_groups) )); #endif return 0; }
void visual_2d_fill_irect(GF_VisualManager *visual, GF_IRect *rc, u32 fill, u32 strike) { GF_Path *path; GF_Path *outline; GF_PenSettings pen; GF_Raster2D *raster = visual->compositor->rasterizer; #ifdef SKIP_DRAW return; #endif if (!rc) return; if (! visual->CheckAttached(visual) ) return; if (!fill && !strike ) return; /*no aa*/ visual_2d_set_options(visual->compositor, visual->raster_surface, 0, 1); raster->surface_set_matrix(visual->raster_surface, NULL); raster->surface_set_raster_level(visual->raster_surface, GF_RASTER_HIGH_SPEED); raster->surface_set_matrix(visual->raster_surface, NULL); path = gf_path_new(); gf_path_add_move_to(path, INT2FIX(rc->x-1), INT2FIX(rc->y+2-rc->height)); gf_path_add_line_to(path, INT2FIX(rc->x+rc->width-2), INT2FIX(rc->y+2-rc->height)); gf_path_add_line_to(path, INT2FIX(rc->x+rc->width), INT2FIX(rc->y)); gf_path_add_line_to(path, INT2FIX(rc->x), INT2FIX(rc->y)); gf_path_close(path); if (fill) { raster->surface_set_path(visual->raster_surface, path); raster->stencil_set_brush_color(visual->raster_brush, fill); raster->surface_set_clipper(visual->raster_surface, rc); raster->surface_fill(visual->raster_surface, visual->raster_brush); raster->surface_set_path(visual->raster_surface, NULL); } if (strike) { memset(&pen, 0, sizeof(GF_PenSettings)); pen.width = 2; pen.align = GF_PATH_LINE_INSIDE; pen.join = GF_LINE_JOIN_BEVEL; outline = gf_path_get_outline(path, pen); outline->flags &= ~GF_PATH_FILL_ZERO_NONZERO; raster->surface_set_path(visual->raster_surface, outline); raster->stencil_set_brush_color(visual->raster_brush, strike); raster->surface_set_clipper(visual->raster_surface, rc); raster->surface_fill(visual->raster_surface, visual->raster_brush); raster->surface_set_path(visual->raster_surface, NULL); gf_path_del(outline); } gf_path_del(path); #ifndef GPAC_DISABLE_3D if (!visual->offscreen && visual->compositor->hybrid_opengl) ra_union_rect(&visual->hybgl_drawn, rc); #endif }
Bool VS2D_TerminateDraw(VisualSurface2D *surf, RenderEffect2D *eff) { u32 j, k, i, num_to_draw, num_empty, count, max_nodes_allowed; M4IRect refreshRect; Bool redraw_all, is_empty; M_Background2D *bck; DrawableContext *bck_ctx; DrawableContext *ctx; Bool use_direct_render = 0; Bool has_changed = 0; /*in direct mode the surface is always redrawn*/ if (eff->trav_flags & TF_RENDER_DIRECT) { has_changed = 1; use_direct_render = 1; } VS2D_ResetSensors(surf); num_to_draw = 0; /*if the aspect ratio has changed redraw everything*/ redraw_all = eff->invalidate_all; /*check for background changes for transparent nodes*/ bck = NULL; bck_ctx = NULL; if (! use_direct_render) { bck = NULL; if (ChainGetCount(surf->back_stack)) bck = ChainGetEntry(surf->back_stack, 0); if (bck) { if (!bck->isBound) redraw_all = 1; surf->last_had_back = 1; bck_ctx = b2D_GetContext(bck, surf->back_stack); if (bck_ctx->redraw_flags) redraw_all = 1; } else if (surf->last_had_back) { surf->last_had_back = 0; redraw_all = 1; } surf->last_had_back = 0; } max_nodes_allowed = (u32) ((surf->top_clipper.width / MIN_BBOX_SIZE) * (surf->top_clipper.height / MIN_BBOX_SIZE)); num_empty = 0; count = surf->num_contexts; for (i=0; i<count; i++) { ctx = surf->contexts[i]; m4_irect_intersect(&ctx->clip, &surf->top_clipper); is_empty = m4_rect_is_empty(ctx->clip); /*store bounds even in direct render*/ if (!is_empty) { drawable_store_bounds(ctx); register_sensor(surf, ctx); } else if (!use_direct_render) { num_empty++; continue; } //register node to draw surf->nodes_to_draw[num_to_draw] = i; num_to_draw++; if (use_direct_render) { drawable_reset_previous_bounds(ctx->node); continue; } drawctx_update_info(ctx); /*node has changed, add to redraw area*/ if (!redraw_all && ctx->redraw_flags) { ra_union_rect(&surf->to_redraw, ctx->clip); CHECK_MAX_NODE } /*otherwise try to remove any sensor hidden below*/ if (!ctx->transparent) remove_hidden_sensors(surf, num_to_draw, ctx); }