Browse Source

Batching across z_indices

Extra functions canvas_render_items_begin and canvas_render_items_end are added to RasterizerCanvas, with noop stubs for non-GLES2 renderers. This enables batching to be spready over multiple z_indices, and multiple calls to canvas_render_items.

It does this by only performing item joining within canvas_render_items, and deferring rendering until canvas_render_items_end().
lawnjelly 5 years ago
parent
commit
93af8e7d1b

+ 57 - 32
drivers/gles2/rasterizer_canvas_gles2.cpp

@@ -67,16 +67,18 @@ RasterizerCanvasGLES2::BatchData::BatchData() {
 	settings_scissor_threshold = -1.0f;
 	settings_scissor_threshold = -1.0f;
 }
 }
 
 
-RasterizerCanvasGLES2::RenderItemState::RenderItemState() {
-	current_clip = NULL;
-	shader_cache = NULL;
+void RasterizerCanvasGLES2::RenderItemState::reset() {
+	current_clip = nullptr;
+	shader_cache = nullptr;
 	rebind_shader = true;
 	rebind_shader = true;
 	prev_use_skeleton = false;
 	prev_use_skeleton = false;
 	last_blend_mode = -1;
 	last_blend_mode = -1;
 	canvas_last_material = RID();
 	canvas_last_material = RID();
 	item_group_z = 0;
 	item_group_z = 0;
-	item_group_light = 0;
+	item_group_light = nullptr;
 	final_modulate = Color(-1.0, -1.0, -1.0, -1.0); // just something unlikely
 	final_modulate = Color(-1.0, -1.0, -1.0, -1.0); // just something unlikely
+
+	joined_item = nullptr;
 }
 }
 
 
 RasterizerStorageGLES2::Texture *RasterizerCanvasGLES2::_get_canvas_texture(const RID &p_texture) const {
 RasterizerStorageGLES2::Texture *RasterizerCanvasGLES2::_get_canvas_texture(const RID &p_texture) const {
@@ -286,8 +288,8 @@ bool RasterizerCanvasGLES2::prefill_joined_item(FillState &r_fill_state, int &r_
 							_prefill_default_batch(r_fill_state, command_num);
 							_prefill_default_batch(r_fill_state, command_num);
 							break;
 							break;
 						}
 						}
-					}
-				} // if use hardware transform
+					} // if use hardware transform
+				}
 
 
 				Color col = rect->modulate;
 				Color col = rect->modulate;
 				if (multiply_final_modulate) {
 				if (multiply_final_modulate) {
@@ -1523,17 +1525,9 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *p_c
 	render_batches(commands, p_current_clip, r_reclip, p_material);
 	render_batches(commands, p_current_clip, r_reclip, p_material);
 }
 }
 
 
