2
0
Эх сурвалжийг харах

Add 2D shadows and canvas SDF to OpenGL3 renderer

This is an initial implementation based on the current RD implementation

Performance will improve later
clayjohn 2 жил өмнө
parent
commit
2ec234ff67

+ 13 - 6
drivers/gles3/effects/copy_effects.cpp

@@ -117,16 +117,12 @@ CopyEffects::~CopyEffects() {
 void CopyEffects::copy_to_rect(const Rect2 &p_rect) {
 	copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION);
 	copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION);
-	glBindVertexArray(quad_array);
-	glDrawArrays(GL_TRIANGLES, 0, 6);
-	glBindVertexArray(0);
+	draw_screen_quad();
 }
 
 void CopyEffects::copy_screen() {
 	copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_DEFAULT);
-	glBindVertexArray(screen_triangle_array);
-	glDrawArrays(GL_TRIANGLES, 0, 3);
-	glBindVertexArray(0);
+	draw_screen_triangle();
 }
 
 void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region) {
@@ -158,8 +154,19 @@ void CopyEffects::set_color(const Color &p_color, const Rect2i &p_region) {
 	copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR);
 	copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_region.position.x, p_region.position.y, p_region.size.x, p_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR);
 	copy.shader.version_set_uniform(CopyShaderGLES3::COLOR_IN, p_color, copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR);
+	draw_screen_quad();
+}
+
+void CopyEffects::draw_screen_triangle() {
+	glBindVertexArray(screen_triangle_array);
+	glDrawArrays(GL_TRIANGLES, 0, 3);
+	glBindVertexArray(0);
+}
+
+void CopyEffects::draw_screen_quad() {
 	glBindVertexArray(quad_array);
 	glDrawArrays(GL_TRIANGLES, 0, 6);
 	glBindVertexArray(0);
 }
+
 #endif // GLES3_ENABLED

+ 2 - 0
drivers/gles3/effects/copy_effects.h

@@ -65,6 +65,8 @@ public:
 	void copy_screen();
 	void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region);
 	void set_color(const Color &p_color, const Rect2i &p_region);
+	void draw_screen_triangle();
+	void draw_screen_quad();
 };
 
 } //namespace GLES3

+ 543 - 45
drivers/gles3/rasterizer_canvas_gles3.cpp

@@ -36,26 +36,13 @@
 #include "rasterizer_scene_gles3.h"
 
 #include "core/config/project_settings.h"
+#include "core/math/geometry_2d.h"
 #include "servers/rendering/rendering_server_default.h"
 #include "storage/config.h"
 #include "storage/material_storage.h"
 #include "storage/mesh_storage.h"
 #include "storage/texture_storage.h"
 
-#ifndef GLES_OVER_GL
-#define glClearDepth glClearDepthf
-#endif
-
-//static const GLenum gl_primitive[] = {
-//	GL_POINTS,
-//	GL_LINES,
-//	GL_LINE_STRIP,
-//	GL_LINE_LOOP,
-//	GL_TRIANGLES,
-//	GL_TRIANGLE_STRIP,
-//	GL_TRIANGLE_FAN
-//};
-
 void RasterizerCanvasGLES3::_update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4) {
 	p_mat4[0] = p_transform.columns[0][0];
 	p_mat4[1] = p_transform.columns[0][1];
@@ -174,7 +161,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
 			state.light_uniforms[index].position[0] = -canvas_light_dir.x;
 			state.light_uniforms[index].position[1] = -canvas_light_dir.y;
 
-			//_update_transform_2d_to_mat2x4(clight->shadow.directional_xform, state.light_uniforms[index].shadow_matrix);
+			_update_transform_2d_to_mat2x4(clight->shadow.directional_xform, state.light_uniforms[index].shadow_matrix);
 
 			state.light_uniforms[index].height = l->height; //0..1 here
 
@@ -185,8 +172,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
 
 			state.light_uniforms[index].color[3] = l->energy; //use alpha for energy, so base color can go separate
 
-			/*
-			if (state.shadow_fb.is_valid()) {
+			if (state.shadow_fb != 0) {
 				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;
@@ -195,15 +181,13 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
 				state.light_uniforms[index].shadow_z_far_inv = 1.0;
 				state.light_uniforms[index].shadow_y_ofs = 0;
 			}
-			*/
 
 			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;
 			}
-			*/
 
 			l->render_index_cache = index;
 
@@ -252,24 +236,22 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
 
 			state.light_uniforms[index].color[3] = l->energy; //use alpha for energy, so base color can go separate
 
