From b954a8c0999551155b95921ff911ed0d816d9f9c Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Thu, 23 Apr 2020 11:50:49 +0100 Subject: [PATCH] Fix batching z_indices with z ranged lights Joining items across z_indices can interfere with light culling for lights which only affect certain z ranges. This PR disables joining across z_indices when lights are present, except specifically for lights with both z_min set to the global minimum (-4096) and z_max set to the global maximum (4096). In addition, the z_index is now stored on the joined_item for accurate light culling. The z_index is also displayed in frame diagnostics. --- drivers/gles2/rasterizer_canvas_gles2.cpp | 34 +++++++++++++++++++++-- drivers/gles2/rasterizer_canvas_gles2.h | 8 ++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index 22e6faecb57..967faaa4cac 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -63,6 +63,7 @@ RasterizerCanvasGLES2::BatchData::BatchData() { diagnose_frame = false; next_diagnose_tick = 10000; diagnose_frame_number = 9999999999; // some high number + join_across_z_indices = true; settings_use_batching_original_choice = false; settings_flash_batching = false; @@ -731,7 +732,7 @@ void RasterizerCanvasGLES2::diagnose_batches(Item::Command *const *p_commands) { for (int batch_num = 0; batch_num < num_batches; batch_num++) { const Batch &batch = bdata.batches[batch_num]; - bdata.frame_string += "\t\tbatch "; + bdata.frame_string += "\t\t\tbatch "; switch (batch.type) { case Batch::BT_RECT: { @@ -1726,6 +1727,12 @@ void RasterizerCanvasGLES2::join_items(Item *p_item_list, int p_z) { // batch_break must be preserved over z_indices, // so is stored in _render_item_state.join_batch_break + // if z ranged lights are present, sometimes we have to disable joining over z_indices. + // we do this here + if (!bdata.join_across_z_indices) { + _render_item_state.join_batch_break = true; + } + while (p_item_list) { Item *ci = p_item_list; @@ -1752,6 +1759,7 @@ void RasterizerCanvasGLES2::join_items(Item *p_item_list, int p_z) { _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; + _render_item_state.joined_item->z_index = p_z; // add the reference BItemRef *r = bdata.item_refs.request_with_grow(); @@ -1828,6 +1836,20 @@ void RasterizerCanvasGLES2::canvas_render_items_begin(const Color &p_modulate, L // batch break must be preserved over the different z indices, // to prevent joining to an item on a previous index if not allowed _render_item_state.join_batch_break = false; + + // whether to join across z indices depends on whether there are z ranged lights. + // joined z_index items can be wrongly classified with z ranged lights. + bdata.join_across_z_indices = true; + + while (p_light) { + if ((p_light->z_min != VS::CANVAS_ITEM_Z_MIN) || (p_light->z_max != VS::CANVAS_ITEM_Z_MAX)) { + // prevent joining across z indices. This would have caused visual regressions + bdata.join_across_z_indices = false; + break; + } + + p_light = p_light->next_ptr; + } } void RasterizerCanvasGLES2::canvas_render_items_end() { @@ -2461,9 +2483,14 @@ void RasterizerCanvasGLES2::render_joined_item(const BItemJoined &p_bij, RenderI storage->info.render._2d_item_count++; +#ifdef DEBUG_ENABLED if (bdata.diagnose_frame) { bdata.frame_string += "\tjoined_item " + itos(p_bij.num_item_refs) + " refs\n"; + if (p_bij.z_index != 0) { + bdata.frame_string += "\t\t(z " + itos(p_bij.z_index) + ")\n"; + } } +#endif // all the joined items will share the same state with the first item Item *ci = bdata.item_refs[p_bij.first_item_ref].item; @@ -2714,7 +2741,10 @@ void RasterizerCanvasGLES2::render_joined_item(const BItemJoined &p_bij, RenderI // use the bounding rect of the joined items, NOT only the bounding rect of the first item. // note this is a cost of batching, the light culling will be less effective - if (ci->light_mask & light->item_mask && r_ris.item_group_z >= light->z_min && r_ris.item_group_z <= light->z_max && p_bij.bounding_rect.intersects_transformed(light->xform_cache, light->rect_cache)) { + + // note that the r_ris.item_group_z will be out of date because we are using deferred rendering till canvas_render_items_end() + // so we have to test z against the stored value in the joined item + if (ci->light_mask & light->item_mask && p_bij.z_index >= light->z_min && p_bij.z_index <= light->z_max && p_bij.bounding_rect.intersects_transformed(light->xform_cache, light->rect_cache)) { //intersects this light diff --git a/drivers/gles2/rasterizer_canvas_gles2.h b/drivers/gles2/rasterizer_canvas_gles2.h index 7d23f0d54be..4743e8be1af 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.h +++ b/drivers/gles2/rasterizer_canvas_gles2.h @@ -122,6 +122,10 @@ class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2 { Rect2 bounding_rect; + // note the z_index may only be correct for the first of the joined item references + // this has implications for light culling with z ranged lights. + int z_index; + // we are always splitting items with lots of commands, // and items with unhandled primitives (default) bool use_hardware_transform() const { return num_item_refs == 1; } @@ -180,6 +184,10 @@ class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2 { uint32_t next_diagnose_tick; uint64_t diagnose_frame_number; + // whether to join items across z_indices - this can interfere with z ranged lights, + // so has to be disabled in some circumstances + bool join_across_z_indices; + // global settings bool settings_use_batching; // the current use_batching (affected by flash) bool settings_use_batching_original_choice; // the choice entered in project settings