-void RasterizerCanvasGLES2::join_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) {
-	bdata.items_joined.reset();
-	bdata.item_refs.reset();
-
-	RenderItemState render_item_state;
-	render_item_state.item_group_z = p_z;
-	render_item_state.item_group_modulate = p_modulate;
-	render_item_state.item_group_light = p_light;
-	render_item_state.item_group_base_transform = p_base_transform;
+void RasterizerCanvasGLES2::join_items(Item *p_item_list, int p_z) {
 
 
-	BItemJoined *j = 0;
+	_render_item_state.item_group_z = p_z;
 
 
 	// join is whether to join to the previous batch.
 	// join is whether to join to the previous batch.
 	// batch_break is whether to PREVENT the next batch from joining with us
 	// batch_break is whether to PREVENT the next batch from joining with us
@@ -1554,17 +1548,17 @@ void RasterizerCanvasGLES2::join_items(Item *p_item_list, int p_z, const Color &
 			// even though we know join is false.
 			// even though we know join is false.
 			// also we need to run try_join_item for every item because it keeps the state up to date,
 			// also we need to run try_join_item for every item because it keeps the state up to date,
 			// if we didn't run it the state would be out of date.
 			// if we didn't run it the state would be out of date.
-			try_join_item(ci, render_item_state, batch_break);
+			try_join_item(ci, _render_item_state, batch_break);
 		} else {
 		} else {
-			join = try_join_item(ci, render_item_state, batch_break);
+			join = try_join_item(ci, _render_item_state, batch_break);
 		}
 		}
 
 
 		// assume the first item will always return no join
 		// assume the first item will always return no join
 		if (!join) {
 		if (!join) {
-			j = bdata.items_joined.request_with_grow();
-			j->first_item_ref = bdata.item_refs.size();
-			j->num_item_refs = 1;
-			j->bounding_rect = ci->global_rect_cache;
+			_render_item_state.joined_item = bdata.items_joined.request_with_grow();
+			_render_item_state.joined_item->first_item_ref = bdata.item_refs.size();
+			_render_item_state.joined_item->num_item_refs = 1;
+			_render_item_state.joined_item->bounding_rect = ci->global_rect_cache;
 
 
 			// add the reference
 			// add the reference
 			BItemRef *r = bdata.item_refs.request_with_grow();
 			BItemRef *r = bdata.item_refs.request_with_grow();
@@ -1573,23 +1567,22 @@ void RasterizerCanvasGLES2::join_items(Item *p_item_list, int p_z, const Color &
 			// for baking into vertex colors.
 			// for baking into vertex colors.
 			// this may not be ideal... as we are increasing the size of item reference,
 			// this may not be ideal... as we are increasing the size of item reference,
 			// but it is stupidly complex to calculate later, which would probably be slower.
 			// but it is stupidly complex to calculate later, which would probably be slower.
-			r->final_modulate = render_item_state.final_modulate;
+			r->final_modulate = _render_item_state.final_modulate;
 		} else {
 		} else {
-			CRASH_COND(j == 0);
-			j->num_item_refs += 1;
-			j->bounding_rect = j->bounding_rect.merge(ci->global_rect_cache);
+			CRASH_COND(_render_item_state.joined_item == 0);
+			_render_item_state.joined_item->num_item_refs += 1;
+			_render_item_state.joined_item->bounding_rect = _render_item_state.joined_item->bounding_rect.merge(ci->global_rect_cache);
 
 
 			BItemRef *r = bdata.item_refs.request_with_grow();
 			BItemRef *r = bdata.item_refs.request_with_grow();
 			r->item = ci;
 			r->item = ci;
-			r->final_modulate = render_item_state.final_modulate;
+			r->final_modulate = _render_item_state.final_modulate;
 		}
 		}
 
 
 		p_item_list = p_item_list->next;
 		p_item_list = p_item_list->next;
 	}
 	}
 }
 }
 
 