-			/*
-				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 (state.shadow_fb != 0) {
+				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;
+			}
+
 			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 (clight->texture.is_valid()) {
 				Rect2 atlas_rect = GLES3::TextureStorage::get_singleton()->texture_atlas_get_texture_rect(clight->texture);
@@ -313,6 +295,13 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
 		}
 		glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 2);
 		glBindTexture(GL_TEXTURE_2D, texture_atlas);
+		GLuint shadow_tex = state.shadow_texture;
+		if (shadow_tex == 0) {
+			GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE));
+			shadow_tex = tex->tex_id;
+		}
+		glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 3);
+		glBindTexture(GL_TEXTURE_2D, shadow_tex);
 	}
 
 	{
@@ -342,8 +331,6 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
 		state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x;
 		state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y;
 
-		glViewport(0, 0, render_target_size.x, render_target_size.y);
-
 		state_buffer.time = state.time;
 		state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel;
 
@@ -366,6 +353,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
 		state_buffer.sdf_to_tex[3] = -sdf_tex_rect.position.y / sdf_tex_rect.size.height;
 
 		state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5);
+
 		glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].state_ubo);
 		glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), &state_buffer, GL_STREAM_DRAW);
 
@@ -375,11 +363,17 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
 		glBindBuffer(GL_UNIFORM_BUFFER, 0);
 	}
 
+	glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 5);
+	glBindTexture(GL_TEXTURE_2D, texture_storage->render_target_get_sdf_texture(p_to_render_target));
+
 	{
 		state.default_filter = p_default_filter;
 		state.default_repeat = p_default_repeat;
 	}
 
+	Size2 render_target_size = texture_storage->render_target_get_size(p_to_render_target);
+	glViewport(0, 0, render_target_size.x, render_target_size.y);
+
 	r_sdf_used = false;
 	int item_count = 0;
 	bool backbuffer_cleared = false;
@@ -621,6 +615,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
 		CanvasShaderGLES3::ShaderVariant variant = state.canvas_instance_batches[i].shader_variant;
 		uint64_t specialization = 0;
 		specialization |= uint64_t(state.canvas_instance_batches[i].lights_disabled);
+		specialization |= uint64_t(!GLES3::Config::get_singleton()->float_texture_supported) << 1;
 		_bind_material(material_data, variant, specialization);
 
 		GLES3::CanvasShaderData::BlendMode blend_mode = state.canvas_instance_batches[i].blend_mode;
@@ -1395,28 +1390,516 @@ void RasterizerCanvasGLES3::light_set_texture(RID p_rid, RID p_texture) {
 }
 
 void RasterizerCanvasGLES3::light_set_use_shadow(RID p_rid, bool p_enable) {
+	CanvasLight *cl = canvas_light_owner.get_or_null(p_rid);
+	ERR_FAIL_COND(!cl);
+
+	cl->shadow.enabled = p_enable;
 }
 
 void RasterizerCanvasGLES3::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) {
+	GLES3::Config *config = GLES3::Config::get_singleton();
+
+	CanvasLight *cl = canvas_light_owner.get_or_null(p_rid);
+	ERR_FAIL_COND(!cl->shadow.enabled);
+
+	_update_shadow_atlas();
+
+	cl->shadow.z_far = p_far;
+	cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(data.max_lights_per_render * 2);
+
+	glBindFramebuffer(GL_FRAMEBUFFER, state.shadow_fb);
+	glViewport(0, p_shadow_index * 2, state.shadow_texture_size, 2);
+
+	glDepthMask(GL_TRUE);
+	glEnable(GL_DEPTH_TEST);
+	glDepthFunc(GL_LESS);
+	glDisable(GL_BLEND);
+
+	glEnable(GL_SCISSOR_TEST);
+	glScissor(0, p_shadow_index * 2, state.shadow_texture_size, 2);
+	glClearColor(p_far, p_far, p_far, 1.0);
+	glClearDepth(1.0);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	glCullFace(GL_BACK);
+	glDisable(GL_CULL_FACE);
+	RS::CanvasOccluderPolygonCullMode cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED;
+
+	CanvasOcclusionShaderGLES3::ShaderVariant variant = config->float_texture_supported ? CanvasOcclusionShaderGLES3::MODE_SHADOW : CanvasOcclusionShaderGLES3::MODE_SHADOW_RGBA;
+	shadow_render.shader.version_bind_shader(shadow_render.shader_version, variant);
+
+	for (int i = 0; i < 4; i++) {
+		glViewport((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2);
+
+		Projection projection;
+		{
+			real_t fov = 90;
+			real_t nearp = p_near;
+			real_t farp = p_far;
+			real_t aspect = 1.0;
+
+			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;
+
+			projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp);
+		}
+
+		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());
+		shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, projection, shadow_render.shader_version, variant);
+
+		static const Vector2 directions[4] = { Vector2(1, 0), Vector2(0, 1), Vector2(-1, 0), Vector2(0, -1) };
+		shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::DIRECTION, directions[i].x, directions[i].y, shadow_render.shader_version, variant);
+		shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::Z_FAR, p_far, shadow_render.shader_version, variant);
+
+		LightOccluderInstance *instance = p_occluders;
+
+		while (instance) {
+			OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder);
+
+			if (!co || co->vertex_array == 0 || !(p_light_mask & instance->light_mask)) {
+				instance = instance->next;
+				continue;
+			}
+
+			Transform2D modelview = p_light_xform * instance->xform_cache;
+			shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW1, modelview.columns[0][0], modelview.columns[1][0], 0, modelview.columns[2][0], shadow_render.shader_version, variant);
+			shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW2, modelview.columns[0][1], modelview.columns[1][1], 0, modelview.columns[2][1], shadow_render.shader_version, variant);
+
+			if (co->cull_mode != cull_mode) {
+				if (co->cull_mode == RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED) {
+					glDisable(GL_CULL_FACE);
+				} else {
+					if (cull_mode == RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED) {
+						// Last time was disabled, so enable and set proper face.
+						glEnable(GL_CULL_FACE);
+					}
+					glCullFace(co->cull_mode == RS::CANVAS_OCCLUDER_POLYGON_CULL_CLOCKWISE ? GL_FRONT : GL_BACK);
+				}
+				cull_mode = co->cull_mode;
+			}
+
+			glBindVertexArray(co->vertex_array);
+			glDrawElements(GL_TRIANGLES, 3 * co->line_point_count, GL_UNSIGNED_SHORT, 0);
+
+			instance = instance->next;
+		}
+	}
+
+	glBindVertexArray(0);
+	glBindFramebuffer(GL_FRAMEBUFFER, 0);
+	glDepthMask(GL_FALSE);
+	glDisable(GL_DEPTH_TEST);
+	glDisable(GL_SCISSOR_TEST);
 }
 
 void RasterizerCanvasGLES3::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) {
+	GLES3::Config *config = GLES3::Config::get_singleton();
+
+	CanvasLight *cl = canvas_light_owner.get_or_null(p_rid);
+	ERR_FAIL_COND(!cl->shadow.enabled);
+
+	_update_shadow_atlas();
+
+	Vector2 light_dir = p_light_xform.columns[1].normalized();
+
+	Vector2 center = p_clip_rect.get_center();
+
+	float to_edge_distance = ABS(light_dir.dot(p_clip_rect.get_support(light_dir)) - light_dir.dot(center));
+
+	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
+
+	cl->shadow.z_far = distance;
+	cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(data.max_lights_per_render * 2);
+
+	Transform2D to_light_xform;
+
+	to_light_xform[2] = from_pos;
+	to_light_xform[1] = light_dir;
+	to_light_xform[0] = -light_dir.orthogonal();
+
+	to_light_xform.invert();
+
+	glBindFramebuffer(GL_FRAMEBUFFER, state.shadow_fb);
+	glViewport(0, p_shadow_index * 2, state.shadow_texture_size, 2);
+
+	glDepthMask(GL_TRUE);
+	glEnable(GL_DEPTH_TEST);
+	glDepthFunc(GL_LESS);
+	glDisable(GL_BLEND);
+
+	glEnable(GL_SCISSOR_TEST);
+	glScissor(0, p_shadow_index * 2, state.shadow_texture_size, 2);
+	glClearColor(1.0, 1.0, 1.0, 1.0);
+	glClearDepth(1.0);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	glCullFace(GL_BACK);
+	glDisable(GL_CULL_FACE);
+	RS::CanvasOccluderPolygonCullMode cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED;
+
+	CanvasOcclusionShaderGLES3::ShaderVariant variant = config->float_texture_supported ? CanvasOcclusionShaderGLES3::MODE_SHADOW : CanvasOcclusionShaderGLES3::MODE_SHADOW_RGBA;
+	shadow_render.shader.version_bind_shader(shadow_render.shader_version, variant);
+
+	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());
+
+	shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, projection, shadow_render.shader_version, variant);
+	shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::DIRECTION, 0.0, 1.0, shadow_render.shader_version, variant);
+	shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::Z_FAR, distance, shadow_render.shader_version, variant);
+
+	LightOccluderInstance *instance = p_occluders;
+
+	while (instance) {
+		OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder);
+
+		if (!co || co->vertex_array == 0 || !(p_light_mask & instance->light_mask)) {
+			instance = instance->next;
+			continue;
+		}
+
+		Transform2D modelview = to_light_xform * instance->xform_cache;
+		shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW1, modelview.columns[0][0], modelview.columns[1][0], 0, modelview.columns[2][0], shadow_render.shader_version, variant);
+		shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW2, modelview.columns[0][1], modelview.columns[1][1], 0, modelview.columns[2][1], shadow_render.shader_version, variant);
+
+		if (co->cull_mode != cull_mode) {
+			if (co->cull_mode == RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED) {
+				glDisable(GL_CULL_FACE);
+			} else {
+				if (cull_mode == RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED) {
+					// Last time was disabled, so enable and set proper face.
+					glEnable(GL_CULL_FACE);
+				}
+				glCullFace(co->cull_mode == RS::CANVAS_OCCLUDER_POLYGON_CULL_CLOCKWISE ? GL_FRONT : GL_BACK);
+			}
+			cull_mode = co->cull_mode;
+		}
+
+		glBindVertexArray(co->vertex_array);
+		glDrawElements(GL_TRIANGLES, 3 * co->line_point_count, GL_UNSIGNED_SHORT, 0);
+
+		instance = instance->next;
+	}
+
+	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;
+
+	glBindVertexArray(0);
+	glBindFramebuffer(GL_FRAMEBUFFER, 0);
+	glDepthMask(GL_FALSE);
+	glDisable(GL_DEPTH_TEST);
+	glDisable(GL_SCISSOR_TEST);
+	glDisable(GL_CULL_FACE);
+}
+
+void RasterizerCanvasGLES3::_update_shadow_atlas() {
+	GLES3::Config *config = GLES3::Config::get_singleton();
+
+	if (state.shadow_fb == 0) {
+		glActiveTexture(GL_TEXTURE0);
+
+		glGenFramebuffers(1, &state.shadow_fb);
+		glBindFramebuffer(GL_FRAMEBUFFER, state.shadow_fb);
+
+		glGenRenderbuffers(1, &state.shadow_depth_buffer);
+		glBindRenderbuffer(GL_RENDERBUFFER, state.shadow_depth_buffer);
+		glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, state.shadow_texture_size, data.max_lights_per_render * 2);
+		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, state.shadow_depth_buffer);
+
+		glGenTextures(1, &state.shadow_texture);
+		glBindTexture(GL_TEXTURE_2D, state.shadow_texture);
+		if (config->float_texture_supported) {
+			glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, state.shadow_texture_size, data.max_lights_per_render * 2, 0, GL_RED, GL_FLOAT, nullptr);
+		} else {
+			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, state.shadow_texture_size, data.max_lights_per_render * 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+		}
+
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, state.shadow_texture, 0);
+
+		GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+		if (status != GL_FRAMEBUFFER_COMPLETE) {
+			glDeleteFramebuffers(1, &state.shadow_fb);
+			glDeleteTextures(1, &state.shadow_texture);
+			glDeleteRenderbuffers(1, &state.shadow_depth_buffer);
+			state.shadow_fb = 0;
+			state.shadow_texture = 0;
+			state.shadow_depth_buffer = 0;
+			WARN_PRINT("Could not create CanvasItem shadow atlas, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
+		}
+		glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
+	}
 }
 
 void RasterizerCanvasGLES3::render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) {
+	GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+
+	GLuint fb = texture_storage->render_target_get_sdf_framebuffer(p_render_target);
+	Rect2i rect = texture_storage->render_target_get_sdf_rect(p_render_target);
+
+	Transform2D to_sdf;
+	to_sdf.columns[0] *= rect.size.width;
+	to_sdf.columns[1] *= rect.size.height;
+	to_sdf.columns[2] = rect.position;
+
+	Transform2D to_clip;
+	to_clip.columns[0] *= 2.0;
+	to_clip.columns[1] *= 2.0;
+	to_clip.columns[2] = -Vector2(1.0, 1.0);
+
+	to_clip = to_clip * to_sdf.affine_inverse();
+
+	glBindFramebuffer(GL_FRAMEBUFFER, fb);
+	glViewport(0, 0, rect.size.width, rect.size.height);
+
+	glDepthMask(GL_FALSE);
+	glDisable(GL_DEPTH_TEST);
+	glDisable(GL_BLEND);
+	glDisable(GL_CULL_FACE);
+	glDisable(GL_SCISSOR_TEST);
+
+	glClearColor(0.0, 0.0, 0.0, 0.0);
+	glClear(GL_COLOR_BUFFER_BIT);
+
+	CanvasOcclusionShaderGLES3::ShaderVariant variant = CanvasOcclusionShaderGLES3::MODE_SDF;
+	shadow_render.shader.version_bind_shader(shadow_render.shader_version, variant);
+
+	shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, Projection(), shadow_render.shader_version, variant);
+	shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::DIRECTION, 0.0, 0.0, shadow_render.shader_version, variant);
+	shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::Z_FAR, 0.0, shadow_render.shader_version, variant);
+
+	LightOccluderInstance *instance = p_occluders;
+
+	while (instance) {
+		OccluderPolygon *oc = occluder_polygon_owner.get_or_null(instance->occluder);
+
+		if (!oc || oc->sdf_vertex_array == 0) {
+			instance = instance->next;
+			continue;
+		}
+
+		Transform2D modelview = to_clip * instance->xform_cache;
+		shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW1, modelview.columns[0][0], modelview.columns[1][0], 0, modelview.columns[2][0], shadow_render.shader_version, variant);
+		shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW2, modelview.columns[0][1], modelview.columns[1][1], 0, modelview.columns[2][1], shadow_render.shader_version, variant);
+
+		glBindVertexArray(oc->sdf_vertex_array);
+		glDrawElements(oc->sdf_is_lines ? GL_LINES : GL_TRIANGLES, oc->sdf_index_count, GL_UNSIGNED_INT, 0);
+
+		instance = instance->next;
+	}
+
+	texture_storage->render_target_sdf_process(p_render_target); //done rendering, process it
+	glBindVertexArray(0);
+	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 }
 
 RID RasterizerCanvasGLES3::occluder_polygon_create() {
-	return RID();
+	OccluderPolygon occluder;
+
+	return occluder_polygon_owner.make_rid(occluder);
 }
 
 void RasterizerCanvasGLES3::occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) {
+	OccluderPolygon *oc = occluder_polygon_owner.get_or_null(p_occluder);
+	ERR_FAIL_COND(!oc);
+
+	Vector<Vector2> lines;
+
+	if (p_points.size()) {
+		int lc = p_points.size() * 2;
+
+		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;
+			}
+		}
+	}
+
+	if (oc->line_point_count != lines.size() && oc->vertex_array != 0) {
+		glDeleteVertexArrays(1, &oc->vertex_array);
+		glDeleteBuffers(1, &oc->vertex_buffer);
+		glDeleteBuffers(1, &oc->index_buffer);
+
+		oc->vertex_array = 0;
+		oc->vertex_buffer = 0;
+		oc->index_buffer = 0;
+	}
+
+	if (lines.size()) {
+		Vector<uint8_t> geometry;
+		Vector<uint8_t> indices;
+		int lc = lines.size();
+
+		geometry.resize(lc * 6 * sizeof(float));
+		indices.resize(lc * 3 * sizeof(uint16_t));
+
+		{
+			uint8_t *vw = geometry.ptrw();
+			float *vwptr = reinterpret_cast<float *>(vw);
+			uint8_t *iw = indices.ptrw();
+			uint16_t *iwptr = (uint16_t *)iw;
+
+			const Vector2 *lr = lines.ptr();
+
+			const int POLY_HEIGHT = 16384;
+
+			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;
+
+				vwptr[i * 12 + 3] = lr[i * 2 + 1].x;
+				vwptr[i * 12 + 4] = lr[i * 2 + 1].y;
+				vwptr[i * 12 + 5] = POLY_HEIGHT;
+
+				vwptr[i * 12 + 6] = lr[i * 2 + 1].x;
+				vwptr[i * 12 + 7] = lr[i * 2 + 1].y;
+				vwptr[i * 12 + 8] = -POLY_HEIGHT;
+
+				vwptr[i * 12 + 9] = lr[i * 2 + 0].x;
+				vwptr[i * 12 + 10] = lr[i * 2 + 0].y;
+				vwptr[i * 12 + 11] = -POLY_HEIGHT;
+
+				iwptr[i * 6 + 0] = i * 4 + 0;
+				iwptr[i * 6 + 1] = i * 4 + 1;
+				iwptr[i * 6 + 2] = i * 4 + 2;
+
+				iwptr[i * 6 + 3] = i * 4 + 2;
+				iwptr[i * 6 + 4] = i * 4 + 3;
+				iwptr[i * 6 + 5] = i * 4 + 0;
+			}
+		}
+
+		if (oc->vertex_array == 0) {
+			oc->line_point_count = lc;
+			glGenVertexArrays(1, &oc->vertex_array);
+			glBindVertexArray(oc->vertex_array);
+			glGenBuffers(1, &oc->vertex_buffer);
+			glBindBuffer(GL_ARRAY_BUFFER, oc->vertex_buffer);
+
+			glBufferData(GL_ARRAY_BUFFER, lc * 6 * sizeof(float), geometry.ptr(), GL_STATIC_DRAW);
+			glEnableVertexAttribArray(RS::ARRAY_VERTEX);
+			glVertexAttribPointer(RS::ARRAY_VERTEX, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
+
+			glGenBuffers(1, &oc->index_buffer);
+			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oc->index_buffer);
+			glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * lc * sizeof(uint16_t), indices.ptr(), GL_STATIC_DRAW);
+			glBindVertexArray(0);
+		} else {
+			glBindVertexArray(oc->vertex_array);
+			glBindBuffer(GL_ARRAY_BUFFER, oc->vertex_buffer);
+			glBufferData(GL_ARRAY_BUFFER, lc * 6 * sizeof(float), geometry.ptr(), GL_STATIC_DRAW);
+			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oc->index_buffer);
+			glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * lc * sizeof(uint16_t), indices.ptr(), GL_STATIC_DRAW);
+		}
+	}
+
+	// sdf
+
+	Vector<int> sdf_indices;
+
+	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);
+
+			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;
+		}
+	}
+
+	if (oc->sdf_index_count != sdf_indices.size() && oc->sdf_point_count != p_points.size() && oc->sdf_vertex_array != 0) {
+		glDeleteVertexArrays(1, &oc->sdf_vertex_array);
+		glDeleteBuffers(1, &oc->sdf_vertex_buffer);
+		glDeleteBuffers(1, &oc->sdf_index_buffer);
+
+		oc->sdf_vertex_array = 0;
+		oc->sdf_vertex_buffer = 0;
+		oc->sdf_index_buffer = 0;
+
+		oc->sdf_index_count = sdf_indices.size();
+		oc->sdf_point_count = p_points.size();
+	}
+
+	if (sdf_indices.size()) {
+		if (oc->sdf_vertex_array == 0) {
+			oc->sdf_index_count = sdf_indices.size();
+			oc->sdf_point_count = p_points.size();
+			glGenVertexArrays(1, &oc->sdf_vertex_array);
+			glBindVertexArray(oc->sdf_vertex_array);
+			glGenBuffers(1, &oc->sdf_vertex_buffer);
+			glBindBuffer(GL_ARRAY_BUFFER, oc->sdf_vertex_buffer);
+
+			glBufferData(GL_ARRAY_BUFFER, p_points.size() * 2 * sizeof(float), p_points.to_byte_array().ptr(), GL_STATIC_DRAW);
+			glEnableVertexAttribArray(RS::ARRAY_VERTEX);
+			glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr);
+
+			glGenBuffers(1, &oc->sdf_index_buffer);
+			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oc->sdf_index_buffer);
+			glBufferData(GL_ELEMENT_ARRAY_BUFFER, sdf_indices.size() * sizeof(uint32_t), sdf_indices.to_byte_array().ptr(), GL_STATIC_DRAW);
+			glBindVertexArray(0);
+		} else {
+			glBindBuffer(GL_ARRAY_BUFFER, oc->sdf_vertex_buffer);
+			glBufferData(GL_ARRAY_BUFFER, p_points.size() * 2 * sizeof(float), p_points.to_byte_array().ptr(), GL_STATIC_DRAW);
+			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oc->sdf_index_buffer);
+			glBufferData(GL_ELEMENT_ARRAY_BUFFER, sdf_indices.size() * sizeof(uint32_t), sdf_indices.to_byte_array().ptr(), GL_STATIC_DRAW);
+			glBindBuffer(GL_ARRAY_BUFFER, 0);
+			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+		}
+	}
 }
 
 void RasterizerCanvasGLES3::occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) {
+	OccluderPolygon *oc = occluder_polygon_owner.get_or_null(p_occluder);
+	ERR_FAIL_COND(!oc);
+	oc->cull_mode = p_mode;
 }
 
 void RasterizerCanvasGLES3::set_shadow_texture_size(int p_size) {
+	GLES3::Config *config = GLES3::Config::get_singleton();
+	p_size = nearest_power_of_2_templated(p_size);
+	if (p_size == state.shadow_texture_size) {
+		return;
+	}
+
+	if (p_size > config->max_texture_size) {
+		p_size = config->max_texture_size;
+		WARN_PRINT("Attempting to set CanvasItem shadow atlas size to " + itos(p_size) + " which is beyond limit of " + itos(config->max_texture_size) + "supported by hardware.");
+	}
+
+	state.shadow_texture_size = p_size;
 }
 
 bool RasterizerCanvasGLES3::free(RID p_rid) {
@@ -1424,6 +1907,9 @@ bool RasterizerCanvasGLES3::free(RID p_rid) {
 		CanvasLight *cl = canvas_light_owner.get_or_null(p_rid);
 		ERR_FAIL_COND_V(!cl, false);
 		canvas_light_owner.free(p_rid);
+	} else if (occluder_polygon_owner.owns(p_rid)) {
+		occluder_polygon_set_shape(p_rid, Vector<Vector2>(), false);
+		occluder_polygon_owner.free(p_rid);
 	} else {
 		return false;
 	}
@@ -1623,16 +2109,16 @@ void RasterizerCanvasGLES3::reset_canvas() {
 	glEnable(GL_BLEND);
 	glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
 
+	glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 2);
+	glBindTexture(GL_TEXTURE_2D, 0);
+	glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 3);
+	glBindTexture(GL_TEXTURE_2D, 0);
+	glActiveTexture(GL_TEXTURE0);
+
 	glBindBuffer(GL_ARRAY_BUFFER, 0);
 	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 }
 
-void RasterizerCanvasGLES3::canvas_debug_viewport_shadows(Light *p_lights_with_shadow) {
-}
-
-void RasterizerCanvasGLES3::canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, Projection *p_xform_cache) {
-}
-
 void RasterizerCanvasGLES3::draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) {
 }
 
@@ -1663,7 +2149,6 @@ RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vec
 	polygon_buffer.resize(buffer_size * sizeof(float));
 	{
 		glBindBuffer(GL_ARRAY_BUFFER, pb.vertex_buffer);
-		glBufferData(GL_ARRAY_BUFFER, stride * vertex_count * sizeof(float), nullptr, GL_STATIC_DRAW); // TODO may not be necessary
 		uint8_t *r = polygon_buffer.ptrw();
 		float *fptr = reinterpret_cast<float *>(r);
 		uint32_t *uptr = (uint32_t *)r;
@@ -1772,7 +2257,6 @@ RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vec
 		}
 		glGenBuffers(1, &pb.index_buffer);
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pb.index_buffer);
-		glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_indices.size() * 4, nullptr, GL_STATIC_DRAW); // TODO may not be necessary
 		glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_indices.size() * 4, index_buffer.ptr(), GL_STATIC_DRAW);
 		pb.count = p_indices.size();
 	}
@@ -2064,6 +2548,9 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() {
 	data.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create();
 	GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD);
 
+	shadow_render.shader.initialize();
+	shadow_render.shader_version = shadow_render.shader.version_create();
+
 	{
 		default_canvas_group_shader = material_storage->shader_allocate();
 		material_storage->shader_initialize(default_canvas_group_shader);
@@ -2116,9 +2603,11 @@ void fragment() {
 }
 
 RasterizerCanvasGLES3::~RasterizerCanvasGLES3() {
-	GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
+	singleton = nullptr;
 
+	GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
 	material_storage->shaders.canvas_shader.version_free(data.canvas_shader_default_version);
+	shadow_render.shader.version_free(shadow_render.shader_version);
 	material_storage->material_free(default_canvas_group_material);
 	material_storage->shader_free(default_canvas_group_shader);
 	material_storage->material_free(default_clip_children_material);
@@ -2134,6 +2623,15 @@ RasterizerCanvasGLES3::~RasterizerCanvasGLES3() {
 	GLES3::TextureStorage::get_singleton()->canvas_texture_free(default_canvas_texture);
 	memdelete_arr(state.instance_data_array);
 	memdelete_arr(state.light_uniforms);
+
+	if (state.shadow_fb != 0) {
+		glDeleteFramebuffers(1, &state.shadow_fb);
+		glDeleteTextures(1, &state.shadow_texture);
+		glDeleteRenderbuffers(1, &state.shadow_depth_buffer);
+		state.shadow_fb = 0;
+		state.shadow_texture = 0;
+		state.shadow_depth_buffer = 0;
+	}
 }
 
 #endif // GLES3_ENABLED

+ 39 - 6
drivers/gles3/rasterizer_canvas_gles3.h

@@ -40,6 +40,7 @@
 #include "storage/texture_storage.h"
 
 #include "shaders/canvas.glsl.gen.h"
+#include "shaders/canvas_occlusion.glsl.gen.h"
 
 class RasterizerSceneGLES3;
 
@@ -102,10 +103,40 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {
 
 	struct CanvasLight {
 		RID texture;
+		struct {
+			bool enabled = false;
+			float z_far;
+			float y_offset;
+			Transform2D directional_xform;
+		} shadow;
 	};
 
 	RID_Owner<CanvasLight> canvas_light_owner;
 
+	struct OccluderPolygon {
+		RS::CanvasOccluderPolygonCullMode cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED;
+		int line_point_count = 0;
+		GLuint vertex_buffer = 0;
+		GLuint vertex_array = 0;
+		GLuint index_buffer = 0;
+
+		int sdf_point_count = 0;
+		int sdf_index_count = 0;
+		GLuint sdf_vertex_buffer = 0;
+		GLuint sdf_vertex_array = 0;
+		GLuint sdf_index_buffer = 0;
+		bool sdf_is_lines = false;
+	};
+
+	RID_Owner<OccluderPolygon> occluder_polygon_owner;
+
+	void _update_shadow_atlas();
+
+	struct {
+		CanvasOcclusionShaderGLES3 shader;
+		RID shader_version;
+	} shadow_render;
+
 	struct LightUniform {
 		float matrix[8]; //light to texture coordinate matrix
 		float shadow_matrix[8]; //light to shadow coordinate matrix
@@ -153,9 +184,9 @@ public:
 	};
 
 	struct PolygonBuffers {
-		GLuint vertex_buffer;
-		GLuint vertex_array;
-		GLuint index_buffer;
+		GLuint vertex_buffer = 0;
+		GLuint vertex_array = 0;
+		GLuint index_buffer = 0;
 		int count = 0;
 		bool color_disabled = false;
 		Color color;
@@ -265,6 +296,11 @@ public:
 
 		LightUniform *light_uniforms = nullptr;
 
+		GLuint shadow_texture = 0;
+		GLuint shadow_depth_buffer = 0;
+		GLuint shadow_fb = 0;
+		int shadow_texture_size = 2048;
+
 		bool using_directional_lights = false;
 
 		RID current_tex = RID();
@@ -295,9 +331,6 @@ public:
 	void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample);
 
 	void reset_canvas();
-	void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, Projection *p_xform_cache);
-
-	virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) override;
 
 	RID light_create() override;
 	void light_set_texture(RID p_rid, RID p_texture) override;

+ 4 - 2
drivers/gles3/shader_gles3.h

@@ -208,8 +208,10 @@ protected:
 			spec = version->variants[p_variant].lookup_ptr(specialization_default_mask);
 		}
 
-		ERR_FAIL_COND(!spec); // Should never happen
-		ERR_FAIL_COND(!spec->ok); // Should never happen
+		if (!spec || !spec->ok) {
+			WARN_PRINT_ONCE("shader failed to compile, unable to bind shader.");
+			return;
+		}
 
 		glUseProgram(spec->id);
 		current_shader = spec;

+ 2 - 0
drivers/gles3/shaders/SCsub

@@ -17,3 +17,5 @@ if "GLES3_GLSL" in env["BUILDERS"]:
     env.GLES3_GLSL("scene.glsl")
     env.GLES3_GLSL("sky.glsl")
     env.GLES3_GLSL("cubemap_filter.glsl")
+    env.GLES3_GLSL("canvas_occlusion.glsl")
+    env.GLES3_GLSL("canvas_sdf.glsl")

+ 148 - 1
drivers/gles3/shaders/canvas.glsl

@@ -10,6 +10,7 @@ mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING
 #[specializations]
 
 DISABLE_LIGHTING = false
+USE_RGBA_SHADOWS = false
 
 #[vertex]
 
@@ -213,8 +214,8 @@ void main() {
 
 #ifndef DISABLE_LIGHTING
 uniform sampler2D atlas_texture; //texunit:-2
+uniform sampler2D shadow_atlas_texture; //texunit:-3
 #endif // DISABLE_LIGHTING
-//uniform sampler2D shadow_atlas_texture; //texunit:-3
 uniform sampler2D screen_texture; //texunit:-4
 uniform sampler2D sdf_texture; //texunit:-5
 uniform sampler2D normal_texture; //texunit:-6
@@ -245,6 +246,35 @@ layout(std140) uniform MaterialUniforms{
 #endif
 
 #GLOBALS
+
+float vec4_to_float(vec4 p_vec) {
+	return dot(p_vec, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0;
+}
+
+vec2 screen_uv_to_sdf(vec2 p_uv) {
+	return screen_to_sdf * p_uv;
+}
+
+float texture_sdf(vec2 p_sdf) {
+	vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw;
+	float d = vec4_to_float(texture(sdf_texture, uv));
+	d *= SDF_MAX_LENGTH;
+	return d * tex_to_sdf;
+}
+
+vec2 texture_sdf_normal(vec2 p_sdf) {
+	vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw;
+
+	const float EPSILON = 0.001;
+	return normalize(vec2(
+			vec4_to_float(texture(sdf_texture, uv + vec2(EPSILON, 0.0))) - vec4_to_float(texture(sdf_texture, uv - vec2(EPSILON, 0.0))),
+			vec4_to_float(texture(sdf_texture, uv + vec2(0.0, EPSILON))) - vec4_to_float(texture(sdf_texture, uv - vec2(0.0, EPSILON)))));
+}
+
+vec2 sdf_to_screen_uv(vec2 p_sdf) {
+	return p_sdf * sdf_to_screen;
+}
+
 #ifndef DISABLE_LIGHTING
 #ifdef LIGHT_CODE_USED
 
@@ -299,6 +329,70 @@ vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 lig
 	}
 }
 
+#ifdef USE_RGBA_SHADOWS
+
+#define SHADOW_DEPTH(m_uv) (dot(textureLod(shadow_atlas_texture, (m_uv), 0.0), vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0)
+
+#else
+
+#define SHADOW_DEPTH(m_uv) (textureLod(shadow_atlas_texture, (m_uv), 0.0).r)
+
+#endif
+
+#define SHADOW_TEST(m_uv)                              \
+	{                                                  \
+		highp float sd = SHADOW_DEPTH(m_uv);           \
+		shadow += step(sd, shadow_uv.z / shadow_uv.w); \
+	}
+
+//float distance = length(shadow_pos);
+vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv
+#ifdef LIGHT_CODE_USED
+		,
+		vec3 shadow_modulate
+#endif
+) {
+	float shadow = 0.0;
+	uint shadow_mode = light_array[light_base].flags & LIGHT_FLAGS_FILTER_MASK;
+
+	if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) {
+		SHADOW_TEST(shadow_uv.xy);
+	} else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) {
+		vec2 shadow_pixel_size = vec2(light_array[light_base].shadow_pixel_size, 0.0);
+		SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 2.0);
+		SHADOW_TEST(shadow_uv.xy - shadow_pixel_size);
+		SHADOW_TEST(shadow_uv.xy);
+		SHADOW_TEST(shadow_uv.xy + shadow_pixel_size);
+		SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 2.0);
+		shadow /= 5.0;
+	} else { //PCF13
+		vec2 shadow_pixel_size = vec2(light_array[light_base].shadow_pixel_size, 0.0);
+		SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 6.0);
+		SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 5.0);
+		SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 4.0);
+		SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 3.0);
+		SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 2.0);
+		SHADOW_TEST(shadow_uv.xy - shadow_pixel_size);
+		SHADOW_TEST(shadow_uv.xy);
+		SHADOW_TEST(shadow_uv.xy + shadow_pixel_size);
+		SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 2.0);
+		SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 3.0);
+		SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 4.0);
+		SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 5.0);
+		SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 6.0);
+		shadow /= 13.0;
+	}
+
+	vec4 shadow_color = unpackUnorm4x8(light_array[light_base].shadow_color);
+#ifdef LIGHT_CODE_USED
+	shadow_color.rgb *= shadow_modulate;
+#endif
+
+	shadow_color.a *= light_color.a; //respect light alpha
+
+	return mix(light_color, shadow_color, shadow);
+}
+
 void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) {
 	uint blend_mode = light_array[light_base].flags & LIGHT_FLAGS_BLEND_MASK;
 
@@ -527,6 +621,19 @@ void main() {
 		}
 #endif
 
+		if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
+			vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_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.
+
+			vec4 shadow_uv = vec4(shadow_pos.x, light_array[light_base].shadow_y_ofs, shadow_pos.y * light_array[light_base].shadow_zfar_inv, 1.0);
+
+			light_color = light_shadow_compute(light_base, light_color, shadow_uv
+#ifdef LIGHT_CODE_USED
+					,
+					shadow_modulate.rgb
+#endif
+			);
+		}
+
 		light_blend_compute(light_base, light_color, color.rgb);
 	}
 
@@ -584,6 +691,46 @@ void main() {
 			light_color.a = 0.0;
 		}
 
+		if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
+			vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_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 pos_norm = normalize(shadow_pos);
+			vec2 pos_abs = abs(pos_norm);
+			vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y);
+			vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot?
+			float tex_ofs;
+			float dist;
+			if (pos_rot.y > 0.0) {
+				if (pos_rot.x > 0.0) {
+					tex_ofs = pos_box.y * 0.125 + 0.125;
+					dist = shadow_pos.x;
+				} else {
+					tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125);
+					dist = shadow_pos.y;
+				}
+			} else {
+				if (pos_rot.x < 0.0) {
+					tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125);
+					dist = -shadow_pos.x;
+				} else {
+					tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125);
+					dist = -shadow_pos.y;
+				}
+			}
+
+			dist *= light_array[light_base].shadow_zfar_inv;
+
+			//float distance = length(shadow_pos);
+			vec4 shadow_uv = vec4(tex_ofs, light_array[light_base].shadow_y_ofs, dist, 1.0);
+
+			light_color = light_shadow_compute(light_base, light_color, shadow_uv
+#ifdef LIGHT_CODE_USED
+					,
+					shadow_modulate.rgb
+#endif
+			);
+		}
+
 		light_blend_compute(light_base, light_color, color.rgb);
 	}
 #endif

+ 68 - 0
drivers/gles3/shaders/canvas_occlusion.glsl

@@ -0,0 +1,68 @@
+/* clang-format off */
+#[modes]
+
+mode_sdf =
+mode_shadow = #define MODE_SHADOW
+mode_shadow_RGBA = #define MODE_SHADOW \n#define USE_RGBA_SHADOWS
+
+#[specializations]
+
+#[vertex]
+
+layout(location = 0) in vec3 vertex;
+
+uniform highp mat4 projection;
+uniform highp vec4 modelview1;
+uniform highp vec4 modelview2;
+uniform highp vec2 direction;
+uniform highp float z_far;
+
+#ifdef MODE_SHADOW
+out float depth;
+#endif
+
+void main() {
+	highp vec4 vtx = vec4(vertex, 1.0) * mat4(modelview1, modelview2, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
+
+#ifdef MODE_SHADOW
+	depth = dot(direction, vtx.xy);
+#endif
+	gl_Position = projection * vtx;
+}
+
+#[fragment]
+
+
+uniform highp mat4 projection;
+uniform highp vec4 modelview1;
+uniform highp vec4 modelview2;
+uniform highp vec2 direction;
+uniform highp float z_far;
+
+#ifdef MODE_SHADOW
+in highp float depth;
+#endif
+
+#ifdef USE_RGBA_SHADOWS
+layout(location = 0) out lowp vec4 out_buf;
+#else
+layout(location = 0) out highp float out_buf;
+#endif
+
+void main() {
+    float out_depth = 1.0;
+
+#ifdef MODE_SHADOW
+	out_depth = depth / z_far;
+#endif
+
+#ifdef USE_RGBA_SHADOWS
+	out_depth = clamp(out_depth, -1.0, 1.0);
+	out_depth = out_depth * 0.5 + 0.5;
+	highp vec4 comp = fract(out_depth * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0));
+	comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
+	out_buf = comp;
+#else
+	out_buf = out_depth;
+#endif
+}

