Beispiel #1
0
/*refreshes the content of the array to have only non-overlapping rects*/
void ra_refresh(GF_RectArray *ra)
{
	u32 i, j, k;
	for (i=0; i<ra->count; i++) {
		for (j=i+1; j<ra->count; j++) {
			switch (gf_irect_relation(&ra->list[j].rect, &ra->list[i].rect)) {

			/*both rectangles overlap, merge them and remove opaque node info*/
			case 1:
				gf_irect_union(&ra->list[i].rect, &ra->list[j].rect);
#ifdef TRACK_OPAQUE_REGIONS
				/*current dirty rect is no longer opaque*/
				ra->list[i].opaque_node_index = 0;
#endif
			/*FALLTHROUGH*/
			/*first rectangle covers second, just remove*/
			case 2:
				/*remove rect*/
				k = ra->count - j - 1;
				if (k) {
					memmove(&ra->list[j], & ra->list[j+1], sizeof(GF_IRect)*k);
				}
				ra->count--;
				if (ra->count>=2)
					ra_refresh(ra);
				return;
			default:
				break;
			}
		}
	}
}
Beispiel #2
0
Bool visual_2d_terminate_draw(GF_VisualManager *visual, GF_TraverseState *tr_state)
{
	u32 k, i, count, num_nodes, num_changed;
	GF_IRect refreshRect;
	Bool redraw_all;
	Bool hyb_force_redraw=GF_FALSE;
#ifndef GPAC_DISABLE_VRML
	M_Background2D *bck = NULL;
	DrawableContext *bck_ctx = NULL;
#endif
	DrawableContext *ctx;
	struct _drawable_store *it, *prev;
	DrawableContext *first_opaque = NULL;
	Bool has_clear = 0;
	Bool has_changed = 0;
	Bool redraw_all_on_background_change = GF_TRUE;

	/*in direct mode the visual is always redrawn*/
	if (tr_state->immediate_draw) {
		/*flush pending contexts due to overlays*/
		visual_2d_flush_overlay_areas(visual, tr_state);

		visual_2d_release_raster(visual);
		visual_clean_contexts(visual);
		visual->num_nodes_prev_frame = visual->num_nodes_current_frame;
		return 1;
	}

	num_changed = 0;

	/*if the aspect ratio has changed redraw everything*/
	redraw_all = tr_state->invalidate_all;

#ifndef GPAC_DISABLE_3D
	if (visual->compositor->hybrid_opengl && !visual->offscreen) redraw_all_on_background_change = GF_FALSE;
#endif
	/*check for background changes for transparent nodes*/
#ifndef GPAC_DISABLE_VRML
	bck = (M_Background2D*)gf_list_get(visual->back_stack, 0);
	if (bck) {
		if (!bck->isBound) {
			if (visual->last_had_back) {
				if (redraw_all_on_background_change) redraw_all = 1;
				else hyb_force_redraw = 1;
			}
			visual->last_had_back = 0;
		} else {
			bck_ctx = b2d_get_context(bck, visual->back_stack);
			if (!visual->last_had_back || (bck_ctx->flags & CTX_REDRAW_MASK) ) {
				if (redraw_all_on_background_change) redraw_all = 1;
				else hyb_force_redraw = 1;
			}
			visual->last_had_back = (bck_ctx->aspect.fill_texture && !bck_ctx->aspect.fill_texture->transparent) ? 2 : 1;
		}
	} else
#endif
		if (visual->last_had_back) {
			visual->last_had_back = 0;
			if (redraw_all_on_background_change) redraw_all = 1;
			else hyb_force_redraw = 1;
		}

	num_nodes = 0;
	ctx = visual->context;
	while (ctx && ctx->drawable) {
		num_nodes++;

		drawctx_update_info(ctx, visual);
		if (!redraw_all) {
			u32 res;
			assert( gf_irect_inside(&visual->top_clipper, &ctx->bi->clip) );
			res = register_context_rect(&visual->to_redraw, ctx, num_nodes, &first_opaque);
			if (res) {
				num_changed ++;
				if (res==2)
					hyb_force_redraw=GF_TRUE;
			}

		}
		ctx = ctx->next;
	}

	/*garbage collection*/

	/*clear all remaining bounds since last frames (the ones that moved or that are not drawn this frame)*/
	prev = NULL;
	it = visual->prev_nodes;
	while (it) {
		while (drawable_get_previous_bound(it->drawable, &refreshRect, visual)) {
			if (!redraw_all) {
				//assert( gf_irect_inside(&visual->top_clipper, &refreshRect) );
				gf_irect_intersect(&refreshRect, &visual->top_clipper);
				register_dirty_rect(&visual->to_redraw, &refreshRect);
				has_clear=1;
			}
		}
		/*if node is marked as undrawn, remove from visual*/
		if (!(it->drawable->flags & DRAWABLE_DRAWN_ON_VISUAL)) {
			GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Node %s no longer on visual - unregistering it\n", gf_node_get_class_name(it->drawable->node)));

			/*remove all bounds info related to this visual and unreg node */
			drawable_reset_bounds(it->drawable, visual);

			it->drawable->flags &= ~DRAWABLE_REGISTERED_WITH_VISUAL;

			if (it->drawable->flags & DRAWABLE_IS_OVERLAY) {
				visual->compositor->video_out->Blit(visual->compositor->video_out, NULL, NULL, NULL, 1);
			}

			if (prev) prev->next = it->next;
			else visual->prev_nodes = it->next;
			if (!it->next) visual->last_prev_entry = prev;
			gf_free(it);
			it = prev ? prev->next : visual->prev_nodes;
		} else {
			prev = it;
			it = it->next;
		}
	}

	if (redraw_all) {
		ra_clear(&visual->to_redraw);
		ra_add(&visual->to_redraw, &visual->surf_rect);
#ifdef TRACK_OPAQUE_REGIONS
		visual->to_redraw.list[0].opaque_node_index=0;
#endif
	} else {
		ra_refresh(&visual->to_redraw);

		if (visual->compositor->debug_defer) {
			visual->ClearSurface(visual, &visual->top_clipper, 0);
		}
	}

	/*nothing to redraw*/
	if (!hyb_force_redraw && ra_is_empty(&visual->to_redraw) ) {
#ifndef GPAC_DISABLE_3D
		//force canvas draw
		visual->nb_objects_on_canvas_since_last_ogl_flush = 1;
#endif
		GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] No changes found since last frame - skipping redraw\n"));
		goto exit;
	}
	has_changed = 1;
	tr_state->traversing_mode = TRAVERSE_DRAW_2D;

	if (first_opaque && (visual->to_redraw.count==1) && gf_rect_equal(first_opaque->bi->clip, visual->to_redraw.list[0].rect)) {
		visual->has_modif=0;
		goto skip_background;
	}

	/*redraw everything*/
