From a657ea42f1e656695f502a0e5f50ea9e0e041c3e Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Tue, 30 Jul 2024 07:23:03 +1000 Subject: [PATCH] 2D: Add batching to RendererCanvasRenderRD --- doc/classes/ProjectSettings.xml | 3 + .../renderer_rd/renderer_canvas_render_rd.cpp | 4174 +++++++++-------- .../renderer_rd/renderer_canvas_render_rd.h | 201 +- .../rendering/renderer_rd/shaders/canvas.glsl | 92 +- .../shaders/canvas_uniforms_inc.glsl | 27 +- servers/rendering_server.cpp | 1 + 6 files changed, 2440 insertions(+), 2058 deletions(-) diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 08427ffe8387..e5b787714f51 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2341,6 +2341,9 @@ [b]Note:[/b] This property is only read when the project starts. To change the physics FPS at runtime, set [member Engine.physics_ticks_per_second] instead. [b]Note:[/b] Only [member physics/common/max_physics_steps_per_frame] physics ticks may be simulated per rendered frame at most. If more physics ticks have to be simulated per rendered frame to keep up with rendering, the project will appear to slow down (even if [code]delta[/code] is used consistently in physics calculations). Therefore, it is recommended to also increase [member physics/common/max_physics_steps_per_frame] if increasing [member physics/common/physics_ticks_per_second] significantly above its default value. + + Maximum number of canvas item commands that can be batched into a single draw call. + Controls how much of the original viewport size should be covered by the 2D signed distance field. This SDF can be sampled in [CanvasItem] shaders and is used for [GPUParticles2D] collision. Higher values allow portions of occluders located outside the viewport to still be taken into account in the generated signed distance field, at the cost of performance. If you notice particles falling through [LightOccluder2D]s as the occluders leave the viewport, increase this setting. The percentage specified is added on each axis and on both sides. For example, with the default setting of 120%, the signed distance field will cover 20% of the viewport's size outside the viewport on each side (top, right, bottom, left). diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index f51b4ae8d0f9..b0851efe5f85 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -351,2546 +351,2751 @@ void RendererCanvasRenderRD::free_polygon(PolygonID p_polygon) { //////////////////// -void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data) { - if (p_texture == RID()) { - p_texture = default_canvas_texture; - } +_FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primitive, uint32_t p_indices) { + static const uint32_t divisor[RS::PRIMITIVE_MAX] = { 1, 2, 1, 3, 1 }; + static const uint32_t subtractor[RS::PRIMITIVE_MAX] = { 0, 0, 1, 0, 1 }; + return (p_indices - subtractor[p_primitive]) / divisor[p_primitive]; +} + +RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, bool p_backbuffer) { + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); + + //re create canvas state + Vector uniforms; - if (r_last_texture == p_texture) { - return; //nothing to do, its the same + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 1; + u.append_id(state.canvas_state_buffer); + uniforms.push_back(u); } - RID uniform_set; - Color specular_shininess; - Size2i size; - bool use_normal; - bool use_specular; + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 2; + u.append_id(state.lights_uniform_buffer); + uniforms.push_back(u); + } - bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(p_texture, p_base_filter, p_base_repeat, shader.default_version_rd_shader, CANVAS_TEXTURE_UNIFORM_SET, bool(push_constant.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR), uniform_set, size, specular_shininess, use_normal, use_specular, p_texture_is_data); - //something odd happened - if (!success) { - _bind_canvas_texture(p_draw_list, default_canvas_texture, p_base_filter, p_base_repeat, r_last_texture, push_constant, r_texpixel_size); - return; + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 3; + u.append_id(RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture()); + uniforms.push_back(u); } - RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, CANVAS_TEXTURE_UNIFORM_SET); + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 4; + u.append_id(state.shadow_texture); + uniforms.push_back(u); + } - if (specular_shininess.a < 0.999) { - push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; - } else { - push_constant.flags &= ~FLAGS_DEFAULT_SPECULAR_MAP_USED; + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 5; + u.append_id(state.shadow_sampler); + uniforms.push_back(u); } - if (use_normal) { - push_constant.flags |= FLAGS_DEFAULT_NORMAL_MAP_USED; - } else { - push_constant.flags &= ~FLAGS_DEFAULT_NORMAL_MAP_USED; + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 6; + RID screen; + if (p_backbuffer) { + screen = texture_storage->render_target_get_rd_texture(p_to_render_target); + } else { + screen = texture_storage->render_target_get_rd_backbuffer(p_to_render_target); + if (screen.is_null()) { //unallocated backbuffer + screen = RendererRD::TextureStorage::get_singleton()->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE); + } + } + u.append_id(screen); + uniforms.push_back(u); } - push_constant.specular_shininess = uint32_t(CLAMP(specular_shininess.a * 255.0, 0, 255)) << 24; - push_constant.specular_shininess |= uint32_t(CLAMP(specular_shininess.b * 255.0, 0, 255)) << 16; - push_constant.specular_shininess |= uint32_t(CLAMP(specular_shininess.g * 255.0, 0, 255)) << 8; - push_constant.specular_shininess |= uint32_t(CLAMP(specular_shininess.r * 255.0, 0, 255)); + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 7; + RID sdf = texture_storage->render_target_get_sdf_texture(p_to_render_target); + u.append_id(sdf); + uniforms.push_back(u); + } - r_texpixel_size.x = 1.0 / float(size.x); - r_texpixel_size.y = 1.0 / float(size.y); + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 9; + u.append_id(RendererRD::MaterialStorage::get_singleton()->global_shader_uniforms_get_storage_buffer()); + uniforms.push_back(u); + } - push_constant.color_texture_pixel_size[0] = r_texpixel_size.x; - push_constant.color_texture_pixel_size[1] = r_texpixel_size.y; + uniforms.append_array(material_storage->samplers_rd_get_default().get_uniforms(SAMPLERS_BINDING_FIRST_INDEX)); - r_last_texture = p_texture; -} + RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader.default_version_rd_shader, BASE_UNIFORM_SET); + if (p_backbuffer) { + texture_storage->render_target_set_backbuffer_uniform_set(p_to_render_target, uniform_set); + } else { + texture_storage->render_target_set_framebuffer_uniform_set(p_to_render_target, uniform_set); + } -_FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primitive, uint32_t p_indices) { - static const uint32_t divisor[RS::PRIMITIVE_MAX] = { 1, 2, 1, 3, 1 }; - static const uint32_t subtractor[RS::PRIMITIVE_MAX] = { 0, 0, 1, 0, 1 }; - return (p_indices - subtractor[p_primitive]) / divisor[p_primitive]; + return uniform_set; } -void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_repeat_offset, RenderingMethod::RenderInfo *r_render_info) { - //create an empty push constant +void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); - RendererRD::ParticlesStorage *particles_storage = RendererRD::ParticlesStorage::get_singleton(); - RS::CanvasItemTextureFilter current_filter = default_filter; - RS::CanvasItemTextureRepeat current_repeat = default_repeat; + r_sdf_used = false; + int item_count = 0; - if (p_item->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT) { - current_filter = p_item->texture_filter; - } + //setup canvas state uniforms if needed - if (p_item->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) { - current_repeat = p_item->texture_repeat; - } + Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse(); - PushConstant push_constant; - Transform2D base_transform = p_item->final_transform; - if (p_item->repeat_source_item && (p_repeat_offset.x || p_repeat_offset.y)) { - base_transform.columns[2] += p_item->repeat_source_item->final_transform.basis_xform(p_repeat_offset); - } - base_transform = p_canvas_transform_inverse * base_transform; + //setup directional lights if exist - Transform2D draw_transform; - _update_transform_2d_to_mat2x3(base_transform, push_constant.world); + uint32_t light_count = 0; + uint32_t directional_light_count = 0; + { + Light *l = p_directional_light_list; + uint32_t index = 0; - Color base_color = p_item->final_modulate; - bool use_linear_colors = texture_storage->render_target_is_using_hdr(p_render_target); + while (l) { + if (index == state.max_lights_per_render) { + l->render_index_cache = -1; + l = l->next_ptr; + continue; + } - for (int i = 0; i < 4; i++) { - push_constant.modulation[i] = 0; - push_constant.ninepatch_margins[i] = 0; - push_constant.src_rect[i] = 0; - push_constant.dst_rect[i] = 0; - } - push_constant.flags = 0; - push_constant.color_texture_pixel_size[0] = 0; - push_constant.color_texture_pixel_size[1] = 0; + CanvasLight *clight = canvas_light_owner.get_or_null(l->light_internal); + if (!clight) { //unused or invalid texture + l->render_index_cache = -1; + l = l->next_ptr; + ERR_CONTINUE(!clight); + } - push_constant.pad[0] = 0; - push_constant.pad[1] = 0; + Vector2 canvas_light_dir = l->xform_cache.columns[1].normalized(); - push_constant.lights[0] = 0; - push_constant.lights[1] = 0; - push_constant.lights[2] = 0; - push_constant.lights[3] = 0; + state.light_uniforms[index].position[0] = -canvas_light_dir.x; + state.light_uniforms[index].position[1] = -canvas_light_dir.y; - uint32_t base_flags = 0; - base_flags |= use_linear_colors ? FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR : 0; + _update_transform_2d_to_mat2x4(clight->shadow.directional_xform, state.light_uniforms[index].shadow_matrix); - uint16_t light_count = 0; - PipelineLightMode light_mode; + state.light_uniforms[index].height = l->height; //0..1 here - { - Light *light = p_lights; + for (int i = 0; i < 4; i++) { + state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255)); + state.light_uniforms[index].color[i] = l->color[i]; + } - while (light) { - if (light->render_index_cache >= 0 && p_item->light_mask & light->item_mask && p_item->z_final >= light->z_min && p_item->z_final <= light->z_max && p_item->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) { - uint32_t light_index = light->render_index_cache; - push_constant.lights[light_count >> 2] |= light_index << ((light_count & 3) * 8); + state.light_uniforms[index].color[3] *= l->energy; //use alpha for energy, so base color can go separate - light_count++; + if (state.shadow_fb.is_valid()) { + state.light_uniforms[index].shadow_pixel_size = (1.0 / state.shadow_texture_size) * (1.0 + l->shadow_smooth); + state.light_uniforms[index].shadow_z_far_inv = 1.0 / clight->shadow.z_far; + state.light_uniforms[index].shadow_y_ofs = clight->shadow.y_offset; + } else { + state.light_uniforms[index].shadow_pixel_size = 1.0; + state.light_uniforms[index].shadow_z_far_inv = 1.0; + state.light_uniforms[index].shadow_y_ofs = 0; + } - if (light_count == MAX_LIGHTS_PER_ITEM - 1) { - break; - } + state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT; + state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT; + if (clight->shadow.enabled) { + state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW; } - light = light->next_ptr; + + l->render_index_cache = index; + + index++; + l = l->next_ptr; } - base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT; + light_count = index; + directional_light_count = light_count; + using_directional_lights = directional_light_count > 0; } - light_mode = (light_count > 0 || using_directional_lights) ? PIPELINE_LIGHT_MODE_ENABLED : PIPELINE_LIGHT_MODE_DISABLED; - - PipelineVariants *pipeline_variants = p_pipeline_variants; + //setup lights if exist - bool reclip = false; + { + Light *l = p_light_list; + uint32_t index = light_count; - RID last_texture; - Size2 texpixel_size; + while (l) { + if (index == state.max_lights_per_render) { + l->render_index_cache = -1; + l = l->next_ptr; + continue; + } - bool skipping = false; + CanvasLight *clight = canvas_light_owner.get_or_null(l->light_internal); + if (!clight) { //unused or invalid texture + l->render_index_cache = -1; + l = l->next_ptr; + ERR_CONTINUE(!clight); + } - const Item::Command *c = p_item->commands; - while (c) { - if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) { - c = c->next; - continue; - } + Transform2D final_xform; + if (!RSG::canvas->_interpolation_data.interpolation_enabled || !l->interpolated) { + final_xform = l->xform_curr; + } else { + real_t f = Engine::get_singleton()->get_physics_interpolation_fraction(); + TransformInterpolator::interpolate_transform_2d(l->xform_prev, l->xform_curr, final_xform, f); + } + // Convert light position to canvas coordinates, as all computation is done in canvas coordinates to avoid precision loss. + Vector2 canvas_light_pos = p_canvas_transform.xform(final_xform.get_origin()); + state.light_uniforms[index].position[0] = canvas_light_pos.x; + state.light_uniforms[index].position[1] = canvas_light_pos.y; - push_constant.flags = base_flags | (push_constant.flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); // Reset on each command for safety, keep canvastexture binding config. + _update_transform_2d_to_mat2x4(l->light_shader_xform.affine_inverse(), state.light_uniforms[index].matrix); + _update_transform_2d_to_mat2x4(l->xform_cache.affine_inverse(), state.light_uniforms[index].shadow_matrix); - switch (c->type) { - case Item::Command::TYPE_RECT: { - const Item::CommandRect *rect = static_cast(c); + state.light_uniforms[index].height = l->height * (p_canvas_transform.columns[0].length() + p_canvas_transform.columns[1].length()) * 0.5; //approximate height conversion to the canvas size, since all calculations are done in canvas coords to avoid precision loss + for (int i = 0; i < 4; i++) { + state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255)); + state.light_uniforms[index].color[i] = l->color[i]; + } - if (rect->flags & CANVAS_RECT_TILE) { - current_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; - } + state.light_uniforms[index].color[3] *= l->energy; //use alpha for energy, so base color can go separate - Color modulated = rect->modulate * base_color; - if (use_linear_colors) { - modulated = modulated.srgb_to_linear(); - } + if (state.shadow_fb.is_valid()) { + state.light_uniforms[index].shadow_pixel_size = (1.0 / state.shadow_texture_size) * (1.0 + l->shadow_smooth); + state.light_uniforms[index].shadow_z_far_inv = 1.0 / clight->shadow.z_far; + state.light_uniforms[index].shadow_y_ofs = clight->shadow.y_offset; + } else { + state.light_uniforms[index].shadow_pixel_size = 1.0; + state.light_uniforms[index].shadow_z_far_inv = 1.0; + state.light_uniforms[index].shadow_y_ofs = 0; + } - //bind pipeline - if (rect->flags & CANVAS_RECT_LCD) { - RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_QUAD_LCD_BLEND].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - RD::get_singleton()->draw_list_set_blend_constants(p_draw_list, modulated); - } else { - RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_QUAD].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - } + state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT; + state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT; + if (clight->shadow.enabled) { + state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW; + } - //bind textures + if (clight->texture.is_valid()) { + Rect2 atlas_rect = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture_rect(clight->texture); + state.light_uniforms[index].atlas_rect[0] = atlas_rect.position.x; + state.light_uniforms[index].atlas_rect[1] = atlas_rect.position.y; + state.light_uniforms[index].atlas_rect[2] = atlas_rect.size.width; + state.light_uniforms[index].atlas_rect[3] = atlas_rect.size.height; - _bind_canvas_texture(p_draw_list, rect->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size, bool(rect->flags & CANVAS_RECT_MSDF)); + } else { + state.light_uniforms[index].atlas_rect[0] = 0; + state.light_uniforms[index].atlas_rect[1] = 0; + state.light_uniforms[index].atlas_rect[2] = 0; + state.light_uniforms[index].atlas_rect[3] = 0; + } - Rect2 src_rect; - Rect2 dst_rect; + l->render_index_cache = index; - if (rect->texture != RID()) { - src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1); - dst_rect = Rect2(rect->rect.position, rect->rect.size); - - if (dst_rect.size.width < 0) { - dst_rect.position.x += dst_rect.size.width; - dst_rect.size.width *= -1; - } - if (dst_rect.size.height < 0) { - dst_rect.position.y += dst_rect.size.height; - dst_rect.size.height *= -1; - } + index++; + l = l->next_ptr; + } - if (rect->flags & CANVAS_RECT_FLIP_H) { - src_rect.size.x *= -1; - push_constant.flags |= FLAGS_FLIP_H; - } + light_count = index; + } - if (rect->flags & CANVAS_RECT_FLIP_V) { - src_rect.size.y *= -1; - push_constant.flags |= FLAGS_FLIP_V; - } + if (light_count > 0) { + RD::get_singleton()->buffer_update(state.lights_uniform_buffer, 0, sizeof(LightUniform) * light_count, &state.light_uniforms[0]); + } - if (rect->flags & CANVAS_RECT_TRANSPOSE) { - push_constant.flags |= FLAGS_TRANSPOSE_RECT; - } + { + //update canvas state uniform buffer + State::Buffer state_buffer; - if (rect->flags & CANVAS_RECT_CLIP_UV) { - push_constant.flags |= FLAGS_CLIP_RECT_UV; - } + Size2i ssize = texture_storage->render_target_get_size(p_to_render_target); - } else { - dst_rect = Rect2(rect->rect.position, rect->rect.size); + Transform3D screen_transform; + screen_transform.translate_local(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f); + screen_transform.scale(Vector3(2.0f / ssize.width, 2.0f / ssize.height, 1.0f)); + _update_transform_to_mat4(screen_transform, state_buffer.screen_transform); + _update_transform_2d_to_mat4(p_canvas_transform, state_buffer.canvas_transform); - if (dst_rect.size.width < 0) { - dst_rect.position.x += dst_rect.size.width; - dst_rect.size.width *= -1; - } - if (dst_rect.size.height < 0) { - dst_rect.position.y += dst_rect.size.height; - dst_rect.size.height *= -1; - } + Transform2D normal_transform = p_canvas_transform; + normal_transform.columns[0].normalize(); + normal_transform.columns[1].normalize(); + normal_transform.columns[2] = Vector2(); + _update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform); - src_rect = Rect2(0, 0, 1, 1); - } + bool use_linear_colors = texture_storage->render_target_is_using_hdr(p_to_render_target); + Color modulate = p_modulate; + if (use_linear_colors) { + modulate = p_modulate.srgb_to_linear(); + } + state_buffer.canvas_modulate[0] = modulate.r; + state_buffer.canvas_modulate[1] = modulate.g; + state_buffer.canvas_modulate[2] = modulate.b; + state_buffer.canvas_modulate[3] = modulate.a; - if (rect->flags & CANVAS_RECT_MSDF) { - push_constant.flags |= FLAGS_USE_MSDF; - push_constant.msdf[0] = rect->px_range; // Pixel range. - push_constant.msdf[1] = rect->outline; // Outline size. - push_constant.msdf[2] = 0.f; // Reserved. - push_constant.msdf[3] = 0.f; // Reserved. - } else if (rect->flags & CANVAS_RECT_LCD) { - push_constant.flags |= FLAGS_USE_LCD; - } + Size2 render_target_size = texture_storage->render_target_get_size(p_to_render_target); + state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x; + state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y; - push_constant.modulation[0] = modulated.r; - push_constant.modulation[1] = modulated.g; - push_constant.modulation[2] = modulated.b; - push_constant.modulation[3] = modulated.a; + state_buffer.time = state.time; + state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel; - push_constant.src_rect[0] = src_rect.position.x; - push_constant.src_rect[1] = src_rect.position.y; - push_constant.src_rect[2] = src_rect.size.width; - push_constant.src_rect[3] = src_rect.size.height; + state_buffer.directional_light_count = directional_light_count; - push_constant.dst_rect[0] = dst_rect.position.x; - push_constant.dst_rect[1] = dst_rect.position.y; - push_constant.dst_rect[2] = dst_rect.size.width; - push_constant.dst_rect[3] = dst_rect.size.height; + Vector2 canvas_scale = p_canvas_transform.get_scale(); - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); - RD::get_singleton()->draw_list_draw(p_draw_list, true); + state_buffer.sdf_to_screen[0] = render_target_size.width / canvas_scale.x; + state_buffer.sdf_to_screen[1] = render_target_size.height / canvas_scale.y; - if (r_render_info) { - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += 2; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; - } + state_buffer.screen_to_sdf[0] = 1.0 / state_buffer.sdf_to_screen[0]; + state_buffer.screen_to_sdf[1] = 1.0 / state_buffer.sdf_to_screen[1]; - } break; + Rect2 sdf_rect = texture_storage->render_target_get_sdf_rect(p_to_render_target); + Rect2 sdf_tex_rect(sdf_rect.position / canvas_scale, sdf_rect.size / canvas_scale); - case Item::Command::TYPE_NINEPATCH: { - const Item::CommandNinePatch *np = static_cast(c); + state_buffer.sdf_to_tex[0] = 1.0 / sdf_tex_rect.size.width; + state_buffer.sdf_to_tex[1] = 1.0 / sdf_tex_rect.size.height; + state_buffer.sdf_to_tex[2] = -sdf_tex_rect.position.x / sdf_tex_rect.size.width; + state_buffer.sdf_to_tex[3] = -sdf_tex_rect.position.y / sdf_tex_rect.size.height; - //bind pipeline - { - RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_NINEPATCH].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - } + //print_line("w: " + itos(ssize.width) + " s: " + rtos(canvas_scale)); + state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5); - //bind textures + RD::get_singleton()->buffer_update(state.canvas_state_buffer, 0, sizeof(State::Buffer), &state_buffer); + } - _bind_canvas_texture(p_draw_list, np->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size); + { //default filter/repeat + default_filter = p_default_filter; + default_repeat = p_default_repeat; + } - Rect2 src_rect; - Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y); + Item *ci = p_item_list; - if (np->texture == RID()) { - texpixel_size = Size2(1, 1); - src_rect = Rect2(0, 0, 1, 1); + //fill the list until rendering is possible. + bool material_screen_texture_cached = false; + bool material_screen_texture_mipmaps_cached = false; - } else { - if (np->source != Rect2()) { - src_rect = Rect2(np->source.position.x * texpixel_size.width, np->source.position.y * texpixel_size.height, np->source.size.x * texpixel_size.width, np->source.size.y * texpixel_size.height); - push_constant.color_texture_pixel_size[0] = 1.0 / np->source.size.width; - push_constant.color_texture_pixel_size[1] = 1.0 / np->source.size.height; + Rect2 back_buffer_rect; + bool backbuffer_copy = false; + bool backbuffer_gen_mipmaps = false; - } else { - src_rect = Rect2(0, 0, 1, 1); - } - } + Item *canvas_group_owner = nullptr; + bool skip_item = false; - Color modulated = np->color * base_color; - if (use_linear_colors) { - modulated = modulated.srgb_to_linear(); - } + state.last_instance_index = 0; - push_constant.modulation[0] = modulated.r; - push_constant.modulation[1] = modulated.g; - push_constant.modulation[2] = modulated.b; - push_constant.modulation[3] = modulated.a; + bool update_skeletons = false; + bool time_used = false; - push_constant.src_rect[0] = src_rect.position.x; - push_constant.src_rect[1] = src_rect.position.y; - push_constant.src_rect[2] = src_rect.size.width; - push_constant.src_rect[3] = src_rect.size.height; + bool backbuffer_cleared = false; - push_constant.dst_rect[0] = dst_rect.position.x; - push_constant.dst_rect[1] = dst_rect.position.y; - push_constant.dst_rect[2] = dst_rect.size.width; - push_constant.dst_rect[3] = dst_rect.size.height; + RenderTarget to_render_target; + to_render_target.render_target = p_to_render_target; + bool use_linear_colors = texture_storage->render_target_is_using_hdr(p_to_render_target); + to_render_target.base_flags = use_linear_colors ? FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR : 0; - push_constant.flags |= int(np->axis_x) << FLAGS_NINEPATCH_H_MODE_SHIFT; - push_constant.flags |= int(np->axis_y) << FLAGS_NINEPATCH_V_MODE_SHIFT; + while (ci) { + if (ci->copy_back_buffer && canvas_group_owner == nullptr) { + backbuffer_copy = true; - if (np->draw_center) { - push_constant.flags |= FLAGS_NINEPACH_DRAW_CENTER; - } + if (ci->copy_back_buffer->full) { + back_buffer_rect = Rect2(); + } else { + back_buffer_rect = ci->copy_back_buffer->rect; + } + } - push_constant.ninepatch_margins[0] = np->margin[SIDE_LEFT]; - push_constant.ninepatch_margins[1] = np->margin[SIDE_TOP]; - push_constant.ninepatch_margins[2] = np->margin[SIDE_RIGHT]; - push_constant.ninepatch_margins[3] = np->margin[SIDE_BOTTOM]; + RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); - RD::get_singleton()->draw_list_draw(p_draw_list, true); + if (material.is_valid()) { + CanvasMaterialData *md = static_cast(material_storage->material_get_data(material, RendererRD::MaterialStorage::SHADER_TYPE_2D)); + if (md && md->shader_data->valid) { + if (md->shader_data->uses_screen_texture && canvas_group_owner == nullptr) { + if (!material_screen_texture_cached) { + backbuffer_copy = true; + back_buffer_rect = Rect2(); + backbuffer_gen_mipmaps = md->shader_data->uses_screen_texture_mipmaps; + } else if (!material_screen_texture_mipmaps_cached) { + backbuffer_gen_mipmaps = md->shader_data->uses_screen_texture_mipmaps; + } + } - if (r_render_info) { - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += 2; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + if (md->shader_data->uses_sdf) { + r_sdf_used = true; + } + if (md->shader_data->uses_time) { + time_used = true; } + } + } - // Restore if overridden. - push_constant.color_texture_pixel_size[0] = texpixel_size.x; - push_constant.color_texture_pixel_size[1] = texpixel_size.y; + if (ci->skeleton.is_valid()) { + const Item::Command *c = ci->commands; - } break; - case Item::Command::TYPE_POLYGON: { - const Item::CommandPolygon *polygon = static_cast(c); + while (c) { + if (c->type == Item::Command::TYPE_MESH) { + const Item::CommandMesh *cm = static_cast(c); + if (cm->mesh_instance.is_valid()) { + mesh_storage->mesh_instance_check_for_update(cm->mesh_instance); + mesh_storage->mesh_instance_set_canvas_item_transform(cm->mesh_instance, canvas_transform_inverse * ci->final_transform); + update_skeletons = true; + } + } + c = c->next; + } + } - PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id); - ERR_CONTINUE(!pb); - //bind pipeline - { - static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP }; - ERR_CONTINUE(polygon->primitive < 0 || polygon->primitive >= RS::PRIMITIVE_MAX); - RID pipeline = pipeline_variants->variants[light_mode][variant[polygon->primitive]].get_render_pipeline(pb->vertex_format_id, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + if (ci->canvas_group_owner != nullptr) { + if (canvas_group_owner == nullptr) { + // Canvas group begins here, render until before this item + if (update_skeletons) { + mesh_storage->update_mesh_instances(); + update_skeletons = false; } + _render_batch_items(to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info); + item_count = 0; - if (polygon->primitive == RS::PRIMITIVE_LINES) { - //not supported in most hardware, so pointless - //RD::get_singleton()->draw_list_set_line_width(p_draw_list, polygon->line_width); + if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { + Rect2i group_rect = ci->canvas_group_owner->global_rect_cache; + texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false); + if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + ci->canvas_group_owner->use_canvas_group = false; + items[item_count++] = ci->canvas_group_owner; + } + } else if (!backbuffer_cleared) { + texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0)); + backbuffer_cleared = true; } - //bind textures + backbuffer_copy = false; + canvas_group_owner = ci->canvas_group_owner; //continue until owner found + } - _bind_canvas_texture(p_draw_list, polygon->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size); + ci->canvas_group_owner = nullptr; //must be cleared + } - Color color = base_color; - if (use_linear_colors) { - color = color.srgb_to_linear(); - } + if (canvas_group_owner == nullptr && ci->canvas_group != nullptr && ci->canvas_group->mode != RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + skip_item = true; + } - push_constant.modulation[0] = color.r; - push_constant.modulation[1] = color.g; - push_constant.modulation[2] = color.b; - push_constant.modulation[3] = color.a; + if (ci == canvas_group_owner) { + if (update_skeletons) { + mesh_storage->update_mesh_instances(); + update_skeletons = false; + } - for (int j = 0; j < 4; j++) { - push_constant.src_rect[j] = 0; - push_constant.dst_rect[j] = 0; - push_constant.ninepatch_margins[j] = 0; - } + _render_batch_items(to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true, r_render_info); + item_count = 0; - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, pb->vertex_array); - if (pb->indices.is_valid()) { - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, pb->indices); - } - RD::get_singleton()->draw_list_draw(p_draw_list, pb->indices.is_valid()); + if (ci->canvas_group->blur_mipmaps) { + texture_storage->render_target_gen_back_buffer_mipmaps(p_to_render_target, ci->global_rect_cache); + } - if (r_render_info) { - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(polygon->primitive, pb->primitive_count); - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; - } + canvas_group_owner = nullptr; + // Backbuffer is dirty now and needs to be re-cleared if another CanvasGroup needs it. + backbuffer_cleared = false; - } break; - case Item::Command::TYPE_PRIMITIVE: { - const Item::CommandPrimitive *primitive = static_cast(c); + // Tell the renderer to paint this as a canvas group + ci->use_canvas_group = true; + } else { + ci->use_canvas_group = false; + } - //bind pipeline - { - static const PipelineVariant variant[4] = { PIPELINE_VARIANT_PRIMITIVE_POINTS, PIPELINE_VARIANT_PRIMITIVE_LINES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES }; - ERR_CONTINUE(primitive->point_count == 0 || primitive->point_count > 4); - RID pipeline = pipeline_variants->variants[light_mode][variant[primitive->point_count - 1]].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - } + if (backbuffer_copy) { + //render anything pending, including clearing if no items + if (update_skeletons) { + mesh_storage->update_mesh_instances(); + update_skeletons = false; + } - //bind textures + _render_batch_items(to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info); + item_count = 0; - _bind_canvas_texture(p_draw_list, primitive->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size); + texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps); - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, primitive_arrays.index_array[MIN(3u, primitive->point_count) - 1]); + backbuffer_copy = false; + material_screen_texture_cached = true; // After a backbuffer copy, screen texture makes no further copies. + material_screen_texture_mipmaps_cached = backbuffer_gen_mipmaps; + backbuffer_gen_mipmaps = false; + } - for (uint32_t j = 0; j < MIN(3u, primitive->point_count); j++) { - push_constant.points[j * 2 + 0] = primitive->points[j].x; - push_constant.points[j * 2 + 1] = primitive->points[j].y; - push_constant.uvs[j * 2 + 0] = primitive->uvs[j].x; - push_constant.uvs[j * 2 + 1] = primitive->uvs[j].y; - Color col = primitive->colors[j] * base_color; - if (use_linear_colors) { - col = col.srgb_to_linear(); - } - push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); - push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); - } - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::get_singleton()->draw_list_draw(p_draw_list, true); - - if (r_render_info) { - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; - } - - if (primitive->point_count == 4) { - for (uint32_t j = 1; j < 3; j++) { - //second half of triangle - push_constant.points[j * 2 + 0] = primitive->points[j + 1].x; - push_constant.points[j * 2 + 1] = primitive->points[j + 1].y; - push_constant.uvs[j * 2 + 0] = primitive->uvs[j + 1].x; - push_constant.uvs[j * 2 + 1] = primitive->uvs[j + 1].y; - Color col = primitive->colors[j + 1] * base_color; - if (use_linear_colors) { - col = col.srgb_to_linear(); - } - push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); - push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); - } - - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::get_singleton()->draw_list_draw(p_draw_list, true); - - if (r_render_info) { - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; - } - } - - } break; - case Item::Command::TYPE_MESH: - case Item::Command::TYPE_MULTIMESH: - case Item::Command::TYPE_PARTICLES: { - RID mesh; - RID mesh_instance; - RID texture; - Color modulate(1, 1, 1, 1); - float world_backup[6]; - int instance_count = 1; - - for (int j = 0; j < 6; j++) { - world_backup[j] = push_constant.world[j]; - } - - if (c->type == Item::Command::TYPE_MESH) { - const Item::CommandMesh *m = static_cast(c); - mesh = m->mesh; - mesh_instance = m->mesh_instance; - texture = m->texture; - modulate = m->modulate; - _update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, push_constant.world); - } else if (c->type == Item::Command::TYPE_MULTIMESH) { - const Item::CommandMultiMesh *mm = static_cast(c); - RID multimesh = mm->multimesh; - mesh = mesh_storage->multimesh_get_mesh(multimesh); - texture = mm->texture; + if (backbuffer_gen_mipmaps) { + texture_storage->render_target_gen_back_buffer_mipmaps(p_to_render_target, back_buffer_rect); - if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) { - break; - } + backbuffer_gen_mipmaps = false; + material_screen_texture_mipmaps_cached = true; + } - instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh); + if (skip_item) { + skip_item = false; + } else { + items[item_count++] = ci; + } - if (instance_count == 0) { - break; - } + if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) { + if (update_skeletons) { + mesh_storage->update_mesh_instances(); + update_skeletons = false; + } - RID uniform_set = mesh_storage->multimesh_get_2d_uniform_set(multimesh, shader.default_version_rd_shader, TRANSFORMS_UNIFORM_SET); - RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, TRANSFORMS_UNIFORM_SET); - push_constant.flags |= 1; //multimesh, trails disabled - if (mesh_storage->multimesh_uses_colors(multimesh)) { - push_constant.flags |= FLAGS_INSTANCING_HAS_COLORS; - } - if (mesh_storage->multimesh_uses_custom_data(multimesh)) { - push_constant.flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA; - } - } else if (c->type == Item::Command::TYPE_PARTICLES) { - const Item::CommandParticles *pt = static_cast(c); - ERR_BREAK(particles_storage->particles_get_mode(pt->particles) != RS::PARTICLES_MODE_2D); - particles_storage->particles_request_process(pt->particles); + _render_batch_items(to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr, r_render_info); + //then reset + item_count = 0; + } - if (particles_storage->particles_is_inactive(pt->particles) || particles_storage->particles_get_frame_counter(pt->particles) == 0) { - break; - } + ci = ci->next; + } - RenderingServerDefault::redraw_request(); // active particles means redraw request + if (time_used) { + RenderingServerDefault::redraw_request(); + } - int dpc = particles_storage->particles_get_draw_passes(pt->particles); - if (dpc == 0) { - break; //nothing to draw - } - uint32_t divisor = 1; - instance_count = particles_storage->particles_get_amount(pt->particles, divisor); + state.current_data_buffer_index = (state.current_data_buffer_index + 1) % state.canvas_instance_data_buffers.size(); + state.current_instance_buffer_index = 0; +} - RID uniform_set = particles_storage->particles_get_instance_buffer_uniform_set(pt->particles, shader.default_version_rd_shader, TRANSFORMS_UNIFORM_SET); - RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, TRANSFORMS_UNIFORM_SET); +RID RendererCanvasRenderRD::light_create() { + CanvasLight canvas_light; + return canvas_light_owner.make_rid(canvas_light); +} - push_constant.flags |= divisor; - instance_count /= divisor; +void RendererCanvasRenderRD::light_set_texture(RID p_rid, RID p_texture) { + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - push_constant.flags |= FLAGS_INSTANCING_HAS_COLORS; - push_constant.flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA; + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_NULL(cl); + if (cl->texture == p_texture) { + return; + } - mesh = particles_storage->particles_get_draw_pass_mesh(pt->particles, 0); //higher ones are ignored - texture = pt->texture; + ERR_FAIL_COND(p_texture.is_valid() && !texture_storage->owns_texture(p_texture)); - if (particles_storage->particles_has_collision(pt->particles) && texture_storage->render_target_is_sdf_enabled(p_render_target)) { - //pass collision information - Transform2D xform = p_item->final_transform; + if (cl->texture.is_valid()) { + texture_storage->texture_remove_from_decal_atlas(cl->texture); + } + cl->texture = p_texture; - RID sdf_texture = texture_storage->render_target_get_sdf_texture(p_render_target); + if (cl->texture.is_valid()) { + texture_storage->texture_add_to_decal_atlas(cl->texture); + } +} - Rect2 to_screen; - { - Rect2 sdf_rect = texture_storage->render_target_get_sdf_rect(p_render_target); +void RendererCanvasRenderRD::light_set_use_shadow(RID p_rid, bool p_enable) { + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_NULL(cl); - to_screen.size = Vector2(1.0 / sdf_rect.size.width, 1.0 / sdf_rect.size.height); - to_screen.position = -sdf_rect.position * to_screen.size; - } + cl->shadow.enabled = p_enable; +} - particles_storage->particles_set_canvas_sdf_collision(pt->particles, true, xform, to_screen, sdf_texture); - } else { - particles_storage->particles_set_canvas_sdf_collision(pt->particles, false, Transform2D(), Rect2(), RID()); - } +void RendererCanvasRenderRD::_update_shadow_atlas() { + if (state.shadow_fb == RID()) { + //ah, we lack the shadow texture.. + RD::get_singleton()->free(state.shadow_texture); //erase placeholder - // Signal that SDF texture needs to be updated. - r_sdf_used |= particles_storage->particles_has_collision(pt->particles); - } + Vector fb_textures; - if (mesh.is_null()) { - break; - } + { //texture + RD::TextureFormat tf; + tf.texture_type = RD::TEXTURE_TYPE_2D; + tf.width = state.shadow_texture_size; + tf.height = state.max_lights_per_render * 2; + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + tf.format = RD::DATA_FORMAT_R32_SFLOAT; - _bind_canvas_texture(p_draw_list, texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size); + state.shadow_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + fb_textures.push_back(state.shadow_texture); + } + { + RD::TextureFormat tf; + tf.texture_type = RD::TEXTURE_TYPE_2D; + tf.width = state.shadow_texture_size; + tf.height = state.max_lights_per_render * 2; + tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + tf.format = RD::DATA_FORMAT_D32_SFLOAT; + //chunks to write + state.shadow_depth_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + fb_textures.push_back(state.shadow_depth_texture); + } - uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh); - static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP }; + state.shadow_fb = RD::get_singleton()->framebuffer_create(fb_textures); + } +} - Color modulated = modulate * base_color; - if (use_linear_colors) { - modulated = modulated.srgb_to_linear(); - } +void RendererCanvasRenderRD::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) { + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_COND(!cl->shadow.enabled); - push_constant.modulation[0] = modulated.r; - push_constant.modulation[1] = modulated.g; - push_constant.modulation[2] = modulated.b; - push_constant.modulation[3] = modulated.a; + _update_shadow_atlas(); - for (int j = 0; j < 4; j++) { - push_constant.src_rect[j] = 0; - push_constant.dst_rect[j] = 0; - push_constant.ninepatch_margins[j] = 0; - } + cl->shadow.z_far = p_far; + cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(state.max_lights_per_render * 2); + Vector cc; + cc.push_back(Color(p_far, p_far, p_far, 1.0)); - for (uint32_t j = 0; j < surf_count; j++) { - void *surface = mesh_storage->mesh_get_surface(mesh, j); + for (int i = 0; i < 4; i++) { + //make sure it remains orthogonal, makes easy to read angle later - RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface); - ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX); + //light.basis.scale(Vector3(to_light.elements[0].length(),to_light.elements[1].length(),1)); - uint64_t input_mask = pipeline_variants->variants[light_mode][variant[primitive]].get_vertex_input_mask(); + Rect2i rect((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect); - RID vertex_array; - RD::VertexFormatID vertex_format = RD::INVALID_FORMAT_ID; + Projection projection; + { + real_t fov = 90; + real_t nearp = p_near; + real_t farp = p_far; + real_t aspect = 1.0; - if (mesh_instance.is_valid()) { - mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, false, vertex_array, vertex_format); - } else { - mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, false, vertex_array, vertex_format); - } + real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5)); + real_t ymin = -ymax; + real_t xmin = ymin * aspect; + real_t xmax = ymax * aspect; - RID pipeline = pipeline_variants->variants[light_mode][variant[primitive]].get_render_pipeline(vertex_format, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp); + } - RID index_array = mesh_storage->mesh_surface_get_index_array(surface, 0); + Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0)); + projection = projection * Projection(Transform3D().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse()); - if (index_array.is_valid()) { - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, index_array); - } + ShadowRenderPushConstant push_constant; + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + push_constant.projection[y * 4 + x] = projection.columns[y][x]; + } + } + static const Vector2 directions[4] = { Vector2(1, 0), Vector2(0, 1), Vector2(-1, 0), Vector2(0, -1) }; + push_constant.direction[0] = directions[i].x; + push_constant.direction[1] = directions[i].y; + push_constant.z_far = p_far; + push_constant.pad = 0; - RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, vertex_array); - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); + LightOccluderInstance *instance = p_occluders; - RD::get_singleton()->draw_list_draw(p_draw_list, index_array.is_valid(), instance_count); + while (instance) { + OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder); - if (r_render_info) { - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(primitive, mesh_storage->mesh_surface_get_vertices_drawn_count(surface)) * instance_count; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; - } - } + if (!co || co->index_array.is_null() || !(p_light_mask & instance->light_mask)) { + instance = instance->next; + continue; + } - for (int j = 0; j < 6; j++) { - push_constant.world[j] = world_backup[j]; - } - } break; - case Item::Command::TYPE_TRANSFORM: { - const Item::CommandTransform *transform = static_cast(c); - draw_transform = transform->xform; - _update_transform_2d_to_mat2x3(base_transform * transform->xform, push_constant.world); + _update_transform_2d_to_mat2x4(p_light_xform * instance->xform_cache, push_constant.modelview); - } break; - case Item::Command::TYPE_CLIP_IGNORE: { - const Item::CommandClipIgnore *ci = static_cast(c); - if (current_clip) { - if (ci->ignore != reclip) { - if (ci->ignore) { - RD::get_singleton()->draw_list_disable_scissor(p_draw_list); - reclip = true; - } else { - RD::get_singleton()->draw_list_enable_scissor(p_draw_list, current_clip->final_clip_rect); - reclip = false; - } - } - } + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.render_pipelines[co->cull_mode]); + RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->vertex_array); + RD::get_singleton()->draw_list_bind_index_array(draw_list, co->index_array); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowRenderPushConstant)); - } break; - case Item::Command::TYPE_ANIMATION_SLICE: { - const Item::CommandAnimationSlice *as = static_cast(c); - double current_time = RendererCompositorRD::get_singleton()->get_total_time(); - double local_time = Math::fposmod(current_time - as->offset, as->animation_length); - skipping = !(local_time >= as->slice_begin && local_time < as->slice_end); + RD::get_singleton()->draw_list_draw(draw_list, true); - RenderingServerDefault::redraw_request(); // animation visible means redraw request - } break; + instance = instance->next; } - c = c->next; + RD::get_singleton()->draw_list_end(); } -#ifdef DEBUG_ENABLED - if (debug_redraw && p_item->debug_redraw_time > 0.0) { - Color dc = debug_redraw_color; - dc.a *= p_item->debug_redraw_time / debug_redraw_time; +} - RID pipeline = pipeline_variants->variants[PIPELINE_LIGHT_MODE_DISABLED][PIPELINE_VARIANT_QUAD].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); +void RendererCanvasRenderRD::light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) { + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_COND(!cl->shadow.enabled); - //bind textures + _update_shadow_atlas(); - _bind_canvas_texture(p_draw_list, RID(), current_filter, current_repeat, last_texture, push_constant, texpixel_size); + Vector2 light_dir = p_light_xform.columns[1].normalized(); - Rect2 src_rect; - Rect2 dst_rect; + Vector2 center = p_clip_rect.get_center(); - dst_rect = Rect2(Vector2(), p_item->rect.size); - src_rect = Rect2(0, 0, 1, 1); + float to_edge_distance = ABS(light_dir.dot(p_clip_rect.get_support(-light_dir)) - light_dir.dot(center)); - push_constant.modulation[0] = dc.r; - push_constant.modulation[1] = dc.g; - push_constant.modulation[2] = dc.b; - push_constant.modulation[3] = dc.a; + Vector2 from_pos = center - light_dir * (to_edge_distance + p_cull_distance); + float distance = to_edge_distance * 2.0 + p_cull_distance; + float half_size = p_clip_rect.size.length() * 0.5; //shadow length, must keep this no matter the angle - push_constant.src_rect[0] = src_rect.position.x; - push_constant.src_rect[1] = src_rect.position.y; - push_constant.src_rect[2] = src_rect.size.width; - push_constant.src_rect[3] = src_rect.size.height; + cl->shadow.z_far = distance; + cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(state.max_lights_per_render * 2); - push_constant.dst_rect[0] = dst_rect.position.x; - push_constant.dst_rect[1] = dst_rect.position.y; - push_constant.dst_rect[2] = dst_rect.size.width; - push_constant.dst_rect[3] = dst_rect.size.height; + Transform2D to_light_xform; - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); - RD::get_singleton()->draw_list_draw(p_draw_list, true); + to_light_xform[2] = from_pos; + to_light_xform[1] = light_dir; + to_light_xform[0] = -light_dir.orthogonal(); - p_item->debug_redraw_time -= RSG::rasterizer->get_frame_delta_time(); + to_light_xform.invert(); - RenderingServerDefault::redraw_request(); - } -#endif - if (current_clip && reclip) { - //will make it re-enable clipping if needed afterwards - current_clip = nullptr; - } -} + Vector cc; + cc.push_back(Color(1, 1, 1, 1)); -RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, bool p_backbuffer) { - RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); + Rect2i rect(0, p_shadow_index * 2, state.shadow_texture_size, 2); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect); - //re create canvas state - Vector uniforms; - - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.binding = 1; - u.append_id(state.canvas_state_buffer); - uniforms.push_back(u); - } + Projection projection; + projection.set_orthogonal(-half_size, half_size, -0.5, 0.5, 0.0, distance); + projection = projection * Projection(Transform3D().looking_at(Vector3(0, 1, 0), Vector3(0, 0, -1)).affine_inverse()); - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.binding = 2; - u.append_id(state.lights_uniform_buffer); - uniforms.push_back(u); + ShadowRenderPushConstant push_constant; + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + push_constant.projection[y * 4 + x] = projection.columns[y][x]; + } } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 3; - u.append_id(RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture()); - uniforms.push_back(u); - } + push_constant.direction[0] = 0.0; + push_constant.direction[1] = 1.0; + push_constant.z_far = distance; + push_constant.pad = 0; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 4; - u.append_id(state.shadow_texture); - uniforms.push_back(u); - } + LightOccluderInstance *instance = p_occluders; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - u.binding = 5; - u.append_id(state.shadow_sampler); - uniforms.push_back(u); - } + while (instance) { + OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder); - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 6; - RID screen; - if (p_backbuffer) { - screen = texture_storage->render_target_get_rd_texture(p_to_render_target); - } else { - screen = texture_storage->render_target_get_rd_backbuffer(p_to_render_target); - if (screen.is_null()) { //unallocated backbuffer - screen = RendererRD::TextureStorage::get_singleton()->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE); - } + if (!co || co->index_array.is_null() || !(p_light_mask & instance->light_mask)) { + instance = instance->next; + continue; } - u.append_id(screen); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 7; - RID sdf = texture_storage->render_target_get_sdf_texture(p_to_render_target); - u.append_id(sdf); - uniforms.push_back(u); - } + _update_transform_2d_to_mat2x4(to_light_xform * instance->xform_cache, push_constant.modelview); - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 9; - u.append_id(RendererRD::MaterialStorage::get_singleton()->global_shader_uniforms_get_storage_buffer()); - uniforms.push_back(u); - } + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.render_pipelines[co->cull_mode]); + RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->vertex_array); + RD::get_singleton()->draw_list_bind_index_array(draw_list, co->index_array); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowRenderPushConstant)); - uniforms.append_array(material_storage->samplers_rd_get_default().get_uniforms(SAMPLERS_BINDING_FIRST_INDEX)); + RD::get_singleton()->draw_list_draw(draw_list, true); - RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader.default_version_rd_shader, BASE_UNIFORM_SET); - if (p_backbuffer) { - texture_storage->render_target_set_backbuffer_uniform_set(p_to_render_target, uniform_set); - } else { - texture_storage->render_target_set_framebuffer_uniform_set(p_to_render_target, uniform_set); + instance = instance->next; } - return uniform_set; + RD::get_singleton()->draw_list_end(); + + Transform2D to_shadow; + to_shadow.columns[0].x = 1.0 / -(half_size * 2.0); + to_shadow.columns[2].x = 0.5; + + cl->shadow.directional_xform = to_shadow * to_light_xform; } -void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer, RenderingMethod::RenderInfo *r_render_info) { - RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); +void RendererCanvasRenderRD::render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - Item *current_clip = nullptr; + RID fb = texture_storage->render_target_get_sdf_framebuffer(p_render_target); + Rect2i rect = texture_storage->render_target_get_sdf_rect(p_render_target); - Transform2D canvas_transform_inverse = p_canvas_transform_inverse; + Transform2D to_sdf; + to_sdf.columns[0] *= rect.size.width; + to_sdf.columns[1] *= rect.size.height; + to_sdf.columns[2] = rect.position; - RID framebuffer; - RID fb_uniform_set; - bool clear = false; - Vector clear_colors; + Transform2D to_clip; + to_clip.columns[0] *= 2.0; + to_clip.columns[1] *= 2.0; + to_clip.columns[2] = -Vector2(1.0, 1.0); - if (p_to_backbuffer) { - framebuffer = texture_storage->render_target_get_rd_backbuffer_framebuffer(p_to_render_target); - fb_uniform_set = texture_storage->render_target_get_backbuffer_uniform_set(p_to_render_target); - } else { - framebuffer = texture_storage->render_target_get_rd_framebuffer(p_to_render_target); - texture_storage->render_target_set_msaa_needs_resolve(p_to_render_target, false); // If MSAA is enabled, our framebuffer will be resolved! + to_clip = to_clip * to_sdf.affine_inverse(); - if (texture_storage->render_target_is_clear_requested(p_to_render_target)) { - clear = true; - clear_colors.push_back(texture_storage->render_target_get_clear_request_color(p_to_render_target)); - texture_storage->render_target_disable_clear_request(p_to_render_target); + Vector cc; + cc.push_back(Color(0, 0, 0, 0)); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc); + + Projection projection; + + ShadowRenderPushConstant push_constant; + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + push_constant.projection[y * 4 + x] = projection.columns[y][x]; } - // TODO: Obtain from framebuffer format eventually when this is implemented. - fb_uniform_set = texture_storage->render_target_get_framebuffer_uniform_set(p_to_render_target); } - if (fb_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(fb_uniform_set)) { - fb_uniform_set = _create_base_uniform_set(p_to_render_target, p_to_backbuffer); - } + push_constant.direction[0] = 0.0; + push_constant.direction[1] = 0.0; + push_constant.z_far = 0; + push_constant.pad = 0; - RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); + LightOccluderInstance *instance = p_occluders; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors, 1, 0, Rect2(), RDD::BreadcrumbMarker::UI_PASS); + while (instance) { + OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder); - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, fb_uniform_set, BASE_UNIFORM_SET); - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.default_transforms_uniform_set, TRANSFORMS_UNIFORM_SET); + if (!co || co->sdf_index_array.is_null() || !instance->sdf_collision) { + instance = instance->next; + continue; + } - RID prev_material; + _update_transform_2d_to_mat2x4(to_clip * instance->xform_cache, push_constant.modelview); - PipelineVariants *pipeline_variants = &shader.pipeline_variants; + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.sdf_render_pipelines[co->sdf_is_lines ? SHADOW_RENDER_SDF_LINES : SHADOW_RENDER_SDF_TRIANGLES]); + RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->sdf_vertex_array); + RD::get_singleton()->draw_list_bind_index_array(draw_list, co->sdf_index_array); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowRenderPushConstant)); - for (int i = 0; i < p_item_count; i++) { - Item *ci = items[i]; + RD::get_singleton()->draw_list_draw(draw_list, true); - if (current_clip != ci->final_clip_owner) { - current_clip = ci->final_clip_owner; + instance = instance->next; + } - //setup clip - if (current_clip) { - RD::get_singleton()->draw_list_enable_scissor(draw_list, current_clip->final_clip_rect); + RD::get_singleton()->draw_list_end(); - } else { - RD::get_singleton()->draw_list_disable_scissor(draw_list); - } - } + texture_storage->render_target_sdf_process(p_render_target); //done rendering, process it +} - RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; +RID RendererCanvasRenderRD::occluder_polygon_create() { + OccluderPolygon occluder; + occluder.line_point_count = 0; + occluder.sdf_point_count = 0; + occluder.sdf_index_count = 0; + occluder.cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED; + return occluder_polygon_owner.make_rid(occluder); +} - if (ci->use_canvas_group) { - if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { - material = default_clip_children_material; - } else { - if (material.is_null()) { - if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) { - material = default_clip_children_material; - } else { - material = default_canvas_group_material; - } - } - } - } +void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Vector &p_points, bool p_closed) { + OccluderPolygon *oc = occluder_polygon_owner.get_or_null(p_occluder); + ERR_FAIL_NULL(oc); - if (material != prev_material) { - CanvasMaterialData *material_data = nullptr; - if (material.is_valid()) { - material_data = static_cast(material_storage->material_get_data(material, RendererRD::MaterialStorage::SHADER_TYPE_2D)); - } + Vector lines; - if (material_data) { - if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { - pipeline_variants = &material_data->shader_data->pipeline_variants; - // Update uniform set. - RID uniform_set = texture_storage->render_target_is_using_hdr(p_to_render_target) ? material_data->uniform_set : material_data->uniform_set_srgb; - if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { // Material may not have a uniform set. - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set, MATERIAL_UNIFORM_SET); - material_data->set_as_used(); - } - } else { - pipeline_variants = &shader.pipeline_variants; - } - } else { - pipeline_variants = &shader.pipeline_variants; - } - } + if (p_points.size()) { + int lc = p_points.size() * 2; - if (!ci->repeat_size.x && !ci->repeat_size.y) { - _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, Point2(), r_render_info); - } else { - Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2); - Point2 offset; - - int repeat_times_x = ci->repeat_size.x ? ci->repeat_times : 0; - int repeat_times_y = ci->repeat_size.y ? ci->repeat_times : 0; - for (int ry = 0; ry <= repeat_times_y; ry++) { - offset.y = start_pos.y + ry * ci->repeat_size.y; - for (int rx = 0; rx <= repeat_times_x; rx++) { - offset.x = start_pos.x + rx * ci->repeat_size.x; - _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, offset, r_render_info); - } + lines.resize(lc - (p_closed ? 0 : 2)); + { + Vector2 *w = lines.ptrw(); + const Vector2 *r = p_points.ptr(); + + int max = lc / 2; + if (!p_closed) { + max--; + } + for (int i = 0; i < max; i++) { + Vector2 a = r[i]; + Vector2 b = r[(i + 1) % (lc / 2)]; + w[i * 2 + 0] = a; + w[i * 2 + 1] = b; } } - - prev_material = material; } - RD::get_singleton()->draw_list_end(); -} + if ((oc->line_point_count != lines.size() || lines.size() == 0) && oc->vertex_array.is_valid()) { + RD::get_singleton()->free(oc->vertex_array); + RD::get_singleton()->free(oc->vertex_buffer); + RD::get_singleton()->free(oc->index_array); + RD::get_singleton()->free(oc->index_buffer); -void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info) { - RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); - RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); + oc->vertex_array = RID(); + oc->vertex_buffer = RID(); + oc->index_array = RID(); + oc->index_buffer = RID(); - r_sdf_used = false; - int item_count = 0; + oc->line_point_count = lines.size(); + } - //setup canvas state uniforms if needed + if (lines.size()) { + oc->line_point_count = lines.size(); + Vector geometry; + Vector indices; + int lc = lines.size(); - Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse(); + geometry.resize(lc * 6 * sizeof(float)); + indices.resize(lc * 3 * sizeof(uint16_t)); - //setup directional lights if exist + { + uint8_t *vw = geometry.ptrw(); + float *vwptr = reinterpret_cast(vw); + uint8_t *iw = indices.ptrw(); + uint16_t *iwptr = (uint16_t *)iw; - uint32_t light_count = 0; - uint32_t directional_light_count = 0; - { - Light *l = p_directional_light_list; - uint32_t index = 0; + const Vector2 *lr = lines.ptr(); - while (l) { - if (index == state.max_lights_per_render) { - l->render_index_cache = -1; - l = l->next_ptr; - continue; - } + const int POLY_HEIGHT = 16384; - CanvasLight *clight = canvas_light_owner.get_or_null(l->light_internal); - if (!clight) { //unused or invalid texture - l->render_index_cache = -1; - l = l->next_ptr; - ERR_CONTINUE(!clight); - } + for (int i = 0; i < lc / 2; i++) { + vwptr[i * 12 + 0] = lr[i * 2 + 0].x; + vwptr[i * 12 + 1] = lr[i * 2 + 0].y; + vwptr[i * 12 + 2] = POLY_HEIGHT; - Vector2 canvas_light_dir = l->xform_cache.columns[1].normalized(); + vwptr[i * 12 + 3] = lr[i * 2 + 1].x; + vwptr[i * 12 + 4] = lr[i * 2 + 1].y; + vwptr[i * 12 + 5] = POLY_HEIGHT; - state.light_uniforms[index].position[0] = -canvas_light_dir.x; - state.light_uniforms[index].position[1] = -canvas_light_dir.y; + vwptr[i * 12 + 6] = lr[i * 2 + 1].x; + vwptr[i * 12 + 7] = lr[i * 2 + 1].y; + vwptr[i * 12 + 8] = -POLY_HEIGHT; - _update_transform_2d_to_mat2x4(clight->shadow.directional_xform, state.light_uniforms[index].shadow_matrix); + vwptr[i * 12 + 9] = lr[i * 2 + 0].x; + vwptr[i * 12 + 10] = lr[i * 2 + 0].y; + vwptr[i * 12 + 11] = -POLY_HEIGHT; - state.light_uniforms[index].height = l->height; //0..1 here + iwptr[i * 6 + 0] = i * 4 + 0; + iwptr[i * 6 + 1] = i * 4 + 1; + iwptr[i * 6 + 2] = i * 4 + 2; - for (int i = 0; i < 4; i++) { - state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255)); - state.light_uniforms[index].color[i] = l->color[i]; + iwptr[i * 6 + 3] = i * 4 + 2; + iwptr[i * 6 + 4] = i * 4 + 3; + iwptr[i * 6 + 5] = i * 4 + 0; } + } - state.light_uniforms[index].color[3] *= l->energy; //use alpha for energy, so base color can go separate + //if same buffer len is being set, just use buffer_update to avoid a pipeline flush - if (state.shadow_fb.is_valid()) { - state.light_uniforms[index].shadow_pixel_size = (1.0 / state.shadow_texture_size) * (1.0 + l->shadow_smooth); - state.light_uniforms[index].shadow_z_far_inv = 1.0 / clight->shadow.z_far; - state.light_uniforms[index].shadow_y_ofs = clight->shadow.y_offset; - } else { - state.light_uniforms[index].shadow_pixel_size = 1.0; - state.light_uniforms[index].shadow_z_far_inv = 1.0; - state.light_uniforms[index].shadow_y_ofs = 0; - } + if (oc->vertex_array.is_null()) { + //create from scratch + //vertices + oc->vertex_buffer = RD::get_singleton()->vertex_buffer_create(lc * 6 * sizeof(float), geometry); - state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT; - state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT; - if (clight->shadow.enabled) { - state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW; - } + Vector buffer; + buffer.push_back(oc->vertex_buffer); + oc->vertex_array = RD::get_singleton()->vertex_array_create(4 * lc / 2, shadow_render.vertex_format, buffer); + //indices - l->render_index_cache = index; + oc->index_buffer = RD::get_singleton()->index_buffer_create(3 * lc, RD::INDEX_BUFFER_FORMAT_UINT16, indices); + oc->index_array = RD::get_singleton()->index_array_create(oc->index_buffer, 0, 3 * lc); - index++; - l = l->next_ptr; + } else { + //update existing + const uint8_t *vr = geometry.ptr(); + RD::get_singleton()->buffer_update(oc->vertex_buffer, 0, geometry.size(), vr); + const uint8_t *ir = indices.ptr(); + RD::get_singleton()->buffer_update(oc->index_buffer, 0, indices.size(), ir); } - - light_count = index; - directional_light_count = light_count; - using_directional_lights = directional_light_count > 0; } - //setup lights if exist - - { - Light *l = p_light_list; - uint32_t index = light_count; + // sdf - while (l) { - if (index == state.max_lights_per_render) { - l->render_index_cache = -1; - l = l->next_ptr; - continue; - } + Vector sdf_indices; - CanvasLight *clight = canvas_light_owner.get_or_null(l->light_internal); - if (!clight) { //unused or invalid texture - l->render_index_cache = -1; - l = l->next_ptr; - ERR_CONTINUE(!clight); - } + if (p_points.size()) { + if (p_closed) { + sdf_indices = Geometry2D::triangulate_polygon(p_points); + oc->sdf_is_lines = false; + } else { + int max = p_points.size(); + sdf_indices.resize(max * 2); - Transform2D final_xform; - if (!RSG::canvas->_interpolation_data.interpolation_enabled || !l->interpolated) { - final_xform = l->xform_curr; - } else { - real_t f = Engine::get_singleton()->get_physics_interpolation_fraction(); - TransformInterpolator::interpolate_transform_2d(l->xform_prev, l->xform_curr, final_xform, f); + int *iw = sdf_indices.ptrw(); + for (int i = 0; i < max; i++) { + iw[i * 2 + 0] = i; + iw[i * 2 + 1] = (i + 1) % max; } - // Convert light position to canvas coordinates, as all computation is done in canvas coordinates to avoid precision loss. - Vector2 canvas_light_pos = p_canvas_transform.xform(final_xform.get_origin()); - state.light_uniforms[index].position[0] = canvas_light_pos.x; - state.light_uniforms[index].position[1] = canvas_light_pos.y; + oc->sdf_is_lines = true; + } + } - _update_transform_2d_to_mat2x4(l->light_shader_xform.affine_inverse(), state.light_uniforms[index].matrix); - _update_transform_2d_to_mat2x4(l->xform_cache.affine_inverse(), state.light_uniforms[index].shadow_matrix); + if (((oc->sdf_index_count != sdf_indices.size() && oc->sdf_point_count != p_points.size()) || p_points.size() == 0) && oc->sdf_vertex_array.is_valid()) { + RD::get_singleton()->free(oc->sdf_vertex_array); + RD::get_singleton()->free(oc->sdf_vertex_buffer); + RD::get_singleton()->free(oc->sdf_index_array); + RD::get_singleton()->free(oc->sdf_index_buffer); - state.light_uniforms[index].height = l->height * (p_canvas_transform.columns[0].length() + p_canvas_transform.columns[1].length()) * 0.5; //approximate height conversion to the canvas size, since all calculations are done in canvas coords to avoid precision loss - for (int i = 0; i < 4; i++) { - state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255)); - state.light_uniforms[index].color[i] = l->color[i]; - } + oc->sdf_vertex_array = RID(); + oc->sdf_vertex_buffer = RID(); + oc->sdf_index_array = RID(); + oc->sdf_index_buffer = RID(); - state.light_uniforms[index].color[3] *= l->energy; //use alpha for energy, so base color can go separate + oc->sdf_index_count = sdf_indices.size(); + oc->sdf_point_count = p_points.size(); - if (state.shadow_fb.is_valid()) { - state.light_uniforms[index].shadow_pixel_size = (1.0 / state.shadow_texture_size) * (1.0 + l->shadow_smooth); - state.light_uniforms[index].shadow_z_far_inv = 1.0 / clight->shadow.z_far; - state.light_uniforms[index].shadow_y_ofs = clight->shadow.y_offset; - } else { - state.light_uniforms[index].shadow_pixel_size = 1.0; - state.light_uniforms[index].shadow_z_far_inv = 1.0; - state.light_uniforms[index].shadow_y_ofs = 0; - } + oc->sdf_is_lines = false; + } - state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT; - state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT; - if (clight->shadow.enabled) { - state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW; + if (sdf_indices.size()) { + if (oc->sdf_vertex_array.is_null()) { + //create from scratch + //vertices +#ifdef REAL_T_IS_DOUBLE + PackedFloat32Array float_points; + float_points.resize(p_points.size() * 2); + float *float_points_ptr = (float *)float_points.ptrw(); + for (int i = 0; i < p_points.size(); i++) { + float_points_ptr[i * 2] = p_points[i].x; + float_points_ptr[i * 2 + 1] = p_points[i].y; } + oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(float), float_points.to_byte_array()); +#else + oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(float), p_points.to_byte_array()); +#endif + oc->sdf_index_buffer = RD::get_singleton()->index_buffer_create(sdf_indices.size(), RD::INDEX_BUFFER_FORMAT_UINT32, sdf_indices.to_byte_array()); + oc->sdf_index_array = RD::get_singleton()->index_array_create(oc->sdf_index_buffer, 0, sdf_indices.size()); - if (clight->texture.is_valid()) { - Rect2 atlas_rect = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture_rect(clight->texture); - state.light_uniforms[index].atlas_rect[0] = atlas_rect.position.x; - state.light_uniforms[index].atlas_rect[1] = atlas_rect.position.y; - state.light_uniforms[index].atlas_rect[2] = atlas_rect.size.width; - state.light_uniforms[index].atlas_rect[3] = atlas_rect.size.height; + Vector buffer; + buffer.push_back(oc->sdf_vertex_buffer); + oc->sdf_vertex_array = RD::get_singleton()->vertex_array_create(p_points.size(), shadow_render.sdf_vertex_format, buffer); + //indices - } else { - state.light_uniforms[index].atlas_rect[0] = 0; - state.light_uniforms[index].atlas_rect[1] = 0; - state.light_uniforms[index].atlas_rect[2] = 0; - state.light_uniforms[index].atlas_rect[3] = 0; + } else { + //update existing +#ifdef REAL_T_IS_DOUBLE + PackedFloat32Array float_points; + float_points.resize(p_points.size() * 2); + float *float_points_ptr = (float *)float_points.ptrw(); + for (int i = 0; i < p_points.size(); i++) { + float_points_ptr[i * 2] = p_points[i].x; + float_points_ptr[i * 2 + 1] = p_points[i].y; } + RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(float) * 2 * p_points.size(), float_points.ptr()); +#else + RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(float) * 2 * p_points.size(), p_points.ptr()); +#endif + RD::get_singleton()->buffer_update(oc->sdf_index_buffer, 0, sdf_indices.size() * sizeof(int32_t), sdf_indices.ptr()); + } + } +} - l->render_index_cache = index; +void RendererCanvasRenderRD::occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) { + OccluderPolygon *oc = occluder_polygon_owner.get_or_null(p_occluder); + ERR_FAIL_NULL(oc); + oc->cull_mode = p_mode; +} - index++; - l = l->next_ptr; - } +void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) { + //compile - light_count = index; - } + code = p_code; + valid = false; + ubo_size = 0; + uniforms.clear(); + uses_screen_texture = false; + uses_screen_texture_mipmaps = false; + uses_sdf = false; + uses_time = false; - if (light_count > 0) { - RD::get_singleton()->buffer_update(state.lights_uniform_buffer, 0, sizeof(LightUniform) * light_count, &state.light_uniforms[0]); + if (code.is_empty()) { + return; //just invalid, but no error } - { - //update canvas state uniform buffer - State::Buffer state_buffer; - - Size2i ssize = texture_storage->render_target_get_size(p_to_render_target); + ShaderCompiler::GeneratedCode gen_code; - Transform3D screen_transform; - screen_transform.translate_local(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f); - screen_transform.scale(Vector3(2.0f / ssize.width, 2.0f / ssize.height, 1.0f)); - _update_transform_to_mat4(screen_transform, state_buffer.screen_transform); - _update_transform_2d_to_mat4(p_canvas_transform, state_buffer.canvas_transform); + int blend_mode = BLEND_MODE_MIX; - Transform2D normal_transform = p_canvas_transform; - normal_transform.columns[0].normalize(); - normal_transform.columns[1].normalize(); - normal_transform.columns[2] = Vector2(); - _update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform); + ShaderCompiler::IdentifierActions actions; + actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX; + actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT; + actions.entry_point_stages["light"] = ShaderCompiler::STAGE_FRAGMENT; - bool use_linear_colors = texture_storage->render_target_is_using_hdr(p_to_render_target); - Color modulate = p_modulate; - if (use_linear_colors) { - modulate = p_modulate.srgb_to_linear(); - } - state_buffer.canvas_modulate[0] = modulate.r; - state_buffer.canvas_modulate[1] = modulate.g; - state_buffer.canvas_modulate[2] = modulate.b; - state_buffer.canvas_modulate[3] = modulate.a; + actions.render_mode_values["blend_add"] = Pair(&blend_mode, BLEND_MODE_ADD); + actions.render_mode_values["blend_mix"] = Pair(&blend_mode, BLEND_MODE_MIX); + actions.render_mode_values["blend_sub"] = Pair(&blend_mode, BLEND_MODE_SUB); + actions.render_mode_values["blend_mul"] = Pair(&blend_mode, BLEND_MODE_MUL); + actions.render_mode_values["blend_premul_alpha"] = Pair(&blend_mode, BLEND_MODE_PMALPHA); + actions.render_mode_values["blend_disabled"] = Pair(&blend_mode, BLEND_MODE_DISABLED); - Size2 render_target_size = texture_storage->render_target_get_size(p_to_render_target); - state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x; - state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y; + actions.usage_flag_pointers["texture_sdf"] = &uses_sdf; + actions.usage_flag_pointers["TIME"] = &uses_time; - state_buffer.time = state.time; - state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel; + actions.uniforms = &uniforms; - state_buffer.directional_light_count = directional_light_count; + RendererCanvasRenderRD *canvas_singleton = static_cast(RendererCanvasRender::singleton); - Vector2 canvas_scale = p_canvas_transform.get_scale(); + Error err = canvas_singleton->shader.compiler.compile(RS::SHADER_CANVAS_ITEM, code, &actions, path, gen_code); + ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed."); - state_buffer.sdf_to_screen[0] = render_target_size.width / canvas_scale.x; - state_buffer.sdf_to_screen[1] = render_target_size.height / canvas_scale.y; + uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps; + uses_screen_texture = gen_code.uses_screen_texture; - state_buffer.screen_to_sdf[0] = 1.0 / state_buffer.sdf_to_screen[0]; - state_buffer.screen_to_sdf[1] = 1.0 / state_buffer.sdf_to_screen[1]; + if (version.is_null()) { + version = canvas_singleton->shader.canvas_shader.version_create(); + } - Rect2 sdf_rect = texture_storage->render_target_get_sdf_rect(p_to_render_target); - Rect2 sdf_tex_rect(sdf_rect.position / canvas_scale, sdf_rect.size / canvas_scale); +#if 0 + print_line("**compiling shader:"); + print_line("**defines:\n"); + for (int i = 0; i < gen_code.defines.size(); i++) { + print_line(gen_code.defines[i]); + } - state_buffer.sdf_to_tex[0] = 1.0 / sdf_tex_rect.size.width; - state_buffer.sdf_to_tex[1] = 1.0 / sdf_tex_rect.size.height; - state_buffer.sdf_to_tex[2] = -sdf_tex_rect.position.x / sdf_tex_rect.size.width; - state_buffer.sdf_to_tex[3] = -sdf_tex_rect.position.y / sdf_tex_rect.size.height; + HashMap::Iterator el = gen_code.code.begin(); + while (el) { + print_line("\n**code " + el->key + ":\n" + el->value); + ++el; + } - //print_line("w: " + itos(ssize.width) + " s: " + rtos(canvas_scale)); - state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5); + print_line("\n**uniforms:\n" + gen_code.uniforms); + print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]); + print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]); +#endif + canvas_singleton->shader.canvas_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines); + ERR_FAIL_COND(!canvas_singleton->shader.canvas_shader.version_is_valid(version)); - RD::get_singleton()->buffer_update(state.canvas_state_buffer, 0, sizeof(State::Buffer), &state_buffer); - } + ubo_size = gen_code.uniform_total_size; + ubo_offsets = gen_code.uniform_offsets; + texture_uniforms = gen_code.texture_uniforms; - { //default filter/repeat - default_filter = p_default_filter; - default_repeat = p_default_repeat; - } + //update them pipelines - Item *ci = p_item_list; + RD::PipelineColorBlendState::Attachment attachment; - //fill the list until rendering is possible. - bool material_screen_texture_cached = false; - bool material_screen_texture_mipmaps_cached = false; + switch (blend_mode) { + case BLEND_MODE_DISABLED: { + // nothing to do here, disabled by default - Rect2 back_buffer_rect; - bool backbuffer_copy = false; - bool backbuffer_gen_mipmaps = false; + } break; + case BLEND_MODE_MIX: { + attachment.enable_blend = true; + attachment.color_blend_op = RD::BLEND_OP_ADD; + attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - Item *canvas_group_owner = nullptr; - bool skip_item = false; + attachment.alpha_blend_op = RD::BLEND_OP_ADD; + attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - bool update_skeletons = false; - bool time_used = false; + } break; + case BLEND_MODE_ADD: { + attachment.enable_blend = true; + attachment.alpha_blend_op = RD::BLEND_OP_ADD; + attachment.color_blend_op = RD::BLEND_OP_ADD; + attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE; + attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE; - bool backbuffer_cleared = false; + } break; + case BLEND_MODE_SUB: { + attachment.enable_blend = true; + attachment.alpha_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT; + attachment.color_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT; + attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE; + attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE; - while (ci) { - if (ci->copy_back_buffer && canvas_group_owner == nullptr) { - backbuffer_copy = true; + } break; + case BLEND_MODE_MUL: { + attachment.enable_blend = true; + attachment.alpha_blend_op = RD::BLEND_OP_ADD; + attachment.color_blend_op = RD::BLEND_OP_ADD; + attachment.src_color_blend_factor = RD::BLEND_FACTOR_DST_COLOR; + attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ZERO; + attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_DST_ALPHA; + attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO; - if (ci->copy_back_buffer->full) { - back_buffer_rect = Rect2(); + } break; + case BLEND_MODE_PMALPHA: { + attachment.enable_blend = true; + attachment.alpha_blend_op = RD::BLEND_OP_ADD; + attachment.color_blend_op = RD::BLEND_OP_ADD; + attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE; + attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + + } break; + } + + RD::PipelineColorBlendState blend_state; + blend_state.attachments.push_back(attachment); + + RD::PipelineColorBlendState::Attachment attachment_lcd; + attachment_lcd.enable_blend = true; + attachment_lcd.alpha_blend_op = RD::BLEND_OP_ADD; + attachment_lcd.color_blend_op = RD::BLEND_OP_ADD; + attachment_lcd.src_color_blend_factor = RD::BLEND_FACTOR_CONSTANT_COLOR; + attachment_lcd.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + attachment_lcd.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + attachment_lcd.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + + RD::PipelineColorBlendState blend_state_lcd; + blend_state_lcd.attachments.push_back(attachment_lcd); + + //update pipelines + + for (int i = 0; i < PIPELINE_LIGHT_MODE_MAX; i++) { + for (int j = 0; j < PIPELINE_VARIANT_MAX; j++) { + RD::RenderPrimitive primitive[PIPELINE_VARIANT_MAX] = { + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_LINES, + RD::RENDER_PRIMITIVE_POINTS, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, + RD::RENDER_PRIMITIVE_LINES, + RD::RENDER_PRIMITIVE_LINESTRIPS, + RD::RENDER_PRIMITIVE_POINTS, + RD::RENDER_PRIMITIVE_TRIANGLES, + }; + + ShaderVariant shader_variants[PIPELINE_LIGHT_MODE_MAX][PIPELINE_VARIANT_MAX] = { + { + //non lit + SHADER_VARIANT_QUAD, + SHADER_VARIANT_NINEPATCH, + SHADER_VARIANT_PRIMITIVE, + SHADER_VARIANT_PRIMITIVE, + SHADER_VARIANT_PRIMITIVE_POINTS, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES_POINTS, + SHADER_VARIANT_QUAD, + }, + { + //lit + SHADER_VARIANT_QUAD_LIGHT, + SHADER_VARIANT_NINEPATCH_LIGHT, + SHADER_VARIANT_PRIMITIVE_LIGHT, + SHADER_VARIANT_PRIMITIVE_LIGHT, + SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT, + SHADER_VARIANT_QUAD_LIGHT, + }, + }; + + RID shader_variant = canvas_singleton->shader.canvas_shader.version_get_shader(version, shader_variants[i][j]); + if (j == PIPELINE_VARIANT_QUAD_LCD_BLEND) { + pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state_lcd, RD::DYNAMIC_STATE_BLEND_CONSTANTS); } else { - back_buffer_rect = ci->copy_back_buffer->rect; + pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0); } } + } - RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; + valid = true; +} - if (material.is_valid()) { - CanvasMaterialData *md = static_cast(material_storage->material_get_data(material, RendererRD::MaterialStorage::SHADER_TYPE_2D)); - if (md && md->shader_data->valid) { - if (md->shader_data->uses_screen_texture && canvas_group_owner == nullptr) { - if (!material_screen_texture_cached) { - backbuffer_copy = true; - back_buffer_rect = Rect2(); - backbuffer_gen_mipmaps = md->shader_data->uses_screen_texture_mipmaps; - } else if (!material_screen_texture_mipmaps_cached) { - backbuffer_gen_mipmaps = md->shader_data->uses_screen_texture_mipmaps; - } - } +bool RendererCanvasRenderRD::CanvasShaderData::is_animated() const { + return false; +} - if (md->shader_data->uses_sdf) { - r_sdf_used = true; - } - if (md->shader_data->uses_time) { - time_used = true; - } - } - } +bool RendererCanvasRenderRD::CanvasShaderData::casts_shadows() const { + return false; +} - if (ci->skeleton.is_valid()) { - const Item::Command *c = ci->commands; +RS::ShaderNativeSourceCode RendererCanvasRenderRD::CanvasShaderData::get_native_source_code() const { + RendererCanvasRenderRD *canvas_singleton = static_cast(RendererCanvasRender::singleton); + return canvas_singleton->shader.canvas_shader.version_get_native_source_code(version); +} - while (c) { - if (c->type == Item::Command::TYPE_MESH) { - const Item::CommandMesh *cm = static_cast(c); - if (cm->mesh_instance.is_valid()) { - mesh_storage->mesh_instance_check_for_update(cm->mesh_instance); - mesh_storage->mesh_instance_set_canvas_item_transform(cm->mesh_instance, canvas_transform_inverse * ci->final_transform); - update_skeletons = true; - } - } - c = c->next; - } - } +RendererCanvasRenderRD::CanvasShaderData::~CanvasShaderData() { + RendererCanvasRenderRD *canvas_singleton = static_cast(RendererCanvasRender::singleton); + ERR_FAIL_NULL(canvas_singleton); + //pipeline variants will clear themselves if shader is gone + if (version.is_valid()) { + canvas_singleton->shader.canvas_shader.version_free(version); + } +} - if (ci->canvas_group_owner != nullptr) { - if (canvas_group_owner == nullptr) { - // Canvas group begins here, render until before this item - if (update_skeletons) { - mesh_storage->update_mesh_instances(); - update_skeletons = false; - } +RendererRD::MaterialStorage::ShaderData *RendererCanvasRenderRD::_create_shader_func() { + CanvasShaderData *shader_data = memnew(CanvasShaderData); + return shader_data; +} - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info); - item_count = 0; +bool RendererCanvasRenderRD::CanvasMaterialData::update_parameters(const HashMap &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { + RendererCanvasRenderRD *canvas_singleton = static_cast(RendererCanvasRender::singleton); + bool uniform_set_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, true, false); + bool uniform_set_srgb_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set_srgb, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, false, false); + return uniform_set_changed || uniform_set_srgb_changed; +} - if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { - Rect2i group_rect = ci->canvas_group_owner->global_rect_cache; - texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false); - if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { - ci->canvas_group_owner->use_canvas_group = false; - items[item_count++] = ci->canvas_group_owner; - } - } else if (!backbuffer_cleared) { - texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0)); - backbuffer_cleared = true; - } +RendererCanvasRenderRD::CanvasMaterialData::~CanvasMaterialData() { + free_parameters_uniform_set(uniform_set); + free_parameters_uniform_set(uniform_set_srgb); +} - backbuffer_copy = false; - canvas_group_owner = ci->canvas_group_owner; //continue until owner found - } +RendererRD::MaterialStorage::MaterialData *RendererCanvasRenderRD::_create_material_func(CanvasShaderData *p_shader) { + CanvasMaterialData *material_data = memnew(CanvasMaterialData); + material_data->shader_data = p_shader; + //update will happen later anyway so do nothing. + return material_data; +} - ci->canvas_group_owner = nullptr; //must be cleared - } +void RendererCanvasRenderRD::set_time(double p_time) { + state.time = p_time; +} - if (canvas_group_owner == nullptr && ci->canvas_group != nullptr && ci->canvas_group->mode != RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { - skip_item = true; - } +void RendererCanvasRenderRD::update() { +} - if (ci == canvas_group_owner) { - if (update_skeletons) { - mesh_storage->update_mesh_instances(); - update_skeletons = false; - } +RendererCanvasRenderRD::RendererCanvasRenderRD() { + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true, r_render_info); - item_count = 0; + { //create default samplers - if (ci->canvas_group->blur_mipmaps) { - texture_storage->render_target_gen_back_buffer_mipmaps(p_to_render_target, ci->global_rect_cache); - } + default_samplers.default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; + default_samplers.default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; + } - canvas_group_owner = nullptr; - // Backbuffer is dirty now and needs to be re-cleared if another CanvasGroup needs it. - backbuffer_cleared = false; + { //shader variants - // Tell the renderer to paint this as a canvas group - ci->use_canvas_group = true; + String global_defines; + + uint64_t uniform_max_size = RD::get_singleton()->limit_get(RD::LIMIT_MAX_UNIFORM_BUFFER_SIZE); + if (uniform_max_size < 65536) { + //Yes, you guessed right, ARM again + state.max_lights_per_render = 64; + global_defines += "#define MAX_LIGHTS 64\n"; } else { - ci->use_canvas_group = false; + state.max_lights_per_render = DEFAULT_MAX_LIGHTS_PER_RENDER; + global_defines += "#define MAX_LIGHTS " + itos(DEFAULT_MAX_LIGHTS_PER_RENDER) + "\n"; } - if (backbuffer_copy) { - //render anything pending, including clearing if no items - if (update_skeletons) { - mesh_storage->update_mesh_instances(); - update_skeletons = false; - } + global_defines += "\n#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n"; - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info); - item_count = 0; + state.light_uniforms = memnew_arr(LightUniform, state.max_lights_per_render); + Vector variants; + //non light variants + variants.push_back(""); //none by default is first variant + variants.push_back("#define USE_NINEPATCH\n"); //ninepatch is the second variant + variants.push_back("#define USE_PRIMITIVE\n"); //primitive is the third + variants.push_back("#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); //points need point size + variants.push_back("#define USE_ATTRIBUTES\n"); // attributes for vertex arrays + variants.push_back("#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); //attributes with point size + //light variants + variants.push_back("#define USE_LIGHTING\n"); //none by default is first variant + variants.push_back("#define USE_LIGHTING\n#define USE_NINEPATCH\n"); //ninepatch is the second variant + variants.push_back("#define USE_LIGHTING\n#define USE_PRIMITIVE\n"); //primitive is the third + variants.push_back("#define USE_LIGHTING\n#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); //points need point size + variants.push_back("#define USE_LIGHTING\n#define USE_ATTRIBUTES\n"); // attributes for vertex arrays + variants.push_back("#define USE_LIGHTING\n#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); //attributes with point size - texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps); + shader.canvas_shader.initialize(variants, global_defines); + + shader.default_version = shader.canvas_shader.version_create(); + shader.default_version_rd_shader = shader.canvas_shader.version_get_shader(shader.default_version, SHADER_VARIANT_QUAD); + + RD::PipelineColorBlendState blend_state; + RD::PipelineColorBlendState::Attachment blend_attachment; + + blend_attachment.enable_blend = true; + blend_attachment.color_blend_op = RD::BLEND_OP_ADD; + blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + + blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD; + blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + + blend_state.attachments.push_back(blend_attachment); + + RD::PipelineColorBlendState::Attachment attachment_lcd; + attachment_lcd.enable_blend = true; + attachment_lcd.alpha_blend_op = RD::BLEND_OP_ADD; + attachment_lcd.color_blend_op = RD::BLEND_OP_ADD; + attachment_lcd.src_color_blend_factor = RD::BLEND_FACTOR_CONSTANT_COLOR; + attachment_lcd.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + attachment_lcd.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + attachment_lcd.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + + RD::PipelineColorBlendState blend_state_lcd; + blend_state_lcd.attachments.push_back(attachment_lcd); + + for (int i = 0; i < PIPELINE_LIGHT_MODE_MAX; i++) { + for (int j = 0; j < PIPELINE_VARIANT_MAX; j++) { + RD::RenderPrimitive primitive[PIPELINE_VARIANT_MAX] = { + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_LINES, + RD::RENDER_PRIMITIVE_POINTS, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, + RD::RENDER_PRIMITIVE_LINES, + RD::RENDER_PRIMITIVE_LINESTRIPS, + RD::RENDER_PRIMITIVE_POINTS, + RD::RENDER_PRIMITIVE_TRIANGLES, + }; + + ShaderVariant shader_variants[PIPELINE_LIGHT_MODE_MAX][PIPELINE_VARIANT_MAX] = { + { + //non lit + SHADER_VARIANT_QUAD, + SHADER_VARIANT_NINEPATCH, + SHADER_VARIANT_PRIMITIVE, + SHADER_VARIANT_PRIMITIVE, + SHADER_VARIANT_PRIMITIVE_POINTS, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES_POINTS, + SHADER_VARIANT_QUAD, + }, + { + //lit + SHADER_VARIANT_QUAD_LIGHT, + SHADER_VARIANT_NINEPATCH_LIGHT, + SHADER_VARIANT_PRIMITIVE_LIGHT, + SHADER_VARIANT_PRIMITIVE_LIGHT, + SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT, + SHADER_VARIANT_QUAD_LIGHT, + }, + }; - backbuffer_copy = false; - material_screen_texture_cached = true; // After a backbuffer copy, screen texture makes no further copies. - material_screen_texture_mipmaps_cached = backbuffer_gen_mipmaps; - backbuffer_gen_mipmaps = false; + RID shader_variant = shader.canvas_shader.version_get_shader(shader.default_version, shader_variants[i][j]); + if (j == PIPELINE_VARIANT_QUAD_LCD_BLEND) { + shader.pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state_lcd, RD::DYNAMIC_STATE_BLEND_CONSTANTS); + } else { + shader.pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0); + } + } } + } - if (backbuffer_gen_mipmaps) { - texture_storage->render_target_gen_back_buffer_mipmaps(p_to_render_target, back_buffer_rect); + { + //shader compiler + ShaderCompiler::DefaultIdentifierActions actions; - backbuffer_gen_mipmaps = false; - material_screen_texture_mipmaps_cached = true; - } + actions.renames["VERTEX"] = "vertex"; + actions.renames["LIGHT_VERTEX"] = "light_vertex"; + actions.renames["SHADOW_VERTEX"] = "shadow_vertex"; + actions.renames["UV"] = "uv"; + actions.renames["POINT_SIZE"] = "point_size"; - if (skip_item) { - skip_item = false; - } else { - items[item_count++] = ci; - } + actions.renames["MODEL_MATRIX"] = "model_matrix"; + actions.renames["CANVAS_MATRIX"] = "canvas_data.canvas_transform"; + actions.renames["SCREEN_MATRIX"] = "canvas_data.screen_transform"; + actions.renames["TIME"] = "canvas_data.time"; + actions.renames["PI"] = _MKSTR(Math_PI); + actions.renames["TAU"] = _MKSTR(Math_TAU); + actions.renames["E"] = _MKSTR(Math_E); + actions.renames["AT_LIGHT_PASS"] = "false"; + actions.renames["INSTANCE_CUSTOM"] = "instance_custom"; - if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) { - if (update_skeletons) { - mesh_storage->update_mesh_instances(); - update_skeletons = false; - } + actions.renames["COLOR"] = "color"; + actions.renames["NORMAL"] = "normal"; + actions.renames["NORMAL_MAP"] = "normal_map"; + actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth"; + actions.renames["TEXTURE"] = "color_texture"; + actions.renames["TEXTURE_PIXEL_SIZE"] = "draw_data.color_texture_pixel_size"; + actions.renames["NORMAL_TEXTURE"] = "normal_texture"; + actions.renames["SPECULAR_SHININESS_TEXTURE"] = "specular_texture"; + actions.renames["SPECULAR_SHININESS"] = "specular_shininess"; + actions.renames["SCREEN_UV"] = "screen_uv"; + actions.renames["SCREEN_PIXEL_SIZE"] = "canvas_data.screen_pixel_size"; + actions.renames["FRAGCOORD"] = "gl_FragCoord"; + actions.renames["POINT_COORD"] = "gl_PointCoord"; + actions.renames["INSTANCE_ID"] = "gl_InstanceIndex"; + actions.renames["VERTEX_ID"] = "gl_VertexIndex"; - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr, r_render_info); - //then reset - item_count = 0; - } + actions.renames["CUSTOM0"] = "custom0"; + actions.renames["CUSTOM1"] = "custom1"; - ci = ci->next; - } + actions.renames["LIGHT_POSITION"] = "light_position"; + actions.renames["LIGHT_DIRECTION"] = "light_direction"; + actions.renames["LIGHT_IS_DIRECTIONAL"] = "is_directional"; + actions.renames["LIGHT_COLOR"] = "light_color"; + actions.renames["LIGHT_ENERGY"] = "light_energy"; + actions.renames["LIGHT"] = "light"; + actions.renames["SHADOW_MODULATE"] = "shadow_modulate"; - if (time_used) { - RenderingServerDefault::redraw_request(); - } -} + actions.renames["texture_sdf"] = "texture_sdf"; + actions.renames["texture_sdf_normal"] = "texture_sdf_normal"; + actions.renames["sdf_to_screen_uv"] = "sdf_to_screen_uv"; + actions.renames["screen_uv_to_sdf"] = "screen_uv_to_sdf"; -RID RendererCanvasRenderRD::light_create() { - CanvasLight canvas_light; - return canvas_light_owner.make_rid(canvas_light); -} + actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; + actions.usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n"; + actions.usage_defines["SCREEN_PIXEL_SIZE"] = "@SCREEN_UV"; + actions.usage_defines["NORMAL"] = "#define NORMAL_USED\n"; + actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n"; + actions.usage_defines["SPECULAR_SHININESS"] = "#define SPECULAR_SHININESS_USED\n"; + actions.usage_defines["POINT_SIZE"] = "#define USE_POINT_SIZE\n"; + actions.usage_defines["CUSTOM0"] = "#define CUSTOM0_USED\n"; + actions.usage_defines["CUSTOM1"] = "#define CUSTOM1_USED\n"; -void RendererCanvasRenderRD::light_set_texture(RID p_rid, RID p_texture) { - RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; + actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n"; + actions.render_mode_defines["light_only"] = "#define MODE_LIGHT_ONLY\n"; + actions.render_mode_defines["world_vertex_coords"] = "#define USE_WORLD_VERTEX_COORDS\n"; - CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); - ERR_FAIL_NULL(cl); - if (cl->texture == p_texture) { - return; - } + actions.custom_samplers["TEXTURE"] = "texture_sampler"; + actions.custom_samplers["NORMAL_TEXTURE"] = "texture_sampler"; + actions.custom_samplers["SPECULAR_SHININESS_TEXTURE"] = "texture_sampler"; + actions.base_texture_binding_index = 1; + actions.texture_layout_set = MATERIAL_UNIFORM_SET; + actions.base_uniform_string = "material."; + actions.default_filter = ShaderLanguage::FILTER_LINEAR; + actions.default_repeat = ShaderLanguage::REPEAT_DISABLE; + actions.base_varying_index = 5; - ERR_FAIL_COND(p_texture.is_valid() && !texture_storage->owns_texture(p_texture)); + actions.global_buffer_array_variable = "global_shader_uniforms.data"; - if (cl->texture.is_valid()) { - texture_storage->texture_remove_from_decal_atlas(cl->texture); + shader.compiler.initialize(actions); } - cl->texture = p_texture; - if (cl->texture.is_valid()) { - texture_storage->texture_add_to_decal_atlas(cl->texture); - } -} + { //shadow rendering + Vector versions; + versions.push_back("\n#define MODE_SHADOW\n"); //shadow + versions.push_back("\n#define MODE_SDF\n"); //sdf + shadow_render.shader.initialize(versions); -void RendererCanvasRenderRD::light_set_use_shadow(RID p_rid, bool p_enable) { - CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); - ERR_FAIL_NULL(cl); + { + Vector attachments; - cl->shadow.enabled = p_enable; -} + RD::AttachmentFormat af_color; + af_color.format = RD::DATA_FORMAT_R32_SFLOAT; + af_color.usage_flags = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; -void RendererCanvasRenderRD::_update_shadow_atlas() { - if (state.shadow_fb == RID()) { - //ah, we lack the shadow texture.. - RD::get_singleton()->free(state.shadow_texture); //erase placeholder + attachments.push_back(af_color); - Vector fb_textures; + RD::AttachmentFormat af_depth; + af_depth.format = RD::DATA_FORMAT_D32_SFLOAT; + af_depth.usage_flags = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - { //texture - RD::TextureFormat tf; - tf.texture_type = RD::TEXTURE_TYPE_2D; - tf.width = state.shadow_texture_size; - tf.height = state.max_lights_per_render * 2; - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; - tf.format = RD::DATA_FORMAT_R32_SFLOAT; + attachments.push_back(af_depth); - state.shadow_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - fb_textures.push_back(state.shadow_texture); - } - { - RD::TextureFormat tf; - tf.texture_type = RD::TEXTURE_TYPE_2D; - tf.width = state.shadow_texture_size; - tf.height = state.max_lights_per_render * 2; - tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - tf.format = RD::DATA_FORMAT_D32_SFLOAT; - //chunks to write - state.shadow_depth_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - fb_textures.push_back(state.shadow_depth_texture); + shadow_render.framebuffer_format = RD::get_singleton()->framebuffer_format_create(attachments); } - state.shadow_fb = RD::get_singleton()->framebuffer_create(fb_textures); - } -} - -void RendererCanvasRenderRD::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) { - CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); - ERR_FAIL_COND(!cl->shadow.enabled); + { + Vector attachments; - _update_shadow_atlas(); + RD::AttachmentFormat af_color; + af_color.format = RD::DATA_FORMAT_R8_UNORM; + af_color.usage_flags = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; - cl->shadow.z_far = p_far; - cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(state.max_lights_per_render * 2); - Vector cc; - cc.push_back(Color(p_far, p_far, p_far, 1.0)); + attachments.push_back(af_color); - for (int i = 0; i < 4; i++) { - //make sure it remains orthogonal, makes easy to read angle later + shadow_render.sdf_framebuffer_format = RD::get_singleton()->framebuffer_format_create(attachments); + } - //light.basis.scale(Vector3(to_light.elements[0].length(),to_light.elements[1].length(),1)); + //pipelines + Vector vf; + RD::VertexAttribute vd; + vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; + vd.stride = sizeof(float) * 3; + vd.location = 0; + vd.offset = 0; + vf.push_back(vd); + shadow_render.vertex_format = RD::get_singleton()->vertex_format_create(vf); - Rect2i rect((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect); + vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; + vd.stride = sizeof(float) * 2; - Projection projection; - { - real_t fov = 90; - real_t nearp = p_near; - real_t farp = p_far; - real_t aspect = 1.0; + vf.write[0] = vd; + shadow_render.sdf_vertex_format = RD::get_singleton()->vertex_format_create(vf); - real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5)); - real_t ymin = -ymax; - real_t xmin = ymin * aspect; - real_t xmax = ymax * aspect; + shadow_render.shader_version = shadow_render.shader.version_create(); - projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp); + for (int i = 0; i < 3; i++) { + RD::PipelineRasterizationState rs; + rs.cull_mode = i == 0 ? RD::POLYGON_CULL_DISABLED : (i == 1 ? RD::POLYGON_CULL_FRONT : RD::POLYGON_CULL_BACK); + RD::PipelineDepthStencilState ds; + ds.enable_depth_write = true; + ds.enable_depth_test = true; + ds.depth_compare_operator = RD::COMPARE_OP_LESS; + shadow_render.render_pipelines[i] = RD::get_singleton()->render_pipeline_create(shadow_render.shader.version_get_shader(shadow_render.shader_version, SHADOW_RENDER_MODE_SHADOW), shadow_render.framebuffer_format, shadow_render.vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0); } - Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0)); - projection = projection * Projection(Transform3D().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse()); - - ShadowRenderPushConstant push_constant; - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - push_constant.projection[y * 4 + x] = projection.columns[y][x]; - } + for (int i = 0; i < 2; i++) { + shadow_render.sdf_render_pipelines[i] = RD::get_singleton()->render_pipeline_create(shadow_render.shader.version_get_shader(shadow_render.shader_version, SHADOW_RENDER_MODE_SDF), shadow_render.sdf_framebuffer_format, shadow_render.sdf_vertex_format, i == 0 ? RD::RENDER_PRIMITIVE_TRIANGLES : RD::RENDER_PRIMITIVE_LINES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); } - static const Vector2 directions[4] = { Vector2(1, 0), Vector2(0, 1), Vector2(-1, 0), Vector2(0, -1) }; - push_constant.direction[0] = directions[i].x; - push_constant.direction[1] = directions[i].y; - push_constant.z_far = p_far; - push_constant.pad = 0; - - LightOccluderInstance *instance = p_occluders; + } - while (instance) { - OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder); + { //bindings - if (!co || co->index_array.is_null() || !(p_light_mask & instance->light_mask)) { - instance = instance->next; - continue; - } + state.canvas_state_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(State::Buffer)); + state.lights_uniform_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(LightUniform) * state.max_lights_per_render); - _update_transform_2d_to_mat2x4(p_light_xform * instance->xform_cache, push_constant.modelview); + RD::SamplerState shadow_sampler_state; + shadow_sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR; + shadow_sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; + shadow_sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_REPEAT; //shadow wrap around + shadow_sampler_state.compare_op = RD::COMPARE_OP_GREATER; + shadow_sampler_state.enable_compare = true; + state.shadow_sampler = RD::get_singleton()->sampler_create(shadow_sampler_state); + } - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.render_pipelines[co->cull_mode]); - RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->vertex_array); - RD::get_singleton()->draw_list_bind_index_array(draw_list, co->index_array); - RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowRenderPushConstant)); + { + //polygon buffers + polygon_buffers.last_id = 1; + } - RD::get_singleton()->draw_list_draw(draw_list, true); + { // default index buffer - instance = instance->next; + Vector pv; + pv.resize(6 * 2); + { + uint8_t *w = pv.ptrw(); + uint16_t *p16 = (uint16_t *)w; + p16[0] = 0; + p16[1] = 1; + p16[2] = 2; + p16[3] = 0; + p16[4] = 2; + p16[5] = 3; } + shader.quad_index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT16, pv); + shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 6); + } - RD::get_singleton()->draw_list_end(); + { //primitive + primitive_arrays.index_array[0] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 1); + primitive_arrays.index_array[1] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 2); + primitive_arrays.index_array[2] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 3); + primitive_arrays.index_array[3] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 6); } -} -void RendererCanvasRenderRD::light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) { - CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); - ERR_FAIL_COND(!cl->shadow.enabled); + { + //default shadow texture to keep uniform set happy + RD::TextureFormat tf; + tf.texture_type = RD::TEXTURE_TYPE_2D; + tf.width = 4; + tf.height = 4; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; + tf.format = RD::DATA_FORMAT_R32_SFLOAT; - _update_shadow_atlas(); + state.shadow_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } - Vector2 light_dir = p_light_xform.columns[1].normalized(); + { + Vector uniforms; - Vector2 center = p_clip_rect.get_center(); + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 0; + u.append_id(RendererRD::MeshStorage::get_singleton()->get_default_rd_storage_buffer()); + uniforms.push_back(u); + } - float to_edge_distance = ABS(light_dir.dot(p_clip_rect.get_support(-light_dir)) - light_dir.dot(center)); + state.default_transforms_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader.default_version_rd_shader, TRANSFORMS_UNIFORM_SET); + } - Vector2 from_pos = center - light_dir * (to_edge_distance + p_cull_distance); - float distance = to_edge_distance * 2.0 + p_cull_distance; - float half_size = p_clip_rect.size.length() * 0.5; //shadow length, must keep this no matter the angle + default_canvas_texture = texture_storage->canvas_texture_allocate(); + texture_storage->canvas_texture_initialize(default_canvas_texture); - cl->shadow.z_far = distance; - cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(state.max_lights_per_render * 2); + state.shadow_texture_size = GLOBAL_GET("rendering/2d/shadow_atlas/size"); - Transform2D to_light_xform; + //create functions for shader and material + material_storage->shader_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_2D, _create_shader_funcs); + material_storage->material_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_2D, _create_material_funcs); - to_light_xform[2] = from_pos; - to_light_xform[1] = light_dir; - to_light_xform[0] = -light_dir.orthogonal(); + state.time = 0; - to_light_xform.invert(); + { + default_canvas_group_shader = material_storage->shader_allocate(); + material_storage->shader_initialize(default_canvas_group_shader); - Vector cc; - cc.push_back(Color(1, 1, 1, 1)); + material_storage->shader_set_code(default_canvas_group_shader, R"( +// Default CanvasGroup shader. - Rect2i rect(0, p_shadow_index * 2, state.shadow_texture_size, 2); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect); +shader_type canvas_item; +render_mode unshaded; - Projection projection; - projection.set_orthogonal(-half_size, half_size, -0.5, 0.5, 0.0, distance); - projection = projection * Projection(Transform3D().looking_at(Vector3(0, 1, 0), Vector3(0, 0, -1)).affine_inverse()); +uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest; - ShadowRenderPushConstant push_constant; - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - push_constant.projection[y * 4 + x] = projection.columns[y][x]; - } +void fragment() { + vec4 c = textureLod(screen_texture, SCREEN_UV, 0.0); + + if (c.a > 0.0001) { + c.rgb /= c.a; } - push_constant.direction[0] = 0.0; - push_constant.direction[1] = 1.0; - push_constant.z_far = distance; - push_constant.pad = 0; + COLOR *= c; +} +)"); + default_canvas_group_material = material_storage->material_allocate(); + material_storage->material_initialize(default_canvas_group_material); - LightOccluderInstance *instance = p_occluders; + material_storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader); + } - while (instance) { - OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder); + { + default_clip_children_shader = material_storage->shader_allocate(); + material_storage->shader_initialize(default_clip_children_shader); - if (!co || co->index_array.is_null() || !(p_light_mask & instance->light_mask)) { - instance = instance->next; - continue; - } + material_storage->shader_set_code(default_clip_children_shader, R"( +// Default clip children shader. - _update_transform_2d_to_mat2x4(to_light_xform * instance->xform_cache, push_constant.modelview); +shader_type canvas_item; +render_mode unshaded; - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.render_pipelines[co->cull_mode]); - RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->vertex_array); - RD::get_singleton()->draw_list_bind_index_array(draw_list, co->index_array); - RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowRenderPushConstant)); +uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest; - RD::get_singleton()->draw_list_draw(draw_list, true); +void fragment() { + vec4 c = textureLod(screen_texture, SCREEN_UV, 0.0); + COLOR.rgb = c.rgb; +} +)"); + default_clip_children_material = material_storage->material_allocate(); + material_storage->material_initialize(default_clip_children_material); - instance = instance->next; + material_storage->material_set_shader(default_clip_children_material, default_clip_children_shader); } - RD::get_singleton()->draw_list_end(); - - Transform2D to_shadow; - to_shadow.columns[0].x = 1.0 / -(half_size * 2.0); - to_shadow.columns[2].x = 0.5; + { + state.max_instances_per_buffer = uint32_t(GLOBAL_GET("rendering/2d/batching/item_buffer_size")); + state.max_instance_buffer_size = state.max_instances_per_buffer * sizeof(InstanceData); + state.canvas_instance_data_buffers.resize(3); + state.canvas_instance_batches.reserve(200); - cl->shadow.directional_xform = to_shadow * to_light_xform; + for (int i = 0; i < 3; i++) { + DataBuffer db; + db.instance_buffers.push_back(RD::get_singleton()->storage_buffer_create(state.max_instance_buffer_size)); + state.canvas_instance_data_buffers[i] = db; + } + state.instance_data_array = memnew_arr(InstanceData, state.max_instances_per_buffer); + } } -void RendererCanvasRenderRD::render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) { - RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); +bool RendererCanvasRenderRD::free(RID p_rid) { + if (canvas_light_owner.owns(p_rid)) { + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(cl, false); + light_set_use_shadow(p_rid, false); + canvas_light_owner.free(p_rid); + } else if (occluder_polygon_owner.owns(p_rid)) { + occluder_polygon_set_shape(p_rid, Vector(), false); + occluder_polygon_owner.free(p_rid); + } else { + return false; + } - RID fb = texture_storage->render_target_get_sdf_framebuffer(p_render_target); - Rect2i rect = texture_storage->render_target_get_sdf_rect(p_render_target); + return true; +} - Transform2D to_sdf; - to_sdf.columns[0] *= rect.size.width; - to_sdf.columns[1] *= rect.size.height; - to_sdf.columns[2] = rect.position; +void RendererCanvasRenderRD::set_shadow_texture_size(int p_size) { + p_size = MAX(1, nearest_power_of_2_templated(p_size)); + if (p_size == state.shadow_texture_size) { + return; + } + state.shadow_texture_size = p_size; + if (state.shadow_fb.is_valid()) { + RD::get_singleton()->free(state.shadow_texture); + RD::get_singleton()->free(state.shadow_depth_texture); + state.shadow_fb = RID(); - Transform2D to_clip; - to_clip.columns[0] *= 2.0; - to_clip.columns[1] *= 2.0; - to_clip.columns[2] = -Vector2(1.0, 1.0); + { + //create a default shadow texture to keep uniform set happy (and that it gets erased when a new one is created) + RD::TextureFormat tf; + tf.texture_type = RD::TEXTURE_TYPE_2D; + tf.width = 4; + tf.height = 4; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; + tf.format = RD::DATA_FORMAT_R32_SFLOAT; - to_clip = to_clip * to_sdf.affine_inverse(); + state.shadow_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } + } +} - Vector cc; - cc.push_back(Color(0, 0, 0, 0)); +void RendererCanvasRenderRD::set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) { + debug_redraw = p_enabled; + debug_redraw_time = p_time; + debug_redraw_color = p_color; +} - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc); +void RendererCanvasRenderRD::_render_batch_items(RenderTarget p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer, RenderingMethod::RenderInfo *r_render_info) { + // Record batches + uint32_t instance_index = 0; + { + RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); + Item *current_clip = nullptr; + + // Record Batches. + // First item always forms its own batch. + bool batch_broken = false; + Batch *current_batch = _new_batch(batch_broken); + // Override the start position and index as we want to start from where we finished off last time. + current_batch->start = state.last_instance_index; + + for (int i = 0; i < p_item_count; i++) { + Item *ci = items[i]; + + if (ci->final_clip_owner != current_batch->clip) { + current_batch = _new_batch(batch_broken); + current_batch->clip = ci->final_clip_owner; + current_clip = ci->final_clip_owner; + } - Projection projection; + RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; - ShadowRenderPushConstant push_constant; - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - push_constant.projection[y * 4 + x] = projection.columns[y][x]; - } - } + if (ci->use_canvas_group) { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + material = default_clip_children_material; + } else { + if (material.is_null()) { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) { + material = default_clip_children_material; + } else { + material = default_canvas_group_material; + } + } + } + } - push_constant.direction[0] = 0.0; - push_constant.direction[1] = 0.0; - push_constant.z_far = 0; - push_constant.pad = 0; + if (material != current_batch->material) { + current_batch = _new_batch(batch_broken); - LightOccluderInstance *instance = p_occluders; + CanvasMaterialData *material_data = nullptr; + if (material.is_valid()) { + material_data = static_cast(material_storage->material_get_data(material, RendererRD::MaterialStorage::SHADER_TYPE_2D)); + } - while (instance) { - OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder); + current_batch->material = material; + current_batch->material_data = material_data; + } - if (!co || co->sdf_index_array.is_null() || !instance->sdf_collision) { - instance = instance->next; - continue; + Transform2D base_transform = p_canvas_transform_inverse * ci->final_transform; + if (!ci->repeat_size.x && !ci->repeat_size.y) { + _record_item_commands(ci, p_to_render_target, base_transform, current_clip, p_lights, instance_index, batch_broken, r_sdf_used); + } else { + Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2); + Point2 end_pos = ci->repeat_size * ci->repeat_times + ci->repeat_size + start_pos; + Point2 pos = start_pos; + do { + do { + Transform2D transform = base_transform * Transform2D(0, pos / ci->xform_curr.get_scale()); + _record_item_commands(ci, p_to_render_target, transform, current_clip, p_lights, instance_index, batch_broken, r_sdf_used); + pos.y += ci->repeat_size.y; + } while (pos.y < end_pos.y); + + pos.x += ci->repeat_size.x; + pos.y = start_pos.y; + } while (pos.x < end_pos.x); + } + } + + // Copy over remaining data needed for rendering. + if (instance_index > 0) { + RD::get_singleton()->buffer_update( + state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.current_instance_buffer_index], + state.last_instance_index * sizeof(InstanceData), + instance_index * sizeof(InstanceData), + state.instance_data_array); } + } + + if (state.canvas_instance_batches.is_empty()) { + // Nothing to render, just return. + return; + } - _update_transform_2d_to_mat2x4(to_clip * instance->xform_cache, push_constant.modelview); + // Render batches - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.sdf_render_pipelines[co->sdf_is_lines ? SHADOW_RENDER_SDF_LINES : SHADOW_RENDER_SDF_TRIANGLES]); - RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->sdf_vertex_array); - RD::get_singleton()->draw_list_bind_index_array(draw_list, co->sdf_index_array); - RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowRenderPushConstant)); + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - RD::get_singleton()->draw_list_draw(draw_list, true); + RID framebuffer; + RID fb_uniform_set; + bool clear = false; + Vector clear_colors; - instance = instance->next; - } + if (p_to_backbuffer) { + framebuffer = texture_storage->render_target_get_rd_backbuffer_framebuffer(p_to_render_target.render_target); + fb_uniform_set = texture_storage->render_target_get_backbuffer_uniform_set(p_to_render_target.render_target); + } else { + framebuffer = texture_storage->render_target_get_rd_framebuffer(p_to_render_target.render_target); + texture_storage->render_target_set_msaa_needs_resolve(p_to_render_target.render_target, false); // If MSAA is enabled, our framebuffer will be resolved! - RD::get_singleton()->draw_list_end(); + if (texture_storage->render_target_is_clear_requested(p_to_render_target.render_target)) { + clear = true; + clear_colors.push_back(texture_storage->render_target_get_clear_request_color(p_to_render_target.render_target)); + texture_storage->render_target_disable_clear_request(p_to_render_target.render_target); + } + // TODO: Obtain from framebuffer format eventually when this is implemented. + fb_uniform_set = texture_storage->render_target_get_framebuffer_uniform_set(p_to_render_target.render_target); + } - texture_storage->render_target_sdf_process(p_render_target); //done rendering, process it -} + if (fb_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(fb_uniform_set)) { + fb_uniform_set = _create_base_uniform_set(p_to_render_target.render_target, p_to_backbuffer); + } -RID RendererCanvasRenderRD::occluder_polygon_create() { - OccluderPolygon occluder; - occluder.line_point_count = 0; - occluder.sdf_point_count = 0; - occluder.sdf_index_count = 0; - occluder.cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED; - return occluder_polygon_owner.make_rid(occluder); -} + RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); -void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Vector &p_points, bool p_closed) { - OccluderPolygon *oc = occluder_polygon_owner.get_or_null(p_occluder); - ERR_FAIL_NULL(oc); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors); - Vector lines; + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, fb_uniform_set, BASE_UNIFORM_SET); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.default_transforms_uniform_set, TRANSFORMS_UNIFORM_SET); - if (p_points.size()) { - int lc = p_points.size() * 2; + Item *current_clip = nullptr; + state.current_tex_uniform_set = RID(); - lines.resize(lc - (p_closed ? 0 : 2)); - { - Vector2 *w = lines.ptrw(); - const Vector2 *r = p_points.ptr(); + for (uint32_t i = 0; i <= state.current_batch_index; i++) { + Batch *current_batch = &state.canvas_instance_batches[i]; + // Skipping when there is no instances. + if (current_batch->instance_count == 0) { + continue; + } - int max = lc / 2; - if (!p_closed) { - max--; + //setup clip + if (current_clip != current_batch->clip) { + current_clip = current_batch->clip; + if (current_clip) { + RD::get_singleton()->draw_list_enable_scissor(draw_list, current_clip->final_clip_rect); + } else { + RD::get_singleton()->draw_list_disable_scissor(draw_list); } - for (int i = 0; i < max; i++) { - Vector2 a = r[i]; - Vector2 b = r[(i + 1) % (lc / 2)]; - w[i * 2 + 0] = a; - w[i * 2 + 1] = b; + } + + PipelineVariants *pipeline_variants = &shader.pipeline_variants; + + CanvasMaterialData *material_data = current_batch->material_data; + if (material_data) { + if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { + pipeline_variants = &material_data->shader_data->pipeline_variants; + // Update uniform set. + RID uniform_set = texture_storage->render_target_is_using_hdr(p_to_render_target.render_target) ? material_data->uniform_set : material_data->uniform_set_srgb; + if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { // Material may not have a uniform set. + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set, MATERIAL_UNIFORM_SET); + material_data->set_as_used(); + } } } + + _render_batch(draw_list, pipeline_variants, fb_format, p_lights, current_batch, r_render_info); } - if ((oc->line_point_count != lines.size() || lines.size() == 0) && oc->vertex_array.is_valid()) { - RD::get_singleton()->free(oc->vertex_array); - RD::get_singleton()->free(oc->vertex_buffer); - RD::get_singleton()->free(oc->index_array); - RD::get_singleton()->free(oc->index_buffer); + RD::get_singleton()->draw_list_end(); - oc->vertex_array = RID(); - oc->vertex_buffer = RID(); - oc->index_array = RID(); - oc->index_buffer = RID(); + state.current_batch_index = 0; + state.canvas_instance_batches.clear(); + state.last_instance_index += instance_index; +} - oc->line_point_count = lines.size(); - } +void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTarget p_render_target, const Transform2D &p_base_transform, Item *&r_current_clip, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used) { + Batch *current_batch = &state.canvas_instance_batches[state.current_batch_index]; - if (lines.size()) { - oc->line_point_count = lines.size(); - Vector geometry; - Vector indices; - int lc = lines.size(); + RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? default_filter : p_item->texture_filter; + RenderingServer::CanvasItemTextureRepeat texture_repeat = p_item->texture_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? default_repeat : p_item->texture_repeat; - geometry.resize(lc * 6 * sizeof(float)); - indices.resize(lc * 3 * sizeof(uint16_t)); + Transform2D base_transform = p_base_transform; - { - uint8_t *vw = geometry.ptrw(); - float *vwptr = reinterpret_cast(vw); - uint8_t *iw = indices.ptrw(); - uint16_t *iwptr = (uint16_t *)iw; + float world[6]; + Transform2D draw_transform; // Used by transform command + _update_transform_2d_to_mat2x3(base_transform, world); - const Vector2 *lr = lines.ptr(); + Color base_color = p_item->final_modulate; + bool use_linear_colors = bool(p_render_target.base_flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR); + uint32_t base_flags = p_render_target.base_flags; - const int POLY_HEIGHT = 16384; + bool reclip = false; - for (int i = 0; i < lc / 2; i++) { - vwptr[i * 12 + 0] = lr[i * 2 + 0].x; - vwptr[i * 12 + 1] = lr[i * 2 + 0].y; - vwptr[i * 12 + 2] = POLY_HEIGHT; + bool skipping = false; - vwptr[i * 12 + 3] = lr[i * 2 + 1].x; - vwptr[i * 12 + 4] = lr[i * 2 + 1].y; - vwptr[i * 12 + 5] = POLY_HEIGHT; + // TODO: consider making lights a per-batch property and then baking light operations in the shader for better performance. + uint32_t lights[4] = { 0, 0, 0, 0 }; - vwptr[i * 12 + 6] = lr[i * 2 + 1].x; - vwptr[i * 12 + 7] = lr[i * 2 + 1].y; - vwptr[i * 12 + 8] = -POLY_HEIGHT; + uint16_t light_count = 0; + PipelineLightMode light_mode; - vwptr[i * 12 + 9] = lr[i * 2 + 0].x; - vwptr[i * 12 + 10] = lr[i * 2 + 0].y; - vwptr[i * 12 + 11] = -POLY_HEIGHT; + { + Light *light = p_lights; - iwptr[i * 6 + 0] = i * 4 + 0; - iwptr[i * 6 + 1] = i * 4 + 1; - iwptr[i * 6 + 2] = i * 4 + 2; + while (light) { + if (light->render_index_cache >= 0 && p_item->light_mask & light->item_mask && p_item->z_final >= light->z_min && p_item->z_final <= light->z_max && p_item->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) { + uint32_t light_index = light->render_index_cache; + lights[light_count >> 2] |= light_index << ((light_count & 3) * 8); - iwptr[i * 6 + 3] = i * 4 + 2; - iwptr[i * 6 + 4] = i * 4 + 3; - iwptr[i * 6 + 5] = i * 4 + 0; + light_count++; + + if (light_count == state.max_lights_per_item - 1) { + break; + } } + light = light->next_ptr; } - //if same buffer len is being set, just use buffer_update to avoid a pipeline flush - - if (oc->vertex_array.is_null()) { - //create from scratch - //vertices - oc->vertex_buffer = RD::get_singleton()->vertex_buffer_create(lc * 6 * sizeof(float), geometry); - - Vector buffer; - buffer.push_back(oc->vertex_buffer); - oc->vertex_array = RD::get_singleton()->vertex_array_create(4 * lc / 2, shadow_render.vertex_format, buffer); - //indices + base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT; + } - oc->index_buffer = RD::get_singleton()->index_buffer_create(3 * lc, RD::INDEX_BUFFER_FORMAT_UINT16, indices); - oc->index_array = RD::get_singleton()->index_array_create(oc->index_buffer, 0, 3 * lc); + light_mode = (light_count > 0 || using_directional_lights) ? PIPELINE_LIGHT_MODE_ENABLED : PIPELINE_LIGHT_MODE_DISABLED; - } else { - //update existing - const uint8_t *vr = geometry.ptr(); - RD::get_singleton()->buffer_update(oc->vertex_buffer, 0, geometry.size(), vr); - const uint8_t *ir = indices.ptr(); - RD::get_singleton()->buffer_update(oc->index_buffer, 0, indices.size(), ir); - } + if (light_mode != current_batch->light_mode) { + current_batch = _new_batch(r_batch_broken); + current_batch->light_mode = light_mode; } - // sdf + // new_instance_data should be called after the current_batch is set. + auto new_instance_data = [&]() -> InstanceData * { + InstanceData *instance_data = &state.instance_data_array[r_index]; + // Zero out most fields. + for (int i = 0; i < 4; i++) { + instance_data->modulation[i] = 0.0; + instance_data->ninepatch_margins[i] = 0.0; + instance_data->src_rect[i] = 0.0; + instance_data->dst_rect[i] = 0.0; + } - Vector sdf_indices; + instance_data->pad[0] = 0.0; + instance_data->pad[1] = 0.0; - if (p_points.size()) { - if (p_closed) { - sdf_indices = Geometry2D::triangulate_polygon(p_points); - oc->sdf_is_lines = false; - } else { - int max = p_points.size(); - sdf_indices.resize(max * 2); + instance_data->lights[0] = lights[0]; + instance_data->lights[1] = lights[1]; + instance_data->lights[2] = lights[2]; + instance_data->lights[3] = lights[3]; - int *iw = sdf_indices.ptrw(); - for (int i = 0; i < max; i++) { - iw[i * 2 + 0] = i; - iw[i * 2 + 1] = (i + 1) % max; - } - oc->sdf_is_lines = true; + for (int i = 0; i < 6; i++) { + instance_data->world[i] = world[i]; } - } - if (((oc->sdf_index_count != sdf_indices.size() && oc->sdf_point_count != p_points.size()) || p_points.size() == 0) && oc->sdf_vertex_array.is_valid()) { - RD::get_singleton()->free(oc->sdf_vertex_array); - RD::get_singleton()->free(oc->sdf_vertex_buffer); - RD::get_singleton()->free(oc->sdf_index_array); - RD::get_singleton()->free(oc->sdf_index_buffer); + instance_data->flags = base_flags | current_batch->tex_flags; // Reset on each command for safety, keep canvas texture binding config. - oc->sdf_vertex_array = RID(); - oc->sdf_vertex_buffer = RID(); - oc->sdf_index_array = RID(); - oc->sdf_index_buffer = RID(); + instance_data->color_texture_pixel_size[0] = current_batch->tex_texpixel_size.width; + instance_data->color_texture_pixel_size[1] = current_batch->tex_texpixel_size.height; + instance_data->specular_shininess = current_batch->tex_specular_shininess; - oc->sdf_index_count = sdf_indices.size(); - oc->sdf_point_count = p_points.size(); + return instance_data; + }; - oc->sdf_is_lines = false; - } + const Item::Command *c = p_item->commands; + while (c) { + if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) { + c = c->next; + continue; + } - if (sdf_indices.size()) { - if (oc->sdf_vertex_array.is_null()) { - //create from scratch - //vertices -#ifdef REAL_T_IS_DOUBLE - PackedFloat32Array float_points; - float_points.resize(p_points.size() * 2); - float *float_points_ptr = (float *)float_points.ptrw(); - for (int i = 0; i < p_points.size(); i++) { - float_points_ptr[i * 2] = p_points[i].x; - float_points_ptr[i * 2 + 1] = p_points[i].y; - } - oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(float), float_points.to_byte_array()); -#else - oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(float), p_points.to_byte_array()); -#endif - oc->sdf_index_buffer = RD::get_singleton()->index_buffer_create(sdf_indices.size(), RD::INDEX_BUFFER_FORMAT_UINT32, sdf_indices.to_byte_array()); - oc->sdf_index_array = RD::get_singleton()->index_array_create(oc->sdf_index_buffer, 0, sdf_indices.size()); + switch (c->type) { + case Item::Command::TYPE_RECT: { + const Item::CommandRect *rect = static_cast(c); - Vector buffer; - buffer.push_back(oc->sdf_vertex_buffer); - oc->sdf_vertex_array = RD::get_singleton()->vertex_array_create(p_points.size(), shadow_render.sdf_vertex_format, buffer); - //indices + // 1: If commands are different, start a new batch. + if (current_batch->command_type != Item::Command::TYPE_RECT) { + current_batch = _new_batch(r_batch_broken); + current_batch->command_type = Item::Command::TYPE_RECT; + current_batch->command = c; + // default variant + current_batch->pipeline_variant = PIPELINE_VARIANT_QUAD; + } - } else { - //update existing -#ifdef REAL_T_IS_DOUBLE - PackedFloat32Array float_points; - float_points.resize(p_points.size() * 2); - float *float_points_ptr = (float *)float_points.ptrw(); - for (int i = 0; i < p_points.size(); i++) { - float_points_ptr[i * 2] = p_points[i].x; - float_points_ptr[i * 2 + 1] = p_points[i].y; - } - RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(float) * 2 * p_points.size(), float_points.ptr()); -#else - RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(float) * 2 * p_points.size(), p_points.ptr()); -#endif - RD::get_singleton()->buffer_update(oc->sdf_index_buffer, 0, sdf_indices.size() * sizeof(int32_t), sdf_indices.ptr()); - } - } -} + if (bool(rect->flags & CANVAS_RECT_TILE)) { + texture_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; + } -void RendererCanvasRenderRD::occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) { - OccluderPolygon *oc = occluder_polygon_owner.get_or_null(p_occluder); - ERR_FAIL_NULL(oc); - oc->cull_mode = p_mode; -} + bool has_msdf = bool(rect->flags & CANVAS_RECT_MSDF); + TextureState tex_state(rect->texture, texture_filter, texture_repeat, has_msdf, use_linear_colors); -void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) { - //compile + if (tex_state != current_batch->tex_state) { + current_batch = _new_batch(r_batch_broken); + current_batch->set_tex_state(tex_state); + _prepare_batch_texture(current_batch, rect->texture); + } - code = p_code; - valid = false; - ubo_size = 0; - uniforms.clear(); - uses_screen_texture = false; - uses_screen_texture_mipmaps = false; - uses_sdf = false; - uses_time = false; + Color modulated = rect->modulate * base_color; + if (use_linear_colors) { + modulated = modulated.srgb_to_linear(); + } - if (code.is_empty()) { - return; //just invalid, but no error - } + bool has_blend = bool(rect->flags & CANVAS_RECT_LCD); + // Start a new batch if the blend mode has changed, + // or blend mode is enabled and the modulation has changed. + if (has_blend != current_batch->has_blend || (has_blend && modulated != current_batch->modulate)) { + current_batch = _new_batch(r_batch_broken); + current_batch->has_blend = has_blend; + current_batch->modulate = modulated; + current_batch->pipeline_variant = has_blend ? PIPELINE_VARIANT_QUAD_LCD_BLEND : PIPELINE_VARIANT_QUAD; + } - ShaderCompiler::GeneratedCode gen_code; + InstanceData *instance_data = new_instance_data(); + Rect2 src_rect; + Rect2 dst_rect; - int blend_mode = BLEND_MODE_MIX; + if (rect->texture.is_valid()) { + src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * current_batch->tex_texpixel_size, rect->source.size * current_batch->tex_texpixel_size) : Rect2(0, 0, 1, 1); + dst_rect = Rect2(rect->rect.position, rect->rect.size); - ShaderCompiler::IdentifierActions actions; - actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX; - actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT; - actions.entry_point_stages["light"] = ShaderCompiler::STAGE_FRAGMENT; + if (dst_rect.size.width < 0) { + dst_rect.position.x += dst_rect.size.width; + dst_rect.size.width *= -1; + } + if (dst_rect.size.height < 0) { + dst_rect.position.y += dst_rect.size.height; + dst_rect.size.height *= -1; + } - actions.render_mode_values["blend_add"] = Pair(&blend_mode, BLEND_MODE_ADD); - actions.render_mode_values["blend_mix"] = Pair(&blend_mode, BLEND_MODE_MIX); - actions.render_mode_values["blend_sub"] = Pair(&blend_mode, BLEND_MODE_SUB); - actions.render_mode_values["blend_mul"] = Pair(&blend_mode, BLEND_MODE_MUL); - actions.render_mode_values["blend_premul_alpha"] = Pair(&blend_mode, BLEND_MODE_PMALPHA); - actions.render_mode_values["blend_disabled"] = Pair(&blend_mode, BLEND_MODE_DISABLED); + if (rect->flags & CANVAS_RECT_FLIP_H) { + src_rect.size.x *= -1; + instance_data->flags |= FLAGS_FLIP_H; + } - actions.usage_flag_pointers["texture_sdf"] = &uses_sdf; - actions.usage_flag_pointers["TIME"] = &uses_time; + if (rect->flags & CANVAS_RECT_FLIP_V) { + src_rect.size.y *= -1; + instance_data->flags |= FLAGS_FLIP_V; + } - actions.uniforms = &uniforms; + if (rect->flags & CANVAS_RECT_TRANSPOSE) { + instance_data->flags |= FLAGS_TRANSPOSE_RECT; + } - RendererCanvasRenderRD *canvas_singleton = static_cast(RendererCanvasRender::singleton); + if (rect->flags & CANVAS_RECT_CLIP_UV) { + instance_data->flags |= FLAGS_CLIP_RECT_UV; + } - Error err = canvas_singleton->shader.compiler.compile(RS::SHADER_CANVAS_ITEM, code, &actions, path, gen_code); - ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed."); + } else { + dst_rect = Rect2(rect->rect.position, rect->rect.size); - uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps; - uses_screen_texture = gen_code.uses_screen_texture; + if (dst_rect.size.width < 0) { + dst_rect.position.x += dst_rect.size.width; + dst_rect.size.width *= -1; + } + if (dst_rect.size.height < 0) { + dst_rect.position.y += dst_rect.size.height; + dst_rect.size.height *= -1; + } - if (version.is_null()) { - version = canvas_singleton->shader.canvas_shader.version_create(); - } + src_rect = Rect2(0, 0, 1, 1); + } -#if 0 - print_line("**compiling shader:"); - print_line("**defines:\n"); - for (int i = 0; i < gen_code.defines.size(); i++) { - print_line(gen_code.defines[i]); - } + if (has_msdf) { + instance_data->flags |= FLAGS_USE_MSDF; + instance_data->msdf[0] = rect->px_range; // Pixel range. + instance_data->msdf[1] = rect->outline; // Outline size. + instance_data->msdf[2] = 0.f; // Reserved. + instance_data->msdf[3] = 0.f; // Reserved. + } else if (rect->flags & CANVAS_RECT_LCD) { + instance_data->flags |= FLAGS_USE_LCD; + } - HashMap::Iterator el = gen_code.code.begin(); - while (el) { - print_line("\n**code " + el->key + ":\n" + el->value); - ++el; - } + instance_data->modulation[0] = modulated.r; + instance_data->modulation[1] = modulated.g; + instance_data->modulation[2] = modulated.b; + instance_data->modulation[3] = modulated.a; - print_line("\n**uniforms:\n" + gen_code.uniforms); - print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]); - print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]); -#endif - canvas_singleton->shader.canvas_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines); - ERR_FAIL_COND(!canvas_singleton->shader.canvas_shader.version_is_valid(version)); + instance_data->src_rect[0] = src_rect.position.x; + instance_data->src_rect[1] = src_rect.position.y; + instance_data->src_rect[2] = src_rect.size.width; + instance_data->src_rect[3] = src_rect.size.height; - ubo_size = gen_code.uniform_total_size; - ubo_offsets = gen_code.uniform_offsets; - texture_uniforms = gen_code.texture_uniforms; + instance_data->dst_rect[0] = dst_rect.position.x; + instance_data->dst_rect[1] = dst_rect.position.y; + instance_data->dst_rect[2] = dst_rect.size.width; + instance_data->dst_rect[3] = dst_rect.size.height; - //update them pipelines + _add_to_batch(r_index, r_batch_broken, current_batch); + } break; - RD::PipelineColorBlendState::Attachment attachment; + case Item::Command::TYPE_NINEPATCH: { + const Item::CommandNinePatch *np = static_cast(c); - switch (blend_mode) { - case BLEND_MODE_DISABLED: { - // nothing to do here, disabled by default + if (current_batch->command_type != Item::Command::TYPE_NINEPATCH) { + current_batch = _new_batch(r_batch_broken); + current_batch->command_type = Item::Command::TYPE_NINEPATCH; + current_batch->command = c; + current_batch->pipeline_variant = PipelineVariant::PIPELINE_VARIANT_NINEPATCH; + } - } break; - case BLEND_MODE_MIX: { - attachment.enable_blend = true; - attachment.color_blend_op = RD::BLEND_OP_ADD; - attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; - attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + TextureState tex_state(np->texture, texture_filter, texture_repeat, false, use_linear_colors); + if (tex_state != current_batch->tex_state) { + current_batch = _new_batch(r_batch_broken); + current_batch->set_tex_state(tex_state); + _prepare_batch_texture(current_batch, np->texture); + } - attachment.alpha_blend_op = RD::BLEND_OP_ADD; - attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; - attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + InstanceData *instance_data = new_instance_data(); - } break; - case BLEND_MODE_ADD: { - attachment.enable_blend = true; - attachment.alpha_blend_op = RD::BLEND_OP_ADD; - attachment.color_blend_op = RD::BLEND_OP_ADD; - attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; - attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE; - attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; - attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + Rect2 src_rect; + Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y); - } break; - case BLEND_MODE_SUB: { - attachment.enable_blend = true; - attachment.alpha_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT; - attachment.color_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT; - attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; - attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE; - attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; - attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + if (np->texture.is_null()) { + src_rect = Rect2(0, 0, 1, 1); + } else { + if (np->source != Rect2()) { + src_rect = Rect2(np->source.position.x * current_batch->tex_texpixel_size.width, np->source.position.y * current_batch->tex_texpixel_size.height, np->source.size.x * current_batch->tex_texpixel_size.width, np->source.size.y * current_batch->tex_texpixel_size.height); + instance_data->color_texture_pixel_size[0] = 1.0 / np->source.size.width; + instance_data->color_texture_pixel_size[1] = 1.0 / np->source.size.height; + } else { + src_rect = Rect2(0, 0, 1, 1); + } + } - } break; - case BLEND_MODE_MUL: { - attachment.enable_blend = true; - attachment.alpha_blend_op = RD::BLEND_OP_ADD; - attachment.color_blend_op = RD::BLEND_OP_ADD; - attachment.src_color_blend_factor = RD::BLEND_FACTOR_DST_COLOR; - attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ZERO; - attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_DST_ALPHA; - attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO; + Color modulated = np->color * base_color; + if (use_linear_colors) { + modulated = modulated.srgb_to_linear(); + } - } break; - case BLEND_MODE_PMALPHA: { - attachment.enable_blend = true; - attachment.alpha_blend_op = RD::BLEND_OP_ADD; - attachment.color_blend_op = RD::BLEND_OP_ADD; - attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE; - attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; - attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + instance_data->modulation[0] = modulated.r; + instance_data->modulation[1] = modulated.g; + instance_data->modulation[2] = modulated.b; + instance_data->modulation[3] = modulated.a; - } break; - } + instance_data->src_rect[0] = src_rect.position.x; + instance_data->src_rect[1] = src_rect.position.y; + instance_data->src_rect[2] = src_rect.size.width; + instance_data->src_rect[3] = src_rect.size.height; - RD::PipelineColorBlendState blend_state; - blend_state.attachments.push_back(attachment); + instance_data->dst_rect[0] = dst_rect.position.x; + instance_data->dst_rect[1] = dst_rect.position.y; + instance_data->dst_rect[2] = dst_rect.size.width; + instance_data->dst_rect[3] = dst_rect.size.height; - RD::PipelineColorBlendState::Attachment attachment_lcd; - attachment_lcd.enable_blend = true; - attachment_lcd.alpha_blend_op = RD::BLEND_OP_ADD; - attachment_lcd.color_blend_op = RD::BLEND_OP_ADD; - attachment_lcd.src_color_blend_factor = RD::BLEND_FACTOR_CONSTANT_COLOR; - attachment_lcd.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_COLOR; - attachment_lcd.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; - attachment_lcd.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + instance_data->flags |= int(np->axis_x) << FLAGS_NINEPATCH_H_MODE_SHIFT; + instance_data->flags |= int(np->axis_y) << FLAGS_NINEPATCH_V_MODE_SHIFT; - RD::PipelineColorBlendState blend_state_lcd; - blend_state_lcd.attachments.push_back(attachment_lcd); + if (np->draw_center) { + instance_data->flags |= FLAGS_NINEPACH_DRAW_CENTER; + } - //update pipelines + instance_data->ninepatch_margins[0] = np->margin[SIDE_LEFT]; + instance_data->ninepatch_margins[1] = np->margin[SIDE_TOP]; + instance_data->ninepatch_margins[2] = np->margin[SIDE_RIGHT]; + instance_data->ninepatch_margins[3] = np->margin[SIDE_BOTTOM]; - for (int i = 0; i < PIPELINE_LIGHT_MODE_MAX; i++) { - for (int j = 0; j < PIPELINE_VARIANT_MAX; j++) { - RD::RenderPrimitive primitive[PIPELINE_VARIANT_MAX] = { - RD::RENDER_PRIMITIVE_TRIANGLES, - RD::RENDER_PRIMITIVE_TRIANGLES, - RD::RENDER_PRIMITIVE_TRIANGLES, - RD::RENDER_PRIMITIVE_LINES, - RD::RENDER_PRIMITIVE_POINTS, - RD::RENDER_PRIMITIVE_TRIANGLES, - RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, - RD::RENDER_PRIMITIVE_LINES, - RD::RENDER_PRIMITIVE_LINESTRIPS, - RD::RENDER_PRIMITIVE_POINTS, - RD::RENDER_PRIMITIVE_TRIANGLES, - }; + _add_to_batch(r_index, r_batch_broken, current_batch); + } break; - ShaderVariant shader_variants[PIPELINE_LIGHT_MODE_MAX][PIPELINE_VARIANT_MAX] = { - { - //non lit - SHADER_VARIANT_QUAD, - SHADER_VARIANT_NINEPATCH, - SHADER_VARIANT_PRIMITIVE, - SHADER_VARIANT_PRIMITIVE, - SHADER_VARIANT_PRIMITIVE_POINTS, - SHADER_VARIANT_ATTRIBUTES, - SHADER_VARIANT_ATTRIBUTES, - SHADER_VARIANT_ATTRIBUTES, - SHADER_VARIANT_ATTRIBUTES, - SHADER_VARIANT_ATTRIBUTES_POINTS, - SHADER_VARIANT_QUAD, - }, - { - //lit - SHADER_VARIANT_QUAD_LIGHT, - SHADER_VARIANT_NINEPATCH_LIGHT, - SHADER_VARIANT_PRIMITIVE_LIGHT, - SHADER_VARIANT_PRIMITIVE_LIGHT, - SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT, - SHADER_VARIANT_ATTRIBUTES_LIGHT, - SHADER_VARIANT_ATTRIBUTES_LIGHT, - SHADER_VARIANT_ATTRIBUTES_LIGHT, - SHADER_VARIANT_ATTRIBUTES_LIGHT, - SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT, - SHADER_VARIANT_QUAD_LIGHT, - }, - }; + case Item::Command::TYPE_POLYGON: { + const Item::CommandPolygon *polygon = static_cast(c); - RID shader_variant = canvas_singleton->shader.canvas_shader.version_get_shader(version, shader_variants[i][j]); - if (j == PIPELINE_VARIANT_QUAD_LCD_BLEND) { - pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state_lcd, RD::DYNAMIC_STATE_BLEND_CONSTANTS); - } else { - pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0); - } - } - } + // Polygon's can't be batched, so always create a new batch + current_batch = _new_batch(r_batch_broken); - valid = true; -} + current_batch->command_type = Item::Command::TYPE_POLYGON; + current_batch->command = c; -bool RendererCanvasRenderRD::CanvasShaderData::is_animated() const { - return false; -} + TextureState tex_state(polygon->texture, texture_filter, texture_repeat, false, use_linear_colors); + if (tex_state != current_batch->tex_state) { + current_batch = _new_batch(r_batch_broken); + current_batch->set_tex_state(tex_state); + _prepare_batch_texture(current_batch, polygon->texture); + } -bool RendererCanvasRenderRD::CanvasShaderData::casts_shadows() const { - return false; -} + // pipeline variant + { + static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP }; + ERR_CONTINUE(polygon->primitive < 0 || polygon->primitive >= RS::PRIMITIVE_MAX); + current_batch->pipeline_variant = variant[polygon->primitive]; + } -RS::ShaderNativeSourceCode RendererCanvasRenderRD::CanvasShaderData::get_native_source_code() const { - RendererCanvasRenderRD *canvas_singleton = static_cast(RendererCanvasRender::singleton); - return canvas_singleton->shader.canvas_shader.version_get_native_source_code(version); -} + InstanceData *instance_data = new_instance_data(); -RendererCanvasRenderRD::CanvasShaderData::~CanvasShaderData() { - RendererCanvasRenderRD *canvas_singleton = static_cast(RendererCanvasRender::singleton); - ERR_FAIL_NULL(canvas_singleton); - //pipeline variants will clear themselves if shader is gone - if (version.is_valid()) { - canvas_singleton->shader.canvas_shader.version_free(version); - } -} + Color color = base_color; + if (use_linear_colors) { + color = color.srgb_to_linear(); + } -RendererRD::MaterialStorage::ShaderData *RendererCanvasRenderRD::_create_shader_func() { - CanvasShaderData *shader_data = memnew(CanvasShaderData); - return shader_data; -} + instance_data->modulation[0] = color.r; + instance_data->modulation[1] = color.g; + instance_data->modulation[2] = color.b; + instance_data->modulation[3] = color.a; -bool RendererCanvasRenderRD::CanvasMaterialData::update_parameters(const HashMap &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { - RendererCanvasRenderRD *canvas_singleton = static_cast(RendererCanvasRender::singleton); - bool uniform_set_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, true, false); - bool uniform_set_srgb_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set_srgb, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, false, false); - return uniform_set_changed || uniform_set_srgb_changed; -} + _add_to_batch(r_index, r_batch_broken, current_batch); + } break; -RendererCanvasRenderRD::CanvasMaterialData::~CanvasMaterialData() { - free_parameters_uniform_set(uniform_set); - free_parameters_uniform_set(uniform_set_srgb); -} + case Item::Command::TYPE_PRIMITIVE: { + const Item::CommandPrimitive *primitive = static_cast(c); -RendererRD::MaterialStorage::MaterialData *RendererCanvasRenderRD::_create_material_func(CanvasShaderData *p_shader) { - CanvasMaterialData *material_data = memnew(CanvasMaterialData); - material_data->shader_data = p_shader; - //update will happen later anyway so do nothing. - return material_data; -} + if (primitive->point_count != current_batch->primitive_points || current_batch->command_type != Item::Command::TYPE_PRIMITIVE) { + current_batch = _new_batch(r_batch_broken); + current_batch->command_type = Item::Command::TYPE_PRIMITIVE; + current_batch->command = c; + current_batch->primitive_points = primitive->point_count; -void RendererCanvasRenderRD::set_time(double p_time) { - state.time = p_time; -} + static const PipelineVariant variant[4] = { PIPELINE_VARIANT_PRIMITIVE_POINTS, PIPELINE_VARIANT_PRIMITIVE_LINES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES }; + ERR_CONTINUE(primitive->point_count == 0 || primitive->point_count > 4); + current_batch->pipeline_variant = variant[primitive->point_count - 1]; -void RendererCanvasRenderRD::update() { -} + TextureState tex_state(primitive->texture, texture_filter, texture_repeat, false, use_linear_colors); + if (tex_state != current_batch->tex_state) { + current_batch = _new_batch(r_batch_broken); + current_batch->set_tex_state(tex_state); + _prepare_batch_texture(current_batch, primitive->texture); + } + } -RendererCanvasRenderRD::RendererCanvasRenderRD() { - RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); + InstanceData *instance_data = new_instance_data(); - { //create default samplers + for (uint32_t j = 0; j < MIN(3u, primitive->point_count); j++) { + instance_data->points[j * 2 + 0] = primitive->points[j].x; + instance_data->points[j * 2 + 1] = primitive->points[j].y; + instance_data->uvs[j * 2 + 0] = primitive->uvs[j].x; + instance_data->uvs[j * 2 + 1] = primitive->uvs[j].y; + Color col = primitive->colors[j] * base_color; + if (use_linear_colors) { + col = col.srgb_to_linear(); + } + instance_data->colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); + instance_data->colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); + } - default_samplers.default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; - default_samplers.default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; - } + _add_to_batch(r_index, r_batch_broken, current_batch); - { //shader variants + if (primitive->point_count == 4) { + instance_data = new_instance_data(); + + for (uint32_t j = 0; j < 3; j++) { + int offset = j == 0 ? 0 : 1; + // Second triangle in the quad. Uses vertices 0, 2, 3. + instance_data->points[j * 2 + 0] = primitive->points[j + offset].x; + instance_data->points[j * 2 + 1] = primitive->points[j + offset].y; + instance_data->uvs[j * 2 + 0] = primitive->uvs[j + offset].x; + instance_data->uvs[j * 2 + 1] = primitive->uvs[j + offset].y; + Color col = primitive->colors[j] * base_color; + if (use_linear_colors) { + col = col.srgb_to_linear(); + } + instance_data->colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); + instance_data->colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); + } - String global_defines; + _add_to_batch(r_index, r_batch_broken, current_batch); + } + } break; - uint64_t uniform_max_size = RD::get_singleton()->limit_get(RD::LIMIT_MAX_UNIFORM_BUFFER_SIZE); - if (uniform_max_size < 65536) { - //Yes, you guessed right, ARM again - state.max_lights_per_render = 64; - global_defines += "#define MAX_LIGHTS 64\n"; - } else { - state.max_lights_per_render = DEFAULT_MAX_LIGHTS_PER_RENDER; - global_defines += "#define MAX_LIGHTS " + itos(DEFAULT_MAX_LIGHTS_PER_RENDER) + "\n"; - } + case Item::Command::TYPE_MESH: + case Item::Command::TYPE_MULTIMESH: + case Item::Command::TYPE_PARTICLES: { + // Mesh's can't be batched, so always create a new batch + current_batch = _new_batch(r_batch_broken); + current_batch->command = c; + current_batch->command_type = c->type; - global_defines += "\n#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n"; + InstanceData *instance_data = nullptr; - state.light_uniforms = memnew_arr(LightUniform, state.max_lights_per_render); - Vector variants; - //non light variants - variants.push_back(""); //none by default is first variant - variants.push_back("#define USE_NINEPATCH\n"); //ninepatch is the second variant - variants.push_back("#define USE_PRIMITIVE\n"); //primitive is the third - variants.push_back("#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); //points need point size - variants.push_back("#define USE_ATTRIBUTES\n"); // attributes for vertex arrays - variants.push_back("#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); //attributes with point size - //light variants - variants.push_back("#define USE_LIGHTING\n"); //none by default is first variant - variants.push_back("#define USE_LIGHTING\n#define USE_NINEPATCH\n"); //ninepatch is the second variant - variants.push_back("#define USE_LIGHTING\n#define USE_PRIMITIVE\n"); //primitive is the third - variants.push_back("#define USE_LIGHTING\n#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); //points need point size - variants.push_back("#define USE_LIGHTING\n#define USE_ATTRIBUTES\n"); // attributes for vertex arrays - variants.push_back("#define USE_LIGHTING\n#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); //attributes with point size + Color modulate(1, 1, 1, 1); + if (c->type == Item::Command::TYPE_MESH) { + const Item::CommandMesh *m = static_cast(c); + TextureState tex_state(m->texture, texture_filter, texture_repeat, false, use_linear_colors); + current_batch->set_tex_state(tex_state); + _prepare_batch_texture(current_batch, m->texture); + instance_data = new_instance_data(); - shader.canvas_shader.initialize(variants, global_defines); + current_batch->mesh_instance_count = 1; + _update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, instance_data->world); + modulate = m->modulate; + } else if (c->type == Item::Command::TYPE_MULTIMESH) { + RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); - shader.default_version = shader.canvas_shader.version_create(); - shader.default_version_rd_shader = shader.canvas_shader.version_get_shader(shader.default_version, SHADER_VARIANT_QUAD); + const Item::CommandMultiMesh *mm = static_cast(c); + RID multimesh = mm->multimesh; - RD::PipelineColorBlendState blend_state; - RD::PipelineColorBlendState::Attachment blend_attachment; + if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) { + break; + } - blend_attachment.enable_blend = true; - blend_attachment.color_blend_op = RD::BLEND_OP_ADD; - blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; - blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + current_batch->mesh_instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh); + if (current_batch->mesh_instance_count == 0) { + break; + } - blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD; - blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; - blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + TextureState tex_state(mm->texture, texture_filter, texture_repeat, false, use_linear_colors); + current_batch->set_tex_state(tex_state); + _prepare_batch_texture(current_batch, mm->texture); + instance_data = new_instance_data(); - blend_state.attachments.push_back(blend_attachment); + instance_data->flags |= 1; // multimesh, trails disabled - RD::PipelineColorBlendState::Attachment attachment_lcd; - attachment_lcd.enable_blend = true; - attachment_lcd.alpha_blend_op = RD::BLEND_OP_ADD; - attachment_lcd.color_blend_op = RD::BLEND_OP_ADD; - attachment_lcd.src_color_blend_factor = RD::BLEND_FACTOR_CONSTANT_COLOR; - attachment_lcd.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_COLOR; - attachment_lcd.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; - attachment_lcd.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + if (mesh_storage->multimesh_uses_colors(mm->multimesh)) { + instance_data->flags |= FLAGS_INSTANCING_HAS_COLORS; + } + if (mesh_storage->multimesh_uses_custom_data(mm->multimesh)) { + instance_data->flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA; + } + } else if (c->type == Item::Command::TYPE_PARTICLES) { + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + RendererRD::ParticlesStorage *particles_storage = RendererRD::ParticlesStorage::get_singleton(); - RD::PipelineColorBlendState blend_state_lcd; - blend_state_lcd.attachments.push_back(attachment_lcd); + const Item::CommandParticles *pt = static_cast(c); + TextureState tex_state(pt->texture, texture_filter, texture_repeat, false, use_linear_colors); + current_batch->set_tex_state(tex_state); + _prepare_batch_texture(current_batch, pt->texture); - for (int i = 0; i < PIPELINE_LIGHT_MODE_MAX; i++) { - for (int j = 0; j < PIPELINE_VARIANT_MAX; j++) { - RD::RenderPrimitive primitive[PIPELINE_VARIANT_MAX] = { - RD::RENDER_PRIMITIVE_TRIANGLES, - RD::RENDER_PRIMITIVE_TRIANGLES, - RD::RENDER_PRIMITIVE_TRIANGLES, - RD::RENDER_PRIMITIVE_LINES, - RD::RENDER_PRIMITIVE_POINTS, - RD::RENDER_PRIMITIVE_TRIANGLES, - RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, - RD::RENDER_PRIMITIVE_LINES, - RD::RENDER_PRIMITIVE_LINESTRIPS, - RD::RENDER_PRIMITIVE_POINTS, - RD::RENDER_PRIMITIVE_TRIANGLES, - }; + instance_data = new_instance_data(); - ShaderVariant shader_variants[PIPELINE_LIGHT_MODE_MAX][PIPELINE_VARIANT_MAX] = { - { - //non lit - SHADER_VARIANT_QUAD, - SHADER_VARIANT_NINEPATCH, - SHADER_VARIANT_PRIMITIVE, - SHADER_VARIANT_PRIMITIVE, - SHADER_VARIANT_PRIMITIVE_POINTS, - SHADER_VARIANT_ATTRIBUTES, - SHADER_VARIANT_ATTRIBUTES, - SHADER_VARIANT_ATTRIBUTES, - SHADER_VARIANT_ATTRIBUTES, - SHADER_VARIANT_ATTRIBUTES_POINTS, - SHADER_VARIANT_QUAD, - }, - { - //lit - SHADER_VARIANT_QUAD_LIGHT, - SHADER_VARIANT_NINEPATCH_LIGHT, - SHADER_VARIANT_PRIMITIVE_LIGHT, - SHADER_VARIANT_PRIMITIVE_LIGHT, - SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT, - SHADER_VARIANT_ATTRIBUTES_LIGHT, - SHADER_VARIANT_ATTRIBUTES_LIGHT, - SHADER_VARIANT_ATTRIBUTES_LIGHT, - SHADER_VARIANT_ATTRIBUTES_LIGHT, - SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT, - SHADER_VARIANT_QUAD_LIGHT, - }, - }; + uint32_t divisor = 1; + current_batch->mesh_instance_count = particles_storage->particles_get_amount(pt->particles, divisor); + instance_data->flags |= (divisor & FLAGS_INSTANCING_MASK); + current_batch->mesh_instance_count /= divisor; - RID shader_variant = shader.canvas_shader.version_get_shader(shader.default_version, shader_variants[i][j]); - if (j == PIPELINE_VARIANT_QUAD_LCD_BLEND) { - shader.pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state_lcd, RD::DYNAMIC_STATE_BLEND_CONSTANTS); - } else { - shader.pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0); - } - } - } - } + RID particles = pt->particles; - { - //shader compiler - ShaderCompiler::DefaultIdentifierActions actions; + instance_data->flags |= FLAGS_INSTANCING_HAS_COLORS; + instance_data->flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA; - actions.renames["VERTEX"] = "vertex"; - actions.renames["LIGHT_VERTEX"] = "light_vertex"; - actions.renames["SHADOW_VERTEX"] = "shadow_vertex"; - actions.renames["UV"] = "uv"; - actions.renames["POINT_SIZE"] = "point_size"; + if (particles_storage->particles_has_collision(particles) && texture_storage->render_target_is_sdf_enabled(p_render_target.render_target)) { + // Pass collision information. + Transform2D xform = p_item->final_transform; - actions.renames["MODEL_MATRIX"] = "model_matrix"; - actions.renames["CANVAS_MATRIX"] = "canvas_data.canvas_transform"; - actions.renames["SCREEN_MATRIX"] = "canvas_data.screen_transform"; - actions.renames["TIME"] = "canvas_data.time"; - actions.renames["PI"] = _MKSTR(Math_PI); - actions.renames["TAU"] = _MKSTR(Math_TAU); - actions.renames["E"] = _MKSTR(Math_E); - actions.renames["AT_LIGHT_PASS"] = "false"; - actions.renames["INSTANCE_CUSTOM"] = "instance_custom"; + RID sdf_texture = texture_storage->render_target_get_sdf_texture(p_render_target.render_target); - actions.renames["COLOR"] = "color"; - actions.renames["NORMAL"] = "normal"; - actions.renames["NORMAL_MAP"] = "normal_map"; - actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth"; - actions.renames["TEXTURE"] = "color_texture"; - actions.renames["TEXTURE_PIXEL_SIZE"] = "draw_data.color_texture_pixel_size"; - actions.renames["NORMAL_TEXTURE"] = "normal_texture"; - actions.renames["SPECULAR_SHININESS_TEXTURE"] = "specular_texture"; - actions.renames["SPECULAR_SHININESS"] = "specular_shininess"; - actions.renames["SCREEN_UV"] = "screen_uv"; - actions.renames["SCREEN_PIXEL_SIZE"] = "canvas_data.screen_pixel_size"; - actions.renames["FRAGCOORD"] = "gl_FragCoord"; - actions.renames["POINT_COORD"] = "gl_PointCoord"; - actions.renames["INSTANCE_ID"] = "gl_InstanceIndex"; - actions.renames["VERTEX_ID"] = "gl_VertexIndex"; + Rect2 to_screen; + { + Rect2 sdf_rect = texture_storage->render_target_get_sdf_rect(p_render_target.render_target); - actions.renames["CUSTOM0"] = "custom0"; - actions.renames["CUSTOM1"] = "custom1"; + to_screen.size = Vector2(1.0 / sdf_rect.size.width, 1.0 / sdf_rect.size.height); + to_screen.position = -sdf_rect.position * to_screen.size; + } - actions.renames["LIGHT_POSITION"] = "light_position"; - actions.renames["LIGHT_DIRECTION"] = "light_direction"; - actions.renames["LIGHT_IS_DIRECTIONAL"] = "is_directional"; - actions.renames["LIGHT_COLOR"] = "light_color"; - actions.renames["LIGHT_ENERGY"] = "light_energy"; - actions.renames["LIGHT"] = "light"; - actions.renames["SHADOW_MODULATE"] = "shadow_modulate"; + particles_storage->particles_set_canvas_sdf_collision(pt->particles, true, xform, to_screen, sdf_texture); + } else { + particles_storage->particles_set_canvas_sdf_collision(pt->particles, false, Transform2D(), Rect2(), RID()); + } + r_sdf_used |= particles_storage->particles_has_collision(particles); + } - actions.renames["texture_sdf"] = "texture_sdf"; - actions.renames["texture_sdf_normal"] = "texture_sdf_normal"; - actions.renames["sdf_to_screen_uv"] = "sdf_to_screen_uv"; - actions.renames["screen_uv_to_sdf"] = "screen_uv_to_sdf"; + Color modulated = modulate * base_color; + if (use_linear_colors) { + modulated = modulated.srgb_to_linear(); + } - actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; - actions.usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n"; - actions.usage_defines["SCREEN_PIXEL_SIZE"] = "@SCREEN_UV"; - actions.usage_defines["NORMAL"] = "#define NORMAL_USED\n"; - actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n"; - actions.usage_defines["SPECULAR_SHININESS"] = "#define SPECULAR_SHININESS_USED\n"; - actions.usage_defines["POINT_SIZE"] = "#define USE_POINT_SIZE\n"; - actions.usage_defines["CUSTOM0"] = "#define CUSTOM0_USED\n"; - actions.usage_defines["CUSTOM1"] = "#define CUSTOM1_USED\n"; + instance_data->modulation[0] = modulated.r; + instance_data->modulation[1] = modulated.g; + instance_data->modulation[2] = modulated.b; + instance_data->modulation[3] = modulated.a; - actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; - actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n"; - actions.render_mode_defines["light_only"] = "#define MODE_LIGHT_ONLY\n"; - actions.render_mode_defines["world_vertex_coords"] = "#define USE_WORLD_VERTEX_COORDS\n"; + _add_to_batch(r_index, r_batch_broken, current_batch); + } break; + + case Item::Command::TYPE_TRANSFORM: { + const Item::CommandTransform *transform = static_cast(c); + draw_transform = transform->xform; + _update_transform_2d_to_mat2x3(base_transform * transform->xform, world); + } break; + + case Item::Command::TYPE_CLIP_IGNORE: { + const Item::CommandClipIgnore *ci = static_cast(c); + if (r_current_clip) { + if (ci->ignore != reclip) { + current_batch = _new_batch(r_batch_broken); + if (ci->ignore) { + current_batch->clip = nullptr; + reclip = true; + } else { + current_batch->clip = r_current_clip; + reclip = false; + } + } + } + } break; - actions.custom_samplers["TEXTURE"] = "texture_sampler"; - actions.custom_samplers["NORMAL_TEXTURE"] = "texture_sampler"; - actions.custom_samplers["SPECULAR_SHININESS_TEXTURE"] = "texture_sampler"; - actions.base_texture_binding_index = 1; - actions.texture_layout_set = MATERIAL_UNIFORM_SET; - actions.base_uniform_string = "material."; - actions.default_filter = ShaderLanguage::FILTER_LINEAR; - actions.default_repeat = ShaderLanguage::REPEAT_DISABLE; - actions.base_varying_index = 4; + case Item::Command::TYPE_ANIMATION_SLICE: { + const Item::CommandAnimationSlice *as = static_cast(c); + double current_time = RSG::rasterizer->get_total_time(); + double local_time = Math::fposmod(current_time - as->offset, as->animation_length); + skipping = !(local_time >= as->slice_begin && local_time < as->slice_end); - actions.global_buffer_array_variable = "global_shader_uniforms.data"; + RenderingServerDefault::redraw_request(); // animation visible means redraw request + } break; + } - shader.compiler.initialize(actions); + c = c->next; + r_batch_broken = false; } - { //shadow rendering - Vector versions; - versions.push_back("\n#define MODE_SHADOW\n"); //shadow - versions.push_back("\n#define MODE_SDF\n"); //sdf - shadow_render.shader.initialize(versions); + if (r_current_clip && reclip) { + // will make it re-enable clipping if needed afterwards + r_current_clip = nullptr; + } +} - { - Vector attachments; +void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineVariants *p_pipeline_variants, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info) { + UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); + ERR_FAIL_NULL(uniform_set_cache); - RD::AttachmentFormat af_color; - af_color.format = RD::DATA_FORMAT_R32_SFLOAT; - af_color.usage_flags = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + ERR_FAIL_NULL(p_batch->command); - attachments.push_back(af_color); + _bind_canvas_texture(p_draw_list, p_batch->tex_uniform_set); - RD::AttachmentFormat af_depth; - af_depth.format = RD::DATA_FORMAT_D32_SFLOAT; - af_depth.usage_flags = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + switch (p_batch->command_type) { + case Item::Command::TYPE_RECT: + case Item::Command::TYPE_NINEPATCH: { + RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][p_batch->pipeline_variant].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + if (p_batch->has_blend) { + RD::get_singleton()->draw_list_set_blend_constants(p_draw_list, p_batch->modulate); + } - attachments.push_back(af_depth); + PushConstant push_constant; + push_constant.base_instance_index = p_batch->start; + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - shadow_render.framebuffer_format = RD::get_singleton()->framebuffer_format_create(attachments); - } + RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 0, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]); + RD::get_singleton()->draw_list_bind_uniform_set( + p_draw_list, + uniform_set_cache->get_cache(shader.default_version_rd_shader, INSTANCE_DATA_UNIFORM_SET, u_instance_data), + INSTANCE_DATA_UNIFORM_SET); - { - Vector attachments; + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); + RD::get_singleton()->draw_list_draw(p_draw_list, true, p_batch->instance_count); - RD::AttachmentFormat af_color; - af_color.format = RD::DATA_FORMAT_R8_UNORM; - af_color.usage_flags = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] += p_batch->instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += 2 * p_batch->instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + } break; - attachments.push_back(af_color); + case Item::Command::TYPE_POLYGON: { + const Item::CommandPolygon *polygon = static_cast(p_batch->command); - shadow_render.sdf_framebuffer_format = RD::get_singleton()->framebuffer_format_create(attachments); - } + PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id); + ERR_FAIL_NULL(pb); - //pipelines - Vector vf; - RD::VertexAttribute vd; - vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; - vd.stride = sizeof(float) * 3; - vd.location = 0; - vd.offset = 0; - vf.push_back(vd); - shadow_render.vertex_format = RD::get_singleton()->vertex_format_create(vf); + RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][p_batch->pipeline_variant].get_render_pipeline(pb->vertex_format_id, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; - vd.stride = sizeof(float) * 2; + PushConstant push_constant; + push_constant.base_instance_index = p_batch->start; + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - vf.write[0] = vd; - shadow_render.sdf_vertex_format = RD::get_singleton()->vertex_format_create(vf); + RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 0, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]); + RD::get_singleton()->draw_list_bind_uniform_set( + p_draw_list, + uniform_set_cache->get_cache(shader.default_version_rd_shader, INSTANCE_DATA_UNIFORM_SET, u_instance_data), + INSTANCE_DATA_UNIFORM_SET); - shadow_render.shader_version = shadow_render.shader.version_create(); + RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, pb->vertex_array); + if (pb->indices.is_valid()) { + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, pb->indices); + } - for (int i = 0; i < 3; i++) { - RD::PipelineRasterizationState rs; - rs.cull_mode = i == 0 ? RD::POLYGON_CULL_DISABLED : (i == 1 ? RD::POLYGON_CULL_FRONT : RD::POLYGON_CULL_BACK); - RD::PipelineDepthStencilState ds; - ds.enable_depth_write = true; - ds.enable_depth_test = true; - ds.depth_compare_operator = RD::COMPARE_OP_LESS; - shadow_render.render_pipelines[i] = RD::get_singleton()->render_pipeline_create(shadow_render.shader.version_get_shader(shadow_render.shader_version, SHADOW_RENDER_MODE_SHADOW), shadow_render.framebuffer_format, shadow_render.vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0); - } + RD::get_singleton()->draw_list_draw(p_draw_list, pb->indices.is_valid()); + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(polygon->primitive, pb->primitive_count); + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + } break; - for (int i = 0; i < 2; i++) { - shadow_render.sdf_render_pipelines[i] = RD::get_singleton()->render_pipeline_create(shadow_render.shader.version_get_shader(shadow_render.shader_version, SHADOW_RENDER_MODE_SDF), shadow_render.sdf_framebuffer_format, shadow_render.sdf_vertex_format, i == 0 ? RD::RENDER_PRIMITIVE_TRIANGLES : RD::RENDER_PRIMITIVE_LINES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); - } - } + case Item::Command::TYPE_PRIMITIVE: { + const Item::CommandPrimitive *primitive = static_cast(p_batch->command); - { //bindings + RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][p_batch->pipeline_variant].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - state.canvas_state_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(State::Buffer)); - state.lights_uniform_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(LightUniform) * state.max_lights_per_render); + PushConstant push_constant; + push_constant.base_instance_index = p_batch->start; + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::SamplerState shadow_sampler_state; - shadow_sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR; - shadow_sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; - shadow_sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_REPEAT; //shadow wrap around - shadow_sampler_state.compare_op = RD::COMPARE_OP_GREATER; - shadow_sampler_state.enable_compare = true; - state.shadow_sampler = RD::get_singleton()->sampler_create(shadow_sampler_state); - } + RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 0, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]); + RD::get_singleton()->draw_list_bind_uniform_set( + p_draw_list, + uniform_set_cache->get_cache(shader.default_version_rd_shader, INSTANCE_DATA_UNIFORM_SET, u_instance_data), + INSTANCE_DATA_UNIFORM_SET); - { - //polygon buffers - polygon_buffers.last_id = 1; - } + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, primitive_arrays.index_array[MIN(3u, primitive->point_count) - 1]); + uint32_t instance_count = p_batch->instance_count; + RD::get_singleton()->draw_list_draw(p_draw_list, true, instance_count); - { // default index buffer + if (r_render_info) { + const RenderingServer::PrimitiveType rs_primitive[5] = { RS::PRIMITIVE_POINTS, RS::PRIMITIVE_POINTS, RS::PRIMITIVE_LINES, RS::PRIMITIVE_TRIANGLES, RS::PRIMITIVE_TRIANGLES }; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] += instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(rs_primitive[p_batch->primitive_points], p_batch->primitive_points) * instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + } break; - Vector pv; - pv.resize(6 * 2); - { - uint8_t *w = pv.ptrw(); - uint16_t *p16 = (uint16_t *)w; - p16[0] = 0; - p16[1] = 1; - p16[2] = 2; - p16[3] = 0; - p16[4] = 2; - p16[5] = 3; - } - shader.quad_index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT16, pv); - shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 6); - } + case Item::Command::TYPE_MESH: + case Item::Command::TYPE_MULTIMESH: + case Item::Command::TYPE_PARTICLES: { + RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); + RendererRD::ParticlesStorage *particles_storage = RendererRD::ParticlesStorage::get_singleton(); + + RID mesh; + RID mesh_instance; + + if (p_batch->command_type == Item::Command::TYPE_MESH) { + const Item::CommandMesh *m = static_cast(p_batch->command); + mesh = m->mesh; + mesh_instance = m->mesh_instance; + } else if (p_batch->command_type == Item::Command::TYPE_MULTIMESH) { + const Item::CommandMultiMesh *mm = static_cast(p_batch->command); + RID multimesh = mm->multimesh; + mesh = mesh_storage->multimesh_get_mesh(multimesh); + + RID uniform_set = mesh_storage->multimesh_get_2d_uniform_set(multimesh, shader.default_version_rd_shader, TRANSFORMS_UNIFORM_SET); + RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, TRANSFORMS_UNIFORM_SET); + } else if (p_batch->command_type == Item::Command::TYPE_PARTICLES) { + const Item::CommandParticles *pt = static_cast(p_batch->command); + RID particles = pt->particles; + mesh = particles_storage->particles_get_draw_pass_mesh(particles, 0); + + ERR_BREAK(particles_storage->particles_get_mode(particles) != RS::PARTICLES_MODE_2D); + particles_storage->particles_request_process(particles); + + if (particles_storage->particles_is_inactive(particles)) { + break; + } - { //primitive - primitive_arrays.index_array[0] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 1); - primitive_arrays.index_array[1] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 2); - primitive_arrays.index_array[2] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 3); - primitive_arrays.index_array[3] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 6); - } + RenderingServerDefault::redraw_request(); // Active particles means redraw request. - { - //default shadow texture to keep uniform set happy - RD::TextureFormat tf; - tf.texture_type = RD::TEXTURE_TYPE_2D; - tf.width = 4; - tf.height = 4; - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; - tf.format = RD::DATA_FORMAT_R32_SFLOAT; + int dpc = particles_storage->particles_get_draw_passes(particles); + if (dpc == 0) { + break; // Nothing to draw. + } - state.shadow_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - } + RID uniform_set = particles_storage->particles_get_instance_buffer_uniform_set(pt->particles, shader.default_version_rd_shader, TRANSFORMS_UNIFORM_SET); + RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, TRANSFORMS_UNIFORM_SET); + } - { - Vector uniforms; + if (mesh.is_null()) { + break; + } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 0; - u.append_id(RendererRD::MeshStorage::get_singleton()->get_default_rd_storage_buffer()); - uniforms.push_back(u); - } + RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 0, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]); + RD::get_singleton()->draw_list_bind_uniform_set( + p_draw_list, + uniform_set_cache->get_cache(shader.default_version_rd_shader, INSTANCE_DATA_UNIFORM_SET, u_instance_data), + INSTANCE_DATA_UNIFORM_SET); - state.default_transforms_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader.default_version_rd_shader, TRANSFORMS_UNIFORM_SET); - } + uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh); + static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP }; - default_canvas_texture = texture_storage->canvas_texture_allocate(); - texture_storage->canvas_texture_initialize(default_canvas_texture); + for (uint32_t j = 0; j < surf_count; j++) { + void *surface = mesh_storage->mesh_get_surface(mesh, j); - state.shadow_texture_size = GLOBAL_GET("rendering/2d/shadow_atlas/size"); + RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface); + ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX); - //create functions for shader and material - material_storage->shader_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_2D, _create_shader_funcs); - material_storage->material_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_2D, _create_material_funcs); + uint64_t input_mask = p_pipeline_variants->variants[p_batch->light_mode][variant[primitive]].get_vertex_input_mask(); - state.time = 0; + RID vertex_array; + RD::VertexFormatID vertex_format = RD::INVALID_FORMAT_ID; - { - default_canvas_group_shader = material_storage->shader_allocate(); - material_storage->shader_initialize(default_canvas_group_shader); + if (mesh_instance.is_valid()) { + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, false, vertex_array, vertex_format); + } else { + mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, false, vertex_array, vertex_format); + } - material_storage->shader_set_code(default_canvas_group_shader, R"( -// Default CanvasGroup shader. + RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][variant[primitive]].get_render_pipeline(vertex_format, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); -shader_type canvas_item; -render_mode unshaded; + PushConstant push_constant; + push_constant.base_instance_index = p_batch->start; + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); -uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest; + RID index_array = mesh_storage->mesh_surface_get_index_array(surface, 0); -void fragment() { - vec4 c = textureLod(screen_texture, SCREEN_UV, 0.0); + if (index_array.is_valid()) { + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, index_array); + } - if (c.a > 0.0001) { - c.rgb /= c.a; - } + RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, vertex_array); + RD::get_singleton()->draw_list_draw(p_draw_list, index_array.is_valid(), p_batch->mesh_instance_count); - COLOR *= c; + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(primitive, mesh_storage->mesh_surface_get_vertices_drawn_count(surface)) * p_batch->mesh_instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + } + } break; + case Item::Command::TYPE_TRANSFORM: + case Item::Command::TYPE_CLIP_IGNORE: + case Item::Command::TYPE_ANIMATION_SLICE: { + // Can ignore these as they only impact batch creation. + } break; + } } -)"); - default_canvas_group_material = material_storage->material_allocate(); - material_storage->material_initialize(default_canvas_group_material); - material_storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader); +RendererCanvasRenderRD::Batch *RendererCanvasRenderRD::_new_batch(bool &r_batch_broken) { + if (state.canvas_instance_batches.size() == 0) { + state.canvas_instance_batches.push_back(Batch()); + return state.canvas_instance_batches.ptr(); } - { - default_clip_children_shader = material_storage->shader_allocate(); - material_storage->shader_initialize(default_clip_children_shader); - - material_storage->shader_set_code(default_clip_children_shader, R"( -// Default clip children shader. - -shader_type canvas_item; -render_mode unshaded; + if (r_batch_broken || state.canvas_instance_batches[state.current_batch_index].instance_count == 0) { + return &state.canvas_instance_batches[state.current_batch_index]; + } -uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest; + r_batch_broken = true; -void fragment() { - vec4 c = textureLod(screen_texture, SCREEN_UV, 0.0); - COLOR.rgb = c.rgb; + // Copy the properties of the current batch, we will manually update the things that changed. + Batch new_batch = state.canvas_instance_batches[state.current_batch_index]; + new_batch.instance_count = 0; + new_batch.start = state.canvas_instance_batches[state.current_batch_index].start + state.canvas_instance_batches[state.current_batch_index].instance_count; + new_batch.instance_buffer_index = state.current_instance_buffer_index; + state.current_batch_index++; + state.canvas_instance_batches.push_back(new_batch); + return &state.canvas_instance_batches[state.current_batch_index]; } -)"); - default_clip_children_material = material_storage->material_allocate(); - material_storage->material_initialize(default_clip_children_material); - material_storage->material_set_shader(default_clip_children_material, default_clip_children_shader); +void RendererCanvasRenderRD::_add_to_batch(uint32_t &r_index, bool &r_batch_broken, Batch *&r_current_batch) { + r_current_batch->instance_count++; + r_index++; + if (r_index + state.last_instance_index >= state.max_instances_per_buffer) { + // Copy over all data needed for rendering right away + // then go back to recording item commands. + RD::get_singleton()->buffer_update( + state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.current_instance_buffer_index], + state.last_instance_index * sizeof(InstanceData), + r_index * sizeof(InstanceData), + state.instance_data_array); + _allocate_instance_buffer(); + r_index = 0; + state.last_instance_index = 0; + r_batch_broken = false; // Force a new batch to be created + r_current_batch = _new_batch(r_batch_broken); + r_current_batch->start = 0; } - - static_assert(sizeof(PushConstant) == 128); } -bool RendererCanvasRenderRD::free(RID p_rid) { - if (canvas_light_owner.owns(p_rid)) { - CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); - ERR_FAIL_NULL_V(cl, false); - light_set_use_shadow(p_rid, false); - canvas_light_owner.free(p_rid); - } else if (occluder_polygon_owner.owns(p_rid)) { - occluder_polygon_set_shape(p_rid, Vector(), false); - occluder_polygon_owner.free(p_rid); - } else { - return false; +void RendererCanvasRenderRD::_allocate_instance_buffer() { + state.current_instance_buffer_index++; + + if (state.current_instance_buffer_index < state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers.size()) { + // We already allocated another buffer in a previous frame, so we can just use it. + return; } - return true; + // Allocate a new buffer. + RID buf = RD::get_singleton()->storage_buffer_create(state.max_instance_buffer_size); + state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers.push_back(buf); } -void RendererCanvasRenderRD::set_shadow_texture_size(int p_size) { - p_size = MAX(1, nearest_power_of_2_templated(p_size)); - if (p_size == state.shadow_texture_size) { +void RendererCanvasRenderRD::_prepare_batch_texture(Batch *p_current_batch, RID p_texture) const { + if (p_texture.is_null()) { + p_texture = default_canvas_texture; + } + + Color specular_shininess; + bool use_normal; + bool use_specular; + Size2i size; + bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set( + p_texture, + p_current_batch->tex_state.texture_filter(), + p_current_batch->tex_state.texture_repeat(), + shader.default_version_rd_shader, + CANVAS_TEXTURE_UNIFORM_SET, + p_current_batch->tex_state.linear_colors(), + p_current_batch->tex_uniform_set, + size, + specular_shininess, + use_normal, + use_specular, + p_current_batch->tex_state.texture_is_data()); + // something odd happened + if (!success) { + _prepare_batch_texture(p_current_batch, default_canvas_texture); return; } - state.shadow_texture_size = p_size; - if (state.shadow_fb.is_valid()) { - RD::get_singleton()->free(state.shadow_texture); - RD::get_singleton()->free(state.shadow_depth_texture); - state.shadow_fb = RID(); - { - //create a default shadow texture to keep uniform set happy (and that it gets erased when a new one is created) - RD::TextureFormat tf; - tf.texture_type = RD::TEXTURE_TYPE_2D; - tf.width = 4; - tf.height = 4; - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; - tf.format = RD::DATA_FORMAT_R32_SFLOAT; + // cache values to be copied to instance data + if (specular_shininess.a < 0.999) { + p_current_batch->tex_flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; + } - state.shadow_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - } + if (use_normal) { + p_current_batch->tex_flags |= FLAGS_DEFAULT_NORMAL_MAP_USED; } + + uint8_t a = uint8_t(CLAMP(specular_shininess.a * 255.0, 0.0, 255.0)); + uint8_t b = uint8_t(CLAMP(specular_shininess.b * 255.0, 0.0, 255.0)); + uint8_t g = uint8_t(CLAMP(specular_shininess.g * 255.0, 0.0, 255.0)); + uint8_t r = uint8_t(CLAMP(specular_shininess.r * 255.0, 0.0, 255.0)); + p_current_batch->tex_specular_shininess = uint32_t(a) << 24 | uint32_t(b) << 16 | uint32_t(g) << 8 | uint32_t(r); + + p_current_batch->tex_texpixel_size = Vector2(1.0 / float(size.width), 1.0 / float(size.height)); } -void RendererCanvasRenderRD::set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) { - debug_redraw = p_enabled; - debug_redraw_time = p_time; - debug_redraw_color = p_color; +void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_uniform_set) { + if (state.current_tex_uniform_set == p_uniform_set) { + return; + } + + state.current_tex_uniform_set = p_uniform_set; + + RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, p_uniform_set, CANVAS_TEXTURE_UNIFORM_SET); } RendererCanvasRenderRD::~RendererCanvasRenderRD() { @@ -2936,6 +3141,13 @@ RendererCanvasRenderRD::~RendererCanvasRenderRD() { } RD::get_singleton()->free(state.shadow_texture); + memdelete_arr(state.instance_data_array); + for (uint32_t i = 0; i < state.canvas_instance_data_buffers.size(); i++) { + for (uint32_t j = 0; j < state.canvas_instance_data_buffers[i].instance_buffers.size(); j++) { + RD::get_singleton()->free(state.canvas_instance_data_buffers[i].instance_buffers[j]); + } + } + RendererRD::TextureStorage::get_singleton()->canvas_texture_free(default_canvas_texture); //pipelines don't need freeing, they are all gone after shaders are gone } diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 9deb4814c71d..87de07464efc 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -46,6 +46,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { MATERIAL_UNIFORM_SET = 1, TRANSFORMS_UNIFORM_SET = 2, CANVAS_TEXTURE_UNIFORM_SET = 3, + INSTANCE_DATA_UNIFORM_SET = 4, }; const int SAMPLERS_BINDING_FIRST_INDEX = 10; @@ -335,6 +336,146 @@ class RendererCanvasRenderRD : public RendererCanvasRender { //state that does not vary across rendering all items + struct InstanceData { + float world[6]; + uint32_t flags; + uint32_t specular_shininess; + union { + //rect + struct { + float modulation[4]; + union { + float msdf[4]; + float ninepatch_margins[4]; + }; + float dst_rect[4]; + float src_rect[4]; + float pad[2]; + }; + //primitive + struct { + float points[6]; // vec2 points[3] + float uvs[6]; // vec2 points[3] + uint32_t colors[6]; // colors encoded as half + }; + }; + float color_texture_pixel_size[2]; + uint32_t lights[4]; + }; + + struct PushConstant { + uint32_t base_instance_index; + uint32_t pad1; + uint32_t pad2; + uint32_t pad3; + }; + + // TextureState is used to determine when a new batch is required due to a change of texture state. + struct TextureState { + static const uint32_t FILTER_SHIFT = 0; + static const uint32_t FILTER_BITS = 3; + static const uint32_t FILTER_MASK = (1 << FILTER_BITS) - 1; + static const uint32_t REPEAT_SHIFT = FILTER_BITS; + static const uint32_t REPEAT_BITS = 2; + static const uint32_t REPEAT_MASK = (1 << REPEAT_BITS) - 1; + static const uint32_t TEXTURE_IS_DATA_SHIFT = REPEAT_SHIFT + REPEAT_BITS; + static const uint32_t TEXTURE_IS_DATA_BITS = 1; + static const uint32_t TEXTURE_IS_DATA_MASK = (1 << TEXTURE_IS_DATA_BITS) - 1; + static const uint32_t LINEAR_COLORS_SHIFT = TEXTURE_IS_DATA_SHIFT + TEXTURE_IS_DATA_BITS; + static const uint32_t LINEAR_COLORS_BITS = 1; + static const uint32_t LINEAR_COLORS_MASK = (1 << LINEAR_COLORS_BITS) - 1; + + RID texture; + uint32_t other = 0; + + TextureState() {} + + TextureState(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, bool p_texture_is_data, bool p_use_linear_colors) { + texture = p_texture; + other = (((uint32_t)p_base_filter & FILTER_MASK) << FILTER_SHIFT) | + (((uint32_t)p_base_repeat & REPEAT_MASK) << REPEAT_SHIFT) | + (((uint32_t)p_texture_is_data & TEXTURE_IS_DATA_MASK) << TEXTURE_IS_DATA_SHIFT) | + (((uint32_t)p_use_linear_colors & LINEAR_COLORS_MASK) << LINEAR_COLORS_SHIFT); + } + + _FORCE_INLINE_ RS::CanvasItemTextureFilter texture_filter() const { + return (RS::CanvasItemTextureFilter)((other >> FILTER_SHIFT) & FILTER_MASK); + } + + _FORCE_INLINE_ RS::CanvasItemTextureRepeat texture_repeat() const { + return (RS::CanvasItemTextureRepeat)((other >> REPEAT_SHIFT) & REPEAT_MASK); + } + + _FORCE_INLINE_ bool linear_colors() const { + return (other >> LINEAR_COLORS_SHIFT) & LINEAR_COLORS_MASK; + } + + _FORCE_INLINE_ bool texture_is_data() const { + return (other >> TEXTURE_IS_DATA_SHIFT) & TEXTURE_IS_DATA_MASK; + } + + bool operator==(const TextureState &p_val) const { + return (texture == p_val.texture) && (other == p_val.other); + } + + bool operator!=(const TextureState &p_val) const { + return (texture != p_val.texture) || (other != p_val.other); + } + }; + + struct Batch { + // Position in the UBO measured in bytes + uint32_t start = 0; + uint32_t instance_count = 0; + uint32_t instance_buffer_index = 0; + + TextureState tex_state; + RID tex_uniform_set; + + // The following tex_ prefixed fields are used to cache the texture data for the current batch. + // These values are applied to new InstanceData for the batch + + // The cached specular shininess derived from the current texture. + uint32_t tex_specular_shininess = 0; + // The cached texture flags, such as FLAGS_DEFAULT_SPECULAR_MAP_USED and FLAGS_DEFAULT_NORMAL_MAP_USED + uint32_t tex_flags = 0; + // The cached texture pixel size. + Vector2 tex_texpixel_size; + + Color modulate = Color(1.0, 1.0, 1.0, 1.0); + + Item *clip = nullptr; + + RID material; + CanvasMaterialData *material_data = nullptr; + PipelineLightMode light_mode = PipelineLightMode::PIPELINE_LIGHT_MODE_DISABLED; + PipelineVariant pipeline_variant = PipelineVariant::PIPELINE_VARIANT_QUAD; + + const Item::Command *command = nullptr; + Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch. + + // batch-specific data + union { + // TYPE_PRIMITIVE + uint32_t primitive_points = 0; + // TYPE_PARTICLES + uint32_t mesh_instance_count; + }; + bool has_blend = false; + + void set_tex_state(TextureState &p_tex_state) { + tex_state = p_tex_state; + tex_uniform_set = RID(); + tex_texpixel_size = Size2(); + tex_specular_shininess = 0; + tex_flags = 0; + } + }; + + struct DataBuffer { + LocalVector instance_buffers; + }; + struct State { //state buffer struct Buffer { @@ -357,6 +498,19 @@ class RendererCanvasRenderRD : public RendererCanvasRender { uint32_t pad2; }; + LocalVector canvas_instance_data_buffers; + LocalVector canvas_instance_batches; + uint32_t current_data_buffer_index = 0; + uint32_t current_instance_buffer_index = 0; + uint32_t current_batch_index = 0; + uint32_t last_instance_index = 0; + InstanceData *instance_data_array = nullptr; + + uint32_t max_instances_per_buffer = 16384; + uint32_t max_instance_buffer_size = 16384 * sizeof(InstanceData); + + RID current_tex_uniform_set; + LightUniform *light_uniforms = nullptr; RID lights_uniform_buffer; @@ -376,33 +530,6 @@ class RendererCanvasRenderRD : public RendererCanvasRender { } state; - struct PushConstant { - float world[6]; - uint32_t flags; - uint32_t specular_shininess; - union { - //rect - struct { - float modulation[4]; - union { - float msdf[4]; - float ninepatch_margins[4]; - }; - float dst_rect[4]; - float src_rect[4]; - float pad[2]; - }; - //primitive - struct { - float points[6]; // vec2 points[3] - float uvs[6]; // vec2 points[3] - uint32_t colors[6]; // colors encoded as half - }; - }; - float color_texture_pixel_size[2]; - uint32_t lights[4]; - }; - Item *items[MAX_RENDER_ITEMS]; bool using_directional_lights = false; @@ -422,9 +549,23 @@ class RendererCanvasRenderRD : public RendererCanvasRender { Color debug_redraw_color; double debug_redraw_time = 1.0; - inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead. - void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_repeat_offset, RenderingMethod::RenderInfo *r_render_info = nullptr); - void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr); + // A structure to store cached render target information + struct RenderTarget { + // Current render target for the canvas. + RID render_target; + // The base flags for each InstanceData, derived from the render target. + // Either FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR or 0 + uint32_t base_flags = 0; + }; + + void _render_batch_items(RenderTarget p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr); + void _record_item_commands(const Item *p_item, RenderTarget p_render_target, const Transform2D &p_base_transform, Item *&r_current_clip, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used); + void _render_batch(RD::DrawListID p_draw_list, PipelineVariants *p_pipeline_variants, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info = nullptr); + void _prepare_batch_texture(Batch *p_current_batch, RID p_texture) const; + void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_uniform_set); + [[nodiscard]] Batch *_new_batch(bool &r_batch_broken); + void _add_to_batch(uint32_t &r_index, bool &r_batch_broken, Batch *&r_current_batch); + void _allocate_instance_buffer(); _FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4); _FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3); diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index 4426d9eb6622..2154d56faf04 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -24,6 +24,12 @@ layout(location = 11) in vec4 weight_attrib; #include "canvas_uniforms_inc.glsl" +#ifndef USE_ATTRIBUTES + +layout(location = 4) out flat uint instance_index_interp; + +#endif // USE_ATTRIBUTES + layout(location = 0) out vec2 uv_interp; layout(location = 1) out vec4 color_interp; layout(location = 2) out vec2 vertex_interp; @@ -59,6 +65,14 @@ void main() { vec4 custom1 = vec4(0.0); #endif +#ifdef USE_ATTRIBUTES + uint instance_index = params.base_instance_index; +#else + uint instance_index = gl_InstanceIndex + params.base_instance_index; + instance_index_interp = instance_index; +#endif // USE_ATTRIBUTES + const InstanceData draw_data = instances.data[instance_index]; + #ifdef USE_PRIMITIVE //weird bug, @@ -117,13 +131,10 @@ void main() { mat4 model_matrix = mat4(vec4(draw_data.world_x, 0.0, 0.0), vec4(draw_data.world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data.world_ofs, 0.0, 1.0)); -#define FLAGS_INSTANCING_MASK 0x7F -#define FLAGS_INSTANCING_HAS_COLORS (1 << 7) -#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 8) +#ifdef USE_ATTRIBUTES uint instancing = draw_data.flags & FLAGS_INSTANCING_MASK; -#ifdef USE_ATTRIBUTES if (instancing > 1) { // trails @@ -160,38 +171,27 @@ void main() { vertex = new_vertex; color *= pcolor; - } else -#endif // USE_ATTRIBUTES - { - if (instancing == 1) { - uint stride = 2; - { - if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_COLORS)) { - stride += 1; - } - if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) { - stride += 1; - } - } - - uint offset = stride * gl_InstanceIndex; + } else if (instancing == 1) { + uint stride = 2 + bitfieldExtract(draw_data.flags, FLAGS_INSTANCING_HAS_COLORS_SHIFT, 1) + bitfieldExtract(draw_data.flags, FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT, 1); - mat4 matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); - offset += 2; + uint offset = stride * gl_InstanceIndex; - if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_COLORS)) { - color *= transforms.data[offset]; - offset += 1; - } + mat4 matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + offset += 2; - if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) { - instance_custom = transforms.data[offset]; - } + if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_COLORS)) { + color *= transforms.data[offset]; + offset += 1; + } - matrix = transpose(matrix); - model_matrix = model_matrix * matrix; + if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) { + instance_custom = transforms.data[offset]; } + + matrix = transpose(matrix); + model_matrix = model_matrix * matrix; } +#endif // USE_ATTRIBUTES #ifdef USE_POINT_SIZE float point_size = 1.0; @@ -241,6 +241,10 @@ void main() { #include "canvas_uniforms_inc.glsl" +#ifndef USE_ATTRIBUTES +layout(location = 4) in flat uint instance_index; +#endif // USE_ATTRIBUTES + layout(location = 0) in vec2 uv_interp; layout(location = 1) in vec4 color_interp; layout(location = 2) in vec2 vertex_interp; @@ -320,6 +324,12 @@ vec4 light_compute( #ifdef USE_NINEPATCH float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) { +#ifdef USE_ATTRIBUTES + const InstanceData draw_data = instances.data[params.base_instance_index]; +#else + const InstanceData draw_data = instances.data[instance_index]; +#endif // USE_ATTRIBUTES + float tex_size = 1.0 / tex_pixel_size; if (pixel < margin_begin) { @@ -327,9 +337,7 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo } else if (pixel >= draw_size - margin_end) { return (tex_size - (draw_size - pixel)) * tex_pixel_size; } else { - if (!bool(draw_data.flags & FLAGS_NINEPACH_DRAW_CENTER)) { - draw_center--; - } + draw_center -= 1 - int(bitfieldExtract(draw_data.flags, FLAGS_NINEPACH_DRAW_CENTER_SHIFT, 1)); // np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum. if (np_repeat == 0) { // Stretch. @@ -462,14 +470,20 @@ void main() { vec2 uv = uv_interp; vec2 vertex = vertex_interp; +#ifdef USE_ATTRIBUTES + const InstanceData draw_data = instances.data[params.base_instance_index]; +#else + const InstanceData draw_data = instances.data[instance_index]; +#endif // USE_ATTRIBUTES + #if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE) #ifdef USE_NINEPATCH int draw_center = 2; uv = vec2( - map_ninepatch_axis(pixel_size_interp.x, abs(draw_data.dst_rect.z), draw_data.color_texture_pixel_size.x, draw_data.ninepatch_margins.x, draw_data.ninepatch_margins.z, int(draw_data.flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center), - map_ninepatch_axis(pixel_size_interp.y, abs(draw_data.dst_rect.w), draw_data.color_texture_pixel_size.y, draw_data.ninepatch_margins.y, draw_data.ninepatch_margins.w, int(draw_data.flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center)); + map_ninepatch_axis(pixel_size_interp.x, abs(draw_data.dst_rect.z), draw_data.color_texture_pixel_size.x, draw_data.ninepatch_margins.x, draw_data.ninepatch_margins.z, int(bitfieldExtract(draw_data.flags, FLAGS_NINEPATCH_H_MODE_SHIFT, 2)), draw_center), + map_ninepatch_axis(pixel_size_interp.y, abs(draw_data.dst_rect.w), draw_data.color_texture_pixel_size.y, draw_data.ninepatch_margins.y, draw_data.ninepatch_margins.w, int(bitfieldExtract(draw_data.flags, FLAGS_NINEPATCH_V_MODE_SHIFT, 2)), draw_center)); if (draw_center == 0) { color.a = 0.0; @@ -519,8 +533,8 @@ void main() { color *= texture(sampler2D(color_texture, texture_sampler), uv); } - uint light_count = (draw_data.flags >> FLAGS_LIGHT_COUNT_SHIFT) & 0xF; //max 16 lights - bool using_light = light_count > 0 || canvas_data.directional_light_count > 0; + uint light_count = bitfieldExtract(draw_data.flags, FLAGS_LIGHT_COUNT_SHIFT, 4); //max 16 lights + bool using_light = (light_count + canvas_data.directional_light_count) > 0; vec3 normal; @@ -652,9 +666,7 @@ void main() { if (i >= light_count) { break; } - uint light_base = draw_data.lights[i >> 2]; - light_base >>= (i & 3) * 8; - light_base &= 0xFF; + uint light_base = bitfieldExtract(draw_data.lights[i >> 2], (int(i) & 0x3) * 8, 8); vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array.data[light_base].texture_matrix[0], light_array.data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. vec2 tex_uv_atlas = tex_uv * light_array.data[light_base].atlas_rect.zw + light_array.data[light_base].atlas_rect.xy; diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl index 8649f4710bf3..7cf5b4576e6a 100644 --- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl @@ -7,13 +7,16 @@ //1 means enabled, 2+ means trails in use #define FLAGS_INSTANCING_MASK 0x7F -#define FLAGS_INSTANCING_HAS_COLORS (1 << 7) -#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 8) +#define FLAGS_INSTANCING_HAS_COLORS_SHIFT 7 +#define FLAGS_INSTANCING_HAS_COLORS (1 << FLAGS_INSTANCING_HAS_COLORS_SHIFT) +#define FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT 8 +#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT) #define FLAGS_CLIP_RECT_UV (1 << 9) #define FLAGS_TRANSPOSE_RECT (1 << 10) #define FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR (1 << 11) -#define FLAGS_NINEPACH_DRAW_CENTER (1 << 12) +#define FLAGS_NINEPACH_DRAW_CENTER_SHIFT 12 +#define FLAGS_NINEPACH_DRAW_CENTER (1 << FLAGS_NINEPACH_DRAW_CENTER_SHIFT) #define FLAGS_NINEPATCH_H_MODE_SHIFT 16 #define FLAGS_NINEPATCH_V_MODE_SHIFT 18 @@ -29,9 +32,7 @@ #define FLAGS_FLIP_H (1 << 30) #define FLAGS_FLIP_V (1 << 31) -// Push Constant - -layout(push_constant, std430) uniform DrawData { +struct InstanceData { vec2 world_x; vec2 world_y; vec2 world_ofs; @@ -51,8 +52,20 @@ layout(push_constant, std430) uniform DrawData { #endif vec2 color_texture_pixel_size; uint lights[4]; +}; + +layout(set = 4, binding = 0, std430) restrict readonly buffer DrawData { + InstanceData data[]; +} +instances; + +layout(push_constant, std430) uniform Params { + uint base_instance_index; // base index to instance data + uint pad1; + uint pad2; + uint pad3; } -draw_data; +params; // In vulkan, sets should always be ordered using the following logic: // Lower Sets: Sets that change format and layout less often diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 1848d5602ef3..f354e83893b2 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -3548,6 +3548,7 @@ void RenderingServer::init() { GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality.mobile", 0); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/2d/shadow_atlas/size", PROPERTY_HINT_RANGE, "128,16384"), 2048); + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/2d/batching/item_buffer_size", PROPERTY_HINT_RANGE, "128,1048576,1"), 16384); // Number of commands that can be drawn per frame. GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/gl_compatibility/item_buffer_size", PROPERTY_HINT_RANGE, "128,1048576,1"), 16384);