From b08ad9ef64892472726f910ed40e55a5e70648e8 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Sun, 3 May 2020 17:25:10 +0100 Subject: [PATCH] GLES2 batching - prevent color baking with MODULATE or COLOR Adding the ability to access MODULATE in the shader breaks when final_modulate is baked into vertex colors (this is a technique used to batch together different colored items). This PR prevents baking vertex colors when MODULATE is detected in the shader. It also prevents baking when COLOR is read in canvas shaders, which could currently produce the wrong result in the shader if colors were baked. It does not prevent baking if COLOR is only written, which happens in most shaders, and will operate correctly without baking. --- drivers/gles2/rasterizer_canvas_gles2.cpp | 23 +++++++++++++++++++++- drivers/gles2/rasterizer_canvas_gles2.h | 4 ++++ drivers/gles2/rasterizer_storage_gles2.cpp | 11 +++++++++++ drivers/gles2/rasterizer_storage_gles2.h | 7 +++++++ drivers/gles2/shader_compiler_gles2.cpp | 4 ++++ drivers/gles2/shader_compiler_gles2.h | 1 + 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index 609cf35770d..69925990f60 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -60,6 +60,7 @@ RasterizerCanvasGLES2::BatchData::BatchData() { settings_colored_vertex_format_threshold = 0.0f; settings_batch_buffer_num_verts = 0; scissor_threshold_area = 0.0f; + prevent_color_baking = false; diagnose_frame = false; next_diagnose_tick = 10000; diagnose_frame_number = 9999999999; // some high number @@ -1687,7 +1688,9 @@ void RasterizerCanvasGLES2::flush_render_batches(Item *p_first_item, Item *p_cur bdata.use_colored_vertices = false; // only check whether to convert if there are quads (prevent divide by zero) - if (bdata.total_quads) { + // and we haven't decided to prevent color baking (due to e.g. MODULATE + // being used in a shader) + if (bdata.total_quads && !bdata.prevent_color_baking) { // minus 1 to prevent single primitives (ratio 1.0) always being converted to colored.. // in that case it is slightly cheaper to just have the color as part of the batch float ratio = (float)(bdata.total_color_changes - 1) / (float)bdata.total_quads; @@ -2252,6 +2255,16 @@ bool RasterizerCanvasGLES2::try_join_item(Item *p_ci, RenderItemState &r_ris, bo bool unshaded = r_ris.shader_cache && (r_ris.shader_cache->canvas_item.light_mode == RasterizerStorageGLES2::Shader::CanvasItem::LIGHT_MODE_UNSHADED || (blend_mode != RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MIX && blend_mode != RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_PMALPHA)); bool reclip = false; + // does the shader contain BUILTINs which should break the batching? + if (r_ris.shader_cache && !unshaded) { + if (r_ris.shader_cache->canvas_item.prevent_color_baking) { + // we will do this same test on the shader during the rendering pass in order to set a bool not to bake vertex colors + // instead of saving this info as it is cheap to calculate + join = false; + r_batch_break = true; + } + } + // we are precalculating the final_modulate ahead of time because we need this for baking of final modulate into vertex colors // (only in software transform mode) // This maybe inefficient storing it... @@ -2974,6 +2987,14 @@ void RasterizerCanvasGLES2::render_joined_item(const BItemJoined &p_bij, RenderI bool unshaded = r_ris.shader_cache && (r_ris.shader_cache->canvas_item.light_mode == RasterizerStorageGLES2::Shader::CanvasItem::LIGHT_MODE_UNSHADED || (blend_mode != RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MIX && blend_mode != RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_PMALPHA)); bool reclip = false; + // does the shader contain BUILTINs which break the batching and should prevent color baking? + bdata.prevent_color_baking = false; + if (r_ris.shader_cache && !unshaded) { + if (r_ris.shader_cache->canvas_item.prevent_color_baking) { + bdata.prevent_color_baking = true; + } + } + if (r_ris.last_blend_mode != blend_mode) { switch (blend_mode) { diff --git a/drivers/gles2/rasterizer_canvas_gles2.h b/drivers/gles2/rasterizer_canvas_gles2.h index a82202157c8..3494b564f6c 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.h +++ b/drivers/gles2/rasterizer_canvas_gles2.h @@ -200,6 +200,10 @@ class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2 { // to alternate batching method and add color to the vertex format. int total_color_changes; + // if the shader is using MODULATE, we prevent baking so the final_modulate can + // be read in the shader + bool prevent_color_baking; + // measured in pixels, recalculated each frame float scissor_threshold_area; diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index a326338895d..e5a4926b33e 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -1424,6 +1424,9 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const { p_shader->canvas_item.uses_screen_texture = false; p_shader->canvas_item.uses_screen_uv = false; p_shader->canvas_item.uses_time = false; + p_shader->canvas_item.uses_modulate = false; + p_shader->canvas_item.reads_color = false; + p_shader->canvas_item.prevent_color_baking = false; shaders.actions_canvas.render_mode_values["blend_add"] = Pair(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_ADD); shaders.actions_canvas.render_mode_values["blend_mix"] = Pair(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_MIX); @@ -1438,6 +1441,9 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const { shaders.actions_canvas.usage_flag_pointers["SCREEN_PIXEL_SIZE"] = &p_shader->canvas_item.uses_screen_uv; shaders.actions_canvas.usage_flag_pointers["SCREEN_TEXTURE"] = &p_shader->canvas_item.uses_screen_texture; shaders.actions_canvas.usage_flag_pointers["TIME"] = &p_shader->canvas_item.uses_time; + shaders.actions_canvas.usage_flag_pointers["MODULATE"] = &p_shader->canvas_item.uses_modulate; + + shaders.actions_canvas.read_flag_pointers["COLOR"] = &p_shader->canvas_item.reads_color; actions = &shaders.actions_canvas; actions->uniforms = &p_shader->uniforms; @@ -1525,6 +1531,11 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const { p_shader->uses_vertex_time = gen_code.uses_vertex_time; p_shader->uses_fragment_time = gen_code.uses_fragment_time; + // some logic for batching + if (p_shader->mode == VS::SHADER_CANVAS_ITEM) { + p_shader->canvas_item.prevent_color_baking = p_shader->canvas_item.uses_modulate | p_shader->canvas_item.reads_color; + } + p_shader->shader->set_custom_shader(p_shader->custom_code_id); p_shader->shader->bind(); diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index c33f6952240..10ff8eb21d1 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -450,6 +450,13 @@ public: bool uses_screen_texture; bool uses_screen_uv; bool uses_time; + bool uses_modulate; + bool reads_color; + + // this flag is specifically for batching + // some of the logic is thus in rasterizer_storage.cpp + // we could alternatively set some bitflags here and test on the fly + bool prevent_color_baking; } canvas_item; diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index b253401db6c..c3b2e5e43dc 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -486,6 +486,10 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener *p_actions.write_flag_pointers[var_node->name] = true; } + if (!p_assigning && p_actions.read_flag_pointers.has(var_node->name)) { + *p_actions.read_flag_pointers[var_node->name] = true; + } + if (p_default_actions.usage_defines.has(var_node->name) && !used_name_defines.has(var_node->name)) { String define = p_default_actions.usage_defines[var_node->name]; String node_name = define.substr(1, define.length()); diff --git a/drivers/gles2/shader_compiler_gles2.h b/drivers/gles2/shader_compiler_gles2.h index 683c8bf3c45..d2ca19fa75c 100644 --- a/drivers/gles2/shader_compiler_gles2.h +++ b/drivers/gles2/shader_compiler_gles2.h @@ -44,6 +44,7 @@ public: Map > render_mode_values; Map render_mode_flags; Map usage_flag_pointers; + Map read_flag_pointers; Map write_flag_pointers; Map *uniforms;