-void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) {
-
+void RasterizerCanvasGLES2::canvas_render_items_begin(const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) {
 	// if we are debugging, flash each frame between batching renderer and old version to compare for regressions
 	// if we are debugging, flash each frame between batching renderer and old version to compare for regressions
 	if (bdata.settings_flash_batching) {
 	if (bdata.settings_flash_batching) {
 		if ((Engine::get_singleton()->get_frames_drawn() % 2) == 0)
 		if ((Engine::get_singleton()->get_frames_drawn() % 2) == 0)
@@ -1598,15 +1591,44 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
 			bdata.settings_use_batching = false;
 			bdata.settings_use_batching = false;
 	}
 	}
 
 
+	if (!bdata.settings_use_batching) {
+		return;
+	}
+
 	// this only needs to be done when screen size changes, but this should be
 	// this only needs to be done when screen size changes, but this should be
 	// infrequent enough
 	// infrequent enough
 	_calculate_scissor_threshold_area();
 	_calculate_scissor_threshold_area();
 
 
-	// state 1 : join similar items, so that their state changes are not repeated,
+	// set up render item state for all the z_indexes (this is common to all z_indexes)
+	_render_item_state.reset();
+	_render_item_state.item_group_modulate = p_modulate;
+	_render_item_state.item_group_light = p_light;
+	_render_item_state.item_group_base_transform = p_base_transform;
+}
+
+void RasterizerCanvasGLES2::canvas_render_items_end() {
+	if (!bdata.settings_use_batching) {
+		return;
+	}
+
+	// batching render is deferred until after going through all the z_indices, joining all the items
+	canvas_render_items_implementation(0, 0, _render_item_state.item_group_modulate,
+			_render_item_state.item_group_light,
+			_render_item_state.item_group_base_transform);
+
+	bdata.items_joined.reset();
+	bdata.item_refs.reset();
+}
+
+void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) {
+	// stage 1 : join similar items, so that their state changes are not repeated,
 	// and commands from joined items can be batched together
 	// and commands from joined items can be batched together
-	if (bdata.settings_use_batching)
-		join_items(p_item_list, p_z, p_modulate, p_light, p_base_transform);
+	if (bdata.settings_use_batching) {
+		join_items(p_item_list, p_z);
+		return;
+	}
 
 
+	// only legacy renders at this stage, batched renderer doesn't render until canvas_render_items_end()
 	canvas_render_items_implementation(p_item_list, p_z, p_modulate, p_light, p_base_transform);
 	canvas_render_items_implementation(p_item_list, p_z, p_modulate, p_light, p_base_transform);
 }
 }
 
 
