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().
This commit is contained in:
lawnjelly 2020-04-12 13:52:25 +01:00
parent 1fb6181ba6
commit 93af8e7d1b
4 changed files with 73 additions and 36 deletions

View File

@ -67,16 +67,18 @@ RasterizerCanvasGLES2::BatchData::BatchData() {
settings_scissor_threshold = -1.0f; settings_scissor_threshold = -1.0f;
} }
RasterizerCanvasGLES2::RenderItemState::RenderItemState() { void RasterizerCanvasGLES2::RenderItemState::reset() {
current_clip = NULL; current_clip = nullptr;
shader_cache = NULL; 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) { void RasterizerCanvasGLES2::join_items(Item *p_item_list, int p_z) {
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_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;
BItemJoined *j = 0;
// 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(); _render_item_state.joined_item = bdata.items_joined.request_with_grow();
j->first_item_ref = bdata.item_refs.size(); _render_item_state.joined_item->first_item_ref = bdata.item_refs.size();
j->num_item_refs = 1; _render_item_state.joined_item->num_item_refs = 1;
j->bounding_rect = ci->global_rect_cache; _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); CRASH_COND(_render_item_state.joined_item == 0);
j->num_item_refs += 1; _render_item_state.joined_item->num_item_refs += 1;
j->bounding_rect = j->bounding_rect.merge(ci->global_rect_cache); _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)
// and commands from joined items can be batched together _render_item_state.reset();
if (bdata.settings_use_batching) _render_item_state.item_group_modulate = p_modulate;
join_items(p_item_list, p_z, p_modulate, p_light, p_base_transform); _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
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) {

View File

@ -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);

View File

@ -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;

View File

@ -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++) {