void group_2d_destroy_svg(GF_Node *node, GroupingNode2D *group) { #ifdef GF_SR_USE_VIDEO_CACHE GF_Compositor *compositor = gf_sc_get_compositor(node); if (gf_cache_remove_entry(compositor, node, group)) { /*simulate a zoom changed for cache recompute*/ compositor->zoom_changed = 1; compositor->draw_next_frame = 1; } if (group->cache) group_cache_del(group->cache); #endif }
static void TraverseOffscreenGroup(GF_Node *node, void *rs, Bool is_destroy) { OffscreenGroupStack *stack = (OffscreenGroupStack *)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *) rs; if (is_destroy) { if (stack->cache) group_cache_del(stack->cache); gf_free(stack); return; } if (tr_state->traversing_mode==TRAVERSE_SORT) { if (!stack->detached && (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY)) { OffscreenGroup_GetNode(node, &stack->og); if (stack->og.offscreen) { stack->flags |= GROUP_IS_CACHED | GROUP_PERMANENT_CACHE; if (!stack->cache) { stack->cache = group_cache_new(tr_state->visual->compositor, (GF_Node*)&stack->og); } stack->cache->opacity = stack->og.opacity; stack->cache->drawable->flags |= DRAWABLE_HAS_CHANGED; } else { if (stack->cache) group_cache_del(stack->cache); stack->cache = NULL; stack->flags &= ~(GROUP_IS_CACHED|GROUP_PERMANENT_CACHE); } gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); /*flag is not set for PROTO*/ gf_node_dirty_set(node, GF_SG_CHILD_DIRTY, 0); } if (stack->cache) { if (stack->detached) gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY); tr_state->subscene_not_over = 0; group_cache_traverse((GF_Node *)&stack->og, stack->cache, tr_state, stack->cache->force_recompute, 1, stack->detached ? 1 : 0); if (gf_node_dirty_get(node)) { gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY); } else if ((stack->og.offscreen==2) && !stack->detached && !tr_state->subscene_not_over && stack->cache->txh.width && stack->cache->txh.height) { GF_FieldInfo field; if (gf_node_get_field(node, 0, &field) == GF_OK) { gf_node_unregister_children(node, *(GF_ChildNodeItem **) field.far_ptr); *(GF_ChildNodeItem **) field.far_ptr = NULL; stack->detached = 1; } if (gf_node_get_field(node, 3, &field) == GF_OK) { *(SFBool *) field.far_ptr = 1; //gf_node_event_out(node, 3); } } } else { group_2d_traverse((GF_Node *)&stack->og, (GroupingNode2D*)stack, tr_state); } } /*draw mode*/ else if (stack->cache && (tr_state->traversing_mode == TRAVERSE_DRAW_2D)) { /*draw it*/ group_cache_draw(stack->cache, tr_state); gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY); } else if (!stack->detached) { group_2d_traverse((GF_Node *)&stack->og, (GroupingNode2D*)stack, tr_state); } else { if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { tr_state->bounds = stack->bounds; } else if (tr_state->traversing_mode == TRAVERSE_PICK) { vrml_drawable_pick(stack->cache->drawable, tr_state); } } }
Bool group_2d_cache_traverse(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state) { Bool is_dirty = gf_node_dirty_get(node) & GF_SG_CHILD_DIRTY; Bool zoom_changed = tr_state->visual->compositor->zoom_changed; Bool needs_recompute = 0; /*we are currently in a group cache, regular traversing*/ if (tr_state->in_group_cache) return 0; /*draw mode*/ if (tr_state->traversing_mode == TRAVERSE_DRAW_2D) { /*shall never happen*/ assert(group->cache); /*draw it*/ group_cache_draw(group->cache, tr_state); return 1; } /*other modes than sorting, use regular traversing*/ if (tr_state->traversing_mode != TRAVERSE_SORT) return 0; /*this is not an offscreen group*/ if (!(group->flags & GROUP_IS_CACHED) ) { Bool cache_on = 0; /*group cache has been turned on in the previous frame*/ if (!is_dirty && (group->flags & GROUP_IS_CACHABLE)) { group->flags |= GROUP_IS_CACHED; group->flags &= ~GROUP_IS_CACHABLE; GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning group %s cache on - size %d\n", gf_node_get_log_name(node), group->cached_size )); cache_on = 1; } /*group cache has been turned off in the previous frame*/ else if (group->cache) { group_cache_del(group->cache); group->cache = NULL; group->changed = is_dirty; group->nb_stats_frame = 0; group->traverse_time = 0; GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning group %s cache off\n", gf_node_get_log_name(node) )); return 0; } if (!cache_on) { if (is_dirty) { group->changed = 1; } /*ask for stats again*/ else if (group->changed) { group->changed = 0; group->nb_stats_frame = 0; group->traverse_time = 0; } else if (zoom_changed) { group->nb_stats_frame = 0; group->traverse_time = 0; } if (is_dirty || (group->nb_stats_frame < NUM_STATS_FRAMES)) { /*force direct draw mode*/ if (!is_dirty) tr_state->visual->compositor->traverse_state->invalidate_all = 1; /*force redraw*/ tr_state->visual->compositor->draw_next_frame = 1; } return 0; } } /*cache is dirty*/ else if (is_dirty) { /*permanent cache, just recompute*/ if (group->flags & GROUP_PERMANENT_CACHE) { group->changed = 1; group->cache->force_recompute = 1; } /*otherwise destroy the cache*/ else if (group->cache) { gf_cache_remove_entry(tr_state->visual->compositor, node, group); group_cache_del(group->cache); group->cache = NULL; group->flags &= ~GROUP_IS_CACHED; group->changed = 0; group->nb_stats_frame = 0; group->traverse_time = 0; GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning group %s cache off due to sub-tree modifications\n", gf_node_get_log_name(node) )); return 0; } } /*zoom has changed*/ else if (zoom_changed) { /*permanent cache, just recompute*/ if (group->flags & GROUP_PERMANENT_CACHE) { group->changed = 1; group->cache->force_recompute = 1; } /*otherwise check if we accept this scale ratio or if we must recompute*/ else if (group->cache) { Fixed scale = MAX(tr_state->transform.m[0], tr_state->transform.m[4]); if (100*scale >= group->cache->scale*(100 + tr_state->visual->compositor->cache_tolerance)) zoom_changed = 1; else if ((100+tr_state->visual->compositor->cache_tolerance)*scale <= 100*group->cache->scale) zoom_changed = 1; else zoom_changed = 0; if (zoom_changed) { gf_cache_remove_entry(tr_state->visual->compositor, node, group); group_cache_del(group->cache); group->cache = NULL; group->flags &= ~GROUP_IS_CACHED; group->changed = 0; group->nb_stats_frame = 0; group->traverse_time = 0; GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning group %s cache off due to zoom changes\n", gf_node_get_log_name(node) )); return 0; } } } /*keep track of this cache object for later removal*/ if (!(group->flags & GROUP_PERMANENT_CACHE)) gf_list_add(tr_state->visual->compositor->cached_groups_queue, group); if (!group->cache) { /*ALLOCATE THE CACHE*/ group->cache = group_cache_new(tr_state->visual->compositor, node); needs_recompute = 1; } /*cache has been modified due to node changes, reset stats*/ group_cache_traverse(node, group->cache, tr_state, needs_recompute, 1, 0); return 1; }
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; }
static void svg_traverse_g(GF_Node *node, void *rs, Bool is_destroy) { GF_Matrix2D backup_matrix; GF_Matrix mx_3d; SVGPropertiesPointers backup_props; u32 backup_flags; u32 styling_size = sizeof(SVGPropertiesPointers); GF_TraverseState *tr_state = (GF_TraverseState *) rs; SVGAllAttributes all_atts; if (is_destroy) { SVGgStack *group = gf_node_get_private(node); #ifdef GF_SR_USE_VIDEO_CACHE group_2d_destroy_svg(node, group); #else if (group->cache) group_cache_del(group->cache); #endif gf_free(group); gf_sc_check_focus_upon_destroy(node); return; } /*group cache traverse routine*/ else if (tr_state->traversing_mode == TRAVERSE_DRAW_2D) { SVGgStack *group = gf_node_get_private(node); group_cache_draw(group->cache, tr_state); return; } gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) return; if (compositor_svg_is_display_off(tr_state->svg_props)) { /* u32 prev_flags = tr_state->switched_off; tr_state->switched_off = 1; compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); tr_state->switched_off = prev_flags;*/ memcpy(tr_state->svg_props, &backup_props, styling_size); tr_state->svg_flags = backup_flags; return; } compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { gf_sc_get_nodes_bounds(node, ((SVG_Element *)node)->children, tr_state, NULL); } else if (tr_state->traversing_mode == TRAVERSE_SORT) { #ifdef GF_SR_USE_DEPTH Fixed scale, offset, dscale, doffset; #endif Fixed opacity = FIX_ONE; Bool clear = 0; SVGgStack *group; if (!tr_state->in_svg_filter && all_atts.filter && all_atts.filter->iri.target) { svg_draw_filter(all_atts.filter->iri.target, node, tr_state); return; } group = gf_node_get_private(node); if (tr_state->parent_use_opacity) { opacity = tr_state->parent_use_opacity->value; tr_state->parent_use_opacity = NULL; } if (all_atts.opacity) { opacity = gf_mulfix(opacity, all_atts.opacity->value); } if (gf_node_dirty_get(node)&GF_SG_CHILD_DIRTY) { drawable_reset_group_highlight(tr_state, node); clear=1; } #ifdef GF_SR_USE_DEPTH dscale = FIX_ONE; doffset=0; if (all_atts.gpac_depthGain && all_atts.gpac_depthGain->type==SVG_NUMBER_VALUE) dscale = all_atts.gpac_depthGain->value; if (all_atts.gpac_depthOffset && all_atts.gpac_depthOffset->type==SVG_NUMBER_VALUE) doffset = all_atts.gpac_depthOffset->value; scale = tr_state->depth_gain; offset = tr_state->depth_offset; // new offset is multiplied by parent gain and added to parent offset tr_state->depth_offset = gf_mulfix(doffset, scale) + offset; // gain is multiplied by parent gain tr_state->depth_gain = gf_mulfix(scale, dscale); #endif if (opacity < FIX_ONE) { if (!group->cache) { group->cache = group_cache_new(tr_state->visual->compositor, node); group->cache->force_recompute = 1; } group->cache->opacity = opacity; if (tr_state->visual->compositor->zoom_changed) group->cache->force_recompute = 1; group->flags |= GROUP_IS_CACHED | GROUP_PERMANENT_CACHE; #ifdef GF_SR_USE_VIDEO_CACHE group_2d_cache_traverse(node, group, tr_state); #else group_cache_traverse(node, group->cache, tr_state, group->cache->force_recompute, 0, 0); #endif } else { #ifdef GF_SR_USE_VIDEO_CACHE Bool group_cached; group_cached = group_2d_cache_traverse(node, group, tr_state); gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY); /*group is not cached, traverse the children*/ if (!group_cached) { GF_ChildNodeItem *child; DrawableContext *first_ctx = tr_state->visual->cur_context; u32 cache_too_small = 0; Bool skip_first_ctx = (first_ctx && first_ctx->drawable) ? 1 : 0; u32 traverse_time = gf_sys_clock(); u32 last_cache_idx = gf_list_count(tr_state->visual->compositor->cached_groups_queue); tr_state->cache_too_small = 0; child = ((GF_ParentNode *)node)->children; while (child) { gf_node_traverse(child->node, tr_state); child = child->next; if (tr_state->cache_too_small) cache_too_small++; } if (cache_too_small) { tr_state->cache_too_small = 1; } else { /*get the traversal time for each group*/ traverse_time = gf_sys_clock() - traverse_time; group->traverse_time += traverse_time; /*record the traversal information and turn cache on if possible*/ group_2d_cache_evaluate(node, group, tr_state, first_ctx, skip_first_ctx, last_cache_idx); } } #else compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); #endif } if (clear) gf_node_dirty_clear(node, 0); drawable_check_focus_highlight(node, tr_state, NULL); #ifdef GF_SR_USE_DEPTH tr_state->depth_gain = scale; tr_state->depth_offset = offset; #endif } else { compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); memcpy(tr_state->svg_props, &backup_props, styling_size); tr_state->svg_flags = backup_flags; }