Browse Source

Merge pull request #36342 from m4gr3d/external_texture_support_for_godot_3_2

Add support for opengl external textures
Rémi Verschelde 5 năm trước cách đây
mục cha
commit
cc70b2fa0a

+ 25 - 0
doc/classes/ExternalTexture.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ExternalTexture" inherits="Texture" category="Core" version="3.2">
+	<brief_description>
+		Adds support for external textures as defined by https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt
+	</brief_description>
+	<description>
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+		<method name="get_external_texture_id" qualifiers="const">
+			<return type="int"/>
+			<description>
+				Returns the external texture name.
+			</description>
+		</method>
+	</methods>
+	<members>
+		<member name="size" type="Vector2" setter="set_size" getter="get_size" default="Vector2( 1, 1 )">
+			External texture size.
+		</member>
+	</members>
+	<constants>
+	</constants>
+</class>

+ 4 - 0
doc/classes/Shader.xml

@@ -55,6 +55,10 @@
 		<member name="code" type="String" setter="set_code" getter="get_code" default="&quot;&quot;">
 			Returns the shader's code as the user has written it, not the full generated code used internally.
 		</member>
+		<member name="custom_defines" type="String" setter="set_custom_defines" getter="get_custom_defines" default="&quot;&quot;">
+			Returns the shader's custom defines. Custom defines can be used in Godot to add GLSL preprocessor directives (e.g: extensions) required for the shader logic.
+			[b]Note:[/b] Custom defines are not validated by the Godot shader parser, so care should be taken when using them.
+		</member>
 	</members>
 	<constants>
 		<constant name="MODE_SPATIAL" value="0" enum="Mode">

+ 4 - 0
drivers/dummy/rasterizer_dummy.h

@@ -262,6 +262,10 @@ public:
 	void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture) {}
 	RID shader_get_default_texture_param(RID p_shader, const StringName &p_name) const { return RID(); }
 
+	void shader_add_custom_define(RID p_shader, const String &p_define) {}
+	void shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const {}
+	void shader_clear_custom_defines(RID p_shader) {}
+
 	/* COMMON MATERIAL API */
 
 	RID material_create() { return RID(); }

+ 77 - 26
drivers/gles2/rasterizer_storage_gles2.cpp

@@ -69,6 +69,8 @@ GLuint RasterizerStorageGLES2::system_fbo = 0;
 #define _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E
 #define _EXT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F
 
+#define _GL_TEXTURE_EXTERNAL_OES 0x8D65
+
 #ifdef GLES_OVER_GL
 #define _GL_HALF_FLOAT_OES 0x140B
 #else
@@ -562,6 +564,10 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_
 			texture->target = GL_TEXTURE_2D;
 			texture->images.resize(1);
 		} break;
+		case VS::TEXTURE_TYPE_EXTERNAL: {
+			texture->target = _GL_TEXTURE_EXTERNAL_OES;
+			texture->images.resize(0);
+		} break;
 		case VS::TEXTURE_TYPE_CUBEMAP: {
 			texture->target = GL_TEXTURE_CUBE_MAP;
 			texture->images.resize(6);
@@ -578,44 +584,59 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_
 		}
 	}
 