+ 205 - 0
drivers/gles3/shaders/canvas_sdf.glsl

@@ -0,0 +1,205 @@
+/* clang-format off */
+#[modes]
+
+mode_load = #define MODE_LOAD
+mode_load_shrink = #define MODE_LOAD_SHRINK
+mode_process = #define MODE_PROCESS
+mode_store = #define MODE_STORE
+mode_store_shrink = #define MODE_STORE_SHRINK
+
+#[specializations]
+
+#[vertex]
+
+layout(location = 0) in vec2 vertex_attrib;
+
+/* clang-format on */
+
+uniform ivec2 size;
+uniform int stride;
+uniform int shift;
+uniform ivec2 base_size;
+
+void main() {
+	gl_Position = vec4(vertex_attrib, 1.0, 1.0);
+}
+
+/* clang-format off */
+#[fragment]
+
+#define SDF_MAX_LENGTH 16384.0
+
+#if defined(MODE_LOAD) || defined(MODE_LOAD_SHRINK)
+uniform lowp sampler2D src_pixels;//texunit:0
+#else
+uniform highp isampler2D src_process;//texunit:0
+#endif
+
+uniform	ivec2 size;
+uniform	int stride;
+uniform	int shift;
+uniform	ivec2 base_size;
+
+#if defined(MODE_LOAD) || defined(MODE_LOAD_SHRINK) || defined(MODE_PROCESS)
+layout(location = 0) out ivec4 distance_field;
+#else
+layout(location = 0) out vec4 distance_field;
+#endif
+
+vec4 float_to_vec4(float p_float) {
+    highp vec4 comp = fract(p_float * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0));
+	comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
+	return comp;
+}
+
+void main() {
+	ivec2 pos = ivec2(gl_FragCoord.xy);
+
+#ifdef MODE_LOAD
+
+	bool solid = texelFetch(src_pixels, pos, 0).r > 0.5;
+	distance_field = solid ? ivec4(ivec2(-32767), 0, 0) : ivec4(ivec2(32767), 0, 0);
+#endif
+
+#ifdef MODE_LOAD_SHRINK
+
+	int s = 1 << shift;
+	ivec2 base = pos << shift;
+	ivec2 center = base + ivec2(shift);
+
+	ivec2 rel = ivec2(32767);
+	float d = 1e20;
+	int found = 0;
+	int solid_found = 0;
+	for (int i = 0; i < s; i++) {
+		for (int j = 0; j < s; j++) {
+			ivec2 src_pos = base + ivec2(i, j);
+			if (any(greaterThanEqual(src_pos, base_size))) {
+				continue;
+			}
+			bool solid = texelFetch(src_pixels, src_pos, 0).r > 0.5;
+			if (solid) {
+				float dist = length(vec2(src_pos - center));
+				if (dist < d) {
+					d = dist;
+					rel = src_pos;
+				}
+				solid_found++;
+			}
+			found++;
+		}
+	}
+
+	if (solid_found == found) {
+		//mark solid only if all are solid
+		rel = ivec2(-32767);
+	}
+
+	distance_field = ivec4(rel, 0, 0);
+#endif
+
+#ifdef MODE_PROCESS
+
+	ivec2 base = pos << shift;
+	ivec2 center = base + ivec2(shift);
+
+	ivec2 rel = texelFetch(src_process, pos, 0).xy;
+
+	bool solid = rel.x < 0;
+
+	if (solid) {
+		rel = -rel - ivec2(1);
+	}
+
+	if (center != rel) {
+		//only process if it does not point to itself
+		const int ofs_table_size = 8;
+		const ivec2 ofs_table[ofs_table_size] = ivec2[](
+				ivec2(-1, -1),
+				ivec2(0, -1),
+				ivec2(+1, -1),
+
+				ivec2(-1, 0),
+				ivec2(+1, 0),
+
+				ivec2(-1, +1),
+				ivec2(0, +1),
+				ivec2(+1, +1));
+
+		float dist = length(vec2(rel - center));
+		for (int i = 0; i < ofs_table_size; i++) {
+			ivec2 src_pos = pos + ofs_table[i] * stride;
+			if (any(lessThan(src_pos, ivec2(0))) || any(greaterThanEqual(src_pos, size))) {
+				continue;
+			}
+			ivec2 src_rel = texelFetch(src_process, src_pos, 0).xy;
+			bool src_solid = src_rel.x < 0;
+			if (src_solid) {
+				src_rel = -src_rel - ivec2(1);
+			}
+
+			if (src_solid != solid) {
+				src_rel = ivec2(src_pos << shift); //point to itself if of different type
+			}
+
+			float src_dist = length(vec2(src_rel - center));
+			if (src_dist < dist) {
+				dist = src_dist;
+				rel = src_rel;
+			}
+		}
+	}
+
+	if (solid) {
+		rel = -rel - ivec2(1);
+	}
+
+	distance_field = ivec4(rel, 0, 0);
+#endif
+
+#ifdef MODE_STORE
+
+	ivec2 rel = texelFetch(src_process, pos, 0).xy;
+
+	bool solid = rel.x < 0;
+
+	if (solid) {
+		rel = -rel - ivec2(1);
+	}
+
+	float d = length(vec2(rel - pos));
+
+	if (solid) {
+		d = -d;
+	}
+
+	d /= SDF_MAX_LENGTH;
+	d = clamp(d, -1.0, 1.0);
+	distance_field = float_to_vec4(d*0.5+0.5);
+
+#endif
+
+#ifdef MODE_STORE_SHRINK
+
+	ivec2 base = pos << shift;
+	ivec2 center = base + ivec2(shift);
+
+	ivec2 rel = texelFetch(src_process, pos, 0).xy;
+
+	bool solid = rel.x < 0;
+
+	if (solid) {
+		rel = -rel - ivec2(1);
+	}
+
+	float d = length(vec2(rel - center));
+
+	if (solid) {
+		d = -d;
+	}
+	d /= SDF_MAX_LENGTH;
+	d = clamp(d, -1.0, 1.0);
+	distance_field = float_to_vec4(d*0.5+0.5);
+
+#endif
+}