#ifndef GPAC_DISABLE_VRML
	if (bck_ctx) {
		drawable_check_bounds(bck_ctx, visual);
		tr_state->ctx = bck_ctx;
		visual->draw_node_index = 0;

		/*force clearing entire zone, not just viewport, when using color. If texture, we MUST
		use the VP clipper in order to compute offsets when blitting bitmaps*/
		if (bck_ctx->aspect.fill_texture && bck_ctx->aspect.fill_texture->stream) {
			bck_ctx->bi->clip = visual->top_clipper;
		} else {
			bck_ctx->bi->clip = visual->surf_rect;
		}
		bck_ctx->bi->unclip = gf_rect_ft(&bck_ctx->bi->clip);
		bck_ctx->next = visual->context;
		bck_ctx->flags |= CTX_BACKROUND_NOT_LAYER;
		gf_node_traverse(bck_ctx->drawable->node, tr_state);
		bck_ctx->flags &= ~CTX_BACKROUND_NOT_LAYER;
	} else
#endif /*GPAC_DISABLE_VRML*/
	{
		count = visual->to_redraw.count;
		for (k=0; k<count; k++) {
			GF_IRect rc;
			/*opaque area, skip*/
#ifdef TRACK_OPAQUE_REGIONS
			if (visual->to_redraw.list[k].opaque_node_index > 0) continue;
#endif
			rc = visual->to_redraw.list[k].rect;
			visual->ClearSurface(visual, &rc, 0);
		}
#ifndef GPAC_DISABLE_3D
		if (!count && hyb_force_redraw) {
			compositor_2d_hybgl_clear_surface_ex(tr_state->visual, NULL, 0, GF_FALSE);
		}
#endif
	}
	if (!redraw_all && !has_clear) visual->has_modif=0;

skip_background:

#ifndef GPAC_DISABLE_LOG
	if (gf_log_tool_level_on(GF_LOG_COMPOSE, GF_LOG_INFO)) {
		GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Visual2D] Redraw %d / %d nodes (all: %s - %d dirty rects\n)", num_changed, num_nodes, redraw_all ? "yes" : "no", visual->to_redraw.count));
		if (visual->to_redraw.count>1) GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("\n"));

		for (i=0; i<visual->to_redraw.count; i++) {
			GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("\tDirtyRect #%d: %d:%d@%dx%d\n", i+1, visual->to_redraw.list[i].rect.x, visual->to_redraw.list[i].rect.y, visual->to_redraw.list[i].rect.width, visual->to_redraw.list[i].rect.height));
			assert(visual->to_redraw.list[i].rect.width);
		}
	}
#endif

	visual->draw_node_index = 0;

	ctx = visual->context;
	while (ctx && ctx->drawable) {

		visual->draw_node_index ++;
		tr_state->ctx = ctx;

		/*if overlay we cannot remove the context and cannot draw directly*/
		if (! visual_2d_overlaps_overlay(tr_state->visual, ctx, tr_state)) {

			if (ctx->drawable->flags & DRAWABLE_USE_TRAVERSE_DRAW) {
				gf_node_traverse(ctx->drawable->node, tr_state);
			} else {
				drawable_draw(ctx->drawable, tr_state);
			}
		}
		ctx = ctx->next;
	}
	/*flush pending contexts due to overlays*/
	visual_2d_flush_overlay_areas(visual, tr_state);
#ifndef GPAC_DISABLE_VRML
	if (bck_ctx) bck_ctx->next = NULL;
#endif

	if (visual->direct_flush) {
		GF_DirtyRectangles dr;
		u32 i;
		dr.count = visual->to_redraw.count;
		dr.list = gf_malloc(sizeof(GF_IRect)*dr.count);
		for (i=0; i<dr.count; i++) {
			dr.list[i] = visual->to_redraw.list[i].rect;
		}
		visual->compositor->video_out->FlushRectangles(visual->compositor->video_out, &dr);
		visual->compositor->skip_flush=1;
		gf_free(dr.list);
	}

exit:
	/*clear dirty rects*/
	ra_clear(&visual->to_redraw);
	visual_2d_release_raster(visual);
	visual_clean_contexts(visual);
	visual->num_nodes_prev_frame = visual->num_nodes_current_frame;
	return has_changed;
}
Beispiel #3
0
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;
}