-	texture->alloc_width = texture->width;
-	texture->alloc_height = texture->height;
-	texture->resize_to_po2 = false;
-	if (!config.support_npot_repeat_mipmap) {
-		int po2_width = next_power_of_2(p_width);
-		int po2_height = next_power_of_2(p_height);
+	if (p_type != VS::TEXTURE_TYPE_EXTERNAL) {
+		texture->alloc_width = texture->width;
+		texture->alloc_height = texture->height;
+		texture->resize_to_po2 = false;
+		if (!config.support_npot_repeat_mipmap) {
+			int po2_width = next_power_of_2(p_width);
+			int po2_height = next_power_of_2(p_height);
 
-		bool is_po2 = p_width == po2_width && p_height == po2_height;
+			bool is_po2 = p_width == po2_width && p_height == po2_height;
 
-		if (!is_po2 && (p_flags & VS::TEXTURE_FLAG_REPEAT || p_flags & VS::TEXTURE_FLAG_MIPMAPS)) {
+			if (!is_po2 && (p_flags & VS::TEXTURE_FLAG_REPEAT || p_flags & VS::TEXTURE_FLAG_MIPMAPS)) {
 
-			if (p_flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) {
-				//not supported
-				ERR_PRINTS("Streaming texture for non power of 2 or has mipmaps on this hardware: " + texture->path + "'. Mipmaps and repeat disabled.");
-				texture->flags &= ~(VS::TEXTURE_FLAG_REPEAT | VS::TEXTURE_FLAG_MIPMAPS);
-			} else {
-				texture->alloc_height = po2_height;
-				texture->alloc_width = po2_width;
-				texture->resize_to_po2 = true;
+				if (p_flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) {
+					//not supported
+					ERR_PRINT("Streaming texture for non power of 2 or has mipmaps on this hardware: " + texture->path + "'. Mipmaps and repeat disabled.");
+					texture->flags &= ~(VS::TEXTURE_FLAG_REPEAT | VS::TEXTURE_FLAG_MIPMAPS);
+				} else {
+					texture->alloc_height = po2_height;
+					texture->alloc_width = po2_width;
+					texture->resize_to_po2 = true;
+				}
 			}
 		}
-	}
 
-	Image::Format real_format;
-	_get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, format, internal_format, type, compressed, texture->resize_to_po2);
+		Image::Format real_format;
+		_get_gl_image_and_format(Ref<Image>(),
+				texture->format,
+				texture->flags,
+				real_format,
+				format,
+				internal_format,
+				type,
+				compressed,
+				texture->resize_to_po2);
 
-	texture->gl_format_cache = format;
-	texture->gl_type_cache = type;
-	texture->gl_internal_format_cache = internal_format;
-	texture->data_size = 0;
-	texture->mipmaps = 1;
+		texture->gl_format_cache = format;
+		texture->gl_type_cache = type;
+		texture->gl_internal_format_cache = internal_format;
+		texture->data_size = 0;
+		texture->mipmaps = 1;
 
-	texture->compressed = compressed;
+		texture->compressed = compressed;
+	}
 
 	glActiveTexture(GL_TEXTURE0);
 	glBindTexture(texture->target, texture->tex_id);
 
-	if (p_flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) {
+	if (p_type == VS::TEXTURE_TYPE_EXTERNAL) {
+		glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+		glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+	} else if (p_flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) {
 		//prealloc if video
 		glTexImage2D(texture->target, 0, internal_format, texture->alloc_width, texture->alloc_height, 0, format, type, NULL);
 	}
@@ -635,6 +656,7 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p
 	ERR_FAIL_COND(texture->render_target);
 	ERR_FAIL_COND(texture->format != p_image->get_format());
 	ERR_FAIL_COND(p_image.is_null());
+	ERR_FAIL_COND(texture->type == VS::TEXTURE_TYPE_EXTERNAL);
 
 	GLenum type;
 	GLenum format;
@@ -1624,6 +1646,7 @@ void RasterizerStorageGLES2::shader_get_param_list(RID p_shader, List<PropertyIn
 			} break;
 
 			case ShaderLanguage::TYPE_SAMPLER2D:
+			case ShaderLanguage::TYPE_SAMPLEREXT:
 			case ShaderLanguage::TYPE_ISAMPLER2D:
 			case ShaderLanguage::TYPE_USAMPLER2D: {
 				pi.type = Variant::OBJECT;
@@ -1680,6 +1703,34 @@ RID RasterizerStorageGLES2::shader_get_default_texture_param(RID p_shader, const
 	return E->get();
 }
 
+void RasterizerStorageGLES2::shader_add_custom_define(RID p_shader, const String &p_define) {
+
+	Shader *shader = shader_owner.get(p_shader);
+	ERR_FAIL_COND(!shader);
+
+	shader->shader->add_custom_define(p_define);
+
+	_shader_make_dirty(shader);
+}
+
+void RasterizerStorageGLES2::shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const {
+
+	Shader *shader = shader_owner.get(p_shader);
+	ERR_FAIL_COND(!shader);
+
+	shader->shader->get_custom_defines(p_defines);
+}
+
+void RasterizerStorageGLES2::shader_clear_custom_defines(RID p_shader) {
+
+	Shader *shader = shader_owner.get(p_shader);
+	ERR_FAIL_COND(!shader);
+
+	shader->shader->clear_custom_defines();
+
+	_shader_make_dirty(shader);
+}
+
 /* COMMON MATERIAL API */
 
 void RasterizerStorageGLES2::_material_make_dirty(Material *p_material) const {

+ 4 - 0
drivers/gles2/rasterizer_storage_gles2.h

@@ -525,6 +525,10 @@ public:
 	virtual void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture);
 	virtual RID shader_get_default_texture_param(RID p_shader, const StringName &p_name) const;
 
+	virtual void shader_add_custom_define(RID p_shader, const String &p_define);
+	virtual void shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const;
+	virtual void shader_clear_custom_defines(RID p_shader);
+
 	void _update_shader(Shader *p_shader) const;
 	void update_dirty_shaders();
 

+ 2 - 1
drivers/gles2/shader_compiler_gles2.cpp

@@ -656,7 +656,8 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
 						if (var_node->name == "texture") {
 							// emit texture call
 
-							if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLER2D) {
+							if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLER2D ||
+									op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLEREXT) {
 								code += "texture2D";
 							} else if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLERCUBE) {
 								code += "textureCube";

+ 18 - 0
drivers/gles2/shader_gles2.cpp

@@ -182,6 +182,16 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() {
 
 #endif
 
+#ifdef ANDROID_ENABLED
+	strings.push_back("#define ANDROID_ENABLED\n");
+#endif
+
+	for (int i = 0; i < custom_defines.size(); i++) {
+
+		strings.push_back(custom_defines[i].get_data());
+		strings.push_back("\n");
+	}
+
 	for (int j = 0; j < conditional_count; j++) {
 		bool enable = (conditional_version.version & (1 << j)) > 0;
 
@@ -941,6 +951,10 @@ void ShaderGLES2::use_material(void *p_material) {
 
 				} break;
 
+				case ShaderLanguage::TYPE_SAMPLEREXT: {
+
+				} break;
+
 				case ShaderLanguage::TYPE_ISAMPLER2D: {
 
 				} break;
@@ -1060,6 +1074,10 @@ void ShaderGLES2::use_material(void *p_material) {
 
 				} break;
 
+				case ShaderLanguage::TYPE_SAMPLEREXT: {
+
+				} break;
+
 				case ShaderLanguage::TYPE_ISAMPLER2D: {
 
 				} break;

+ 10 - 0
drivers/gles2/shader_gles2.h

@@ -246,6 +246,16 @@ public:
 		custom_defines.push_back(p_define.utf8());
 	}
 
+	void get_custom_defines(Vector<String> *p_defines) {
+		for (int i = 0; i < custom_defines.size(); i++) {
+			p_defines->push_back(custom_defines[i].get_data());
+		}
+	}
+
+	void clear_custom_defines() {
+		custom_defines.clear();
+	}
+
 	virtual ~ShaderGLES2();
 };
 

+ 81 - 26
drivers/gles3/rasterizer_storage_gles3.cpp

@@ -101,6 +101,8 @@
 #define _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E
 #define _EXT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F
 
+#define _GL_TEXTURE_EXTERNAL_OES 0x8D65
+
 #ifndef GLES_OVER_GL
 #define glClearDepth glClearDepthf
 #endif
@@ -663,6 +665,10 @@ void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_
 			texture->target = GL_TEXTURE_2D;
 			texture->images.resize(1);
 		} break;
+		case VS::TEXTURE_TYPE_EXTERNAL: {
+			texture->target = _GL_TEXTURE_EXTERNAL_OES;
+			texture->images.resize(0);
+		} break;
 		case VS::TEXTURE_TYPE_CUBEMAP: {
 			texture->target = GL_TEXTURE_CUBE_MAP;
 			texture->images.resize(6);
@@ -677,39 +683,55 @@ void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_
 		} break;
 	}
 
-	texture->is_npot_repeat_mipmap = false;
+	if (p_type != VS::TEXTURE_TYPE_EXTERNAL) {
+		texture->is_npot_repeat_mipmap = false;
 #ifdef JAVASCRIPT_ENABLED
-	// WebGL 2.0 on browsers does not seem to properly support compressed non power-of-two (NPOT)
-	// textures with repeat/mipmaps, even though NPOT textures should be supported as per the spec.
-	// Force decompressing them to work it around on WebGL 2.0 at a performance cost (GH-33058).
-	int po2_width = next_power_of_2(p_width);
-	int po2_height = next_power_of_2(p_height);
-	bool is_po2 = p_width == po2_width && p_height == po2_height;
-
-	if (!is_po2 && (p_flags & VS::TEXTURE_FLAG_REPEAT || p_flags & VS::TEXTURE_FLAG_MIPMAPS)) {
-		texture->is_npot_repeat_mipmap = true;
-	}
+		// WebGL 2.0 on browsers does not seem to properly support compressed non power-of-two (NPOT)
+		// textures with repeat/mipmaps, even though NPOT textures should be supported as per the spec.
+		// Force decompressing them to work it around on WebGL 2.0 at a performance cost (GH-33058).
+		int po2_width = next_power_of_2(p_width);
+		int po2_height = next_power_of_2(p_height);
+		bool is_po2 = p_width == po2_width && p_height == po2_height;
+
+		if (!is_po2 && (p_flags & VS::TEXTURE_FLAG_REPEAT || p_flags & VS::TEXTURE_FLAG_MIPMAPS)) {
+			texture->is_npot_repeat_mipmap = true;
+		}
 #endif // JAVASCRIPT_ENABLED
 
-	Image::Format real_format;
-	_get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, format, internal_format, type, compressed, srgb, texture->is_npot_repeat_mipmap);
+		Image::Format real_format;
+		_get_gl_image_and_format(Ref<Image>(),
+				texture->format,
+				texture->flags,
+				real_format,
+				format,
+				internal_format,
+				type,
+				compressed,
+				srgb,
+				texture->is_npot_repeat_mipmap);
 
-	texture->alloc_width = texture->width;
-	texture->alloc_height = texture->height;
-	texture->alloc_depth = texture->depth;
+		texture->alloc_width = texture->width;
+		texture->alloc_height = texture->height;
+		texture->alloc_depth = texture->depth;
 
-	texture->gl_format_cache = format;
-	texture->gl_type_cache = type;
-	texture->gl_internal_format_cache = internal_format;
-	texture->compressed = compressed;
-	texture->srgb = srgb;
-	texture->data_size = 0;
-	texture->mipmaps = 1;
+		texture->gl_format_cache = format;
+		texture->gl_type_cache = type;
+		texture->gl_internal_format_cache = internal_format;
+		texture->compressed = compressed;
+		texture->srgb = srgb;
+		texture->data_size = 0;
+		texture->mipmaps = 1;
+	}
 
 	glActiveTexture(GL_TEXTURE0);
 	glBindTexture(texture->target, texture->tex_id);
 