+ 0 - 60
drivers/gles3/shaders/canvas_shadow.glsl

@@ -1,60 +0,0 @@
-/* clang-format off */
-[vertex]
-
-#ifdef USE_GLES_OVER_GL
-#define lowp
-#define mediump
-#define highp
-#else
-precision highp float;
-precision highp int;
-#endif
-
-layout(location = 0) in highp vec3 vertex;
-
-uniform highp mat4 projection_matrix;
-/* clang-format on */
-uniform highp mat4 light_matrix;
-uniform highp mat4 model_matrix;
-uniform highp float distance_norm;
-
-out highp vec4 position_interp;
-
-void main() {
-	gl_Position = projection_matrix * (light_matrix * (model_matrix * vec4(vertex, 1.0)));
-	position_interp = gl_Position;
-}
-
-/* clang-format off */
-[fragment]
-
-#ifdef USE_GLES_OVER_GL
-#define lowp
-#define mediump
-#define highp
-#else
-#if defined(USE_HIGHP_PRECISION)
-precision highp float;
-precision highp int;
-#else
-precision mediump float;
-precision mediump int;
-#endif
-#endif
-
-in highp vec4 position_interp;
-/* clang-format on */
-
-void main() {
-	highp float depth = ((position_interp.z / position_interp.w) + 1.0) * 0.5 + 0.0; // bias
-
-#ifdef USE_RGBA_SHADOWS
-
-	highp vec4 comp = fract(depth * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0));
-	comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
-	frag_color = comp;
-#else
-
-	frag_color = vec4(depth);
-#endif
-}