@@ -1767,6 +1789,9 @@ bool RasterizerCanvasGLES2::try_join_item(Item *p_ci, RenderItemState &r_ris, bo
 		// a + light_blend + b + light_blend IS NOT THE SAME AS
 		// a + light_blend + b + light_blend IS NOT THE SAME AS
 		// a + b + light_blend
 		// a + b + light_blend
 		join = false;
 		join = false;
+
+		// we also dont want to allow joining this item with the next item, because the next item could have no lights!
+		r_batch_break = true;
 	}
 	}
 
 
 	if (reclip) {
 	if (reclip) {

+ 9 - 3
drivers/gles2/rasterizer_canvas_gles2.h

@@ -181,7 +181,8 @@ class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2 {
 	} bdata;
 	} bdata;
 
 
 	struct RenderItemState {
 	struct RenderItemState {
-		RenderItemState();
+		RenderItemState() { reset(); }
+		void reset();
 		Item *current_clip;
 		Item *current_clip;
 		RasterizerStorageGLES2::Shader *shader_cache;
 		RasterizerStorageGLES2::Shader *shader_cache;
 		bool rebind_shader;
 		bool rebind_shader;
@@ -190,12 +191,15 @@ class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2 {
 		RID canvas_last_material;
 		RID canvas_last_material;
 		Color final_modulate;
 		Color final_modulate;
 
 
+		// used for joining items only
+		BItemJoined *joined_item;
+
 		// 'item group' is data over a single call to canvas_render_items
 		// 'item group' is data over a single call to canvas_render_items
 		int item_group_z;
 		int item_group_z;
 		Color item_group_modulate;
 		Color item_group_modulate;
 		Light *item_group_light;
 		Light *item_group_light;
 		Transform2D item_group_base_transform;
 		Transform2D item_group_base_transform;
-	};
+	} _render_item_state;
 
 
 	struct FillState {
 	struct FillState {
 		void reset() {
 		void reset() {
@@ -212,6 +216,8 @@ class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2 {
 	};
 	};
 
 
 public:
 public:
+	virtual void canvas_render_items_begin(const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
+	virtual void canvas_render_items_end();
 	virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
 	virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
 
 
 private:
 private:
@@ -222,7 +228,7 @@ private:
 	// high level batch funcs
 	// high level batch funcs
 	void canvas_render_items_implementation(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
 	void canvas_render_items_implementation(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
 	void render_joined_item(const BItemJoined &p_bij, RenderItemState &r_ris);
 	void render_joined_item(const BItemJoined &p_bij, RenderItemState &r_ris);
-	void join_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
+	void join_items(Item *p_item_list, int p_z);
 	bool try_join_item(Item *p_ci, RenderItemState &r_ris, bool &r_batch_break);
 	bool try_join_item(Item *p_ci, RenderItemState &r_ris, bool &r_batch_break);
 	void render_joined_item_commands(const BItemJoined &p_bij, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material);
 	void render_joined_item_commands(const BItemJoined &p_bij, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material);
 	void render_batches(Item::Command *const *p_commands, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material);
 	void render_batches(Item::Command *const *p_commands, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material);

+ 2 - 0
servers/visual/rasterizer.h

@@ -1066,6 +1066,8 @@ public:
 	virtual void canvas_begin() = 0;
 	virtual void canvas_begin() = 0;
 	virtual void canvas_end() = 0;
 	virtual void canvas_end() = 0;
 
 
+	virtual void canvas_render_items_begin(const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) {}
+	virtual void canvas_render_items_end() {}
 	virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) = 0;
 	virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) = 0;
 	virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) = 0;
 	virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) = 0;
 
 

+ 4 - 0
servers/visual/visual_server_canvas.cpp

@@ -42,11 +42,13 @@ void VisualServerCanvas::_render_canvas_item_tree(Item *p_canvas_item, const Tra
 
 
 	_render_canvas_item(p_canvas_item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, NULL, NULL);
 	_render_canvas_item(p_canvas_item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, NULL, NULL);
 
 
+	VSG::canvas_render->canvas_render_items_begin(p_modulate, p_lights, p_transform);
 	for (int i = 0; i < z_range; i++) {
 	for (int i = 0; i < z_range; i++) {
 		if (!z_list[i])
 		if (!z_list[i])
 			continue;
 			continue;
 		VSG::canvas_render->canvas_render_items(z_list[i], VS::CANVAS_ITEM_Z_MIN + i, p_modulate, p_lights, p_transform);
 		VSG::canvas_render->canvas_render_items(z_list[i], VS::CANVAS_ITEM_Z_MIN + i, p_modulate, p_lights, p_transform);
 	}
 	}
+	VSG::canvas_render->canvas_render_items_end();
 }
 }
 
 
 void _collect_ysort_children(VisualServerCanvas::Item *p_canvas_item, Transform2D p_transform, VisualServerCanvas::Item *p_material_owner, const Color p_modulate, VisualServerCanvas::Item **r_items, int &r_index) {
 void _collect_ysort_children(VisualServerCanvas::Item *p_canvas_item, Transform2D p_transform, VisualServerCanvas::Item *p_material_owner, const Color p_modulate, VisualServerCanvas::Item **r_items, int &r_index) {
@@ -259,6 +261,7 @@ void VisualServerCanvas::render_canvas(Canvas *p_canvas, const Transform2D &p_tr
 			_render_canvas_item(ci[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, NULL, NULL);
 			_render_canvas_item(ci[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, NULL, NULL);
 		}
 		}
 
 
+		VSG::canvas_render->canvas_render_items_begin(p_canvas->modulate, p_lights, p_transform);
 		for (int i = 0; i < z_range; i++) {
 		for (int i = 0; i < z_range; i++) {
 			if (!z_list[i])
 			if (!z_list[i])
 				continue;
 				continue;
@@ -269,6 +272,7 @@ void VisualServerCanvas::render_canvas(Canvas *p_canvas, const Transform2D &p_tr
 
 
 			VSG::canvas_render->canvas_render_items(z_list[i], VS::CANVAS_ITEM_Z_MIN + i, p_canvas->modulate, p_lights, p_transform);
 			VSG::canvas_render->canvas_render_items(z_list[i], VS::CANVAS_ITEM_Z_MIN + i, p_canvas->modulate, p_lights, p_transform);
 		}
 		}
+		VSG::canvas_render->canvas_render_items_end();
 	} else {
 	} else {
 
 
 		for (int i = 0; i < l; i++) {
 		for (int i = 0; i < l; i++) {