-	if (p_type == VS::TEXTURE_TYPE_3D || p_type == VS::TEXTURE_TYPE_2D_ARRAY) {
+	if (p_type == VS::TEXTURE_TYPE_EXTERNAL) {
+		glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+		glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+	} else if (p_type == VS::TEXTURE_TYPE_3D || p_type == VS::TEXTURE_TYPE_2D_ARRAY) {
 
 		int width = p_width;
 		int height = p_height;
@@ -757,6 +779,7 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p
 	ERR_FAIL_COND(texture->render_target);
 	ERR_FAIL_COND(texture->format != p_image->get_format());
 	ERR_FAIL_COND(p_image.is_null());
+	ERR_FAIL_COND(texture->type == VS::TEXTURE_TYPE_EXTERNAL);
 
 	GLenum type;
 	GLenum format;
@@ -788,7 +811,8 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p
 	GLenum blit_target = GL_TEXTURE_2D;
 
 	switch (texture->type) {
-		case VS::TEXTURE_TYPE_2D: {
+		case VS::TEXTURE_TYPE_2D:
+		case VS::TEXTURE_TYPE_EXTERNAL: {
 			blit_target = GL_TEXTURE_2D;
 		} break;
 		case VS::TEXTURE_TYPE_CUBEMAP: {
@@ -989,6 +1013,7 @@ void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<I
 	ERR_FAIL_COND(src_x < 0 || src_y < 0 || src_x + src_w > p_image->get_width() || src_y + src_h > p_image->get_height());
 	ERR_FAIL_COND(dst_x < 0 || dst_y < 0 || dst_x + src_w > texture->alloc_width || dst_y + src_h > texture->alloc_height);
 	ERR_FAIL_COND(p_dst_mip < 0 || p_dst_mip >= texture->mipmaps);
+	ERR_FAIL_COND(texture->type == VS::TEXTURE_TYPE_EXTERNAL);
 
 	GLenum type;
 	GLenum format;
@@ -1008,7 +1033,8 @@ void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<I
 	GLenum blit_target = GL_TEXTURE_2D;
 
 	switch (texture->type) {
-		case VS::TEXTURE_TYPE_2D: {
+		case VS::TEXTURE_TYPE_2D:
+		case VS::TEXTURE_TYPE_EXTERNAL: {
 			blit_target = GL_TEXTURE_2D;
 		} break;
 		case VS::TEXTURE_TYPE_CUBEMAP: {
@@ -2466,6 +2492,7 @@ void RasterizerStorageGLES3::shader_get_param_list(RID p_shader, List<PropertyIn
 			case ShaderLanguage::TYPE_MAT3: pi.type = Variant::BASIS; break;
 			case ShaderLanguage::TYPE_MAT4: pi.type = Variant::TRANSFORM; break;
 			case ShaderLanguage::TYPE_SAMPLER2D:
+			case ShaderLanguage::TYPE_SAMPLEREXT:
 			case ShaderLanguage::TYPE_ISAMPLER2D:
 			case ShaderLanguage::TYPE_USAMPLER2D: {
 
@@ -2524,6 +2551,34 @@ RID RasterizerStorageGLES3::shader_get_default_texture_param(RID p_shader, const
 	return E->get();
 }
 
+void RasterizerStorageGLES3::shader_add_custom_define(RID p_shader, const String &p_define) {
+
+	Shader *shader = shader_owner.get(p_shader);
+	ERR_FAIL_COND(!shader);
+
+	shader->shader->add_custom_define(p_define);
+
+	_shader_make_dirty(shader);
+}
+
+void RasterizerStorageGLES3::shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const {
+
+	Shader *shader = shader_owner.get(p_shader);
+	ERR_FAIL_COND(!shader);
+
+	shader->shader->get_custom_defines(p_defines);
+}
+
+void RasterizerStorageGLES3::shader_clear_custom_defines(RID p_shader) {
+
+	Shader *shader = shader_owner.get(p_shader);
+	ERR_FAIL_COND(!shader);
+
+	shader->shader->clear_custom_defines();
+
+	_shader_make_dirty(shader);
+}
+
 /* COMMON MATERIAL API */
 
 void RasterizerStorageGLES3::_material_make_dirty(Material *p_material) const {

+ 4 - 0
drivers/gles3/rasterizer_storage_gles3.h

@@ -533,6 +533,10 @@ public:
 	virtual void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture);
 	virtual RID shader_get_default_texture_param(RID p_shader, const StringName &p_name) const;
 
+	virtual void shader_add_custom_define(RID p_shader, const String &p_define);
+	virtual void shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const;
+	virtual void shader_clear_custom_defines(RID p_shader);
+
 	void _update_shader(Shader *p_shader) const;
 
 	void update_dirty_shaders();

+ 2 - 0
drivers/gles3/shader_compiler_gles3.cpp

@@ -86,6 +86,7 @@ static int _get_datatype_size(SL::DataType p_type) {
 		case SL::TYPE_ISAMPLER3D: return 16;
 		case SL::TYPE_USAMPLER3D: return 16;
 		case SL::TYPE_SAMPLERCUBE: return 16;
+		case SL::TYPE_SAMPLEREXT: return 16;
 	}
 
 	ERR_FAIL_V(0);
@@ -125,6 +126,7 @@ static int _get_datatype_alignment(SL::DataType p_type) {
 		case SL::TYPE_ISAMPLER3D: return 16;
 		case SL::TYPE_USAMPLER3D: return 16;
 		case SL::TYPE_SAMPLERCUBE: return 16;
+		case SL::TYPE_SAMPLEREXT: return 16;
 	}
 
 	ERR_FAIL_V(0);

+ 5 - 0
drivers/gles3/shader_gles3.cpp

@@ -210,9 +210,14 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() {
 	strings.push_back("#version 300 es\n");
 #endif
 
+#ifdef ANDROID_ENABLED
+	strings.push_back("#define ANDROID_ENABLED\n");
+#endif
+
 	for (int i = 0; i < custom_defines.size(); i++) {
 
 		strings.push_back(custom_defines[i].get_data());
+		strings.push_back("\n");
 	}
 
 	for (int j = 0; j < conditional_count; j++) {

+ 10 - 0
drivers/gles3/shader_gles3.h

@@ -368,6 +368,16 @@ public:
 		custom_defines.push_back(p_define.utf8());
 	}
 
+	void get_custom_defines(Vector<String> *p_defines) {
+		for (int i = 0; i < custom_defines.size(); i++) {
+			p_defines->push_back(custom_defines[i].get_data());
+		}
+	}
+
+	void clear_custom_defines() {
+		custom_defines.clear();
+	}
+
 	virtual ~ShaderGLES3();
 };
 

+ 1 - 0
scene/register_scene_types.cpp

@@ -658,6 +658,7 @@ void register_scene_types() {
 	ClassDB::register_class<ProxyTexture>();
 	ClassDB::register_class<AnimatedTexture>();
 	ClassDB::register_class<CameraTexture>();
+	ClassDB::register_class<ExternalTexture>();
 	ClassDB::register_class<CubeMap>();
 	ClassDB::register_virtual_class<TextureLayered>();
 	ClassDB::register_class<Texture3D>();

+ 25 - 0
scene/resources/shader.cpp

@@ -127,6 +127,27 @@ void Shader::get_default_texture_param_list(List<StringName> *r_textures) const
 	}
 }
 
+void Shader::set_custom_defines(const String &p_defines) {
+
+	VS::get_singleton()->shader_clear_custom_defines(shader);
+	VS::get_singleton()->shader_add_custom_define(shader, p_defines);
+}
+
+String Shader::get_custom_defines() {
+	Vector<String> custom_defines;
+	VS::get_singleton()->shader_get_custom_defines(shader, &custom_defines);
+
+	String concatenated_defines;
+	for (int i = 0; i < custom_defines.size(); i++) {
+		if (i != 0) {
+			concatenated_defines += "\n";
+		}
+		concatenated_defines += custom_defines[i];
+	}
+
+	return concatenated_defines;
+}
+
 bool Shader::is_text_shader() const {
 	return true;
 }
@@ -149,11 +170,15 @@ void Shader::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_default_texture_param", "param", "texture"), &Shader::set_default_texture_param);
 	ClassDB::bind_method(D_METHOD("get_default_texture_param", "param"), &Shader::get_default_texture_param);
 
+	ClassDB::bind_method(D_METHOD("set_custom_defines", "custom_defines"), &Shader::set_custom_defines);
+	ClassDB::bind_method(D_METHOD("get_custom_defines"), &Shader::get_custom_defines);
+
 	ClassDB::bind_method(D_METHOD("has_param", "name"), &Shader::has_param);
 
 	//ClassDB::bind_method(D_METHOD("get_param_list"),&Shader::get_fragment_code);
 
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_code", "get_code");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_defines", PROPERTY_HINT_MULTILINE_TEXT), "set_custom_defines", "get_custom_defines");
 
 	BIND_ENUM_CONSTANT(MODE_SPATIAL);
 	BIND_ENUM_CONSTANT(MODE_CANVAS_ITEM);

+ 3 - 0
scene/resources/shader.h

@@ -79,6 +79,9 @@ public:
 	Ref<Texture> get_default_texture_param(const StringName &p_param) const;
 	void get_default_texture_param_list(List<StringName> *r_textures) const;
 
+	void set_custom_defines(const String &p_defines);
+	String get_custom_defines();
+
 	virtual bool is_text_shader() const;
 
 	_FORCE_INLINE_ StringName remap_param(const StringName &p_param) const {

+ 61 - 0
scene/resources/texture.cpp

@@ -2621,3 +2621,64 @@ CameraTexture::CameraTexture() {
 CameraTexture::~CameraTexture() {
 	// nothing to do here yet
 }
+
+void ExternalTexture::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("set_size", "size"), &ExternalTexture::set_size);
+	ClassDB::bind_method(D_METHOD("get_external_texture_id"), &ExternalTexture::get_external_texture_id);
+
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
+}
+
+uint32_t ExternalTexture::get_external_texture_id() {
+	return VisualServer::get_singleton()->texture_get_texid(texture);
+}
+
+void ExternalTexture::set_size(const Size2 &p_size) {
+
+	if (p_size.width > 0 && p_size.height > 0) {
+		size = p_size;
+		VisualServer::get_singleton()->texture_set_size_override(texture, size.width, size.height, 0);
+	}
+}
+
+int ExternalTexture::get_width() const {
+	return size.width;
+}
+
+int ExternalTexture::get_height() const {
+	return size.height;
+}
+
+Size2 ExternalTexture::get_size() const {
+	return size;
+}
+
+RID ExternalTexture::get_rid() const {
+	return texture;
+}
+
+bool ExternalTexture::has_alpha() const {
+	return true;
+}
+
+void ExternalTexture::set_flags(uint32_t p_flags) {
+	// not supported
+}
+
+uint32_t ExternalTexture::get_flags() const {
+	// not supported
+	return 0;
+}
+
+ExternalTexture::ExternalTexture() {
+	size = Size2(1.0, 1.0);
+	texture = VisualServer::get_singleton()->texture_create();
+
+	VisualServer::get_singleton()->texture_allocate(texture, size.width, size.height, 0, Image::FORMAT_RGBA8, VS::TEXTURE_TYPE_EXTERNAL, 0);
+	_change_notify();
+	emit_changed();
+}
+
+ExternalTexture::~ExternalTexture() {
+	VisualServer::get_singleton()->free(texture);
+}

+ 30 - 0
scene/resources/texture.h

@@ -772,4 +772,34 @@ public:
 	~CameraTexture();
 };
 
+// External textures as defined by https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt
+class ExternalTexture : public Texture {
+	GDCLASS(ExternalTexture, Texture);
+
+private:
+	RID texture;
+	Size2 size;
+
+protected:
+	static void _bind_methods();
+
+public:
+	uint32_t get_external_texture_id();
+
+	virtual Size2 get_size() const;
+	void set_size(const Size2 &p_size);
+
+	virtual int get_width() const;
+	virtual int get_height() const;
+
+	virtual RID get_rid() const;
+	virtual bool has_alpha() const;
+
+	virtual void set_flags(uint32_t p_flags);
+	virtual uint32_t get_flags() const;
+
+	ExternalTexture();
+	~ExternalTexture();
+};
+
 #endif

+ 4 - 0
servers/visual/rasterizer.h

@@ -242,6 +242,10 @@ public:
 	virtual void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture) = 0;
 	virtual RID shader_get_default_texture_param(RID p_shader, const StringName &p_name) const = 0;
 
+	virtual void shader_add_custom_define(RID p_shader, const String &p_define) = 0;
+	virtual void shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const = 0;
+	virtual void shader_clear_custom_defines(RID p_shader) = 0;
+
 	/* COMMON MATERIAL API */
 
 	virtual RID material_create() = 0;

+ 15 - 3
servers/visual/shader_language.cpp

@@ -132,6 +132,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = {
 	"TYPE_ISAMPLER3D",
 	"TYPE_USAMPLER3D",
 	"TYPE_SAMPLERCUBE",
+	"TYPE_SAMPLEREXT",
 	"INTERPOLATION_FLAT",
 	"INTERPOLATION_SMOOTH",
 	"CONST",
@@ -272,6 +273,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
 	{ TK_TYPE_ISAMPLER3D, "isampler3D" },
 	{ TK_TYPE_USAMPLER3D, "usampler3D" },
 	{ TK_TYPE_SAMPLERCUBE, "samplerCube" },
+	{ TK_TYPE_SAMPLEREXT, "samplerExternalOES" },
 	{ TK_INTERPOLATION_FLAT, "flat" },
 	{ TK_INTERPOLATION_SMOOTH, "smooth" },
 	{ TK_CONST, "const" },
@@ -755,7 +757,8 @@ bool ShaderLanguage::is_token_datatype(TokenType p_type) {
 			p_type == TK_TYPE_SAMPLER3D ||
 			p_type == TK_TYPE_ISAMPLER3D ||
 			p_type == TK_TYPE_USAMPLER3D ||
-			p_type == TK_TYPE_SAMPLERCUBE);
+			p_type == TK_TYPE_SAMPLERCUBE ||
+			p_type == TK_TYPE_SAMPLEREXT);
 }
 
 ShaderLanguage::DataType ShaderLanguage::get_token_datatype(TokenType p_type) {
@@ -841,6 +844,7 @@ String ShaderLanguage::get_datatype_name(DataType p_type) {
 		case TYPE_ISAMPLER3D: return "isampler3D";
 		case TYPE_USAMPLER3D: return "usampler3D";
 		case TYPE_SAMPLERCUBE: return "samplerCube";
+		case TYPE_SAMPLEREXT: return "samplerExternalOES";
 	}
 
 	return "";
@@ -1983,6 +1987,8 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
 	{ "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true },
 	{ "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false },
 	{ "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false },
+	{ "texture", TYPE_VEC4, { TYPE_SAMPLEREXT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false },
+	{ "texture", TYPE_VEC4, { TYPE_SAMPLEREXT, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false },
 
 	{ "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true },
 	{ "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true },
@@ -2002,6 +2008,10 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
 	{ "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true },
 	{ "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true },
 	{ "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true },
+	{ "textureProj", TYPE_VEC4, { TYPE_SAMPLEREXT, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true },
+	{ "textureProj", TYPE_VEC4, { TYPE_SAMPLEREXT, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true },
+	{ "textureProj", TYPE_VEC4, { TYPE_SAMPLEREXT, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true },
+	{ "textureProj", TYPE_VEC4, { TYPE_SAMPLEREXT, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true },
 
 	{ "textureLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false },
 	{ "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true },
@@ -2452,7 +2462,8 @@ bool ShaderLanguage::is_sampler_type(DataType p_type) {
 		   p_type == TYPE_SAMPLER3D ||
 		   p_type == TYPE_ISAMPLER3D ||
 		   p_type == TYPE_USAMPLER3D ||
-		   p_type == TYPE_SAMPLERCUBE;
+		   p_type == TYPE_SAMPLERCUBE ||
+		   p_type == TYPE_SAMPLEREXT;
 }
 
 Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) {
@@ -2546,7 +2557,8 @@ Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::C
 			case ShaderLanguage::TYPE_USAMPLER2DARRAY:
 			case ShaderLanguage::TYPE_USAMPLER2D:
 			case ShaderLanguage::TYPE_USAMPLER3D:
-			case ShaderLanguage::TYPE_SAMPLERCUBE: {
+			case ShaderLanguage::TYPE_SAMPLERCUBE:
+			case ShaderLanguage::TYPE_SAMPLEREXT: {
 				// Texture types, likely not relevant here.
 				break;
 			}

+ 2 - 0
servers/visual/shader_language.h

@@ -79,6 +79,7 @@ public:
 		TK_TYPE_ISAMPLER3D,
 		TK_TYPE_USAMPLER3D,
 		TK_TYPE_SAMPLERCUBE,
+		TK_TYPE_SAMPLEREXT,
 		TK_INTERPOLATION_FLAT,
 		TK_INTERPOLATION_SMOOTH,
 		TK_CONST,
@@ -200,6 +201,7 @@ public:
 		TYPE_ISAMPLER3D,
 		TYPE_USAMPLER3D,
 		TYPE_SAMPLERCUBE,
+		TYPE_SAMPLEREXT,
 	};
 
 	enum DataPrecision {

+ 4 - 0
servers/visual/visual_server_raster.h

@@ -190,6 +190,10 @@ public:
 	BIND3(shader_set_default_texture_param, RID, const StringName &, RID)
 	BIND2RC(RID, shader_get_default_texture_param, RID, const StringName &)
 
+	BIND2(shader_add_custom_define, RID, const String &)
+	BIND2C(shader_get_custom_defines, RID, Vector<String> *)
+	BIND1(shader_clear_custom_defines, RID)
+
 	/* COMMON MATERIAL API */
 
 	BIND0R(RID, material_create)

+ 4 - 0
servers/visual/visual_server_wrap_mt.h

@@ -126,6 +126,10 @@ public:
 	FUNC3(shader_set_default_texture_param, RID, const StringName &, RID)
 	FUNC2RC(RID, shader_get_default_texture_param, RID, const StringName &)
 
+	FUNC2(shader_add_custom_define, RID, const String &)
+	FUNC2SC(shader_get_custom_defines, RID, Vector<String> *)
+	FUNC1(shader_clear_custom_defines, RID)
+
 	/* COMMON MATERIAL API */
 
 	FUNCRID(material)

+ 5 - 0
servers/visual_server.h

@@ -93,6 +93,7 @@ public:
 
 	enum TextureType {
 		TEXTURE_TYPE_2D,
+		TEXTURE_TYPE_EXTERNAL,
 		TEXTURE_TYPE_CUBEMAP,
 		TEXTURE_TYPE_2D_ARRAY,
 		TEXTURE_TYPE_3D,
@@ -193,6 +194,10 @@ public:
 	virtual void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture) = 0;
 	virtual RID shader_get_default_texture_param(RID p_shader, const StringName &p_name) const = 0;
 
+	virtual void shader_add_custom_define(RID p_shader, const String &p_define) = 0;
+	virtual void shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const = 0;
+	virtual void shader_clear_custom_defines(RID p_shader) = 0;
+
 	/* COMMON MATERIAL API */
 
 	enum {