+ 2 - 12
drivers/gles3/storage/config.cpp

@@ -71,7 +71,7 @@ Config::Config() {
 	s3tc_supported = true;
 	rgtc_supported = true; //RGTC - core since OpenGL version 3.0
 #else
-	float_texture_supported = extensions.has("GL_ARB_texture_float") || extensions.has("GL_OES_texture_float");
+	float_texture_supported = extensions.has("GL_EXT_color_buffer_float");
 	etc2_supported = true;
 #if defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
 	// Some Android devices report support for S3TC but we don't expect that and don't export the textures.
@@ -84,24 +84,14 @@ Config::Config() {
 	rgtc_supported = extensions.has("GL_EXT_texture_compression_rgtc") || extensions.has("GL_ARB_texture_compression_rgtc") || extensions.has("EXT_texture_compression_rgtc");
 #endif
 
-#ifdef GLES_OVER_GL
-	use_rgba_2d_shadows = false;
-#else
-	use_rgba_2d_shadows = !(float_texture_supported && extensions.has("GL_EXT_texture_rg"));
-#endif
-
 	glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &max_vertex_texture_image_units);
 	glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_image_units);
 	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
 	glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size);
-	glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &max_viewport_size);
+	glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size);
 
 	glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_offset_alignment);
 
-	// the use skeleton software path should be used if either float texture is not supported,
-	// OR max_vertex_texture_image_units is zero
-	use_skeleton_software = (float_texture_supported == false) || (max_vertex_texture_image_units == 0);
-
 	support_anisotropic_filter = extensions.has("GL_EXT_texture_filter_anisotropic");
 	if (support_anisotropic_filter) {
 		glGetFloatv(_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic_level);

+ 1 - 3
drivers/gles3/storage/config.h

@@ -56,15 +56,13 @@ private:
 
 public:
 	bool use_nearest_mip_filter = false;
-	bool use_skeleton_software = false;
 	bool use_depth_prepass = true;
-	bool use_rgba_2d_shadows = false;
 
 	int max_vertex_texture_image_units = 0;
 	int max_texture_image_units = 0;
 	int max_texture_size = 0;
+	int max_viewport_size[2] = { 0, 0 };
 	int max_uniform_buffer_size = 0;
-	int max_viewport_size = 0;
 	int max_renderable_elements = 0;
 	int max_renderable_lights = 0;
 	int max_lights_per_object = 0;

+ 266 - 50
drivers/gles3/storage/texture_storage.cpp

@@ -207,6 +207,11 @@ TextureStorage::TextureStorage() {
 
 	glBindTexture(GL_TEXTURE_2D, 0);
 
+	{
+		sdf_shader.shader.initialize();
+		sdf_shader.shader_version = sdf_shader.shader.version_create();
+	}
+
 #ifdef GLES_OVER_GL
 	glEnable(GL_PROGRAM_POINT_SIZE);
 #endif
@@ -222,6 +227,7 @@ TextureStorage::~TextureStorage() {
 	texture_atlas.texture = 0;
 	glDeleteFramebuffers(1, &texture_atlas.framebuffer);
 	texture_atlas.framebuffer = 0;
+	sdf_shader.shader.version_free(sdf_shader.shader_version);
 }
 
 //TODO, move back to storage
@@ -276,55 +282,6 @@ void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS:
 	ct->texture_repeat = p_repeat;
 }
 
-/* CANVAS SHADOW */
-
-RID TextureStorage::canvas_light_shadow_buffer_create(int p_width) {
-	Config *config = Config::get_singleton();
-	CanvasLightShadow *cls = memnew(CanvasLightShadow);
-
-	if (p_width > config->max_texture_size) {
-		p_width = config->max_texture_size;
-	}
-
-	cls->size = p_width;
-	cls->height = 16;
-
-	glActiveTexture(GL_TEXTURE0);
-
-	glGenFramebuffers(1, &cls->fbo);
-	glBindFramebuffer(GL_FRAMEBUFFER, cls->fbo);
-
-	glGenRenderbuffers(1, &cls->depth);
-	glBindRenderbuffer(GL_RENDERBUFFER, cls->depth);
-	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, cls->size, cls->height);
-	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, cls->depth);
-
-	glGenTextures(1, &cls->distance);
-	glBindTexture(GL_TEXTURE_2D, cls->distance);
-	if (config->use_rgba_2d_shadows) {
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, cls->size, cls->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
-	} else {
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, cls->size, cls->height, 0, GL_RED, GL_FLOAT, nullptr);
-	}
-
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, cls->distance, 0);
-
-	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-	//printf("errnum: %x\n",status);
-	glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
-
-	if (status != GL_FRAMEBUFFER_COMPLETE) {
-		memdelete(cls);
-		ERR_FAIL_COND_V(status != GL_FRAMEBUFFER_COMPLETE, RID());
-	}
-
-	return canvas_light_shadow_owner.make_rid(cls);
-}
-
 /* Texture API */
 
 Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool p_force_decompress) const {
@@ -1599,6 +1556,7 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
 		rt->backbuffer = 0;
 		rt->backbuffer_fbo = 0;
 	}
+	_render_target_clear_sdf(rt);
 }
 
 RID TextureStorage::render_target_create() {
@@ -1784,13 +1742,271 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) {
 }
 
 void TextureStorage::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND(!rt);
+	if (rt->sdf_oversize == p_size && rt->sdf_scale == p_scale) {
+		return;
+	}
+
+	rt->sdf_oversize = p_size;
+	rt->sdf_scale = p_scale;
+
+	_render_target_clear_sdf(rt);
+}
+
+Rect2i TextureStorage::_render_target_get_sdf_rect(const RenderTarget *rt) const {
+	Size2i margin;
+	int scale;
+	switch (rt->sdf_oversize) {
+		case RS::VIEWPORT_SDF_OVERSIZE_100_PERCENT: {
+			scale = 100;
+		} break;
+		case RS::VIEWPORT_SDF_OVERSIZE_120_PERCENT: {
+			scale = 120;
+		} break;
+		case RS::VIEWPORT_SDF_OVERSIZE_150_PERCENT: {
+			scale = 150;
+		} break;
+		case RS::VIEWPORT_SDF_OVERSIZE_200_PERCENT: {
+			scale = 200;
+		} break;
+		default: {
+		}
+	}
+
+	margin = (rt->size * scale / 100) - rt->size;
+
+	Rect2i r(Vector2i(), rt->size);
+	r.position -= margin;
+	r.size += margin * 2;
+
+	return r;
 }
 
 Rect2i TextureStorage::render_target_get_sdf_rect(RID p_render_target) const {
-	return Rect2i();
+	const RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, Rect2i());
+
+	return _render_target_get_sdf_rect(rt);
 }
 
 void TextureStorage::render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND(!rt);
+
+	rt->sdf_enabled = p_enabled;
+}
+
+bool TextureStorage::render_target_is_sdf_enabled(RID p_render_target) const {
+	const RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, false);
+
+	return rt->sdf_enabled;
+}
+
+GLuint TextureStorage::render_target_get_sdf_texture(RID p_render_target) {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, 0);
+	if (rt->sdf_texture_read == 0) {
+		Texture *texture = texture_owner.get_or_null(default_gl_textures[DEFAULT_GL_TEXTURE_BLACK]);
+		return texture->tex_id;
+	}
+
+	return rt->sdf_texture_read;
+}
+
+void TextureStorage::_render_target_allocate_sdf(RenderTarget *rt) {
+	ERR_FAIL_COND(rt->sdf_texture_write_fb != 0);
+
+	Size2i size = _render_target_get_sdf_rect(rt).size;
+
+	glGenTextures(1, &rt->sdf_texture_write);
+	glActiveTexture(GL_TEXTURE0);
+	glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_write);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, size.width, size.height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+	glGenFramebuffers(1, &rt->sdf_texture_write_fb);
+	glBindFramebuffer(GL_FRAMEBUFFER, rt->sdf_texture_write_fb);
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->sdf_texture_write, 0);
+
+	int scale;
+	switch (rt->sdf_scale) {
+		case RS::VIEWPORT_SDF_SCALE_100_PERCENT: {
+			scale = 100;
+		} break;
+		case RS::VIEWPORT_SDF_SCALE_50_PERCENT: {
+			scale = 50;
+		} break;
+		case RS::VIEWPORT_SDF_SCALE_25_PERCENT: {
+			scale = 25;
+		} break;
+		default: {
+			scale = 100;
+		} break;
+	}
+
+	rt->process_size = size * scale / 100;
+	rt->process_size.x = MAX(rt->process_size.x, 1);
+	rt->process_size.y = MAX(rt->process_size.y, 1);
+
+	glGenTextures(2, rt->sdf_texture_process);
+	glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[0]);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16I, rt->process_size.width, rt->process_size.height, 0, GL_RG_INTEGER, GL_SHORT, nullptr);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+	glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[1]);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16I, rt->process_size.width, rt->process_size.height, 0, GL_RG_INTEGER, GL_SHORT, nullptr);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+	glGenTextures(1, &rt->sdf_texture_read);
+	glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_read);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, rt->process_size.width, rt->process_size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+void TextureStorage::_render_target_clear_sdf(RenderTarget *rt) {
+	if (rt->sdf_texture_write_fb != 0) {
+		glDeleteTextures(1, &rt->sdf_texture_read);
+		glDeleteTextures(1, &rt->sdf_texture_write);
+		glDeleteTextures(2, rt->sdf_texture_process);
+		glDeleteFramebuffers(1, &rt->sdf_texture_write_fb);
+		rt->sdf_texture_read = 0;
+		rt->sdf_texture_write = 0;
+		rt->sdf_texture_process[0] = 0;
+		rt->sdf_texture_process[1] = 0;
+		rt->sdf_texture_write_fb = 0;
+	}
+}
+
+GLuint TextureStorage::render_target_get_sdf_framebuffer(RID p_render_target) {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND_V(!rt, 0);
+
+	if (rt->sdf_texture_write_fb == 0) {
+		_render_target_allocate_sdf(rt);
+	}
+
+	return rt->sdf_texture_write_fb;
+}
+void TextureStorage::render_target_sdf_process(RID p_render_target) {
+	CopyEffects *copy_effects = CopyEffects::get_singleton();
+
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_COND(!rt);
+	ERR_FAIL_COND(rt->sdf_texture_write_fb == 0);
+
+	Rect2i r = _render_target_get_sdf_rect(rt);
+
+	Size2i size = r.size;
+	int32_t shift = 0;
+
+	bool shrink = false;
+
+	switch (rt->sdf_scale) {
+		case RS::VIEWPORT_SDF_SCALE_50_PERCENT: {
+			size[0] >>= 1;
+			size[1] >>= 1;
+			shift = 1;
+			shrink = true;
+		} break;
+		case RS::VIEWPORT_SDF_SCALE_25_PERCENT: {
+			size[0] >>= 2;
+			size[1] >>= 2;
+			shift = 2;
+			shrink = true;
+		} break;
+		default: {
+		};
+	}
+
+	GLuint temp_fb;
+	glGenFramebuffers(1, &temp_fb);
+	glBindFramebuffer(GL_FRAMEBUFFER, temp_fb);
+
+	// Load
+	CanvasSdfShaderGLES3::ShaderVariant variant = shrink ? CanvasSdfShaderGLES3::MODE_LOAD_SHRINK : CanvasSdfShaderGLES3::MODE_LOAD;
+	sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant);
+	sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::BASE_SIZE, r.size, sdf_shader.shader_version, variant);
+	sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SIZE, size, sdf_shader.shader_version, variant);
+	sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, 0, sdf_shader.shader_version, variant);
+	sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SHIFT, shift, sdf_shader.shader_version, variant);
+
+	glActiveTexture(GL_TEXTURE0);
+	glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_write);
+
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->sdf_texture_process[0], 0);
+	glViewport(0, 0, size.width, size.height);
+	glEnable(GL_SCISSOR_TEST);
+	glScissor(0, 0, size.width, size.height);
+
+	copy_effects->draw_screen_triangle();
+
+	// Process
+
+	int stride = nearest_power_of_2_templated(MAX(size.width, size.height) / 2);
+
+	variant = CanvasSdfShaderGLES3::MODE_PROCESS;
+	sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant);
+	sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::BASE_SIZE, r.size, sdf_shader.shader_version, variant);
+	sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SIZE, size, sdf_shader.shader_version, variant);
+	sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, stride, sdf_shader.shader_version, variant);
+	sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SHIFT, shift, sdf_shader.shader_version, variant);
+
+	bool swap = false;
+
+	//jumpflood
+	while (stride > 0) {
+		glBindTexture(GL_TEXTURE_2D, 0);
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->sdf_texture_process[swap ? 0 : 1], 0);
+		glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[swap ? 1 : 0]);
+
+		sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, stride, sdf_shader.shader_version, variant);
+
+		copy_effects->draw_screen_triangle();
+
+		stride /= 2;
+		swap = !swap;
+	}
+
+	// Store
+	variant = shrink ? CanvasSdfShaderGLES3::MODE_STORE_SHRINK : CanvasSdfShaderGLES3::MODE_STORE;
+	sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant);
+	sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::BASE_SIZE, r.size, sdf_shader.shader_version, variant);
+	sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SIZE, size, sdf_shader.shader_version, variant);
+	sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, stride, sdf_shader.shader_version, variant);
+	sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SHIFT, shift, sdf_shader.shader_version, variant);
+
+	glBindTexture(GL_TEXTURE_2D, 0);
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->sdf_texture_read, 0);
+	glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[swap ? 1 : 0]);
+
+	copy_effects->draw_screen_triangle();
+
+	glBindTexture(GL_TEXTURE_2D, 0);
+	glBindFramebuffer(GL_FRAMEBUFFER, system_fbo);
+	glDeleteFramebuffers(1, &temp_fb);
+	glDisable(GL_SCISSOR_TEST);
 }
 
 void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps) {

+ 26 - 32
drivers/gles3/storage/texture_storage.h

@@ -39,6 +39,8 @@
 #include "servers/rendering/renderer_compositor.h"
 #include "servers/rendering/storage/texture_storage.h"
 
+#include "../shaders/canvas_sdf.glsl.gen.h"
+
 // This must come first to avoid windows.h mess
 #include "platform_config.h"
 #ifndef OPENGL_INCLUDE_H
@@ -84,18 +86,8 @@ namespace GLES3 {
 
 #define _GL_TEXTURE_EXTERNAL_OES 0x8D65
 
-#ifdef GLES_OVER_GL
-#define _GL_HALF_FLOAT_OES 0x140B
-#else
-#define _GL_HALF_FLOAT_OES 0x8D61
-#endif
-
 #define _EXT_TEXTURE_CUBE_MAP_SEAMLESS 0x884F
 
-#define _RED_OES 0x1903
-
-#define _DEPTH_COMPONENT24_OES 0x81A6
-
 #ifndef GLES_OVER_GL
 #define glClearDepth glClearDepthf
 #endif //!GLES_OVER_GL
@@ -128,17 +120,6 @@ struct CanvasTexture {
 	RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
 };
 
-/* CANVAS SHADOW */
-
-struct CanvasLightShadow {
-	RID self;
-	int size;
-	int height;
-	GLuint fbo;
-	GLuint depth;
-	GLuint distance; //for older devices
-};
-
 struct RenderTarget;
 
 struct Texture {
@@ -337,6 +318,15 @@ struct RenderTarget {
 	GLuint color_type = GL_UNSIGNED_BYTE;
 	Image::Format image_format = Image::FORMAT_RGBA8;
 
+	GLuint sdf_texture_write = 0;
+	GLuint sdf_texture_write_fb = 0;
+	GLuint sdf_texture_process[2] = { 0, 0 };
+	GLuint sdf_texture_read = 0;
+	RS::ViewportSDFOversize sdf_oversize = RS::VIEWPORT_SDF_OVERSIZE_120_PERCENT;
+	RS::ViewportSDFScale sdf_scale = RS::VIEWPORT_SDF_SCALE_50_PERCENT;
+	Size2i process_size;
+	bool sdf_enabled = false;
+
 	bool is_transparent = false;
 	bool direct_to_screen = false;
 
@@ -362,10 +352,6 @@ private:
 
 	RID_Owner<CanvasTexture, true> canvas_texture_owner;
 
-	/* CANVAS SHADOW */
-
-	RID_PtrOwner<CanvasLightShadow> canvas_light_shadow_owner;
-
 	/* Texture API */
 
 	mutable RID_Owner<Texture> texture_owner;
@@ -411,6 +397,14 @@ private:
 	void _clear_render_target(RenderTarget *rt);
 	void _update_render_target(RenderTarget *rt);
 	void _create_render_target_backbuffer(RenderTarget *rt);
+	void _render_target_allocate_sdf(RenderTarget *rt);
+	void _render_target_clear_sdf(RenderTarget *rt);
+	Rect2i _render_target_get_sdf_rect(const RenderTarget *rt) const;
+
+	struct RenderTargetSDF {
+		CanvasSdfShaderGLES3 shader;
+		RID shader_version;
+	} sdf_shader;
 
 public:
 	static TextureStorage *get_singleton();
@@ -437,10 +431,6 @@ public:
 	virtual void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override;
 	virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override;
 
-	/* CANVAS SHADOW */
-
-	RID canvas_light_shadow_buffer_create(int p_width);
-
 	/* Texture API */
 
 	Texture *get_texture(RID p_rid) {
@@ -586,9 +576,13 @@ public:
 	void render_target_disable_clear_request(RID p_render_target) override;
 	void render_target_do_clear_request(RID p_render_target) override;
 
-	void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override;
-	Rect2i render_target_get_sdf_rect(RID p_render_target) const override;
-	void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) override;
+	virtual void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override;
+	virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const override;
+	GLuint render_target_get_sdf_texture(RID p_render_target);
+	GLuint render_target_get_sdf_framebuffer(RID p_render_target);
+	void render_target_sdf_process(RID p_render_target);
+	virtual void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) override;
+	bool render_target_is_sdf_enabled(RID p_render_target) const;
 
 	void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps);
 	void render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color);

+ 1 - 1
drivers/gles3/storage/utilities.cpp

@@ -362,7 +362,7 @@ Size2i Utilities::get_maximum_viewport_size() const {
 		return Size2i();
 	}
 
-	return Size2i(config->max_viewport_size, config->max_viewport_size);
+	return Size2i(config->max_viewport_size[0], config->max_viewport_size[1]);
 }
 
 #endif // GLES3_ENABLED

+ 0 - 1
servers/rendering/dummy/rasterizer_canvas_dummy.h

@@ -39,7 +39,6 @@ public:
 	void free_polygon(PolygonID p_polygon) override {}
 
 	void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) override {}
-	void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) override {}
 
 	RID light_create() override { return RID(); }
 	void light_set_texture(RID p_rid, RID p_texture) override {}

+ 0 - 1
servers/rendering/renderer_canvas_render.h

@@ -476,7 +476,6 @@ public:
 	};
 
 	virtual void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) = 0;
-	virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) = 0;
 
 	struct LightOccluderInstance {
 		bool enabled;

+ 2 - 1
servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp

@@ -1914,11 +1914,12 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve
 			}
 		}
 
-		//if same buffer len is being set, just use BufferSubData to avoid a pipeline flush
+		//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
+			// TODO: geometry is always of length lc * 6 * sizeof(float), so in doubles builds this will receive half the data it needs
 			oc->vertex_buffer = RD::get_singleton()->vertex_buffer_create(lc * 6 * sizeof(real_t), geometry);
 
 			Vector<RID> buffer;

+ 0 - 2
servers/rendering/renderer_rd/renderer_canvas_render_rd.h

@@ -459,8 +459,6 @@ public:
 
 	void 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, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used);
 
-	void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) {}
-
 	virtual void set_shadow_texture_size(int p_size);
 
 	void set_time(double p_time);