diff --git a/doc/classes/BlitMaterial.xml b/doc/classes/BlitMaterial.xml
new file mode 100644
index 0000000000..d2b1ad7c0b
--- /dev/null
+++ b/doc/classes/BlitMaterial.xml
@@ -0,0 +1,33 @@
+
+
+
+ A material that processes blit calls to a DrawableTexture.
+
+
+ A material resource that can be used by DrawableTextures when processing blit calls to draw.
+
+
+
+
+
+ The manner in which the newly blitted texture is blended with the original DrawableTexture.
+
+
+
+
+ Mix blending mode. Colors are assumed to be independent of the alpha (opacity) value.
+
+
+ Additive blending mode.
+
+
+ Subtractive blending mode.
+
+
+ Multiplicative blending mode.
+
+
+ No blending mode, direct color copy.
+
+
+
diff --git a/doc/classes/DrawableTexture2D.xml b/doc/classes/DrawableTexture2D.xml
new file mode 100644
index 0000000000..638410a995
--- /dev/null
+++ b/doc/classes/DrawableTexture2D.xml
@@ -0,0 +1,71 @@
+
+
+
+ A 2D texture that supports drawing to itself via Blit calls.
+
+
+ A 2D texture that can be modified via blit calls, copying from a target texture to itself. Primarily intended to be managed in code, a user must call [method setup] to initialize the state before drawing. Each [method blit_rect] call takes at least a rectangle, the area to draw to, and another texture, what to be drawn. The draw calls use a Texture_Blit Shader to process and calculate the result, pixel by pixel. Users can supply their own ShaderMaterial with custom Texture_Blit shaders for more complex behaviors.
+
+
+
+
+
+
+
+
+
+
+
+
+ Draws to given [param rect] on this texture by copying from the given [param source]. A [param modulate] color can be passed in for the shader to use, but defaults to White. The [param mipmap] value can specify a draw to a lower mipmap level. The [param material] parameter can take a ShaderMaterial with a TextureBlit Shader for custom drawing behavior.
+
+
+
+
+
+
+
+
+
+
+
+ Draws to the given [param rect] on this texture, as well as on up to 3 DrawableTexture [param extra_targets]. All [param extra_targets] must be the same size and DrawableFormat as the original target, otherwise the Shader may fail. Expects up to 4 Texture [param sources], but will replace missing [param sources] with default Black Textures.
+
+
+
+
+
+ Re-calculates the mipmaps for this texture on demand.
+
+
+
+
+
+
+
+
+
+
+ Initializes the DrawableTexture to a White texture of the given [param width], [param height], and [param format].
+
+
+
+
+
+
+
+
+ OpenGL texture format RGBA with four components, each with a bitdepth of 8.
+
+
+ OpenGL texture format RGBA with four components, each with a bitdepth of 8.
+ When drawn to, an sRGB to linear color space conversion is performed.
+
+
+ OpenGL texture format GL_RGBA16F where there are four components, each a 16-bit "half-precision" floating-point value.
+
+
+ OpenGL texture format GL_RGBA32F where there are four components, each a 32-bit floating-point value.
+
+
+
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 5b45d7320b..9b171fd7f3 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3878,6 +3878,45 @@
[b]Note:[/b] If using only the rendering device renderer, it's recommend to use [method RenderingDevice.texture_create_from_extension] together with [method RenderingServer.texture_rd_create], rather than this method. This way, the texture's format and usage can be controlled more effectively.
+
+
+
+
+
+
+
+
+
+ Draws to [param rect] on up to 4 given Drawable [param textures], using a TextureBlit Shader from [param material]. [param modulate] and up to 4 [param source_textures] are uniforms for the Shader to process with. [param to_mipmap] can specify to perform this draw to a lower mipmap level.
+ [b]Note:[/b] All [param textures] must be the same size and format.
+
+
+
+
+
+
+
+
+
+
+ Creates a 2-dimensional texture and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]texture_drawable*[/code] RenderingServer functions.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent resource is [DrawableTexture2D].
+
+
+
+
+
+
+ Calculates new MipMaps for the given Drawable [param texture].
+
+
+
+
+
+ Returns a ShaderMaterial with the default texture_blit Shader.
+
+
@@ -4677,6 +4716,19 @@
Back face of a [Cubemap].
+
+ OpenGL texture format RGBA with four components, each with a bitdepth of 8.
+
+
+ OpenGL texture format RGBA with four components, each with a bitdepth of 8.
+ When drawn to, an sRGB to linear color space conversion is performed.
+
+
+ OpenGL texture format GL_RGBA16F where there are four components, each a 16-bit "half-precision" floating-point value.
+
+
+ OpenGL texture format GL_RGBA32F where there are four components, each a 32-bit floating-point value.
+
Shader is a 3D shader.
@@ -4692,7 +4744,10 @@
Shader is a 3D fog shader.
-
+
+ Shader is a texture_blit shader.
+
+
Represents the size of the [enum ShaderMode] enum.
diff --git a/doc/classes/Shader.xml b/doc/classes/Shader.xml
index 3fbe1da687..dc341cd03c 100644
--- a/doc/classes/Shader.xml
+++ b/doc/classes/Shader.xml
@@ -74,5 +74,8 @@
Mode used for setting the color and density of volumetric fog effect.
+
+ Mode used for drawing to DrawableTexture resources via blit calls.
+
diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml
index 745a7aa626..4665252158 100644
--- a/doc/classes/VisualShader.xml
+++ b/doc/classes/VisualShader.xml
@@ -222,7 +222,10 @@
A compute shader that runs for each froxel of the volumetric fog map.
-
+
+ A shader used to process blit calls to a DrawableTexture.
+
+
Represents the size of the [enum Type] enum.
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index 1df41493d5..58eaa42dde 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -210,6 +210,8 @@ void RasterizerGLES3::initialize() {
}
void RasterizerGLES3::finalize() {
+ // Has to be a separate call due to TextureStorage & MaterialStorage needing to interact for TexBlit Shaders
+ texture_storage->_tex_blit_shader_free();
memdelete(scene);
memdelete(canvas);
memdelete(gi);
@@ -372,6 +374,8 @@ RasterizerGLES3::RasterizerGLES3() {
fog = memnew(GLES3::Fog);
canvas = memnew(RasterizerCanvasGLES3());
scene = memnew(RasterizerSceneGLES3());
+ // Has to be a separate call due to TextureStorage & MaterialStorage needing to interact for TexBlit Shaders
+ texture_storage->_tex_blit_shader_initialize();
// Disable OpenGL linear to sRGB conversion, because Godot will always do this conversion itself.
if (config->srgb_framebuffer_supported) {
diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub
index 0207ba12b7..ecbaaf0c93 100644
--- a/drivers/gles3/shaders/SCsub
+++ b/drivers/gles3/shaders/SCsub
@@ -25,6 +25,7 @@ if "GLES3_GLSL" in env["BUILDERS"]:
env.GLES3_GLSL("particles.glsl")
env.GLES3_GLSL("particles_copy.glsl")
env.GLES3_GLSL("skeleton.glsl")
+ env.GLES3_GLSL("tex_blit.glsl")
# once we finish conversion we can introduce this to cover all files:
# for glsl_file in glsl_files:
diff --git a/drivers/gles3/shaders/tex_blit.glsl b/drivers/gles3/shaders/tex_blit.glsl
new file mode 100644
index 0000000000..3358ff6d27
--- /dev/null
+++ b/drivers/gles3/shaders/tex_blit.glsl
@@ -0,0 +1,121 @@
+/* clang-format off */
+
+#[modes]
+
+mode_default =
+
+#[specializations]
+
+USE_OUTPUT1 = true
+USE_OUTPUT2 = true
+USE_OUTPUT3 = true
+
+#[vertex]
+
+layout(location = 0) in vec2 vertex_attrib;
+
+uniform vec2 offset;
+uniform vec2 size;
+
+out vec2 uv;
+
+void main() {
+ uv = vertex_attrib * 0.5 + 0.5;
+ // This math scales the Vertex Attribute Quad to match the Rect the user passed in, based on Offset & Size
+ gl_Position = vec4( (offset * 2.0 - 1.0) + (size * (vertex_attrib + 1.0)), 1.0, 1.0);
+}
+
+#[fragment]
+
+uniform sampler2D source0; // texunit:0
+
+uniform sampler2D source1; // texunit:-1
+
+uniform sampler2D source2; // texunit:-2
+
+uniform sampler2D source3; // texunit:-3
+
+#define OUTPUT0_SRGB uint(1)
+#define OUTPUT1_SRGB uint(2)
+#define OUTPUT2_SRGB uint(4)
+#define OUTPUT3_SRGB uint(8)
+
+uniform uint convert_to_srgb;
+uniform vec4 modulate;
+uniform float time;
+
+in vec2 uv;
+
+layout (location = 0) out vec4 out_color0;
+
+#ifdef USE_OUTPUT1
+layout (location = 1) out vec4 out_color1;
+#endif
+
+#ifdef USE_OUTPUT2
+layout (location = 2) out vec4 out_color2;
+#endif
+
+#ifdef USE_OUTPUT3
+layout (location = 3) out vec4 out_color3;
+#endif
+
+// This needs to be outside clang-format so the ubo comment is in the right place
+#ifdef MATERIAL_UNIFORMS_USED
+layout(std140) uniform MaterialUniforms{ //ubo:0
+
+#MATERIAL_UNIFORMS
+
+};
+#endif
+
+#GLOBALS
+
+vec3 linear_to_srgb(vec3 color) {
+ // If going to srgb, clamp from 0 to 1.
+ color = clamp(color, vec3(0.0), vec3(1.0));
+ const vec3 a = vec3(0.055f);
+ return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
+}
+
+void main() {
+ // Handles the case where user code uses extra outputs, but extra output targets were not bound
+ vec4 color0 = vec4(0.0, 0.0, 0.0, 1.0);
+ vec4 color1 = vec4(0.0, 0.0, 0.0, 1.0);
+ vec4 color2 = vec4(0.0, 0.0, 0.0, 1.0);
+ vec4 color3 = vec4(0.0, 0.0, 0.0, 1.0);
+
+#CODE : BLIT
+
+ // Discards extra outputs if extra output targets were not bound
+ out_color0 = color0;
+
+#ifdef USE_OUTPUT1
+ out_color1 = color1;
+#endif
+#ifdef USE_OUTPUT2
+ out_color2 = color2;
+#endif
+#ifdef USE_OUTPUT3
+ out_color3 = color3;
+#endif
+
+ if (bool(convert_to_srgb & OUTPUT0_SRGB)) {
+ out_color0.rgb = linear_to_srgb(out_color0.rgb); // Regular linear -> SRGB conversion.
+ }
+#ifdef USE_OUTPUT1
+ if (bool(convert_to_srgb & OUTPUT1_SRGB)) {
+ out_color1.rgb = linear_to_srgb(out_color1.rgb);
+ }
+#endif
+#ifdef USE_OUTPUT2
+ if (bool(convert_to_srgb & OUTPUT2_SRGB)) {
+ out_color2.rgb = linear_to_srgb(out_color2.rgb);
+ }
+#endif
+#ifdef USE_OUTPUT3
+ if (bool(convert_to_srgb & OUTPUT3_SRGB)) {
+ out_color3.rgb = linear_to_srgb(out_color3.rgb);
+ }
+#endif
+}
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 2a11a1c5ee..1f7f5932e0 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1137,12 +1137,14 @@ MaterialStorage::MaterialStorage() {
shader_data_request_func[RS::SHADER_CANVAS_ITEM] = _create_canvas_shader_func;
shader_data_request_func[RS::SHADER_PARTICLES] = _create_particles_shader_func;
shader_data_request_func[RS::SHADER_SKY] = _create_sky_shader_func;
+ shader_data_request_func[RS::SHADER_TEXTURE_BLIT] = _create_tex_blit_shader_func;
shader_data_request_func[RS::SHADER_FOG] = nullptr;
material_data_request_func[RS::SHADER_SPATIAL] = _create_scene_material_func;
material_data_request_func[RS::SHADER_CANVAS_ITEM] = _create_canvas_material_func;
material_data_request_func[RS::SHADER_PARTICLES] = _create_particles_material_func;
material_data_request_func[RS::SHADER_SKY] = _create_sky_material_func;
+ material_data_request_func[RS::SHADER_TEXTURE_BLIT] = _create_tex_blit_material_func;
material_data_request_func[RS::SHADER_FOG] = nullptr;
static_assert(sizeof(GlobalShaderUniforms::Value) == 16);
@@ -1549,6 +1551,28 @@ MaterialStorage::MaterialStorage() {
shaders.compiler_sky.initialize(actions);
}
+
+ {
+ // Setup TextureBlit compiler
+ ShaderCompiler::DefaultIdentifierActions actions;
+
+ actions.renames["TIME"] = "time";
+ actions.renames["PI"] = _MKSTR(Math_PI);
+ actions.renames["TAU"] = _MKSTR(Math_TAU);
+ actions.renames["E"] = _MKSTR(Math_E);
+
+ actions.renames["FRAGCOORD"] = "gl_FragCoord";
+
+ actions.renames["UV"] = "uv";
+ actions.renames["MODULATE"] = "modulate";
+
+ actions.renames["COLOR0"] = "color0";
+ actions.renames["COLOR1"] = "color1";
+ actions.renames["COLOR2"] = "color2";
+ actions.renames["COLOR3"] = "color3";
+
+ shaders.compiler_tex_blit.initialize(actions);
+ }
}
MaterialStorage::~MaterialStorage() {
@@ -2233,6 +2257,8 @@ void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) {
new_mode = RS::SHADER_SKY;
//} else if (mode_string == "fog") {
// new_mode = RS::SHADER_FOG;
+ } else if (mode_string == "texture_blit") {
+ new_mode = RS::SHADER_TEXTURE_BLIT;
} else {
new_mode = RS::SHADER_MAX;
ERR_PRINT("shader type " + mode_string + " not supported in OpenGL renderer");
@@ -3329,4 +3355,119 @@ void ParticleProcessMaterialData::bind_uniforms() {
bind_uniforms_generic(texture_cache, shader_data->texture_uniforms, 1); // Start at GL_TEXTURE1 because texture slot 0 is reserved for the heightmap texture.
}
+/* TextureBlit SHADER */
+
+void TexBlitShaderData::set_code(const String &p_code) {
+ // Initialize and compile the shader.
+
+ code = p_code;
+ valid = false;
+ ubo_size = 0;
+ uniforms.clear();
+
+ if (code.is_empty()) {
+ return; // Just invalid, but no error.
+ }
+
+ ShaderCompiler::GeneratedCode gen_code;
+
+ // Actual enum set further down after compilation.
+ int blend_modei = BLEND_MODE_MIX;
+
+ ShaderCompiler::IdentifierActions actions;
+ actions.entry_point_stages["blit"] = ShaderCompiler::STAGE_FRAGMENT;
+
+ actions.render_mode_values["blend_add"] = Pair(&blend_modei, BLEND_MODE_ADD);
+ actions.render_mode_values["blend_mix"] = Pair(&blend_modei, BLEND_MODE_MIX);
+ actions.render_mode_values["blend_sub"] = Pair(&blend_modei, BLEND_MODE_SUB);
+ actions.render_mode_values["blend_mul"] = Pair(&blend_modei, BLEND_MODE_MUL);
+ actions.render_mode_values["blend_disabled"] = Pair(&blend_modei, BLEND_MODE_DISABLED);
+
+ actions.uniforms = &uniforms;
+ Error err = MaterialStorage::get_singleton()->shaders.compiler_tex_blit.compile(RS::SHADER_TEXTURE_BLIT, code, &actions, path, gen_code);
+ ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
+
+ if (version.is_null()) {
+ version = MaterialStorage::get_singleton()->shaders.tex_blit_shader.version_create();
+ }
+
+ blend_mode = BlendMode(blend_modei);
+
+#if 0
+ print_line("**compiling shader:");
+ print_line("**defines:\n");
+ for (int i = 0; i < gen_code.defines.size(); i++) {
+ print_line(gen_code.defines[i]);
+ }
+
+ HashMap::Iterator el = gen_code.code.begin();
+ while (el) {
+ print_line("\n**code " + el->key + ":\n" + el->value);
+ ++el;
+ }
+
+ print_line("\n**uniforms:\n" + gen_code.uniforms);
+ print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]);
+ print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]);
+#endif
+
+ LocalVector texture_uniform_data = get_texture_uniform_data(gen_code.texture_uniforms);
+
+ MaterialStorage::get_singleton()->shaders.tex_blit_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_data);
+ ERR_FAIL_COND(!MaterialStorage::get_singleton()->shaders.tex_blit_shader.version_is_valid(version));
+
+ ubo_size = gen_code.uniform_total_size;
+ ubo_offsets = gen_code.uniform_offsets;
+ texture_uniforms = gen_code.texture_uniforms;
+
+ valid = true;
+}
+
+bool TexBlitShaderData::is_animated() const {
+ return false;
+}
+
+bool TexBlitShaderData::casts_shadows() const {
+ return false;
+}
+
+RS::ShaderNativeSourceCode TexBlitShaderData::get_native_source_code() const {
+ return MaterialStorage::get_singleton()->shaders.tex_blit_shader.version_get_native_source_code(version);
+}
+
+TexBlitShaderData::TexBlitShaderData() {
+ valid = false;
+}
+
+TexBlitShaderData::~TexBlitShaderData() {
+ if (version.is_valid()) {
+ MaterialStorage::get_singleton()->shaders.tex_blit_shader.version_free(version);
+ }
+}
+
+GLES3::ShaderData *GLES3::_create_tex_blit_shader_func() {
+ TexBlitShaderData *shader_data = memnew(TexBlitShaderData);
+ return shader_data;
+}
+
+void TexBlitMaterialData::update_parameters(const HashMap &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
+ update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, false);
+}
+
+void TexBlitMaterialData::bind_uniforms() {
+ glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniform_buffer);
+
+ bind_uniforms_generic(texture_cache, shader_data->texture_uniforms, 1);
+}
+
+TexBlitMaterialData::~TexBlitMaterialData() {
+}
+
+GLES3::MaterialData *GLES3::_create_tex_blit_material_func(ShaderData *p_shader) {
+ TexBlitMaterialData *material_data = memnew(TexBlitMaterialData);
+ material_data->shader_data = static_cast(p_shader);
+ //update will happen later anyway so do nothing.
+ return material_data;
+}
+
#endif // !GLES3_ENABLED
diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h
index ec93b8fbdc..7136f1a03d 100644
--- a/drivers/gles3/storage/material_storage.h
+++ b/drivers/gles3/storage/material_storage.h
@@ -43,6 +43,7 @@
#include "drivers/gles3/shaders/particles.glsl.gen.h"
#include "drivers/gles3/shaders/scene.glsl.gen.h"
#include "drivers/gles3/shaders/sky.glsl.gen.h"
+#include "drivers/gles3/shaders/tex_blit.glsl.gen.h"
namespace GLES3 {
@@ -423,6 +424,52 @@ struct ParticleProcessMaterialData : public MaterialData {
MaterialData *_create_particles_material_func(ShaderData *p_shader);
+/* Texture Blit Shader */
+
+struct TexBlitShaderData : public ShaderData {
+ enum BlendMode { // Used internally.
+ BLEND_MODE_MIX,
+ BLEND_MODE_ADD,
+ BLEND_MODE_SUB,
+ BLEND_MODE_MUL,
+ BLEND_MODE_DISABLED,
+ };
+
+ bool valid;
+ RID version;
+
+ Vector texture_uniforms;
+
+ Vector ubo_offsets;
+ uint32_t ubo_size;
+
+ String code;
+
+ BlendMode blend_mode;
+
+ virtual void set_code(const String &p_code);
+ virtual bool is_animated() const;
+ virtual bool casts_shadows() const;
+ virtual RS::ShaderNativeSourceCode get_native_source_code() const;
+
+ TexBlitShaderData();
+ virtual ~TexBlitShaderData();
+};
+
+ShaderData *_create_tex_blit_shader_func();
+
+struct TexBlitMaterialData : public MaterialData {
+ TexBlitShaderData *shader_data = nullptr;
+
+ virtual void set_render_priority(int p_priority) {}
+ virtual void set_next_pass(RID p_pass) {}
+ virtual void update_parameters(const HashMap &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
+ virtual void bind_uniforms();
+ virtual ~TexBlitMaterialData();
+};
+
+MaterialData *_create_tex_blit_material_func(ShaderData *p_shader);
+
/* Global shader uniform structs */
struct GlobalShaderUniforms {
enum {
@@ -560,11 +607,13 @@ public:
SkyShaderGLES3 sky_shader;
SceneShaderGLES3 scene_shader;
ParticlesShaderGLES3 particles_process_shader;
+ TexBlitShaderGLES3 tex_blit_shader;
ShaderCompiler compiler_canvas;
ShaderCompiler compiler_scene;
ShaderCompiler compiler_particles;
ShaderCompiler compiler_sky;
+ ShaderCompiler compiler_tex_blit;
} shaders;
/* GLOBAL SHADER UNIFORM API */
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index bd7b1db65c..178c7749d0 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -303,6 +303,94 @@ TextureStorage::~TextureStorage() {
sdf_shader.shader.version_free(sdf_shader.shader_version);
}
+// Has to be a separate call from TextureStorage initialization due to interacting with MaterialStorage
+void TextureStorage::_tex_blit_shader_initialize() {
+ GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
+
+ {
+ String global_defines;
+ global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now
+ material_storage->shaders.tex_blit_shader.initialize(global_defines, 1);
+ }
+
+ {
+ // default material and shader for Texture Blit shader
+ tex_blit_shader.default_shader = material_storage->shader_allocate();
+ material_storage->shader_initialize(tex_blit_shader.default_shader);
+ material_storage->shader_set_code(tex_blit_shader.default_shader, R"(
+// Default Texture Blit shader.
+
+shader_type texture_blit;
+render_mode blend_mix;
+
+uniform sampler2D source_texture0 : hint_blit_source0;
+uniform sampler2D source_texture1 : hint_blit_source1;
+uniform sampler2D source_texture2 : hint_blit_source2;
+uniform sampler2D source_texture3 : hint_blit_source3;
+
+void blit() {
+ // Copies from each whole source texture to a rect on each output texture.
+ COLOR0 = texture(source_texture0, UV) * MODULATE;
+ COLOR1 = texture(source_texture1, UV) * MODULATE;
+ COLOR2 = texture(source_texture2, UV) * MODULATE;
+ COLOR3 = texture(source_texture3, UV) * MODULATE;
+}
+)");
+ tex_blit_shader.default_material = material_storage->material_allocate();
+ material_storage->material_initialize(tex_blit_shader.default_material);
+ material_storage->material_set_shader(tex_blit_shader.default_material, tex_blit_shader.default_shader);
+ }
+
+ {
+ // Set up Frame & Vertex Buffers for TextureBlit Shaders
+ // Just a 1x1 Quad to draw
+ glGenFramebuffers(1, &tex_blit_fbo);
+
+ glGenBuffers(1, &tex_blit_quad);
+ glBindBuffer(GL_ARRAY_BUFFER, tex_blit_quad);
+
+ const float qv[12] = {
+ -1.0f,
+ -1.0f,
+ 1.0f,
+ -1.0f,
+ 1.0f,
+ 1.0f,
+ -1.0f,
+ -1.0f,
+ 1.0f,
+ 1.0f,
+ -1.0f,
+ 1.0f,
+ };
+
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, qv, GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+
+ glGenVertexArrays(1, &tex_blit_quad_array);
+ glBindVertexArray(tex_blit_quad_array);
+ glBindBuffer(GL_ARRAY_BUFFER, tex_blit_quad);
+ glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
+ glEnableVertexAttribArray(RS::ARRAY_VERTEX);
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+ }
+
+ tex_blit_shader.initialized = true;
+}
+
+// Has to be a separate call from TextureStorage destruction due to interacting with Material Storage
+void TextureStorage::_tex_blit_shader_free() {
+ if (tex_blit_shader.initialized) {
+ GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
+ glDeleteFramebuffers(1, &tex_blit_fbo);
+ glDeleteBuffers(1, &tex_blit_quad);
+ glDeleteVertexArrays(1, &tex_blit_quad_array);
+ material_storage->material_free(tex_blit_shader.default_material);
+ material_storage->shader_free(tex_blit_shader.default_shader);
+ }
+}
+
/* Canvas Texture API */
RID TextureStorage::canvas_texture_allocate() {
@@ -1113,6 +1201,51 @@ void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) {
texture_owner.initialize_rid(p_texture, proxy_tex);
}
+void TextureStorage::texture_drawable_initialize(RID p_texture, int p_width, int p_height, RS::TextureDrawableFormat p_format, const Color &p_color, bool p_with_mipmaps) {
+ // Behaves identically to Texture_2D_Initialize by generating a white image based on parameters.
+
+ // GUARDRAIL: Bad Widths/Heights
+ ERR_FAIL_COND_MSG(p_width <= 0 || p_height <= 0, "Drawable Texture Width or Height cannot be less than 1.");
+ ERR_FAIL_COND_MSG(p_width >= 16384 || p_height >= 16384, "Drawable Texture Width or Height cannot be greater than 16383.");
+
+ Image::Format format;
+ switch (p_format) {
+ case RS::TEXTURE_DRAWABLE_FORMAT_RGBA8:
+ format = Image::FORMAT_RGBA8;
+ break;
+ case RS::TEXTURE_DRAWABLE_FORMAT_RGBA8_SRGB:
+ format = Image::FORMAT_RGBA8;
+ break;
+ case RS::TEXTURE_DRAWABLE_FORMAT_RGBAH:
+ format = Image::FORMAT_RGBAH;
+ break;
+ case RS::TEXTURE_DRAWABLE_FORMAT_RGBAF:
+ format = Image::FORMAT_RGBAF;
+ break;
+ default:
+ format = Image::FORMAT_RGBA8;
+ }
+
+ Ref image = Image::create_empty(p_width, p_height, p_with_mipmaps, format);
+ image->fill(p_color);
+ Texture texture;
+ texture.width = image->get_width();
+ texture.height = image->get_height();
+ texture.alloc_width = texture.width;
+ texture.alloc_height = texture.height;
+ texture.mipmaps = image->get_mipmap_count() + 1;
+ texture.format = image->get_format();
+ texture.type = Texture::TYPE_2D;
+ texture.target = GL_TEXTURE_2D;
+ _get_gl_image_and_format(Ref(), texture.format, texture.real_format, texture.gl_format_cache, texture.gl_internal_format_cache, texture.gl_type_cache, texture.compressed, false);
+ texture.total_data_size = image->get_image_data_size(texture.width, texture.height, texture.format, texture.mipmaps);
+ texture.active = true;
+ glGenTextures(1, &texture.tex_id);
+ GLES3::Utilities::get_singleton()->texture_allocated_data(texture.tex_id, texture.total_data_size, "Texture 2D");
+ texture_owner.initialize_rid(p_texture, texture);
+ texture_set_data(p_texture, image);
+}
+
RID TextureStorage::texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers, RS::TextureLayeredType p_layered_type) {
Texture texture;
texture.active = true;
@@ -1231,6 +1364,135 @@ void TextureStorage::texture_remap_proxies(RID p_from_texture, RID p_to_texture)
}
}
+// Output textures in p_textures must ALL BE THE SAME SIZE
+void TextureStorage::texture_drawable_blit_rect(const TypedArray &p_textures, const Rect2i &p_rect, RID p_material, const Color &p_modulate, const TypedArray &p_source_textures, int p_to_mipmap) {
+ ERR_FAIL_COND_MSG(!tex_blit_shader.initialized, "Texture Blit shader & materials not yet initialized.");
+ ERR_FAIL_COND_MSG(p_textures.size() == 0 || p_source_textures.size() == 0, "Blit Rect texture output and source arrays must contain at least 1 texture.");
+ GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
+
+ TexBlitMaterialData *m = static_cast(material_storage->material_get_data(p_material, RS::SHADER_TEXTURE_BLIT));
+ if (!m) {
+ m = static_cast(material_storage->material_get_data(tex_blit_shader.default_material, RS::SHADER_TEXTURE_BLIT));
+ }
+ // GUARDRAIL: p_material MUST BE ShaderType TextureBlit
+ ERR_FAIL_NULL(m);
+
+ TexBlitShaderGLES3::ShaderVariant variant = TexBlitShaderGLES3::MODE_DEFAULT;
+ RID version = tex_blit_shader.default_shader_version;
+ if (m->shader_data->version.is_valid() && m->shader_data->valid) {
+ // Must be called to force user ShaderMaterials to actually populate uniform buffer before binding
+ // NOTE: Not an ideal work around, maybe in the future this can only update this MaterialData and remove it from the queue, instead of processing all queued updates
+ material_storage->_update_queued_materials();
+ // Bind material uniform buffer and textures.
+ m->bind_uniforms();
+ version = m->shader_data->version;
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, tex_blit_fbo);
+ TightLocalVector draw_buffers;
+
+ Texture *tar_textures[4];
+ int convert_to_srgb_mask = 0;
+ Texture *src_textures[4];
+
+ int i = 0;
+ uint32_t specialization = 0;
+ const int outputFlagArray[4] = { 0, TexBlitShaderGLES3::USE_OUTPUT1, TexBlitShaderGLES3::USE_OUTPUT2, TexBlitShaderGLES3::USE_OUTPUT3 };
+ const int srgbMaskArray[4] = { 1, 2, 4, 8 };
+ while (i < 4) {
+ // Attach Targets to Framebuffer
+ if (i < p_textures.size()) {
+ tar_textures[i] = get_texture(p_textures[i]);
+ ERR_FAIL_NULL_MSG(tar_textures[i], "Drawable Texture target cannot be null.");
+ if (i > 0) {
+ ERR_FAIL_COND_MSG(texture_get_size(p_textures[i - 1]) != texture_get_size(p_textures[i]), "All Blit_Rect output textures must be same size.");
+ }
+ specialization |= outputFlagArray[i];
+ draw_buffers.push_back(GL_COLOR_ATTACHMENT0 + i);
+ ERR_FAIL_COND_MSG(p_to_mipmap >= tar_textures[i]->mipmaps, vformat("Drawable Texture Target does not have mipmap level %d.", p_to_mipmap));
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, tar_textures[i]->tex_id, p_to_mipmap);
+ convert_to_srgb_mask += tar_textures[i]->drawable_type == RS::TEXTURE_DRAWABLE_FORMAT_RGBA8_SRGB ? srgbMaskArray[i] : 0;
+ }
+
+ // Bind Sources to buffer. Use placeholder Black Texture if source is bad.
+ if (i < p_source_textures.size()) {
+ src_textures[i] = get_texture(p_source_textures[i]);
+ if (!src_textures[i]) {
+ src_textures[i] = get_texture(default_gl_textures[DEFAULT_GL_TEXTURE_WHITE]);
+ }
+ } else {
+ src_textures[i] = get_texture(default_gl_textures[DEFAULT_GL_TEXTURE_WHITE]);
+ }
+ int shift = i == 0 ? 0 : GLES3::Config::get_singleton()->max_texture_image_units - i;
+ glActiveTexture(GL_TEXTURE0 + shift);
+ glBindTexture(GL_TEXTURE_2D, src_textures[i]->tex_id);
+
+ i += 1;
+ }
+
+ bool success = material_storage->shaders.tex_blit_shader.version_bind_shader(version, variant, specialization);
+ if (!success) {
+ return;
+ }
+
+ // Calculates the Rects Offset & Size in UV space for Shader to scale Vertex Quad correctly
+ Vector3 size = texture_get_size(p_textures[0]);
+ Vector2 offset = Vector2(p_rect.position.x / size.x, p_rect.position.y / size.y);
+ Vector2 rect_size = Vector2(p_rect.size.x / size.x, p_rect.size.y / size.y);
+ Vector2i vp_size = Vector2i(tar_textures[0]->alloc_width, tar_textures[0]->alloc_height);
+ if (p_to_mipmap != 0) {
+ vp_size.x >>= p_to_mipmap;
+ vp_size.y >>= p_to_mipmap;
+ }
+
+ glViewport(0, 0, vp_size.x, vp_size.y);
+
+ material_storage->shaders.tex_blit_shader.version_set_uniform(TexBlitShaderGLES3::CONVERT_TO_SRGB, convert_to_srgb_mask, version, variant, specialization);
+ material_storage->shaders.tex_blit_shader.version_set_uniform(TexBlitShaderGLES3::SIZE, rect_size, version, variant, specialization);
+ material_storage->shaders.tex_blit_shader.version_set_uniform(TexBlitShaderGLES3::OFFSET, offset, version, variant, specialization);
+ material_storage->shaders.tex_blit_shader.version_set_uniform(TexBlitShaderGLES3::MODULATE, p_modulate, version, variant, specialization);
+ material_storage->shaders.tex_blit_shader.version_set_uniform(TexBlitShaderGLES3::TIME, RasterizerGLES3::get_singleton()->get_total_time(), version, variant, specialization);
+
+ // Set Blend_Mode correctly
+ GLES3::TexBlitShaderData::BlendMode blend_mode = m->shader_data->blend_mode;
+ glEnable(GL_BLEND);
+ switch (blend_mode) {
+ case GLES3::TexBlitShaderData::BLEND_MODE_ADD:
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE);
+ break;
+
+ case GLES3::TexBlitShaderData::BLEND_MODE_SUB:
+ glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
+ glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE);
+ break;
+
+ case GLES3::TexBlitShaderData::BLEND_MODE_MIX:
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ break;
+
+ case GLES3::TexBlitShaderData::BLEND_MODE_MUL:
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_DST_ALPHA, GL_ZERO);
+ break;
+
+ case GLES3::TexBlitShaderData::BLEND_MODE_DISABLED:
+ glDisable(GL_BLEND);
+ break;
+ }
+
+ glDrawBuffers(draw_buffers.size(), draw_buffers.ptr());
+
+ // DRAW!!
+ glBindVertexArray(tex_blit_quad_array);
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+ glBindVertexArray(0);
+
+ // Reset to system FBO
+ glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
+}
+
void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) {
texture_2d_initialize(p_texture, texture_2d_placeholder);
}
@@ -1537,6 +1799,17 @@ Vector[> TextureStorage::texture_3d_get(RID p_texture) const {
return ret;
}
+void TextureStorage::texture_drawable_generate_mipmaps(RID p_texture) {
+ Texture *texture = get_texture(p_texture);
+ Vector3i size = texture_get_size(p_texture);
+ CopyEffects::get_singleton()->bilinear_blur(texture->tex_id, texture->mipmaps, Rect2i(0, 0, size.x, size.y));
+}
+
+RID TextureStorage::texture_drawable_get_default_material() const {
+ // This should never be called before DrawableTexture stuff is initialized.
+ return tex_blit_shader.default_material;
+}
+
void TextureStorage::texture_replace(RID p_texture, RID p_by_texture) {
Texture *tex_to = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL(tex_to);
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index a77c361df8..2f153c9297 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -182,6 +182,7 @@ struct Texture {
Type type = TYPE_2D;
RS::TextureLayeredType layered_type = RS::TEXTURE_LAYERED_2D_ARRAY;
+ RS::TextureDrawableFormat drawable_type = RS::TEXTURE_DRAWABLE_FORMAT_RGBA8;
GLenum target = GL_TEXTURE_2D;
GLenum gl_format_cache = 0;
@@ -487,9 +488,25 @@ private:
RID shader_version;
} sdf_shader;
+ /* Texture Blit Shader API */
+
+ struct TexBlitShader {
+ bool initialized = false;
+ RID default_shader;
+ RID default_material;
+ RID default_shader_version;
+ } tex_blit_shader;
+
+ GLuint tex_blit_fbo;
+ GLuint tex_blit_quad;
+ GLuint tex_blit_quad_array;
+
public:
static TextureStorage *get_singleton();
+ void _tex_blit_shader_initialize();
+ void _tex_blit_shader_free();
+
TextureStorage();
virtual ~TextureStorage();
@@ -535,6 +552,7 @@ public:
virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector][> &p_data) override;
virtual void texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent
+ virtual void texture_drawable_initialize(RID p_texture, int p_width, int p_height, RS::TextureDrawableFormat p_format, const Color &p_color, bool p_with_mipmaps) override;
virtual RID texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY) override;
@@ -544,6 +562,8 @@ public:
virtual void texture_proxy_update(RID p_proxy, RID p_base) override;
void texture_remap_proxies(RID p_from_texture, RID p_to_texture);
+ virtual void texture_drawable_blit_rect(const TypedArray &p_textures, const Rect2i &p_rect, RID p_material, const Color &p_modulate, const TypedArray &p_source_textures, int p_to_mipmap) override;
+
Ref texture_2d_placeholder;
Vector][> texture_2d_array_placeholder;
Vector][> cubemap_placeholder;
@@ -558,6 +578,9 @@ public:
virtual Ref texture_2d_layer_get(RID p_texture, int p_layer) const override;
virtual Vector][> texture_3d_get(RID p_texture) const override;
+ virtual void texture_drawable_generate_mipmaps(RID p_texture) override;
+ virtual RID texture_drawable_get_default_material() const override;
+
virtual void texture_replace(RID p_texture, RID p_by_texture) override;
virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) override;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 4e20ae4ada..54b3d6cc05 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -9225,6 +9225,10 @@ EditorNode::EditorNode() {
canvas_item_mat_convert.instantiate();
resource_conversion_plugins.push_back(canvas_item_mat_convert);
+ Ref blit_mat_convert;
+ blit_mat_convert.instantiate();
+ resource_conversion_plugins.push_back(blit_mat_convert);
+
Ref particles_mat_convert;
particles_mat_convert.instantiate();
resource_conversion_plugins.push_back(particles_mat_convert);
diff --git a/editor/icons/BlitMaterial.svg b/editor/icons/BlitMaterial.svg
new file mode 100644
index 0000000000..b46db60c74
--- /dev/null
+++ b/editor/icons/BlitMaterial.svg
@@ -0,0 +1 @@
+
diff --git a/editor/icons/DrawableTexture2D.svg b/editor/icons/DrawableTexture2D.svg
new file mode 100644
index 0000000000..695d709cb1
--- /dev/null
+++ b/editor/icons/DrawableTexture2D.svg
@@ -0,0 +1 @@
+
diff --git a/editor/scene/material_editor_plugin.cpp b/editor/scene/material_editor_plugin.cpp
index a5554ebe29..88ce1ba62d 100644
--- a/editor/scene/material_editor_plugin.cpp
+++ b/editor/scene/material_editor_plugin.cpp
@@ -42,6 +42,7 @@
#include "scene/gui/label.h"
#include "scene/gui/subviewport_container.h"
#include "scene/main/viewport.h"
+#include "scene/resources/blit_material.h"
#include "scene/resources/canvas_item_material.h"
#include "scene/resources/particle_process_material.h"
@@ -499,3 +500,42 @@ Ref CanvasItemMaterialConversionPlugin::convert(const Ref &p
ERR_FAIL_COND_V(!Object::cast_to(*p_resource) || !Object::cast_to(*p_resource)->_is_initialized(), Ref());
return MaterialEditor::make_shader_material(p_resource);
}
+
+String BlitMaterialConversionPlugin::converts_to() const {
+ return "ShaderMaterial";
+}
+
+bool BlitMaterialConversionPlugin::handles(const Ref &p_resource) const {
+ Ref mat = p_resource;
+ return mat.is_valid();
+}
+
+Ref BlitMaterialConversionPlugin::convert(const Ref &p_resource) const {
+ Ref mat = p_resource;
+ ERR_FAIL_COND_V(mat.is_null(), Ref());
+
+ Ref smat;
+ smat.instantiate();
+
+ Ref shader;
+ shader.instantiate();
+
+ String code = RS::get_singleton()->shader_get_code(mat->get_shader_rid());
+
+ shader->set_code(code);
+
+ smat->set_shader(shader);
+
+ List params;
+ RS::get_singleton()->get_shader_parameter_list(mat->get_shader_rid(), ¶ms);
+
+ for (const PropertyInfo &E : params) {
+ Variant value = RS::get_singleton()->material_get_param(mat->get_rid(), E.name);
+ smat->set_shader_parameter(E.name, value);
+ }
+
+ smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
+ return smat;
+}
diff --git a/editor/scene/material_editor_plugin.h b/editor/scene/material_editor_plugin.h
index f79f748c3f..c6c02c8d35 100644
--- a/editor/scene/material_editor_plugin.h
+++ b/editor/scene/material_editor_plugin.h
@@ -153,3 +153,12 @@ public:
virtual bool handles(const Ref &p_resource) const override;
virtual Ref convert(const Ref &p_resource) const override;
};
+
+class BlitMaterialConversionPlugin : public EditorResourceConversionPlugin {
+ GDCLASS(BlitMaterialConversionPlugin, EditorResourceConversionPlugin);
+
+public:
+ virtual String converts_to() const override;
+ virtual bool handles(const Ref &p_resource) const override;
+ virtual Ref convert(const Ref &p_resource) const override;
+};
diff --git a/editor/shader/text_shader_editor.cpp b/editor/shader/text_shader_editor.cpp
index b6a5ef5f7d..41e9d7a87d 100644
--- a/editor/shader/text_shader_editor.cpp
+++ b/editor/shader/text_shader_editor.cpp
@@ -379,6 +379,8 @@ void ShaderTextEditor::_check_shader_mode() {
mode = Shader::MODE_SKY;
} else if (type == "fog") {
mode = Shader::MODE_FOG;
+ } else if (type == "texture_blit") {
+ mode = Shader::MODE_TEXTURE_BLIT;
} else {
mode = Shader::MODE_SPATIAL;
}
diff --git a/editor/shader/text_shader_language_plugin.cpp b/editor/shader/text_shader_language_plugin.cpp
index 530e8ab805..48ce7bff71 100644
--- a/editor/shader/text_shader_language_plugin.cpp
+++ b/editor/shader/text_shader_language_plugin.cpp
@@ -125,6 +125,13 @@ void fog() {
// of the associated FogVolume. This means that froxels that just barely touch
// a given FogVolume will still be used.
}
+)";
+ } break;
+ case Shader::MODE_TEXTURE_BLIT: {
+ code += R"(
+void blit() {
+ // Called for each pixel inside the given rect on the DrawableTexture.
+}
)";
} break;
case Shader::MODE_MAX: {
diff --git a/editor/shader/visual_shader_editor_plugin.cpp b/editor/shader/visual_shader_editor_plugin.cpp
index 2ddfe38fbe..21cf3e6ac1 100644
--- a/editor/shader/visual_shader_editor_plugin.cpp
+++ b/editor/shader/visual_shader_editor_plugin.cpp
@@ -1732,6 +1732,10 @@ void VisualShaderEditor::_get_current_mode_limits(int &r_begin_type, int &r_end_
} break;
case Shader::MODE_FOG: {
r_begin_type = VisualShader::TYPE_FOG;
+ r_end_type = VisualShader::TYPE_TEXTURE_BLIT;
+ } break;
+ case Shader::MODE_TEXTURE_BLIT: {
+ r_begin_type = VisualShader::TYPE_TEXTURE_BLIT;
r_end_type = VisualShader::TYPE_MAX;
} break;
default: {
@@ -2470,6 +2474,7 @@ void VisualShaderEditor::_set_mode(int p_which) {
edit_type_particles->set_visible(false);
edit_type_sky->set_visible(true);
edit_type_fog->set_visible(false);
+ edit_type_texture_blit->set_visible(false);
edit_type = edit_type_sky;
custom_mode_box->set_visible(false);
varying_button->hide();
@@ -2479,6 +2484,7 @@ void VisualShaderEditor::_set_mode(int p_which) {
edit_type_particles->set_visible(false);
edit_type_sky->set_visible(false);
edit_type_fog->set_visible(true);
+ edit_type_texture_blit->set_visible(false);
edit_type = edit_type_fog;
custom_mode_box->set_visible(false);
varying_button->hide();
@@ -2488,6 +2494,7 @@ void VisualShaderEditor::_set_mode(int p_which) {
edit_type_particles->set_visible(true);
edit_type_sky->set_visible(false);
edit_type_fog->set_visible(false);
+ edit_type_texture_blit->set_visible(false);
edit_type = edit_type_particles;
if ((edit_type->get_selected() + 3) > VisualShader::TYPE_PROCESS) {
custom_mode_box->set_visible(false);
@@ -2496,11 +2503,26 @@ void VisualShaderEditor::_set_mode(int p_which) {
}
varying_button->hide();
mode = MODE_FLAGS_PARTICLES;
+ } else if (p_which == VisualShader::MODE_TEXTURE_BLIT) {
+ edit_type_standard->set_visible(false);
+ edit_type_particles->set_visible(false);
+ edit_type_sky->set_visible(false);
+ edit_type_fog->set_visible(false);
+ edit_type_texture_blit->set_visible(true);
+ edit_type = edit_type_texture_blit;
+ if ((edit_type->get_selected() + 3) > VisualShader::TYPE_PROCESS) {
+ custom_mode_box->set_visible(false);
+ } else {
+ custom_mode_box->set_visible(true);
+ }
+ varying_button->hide();
+ mode = MODE_FLAGS_TEXTURE_BLIT;
} else {
edit_type_particles->set_visible(false);
edit_type_standard->set_visible(true);
edit_type_sky->set_visible(false);
edit_type_fog->set_visible(false);
+ edit_type_texture_blit->set_visible(false);
edit_type = edit_type_standard;
custom_mode_box->set_visible(false);
varying_button->show();
@@ -2519,6 +2541,9 @@ void VisualShaderEditor::_set_mode(int p_which) {
upper_type = VisualShader::TYPE_FOG;
} else if (mode & MODE_FLAGS_FOG) {
default_type = VisualShader::TYPE_FOG;
+ upper_type = VisualShader::TYPE_TEXTURE_BLIT;
+ } else if (mode & MODE_FLAGS_TEXTURE_BLIT) {
+ default_type = VisualShader::TYPE_TEXTURE_BLIT;
upper_type = VisualShader::TYPE_MAX;
}
@@ -5679,6 +5704,8 @@ void VisualShaderEditor::_type_selected(int p_id) {
offset = VisualShader::TYPE_SKY;
} else if (mode & MODE_FLAGS_FOG) {
offset = VisualShader::TYPE_FOG;
+ } else if (mode & MODE_FLAGS_TEXTURE_BLIT) {
+ offset = VisualShader::TYPE_TEXTURE_BLIT;
}
set_current_shader_type(VisualShader::Type(p_id + offset));
@@ -6686,6 +6713,11 @@ VisualShaderEditor::VisualShaderEditor() {
edit_type_fog->select(0);
edit_type_fog->connect(SceneStringName(item_selected), callable_mp(this, &VisualShaderEditor::_type_selected));
+ edit_type_texture_blit = memnew(OptionButton);
+ edit_type_texture_blit->add_item(TTR("Blit"));
+ edit_type_texture_blit->select(0);
+ edit_type_texture_blit->connect(SceneStringName(item_selected), callable_mp(this, &VisualShaderEditor::_type_selected));
+
edit_type = edit_type_standard;
toolbar_hflow->add_child(custom_mode_box);
@@ -6698,6 +6730,8 @@ VisualShaderEditor::VisualShaderEditor() {
toolbar_hflow->move_child(edit_type_sky, 0);
toolbar_hflow->add_child(edit_type_fog);
toolbar_hflow->move_child(edit_type_fog, 0);
+ toolbar_hflow->add_child(edit_type_texture_blit);
+ toolbar_hflow->move_child(edit_type_texture_blit, 0);
add_node = memnew(Button);
add_node->set_theme_type_variation(SceneStringName(FlatButton));
@@ -7192,6 +7226,7 @@ VisualShaderEditor::VisualShaderEditor() {
const String input_param_for_fragment_shader_mode = TTR("'%s' input parameter for fragment shader mode.") + translation_gdsl;
const String input_param_for_sky_shader_mode = TTR("'%s' input parameter for sky shader mode.") + translation_gdsl;
const String input_param_for_fog_shader_mode = TTR("'%s' input parameter for fog shader mode.") + translation_gdsl;
+ const String input_param_for_texture_blit_shader_mode = TTR("'%s' input parameter for blit shader mode.") + translation_gdsl;
const String input_param_for_light_shader_mode = TTR("'%s' input parameter for light shader mode.") + translation_gdsl;
const String input_param_for_vertex_shader_mode = TTR("'%s' input parameter for vertex shader mode.") + translation_gdsl;
const String input_param_for_start_shader_mode = TTR("'%s' input parameter for start shader mode.") + translation_gdsl;
@@ -7341,6 +7376,15 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("UVW", "Input/Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "uvw", "UVW"), { "uvw" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FOG, Shader::MODE_FOG));
add_options.push_back(AddOption("WorldPosition", "Input/Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "world_position", "WORLD_POSITION"), { "world_position" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ // TEXTURE BLIT INPUTS
+ add_options.push_back(AddOption("UV", "Input/Texture_blit", "VisualShaderNodeInput", vformat(input_param_for_texture_blit_shader_mode, "uv", "UV"), { "uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_BLIT, Shader::MODE_TEXTURE_BLIT));
+ add_options.push_back(AddOption("Modulate", "Input/Texture_blit", "VisualShaderNodeInput", vformat(input_param_for_texture_blit_shader_mode, "Modulate", "MODULATE"), { "Modulate" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_BLIT, Shader::MODE_TEXTURE_BLIT));
+ add_options.push_back(AddOption("Fragcoord", "Input/Texture_blit", "VisualShaderNodeInput", vformat(input_param_for_texture_blit_shader_mode, "Fragcoord", "FRAGCOORD"), { "Fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_BLIT, Shader::MODE_TEXTURE_BLIT));
+ add_options.push_back(AddOption("Source Texture", "Input/Texture_blit", "VisualShaderNodeInput", vformat(input_param_for_texture_blit_shader_mode, "Source Texture", "source_texture"), { "Source Texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_BLIT, Shader::MODE_TEXTURE_BLIT));
+ add_options.push_back(AddOption("Source Texture 2", "Input/Texture_blit", "VisualShaderNodeInput", vformat(input_param_for_texture_blit_shader_mode, "Source Texture 2", "source_texture2"), { "Source Texture 2" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_BLIT, Shader::MODE_TEXTURE_BLIT));
+ add_options.push_back(AddOption("Source Texture 3", "Input/Texture_blit", "VisualShaderNodeInput", vformat(input_param_for_texture_blit_shader_mode, "Source Texture 3", "source_texture3"), { "Source Texture 3" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_BLIT, Shader::MODE_TEXTURE_BLIT));
+ add_options.push_back(AddOption("Source Texture 4", "Input/Texture_blit", "VisualShaderNodeInput", vformat(input_param_for_texture_blit_shader_mode, "Source Texture 4", "source_texture4"), { "Source Texture 4" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_BLIT, Shader::MODE_TEXTURE_BLIT));
+
// PARTICLES INPUTS
add_options.push_back(AddOption("CollisionDepth", "Input/Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_depth", "COLLISION_DEPTH"), { "collision_depth" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
@@ -8288,7 +8332,7 @@ void EditorPropertyVisualShaderMode::_option_selected(int p_which) {
}
//4. delete varyings (if needed)
- if (p_which == VisualShader::MODE_PARTICLES || p_which == VisualShader::MODE_SKY || p_which == VisualShader::MODE_FOG) {
+ if (p_which == VisualShader::MODE_PARTICLES || p_which == VisualShader::MODE_SKY || p_which == VisualShader::MODE_FOG || p_which == VisualShader::MODE_TEXTURE_BLIT) {
int var_count = visual_shader->get_varyings_count();
if (var_count > 0) {
diff --git a/editor/shader/visual_shader_editor_plugin.h b/editor/shader/visual_shader_editor_plugin.h
index 3854315c86..c2ac2e380f 100644
--- a/editor/shader/visual_shader_editor_plugin.h
+++ b/editor/shader/visual_shader_editor_plugin.h
@@ -232,6 +232,7 @@ class VisualShaderEditor : public ShaderEditor {
OptionButton *edit_type_particles = nullptr;
OptionButton *edit_type_sky = nullptr;
OptionButton *edit_type_fog = nullptr;
+ OptionButton *edit_type_texture_blit = nullptr;
CheckBox *custom_mode_box = nullptr;
bool custom_mode_enabled = false;
@@ -297,6 +298,7 @@ class VisualShaderEditor : public ShaderEditor {
MODE_FLAGS_SKY = 2,
MODE_FLAGS_PARTICLES = 4,
MODE_FLAGS_FOG = 8,
+ MODE_FLAGS_TEXTURE_BLIT = 16,
};
int mode = MODE_FLAGS_SPATIAL_CANVASITEM;
@@ -324,6 +326,10 @@ class VisualShaderEditor : public ShaderEditor {
TYPE_FLAGS_FOG = 1,
};
+ enum TextureBlitTypeFlags {
+ TYPE_FLAGS_BLIT = 1,
+ };
+
enum ToolsMenuOptions {
EXPAND_ALL,
COLLAPSE_ALL
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index f01e82cd86..dd51cdc624 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -113,6 +113,7 @@
#include "scene/resources/audio_stream_polyphonic.h"
#include "scene/resources/audio_stream_wav.h"
#include "scene/resources/bit_map.h"
+#include "scene/resources/blit_material.h"
#include "scene/resources/bone_map.h"
#include "scene/resources/camera_attributes.h"
#include "scene/resources/camera_texture.h"
@@ -121,6 +122,7 @@
#include "scene/resources/compositor.h"
#include "scene/resources/compressed_texture.h"
#include "scene/resources/curve_texture.h"
+#include "scene/resources/drawable_texture_2d.h"
#include "scene/resources/environment.h"
#include "scene/resources/external_texture.h"
#include "scene/resources/font.h"
@@ -889,8 +891,10 @@ void register_scene_types() {
GDREGISTER_CLASS(ShaderMaterial);
GDREGISTER_CLASS(CanvasTexture);
GDREGISTER_CLASS(CanvasItemMaterial);
+
SceneTree::add_idle_callback(CanvasItemMaterial::flush_changes);
CanvasItemMaterial::init_shaders();
+ GDREGISTER_CLASS(BlitMaterial);
/* REGISTER 2D */
@@ -1057,6 +1061,7 @@ void register_scene_types() {
GDREGISTER_CLASS(GradientTexture2D);
GDREGISTER_CLASS(CameraTexture);
GDREGISTER_CLASS(ExternalTexture);
+ GDREGISTER_CLASS(DrawableTexture2D);
GDREGISTER_VIRTUAL_CLASS(TextureLayered);
GDREGISTER_ABSTRACT_CLASS(ImageTextureLayered);
GDREGISTER_VIRTUAL_CLASS(Texture3D);
@@ -1475,6 +1480,7 @@ void unregister_scene_types() {
ParticleProcessMaterial::finish_shaders();
CanvasItemMaterial::finish_shaders();
ColorPickerShape::finish_shaders();
+ BlitMaterial::cleanup_shader();
GraphEdit::finish_shaders();
SceneStringNames::free();
diff --git a/scene/resources/blit_material.cpp b/scene/resources/blit_material.cpp
new file mode 100644
index 0000000000..785f18ab7e
--- /dev/null
+++ b/scene/resources/blit_material.cpp
@@ -0,0 +1,140 @@
+/**************************************************************************/
+/* blit_material.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "blit_material.h"
+
+#include "core/version.h"
+
+void BlitMaterial::_update_shader(BlendMode p_blend) {
+ MutexLock shader_lock(shader_mutex);
+ int index = int(p_blend);
+ if (shader_cache[p_blend].is_null()) {
+ shader_cache[p_blend] = RS::get_singleton()->shader_create();
+ String code = "// NOTE: Shader automatically converted from " GODOT_VERSION_NAME " " GODOT_VERSION_FULL_CONFIG "'s BlitMaterial.\n\n";
+
+ code += "shader_type texture_blit;\nrender_mode ";
+ switch (p_blend) {
+ case BLEND_MODE_MIX:
+ code += "blend_mix";
+ break;
+ case BLEND_MODE_ADD:
+ code += "blend_add";
+ break;
+ case BLEND_MODE_SUB:
+ code += "blend_sub";
+ break;
+ case BLEND_MODE_MUL:
+ code += "blend_mul";
+ break;
+ case BLEND_MODE_DISABLED:
+ code += "blend_disabled";
+ break;
+ default:
+ code += "blend_mix";
+ break;
+ }
+ code += ";\n\n";
+
+ code += "uniform sampler2D source_texture0 : hint_blit_source0;\n";
+ code += "uniform sampler2D source_texture1 : hint_blit_source1;\n";
+ code += "uniform sampler2D source_texture2 : hint_blit_source2;\n";
+ code += "uniform sampler2D source_texture3 : hint_blit_source3;\n\n";
+
+ code += "void blit() {\n";
+ code += " // Copies from each whole source texture to a rect on each output texture.\n";
+ code += " COLOR0 = texture(source_texture0, UV) * MODULATE;\n";
+ code += " COLOR1 = texture(source_texture1, UV) * MODULATE;\n";
+ code += " COLOR2 = texture(source_texture2, UV) * MODULATE;\n";
+ code += " COLOR3 = texture(source_texture3, UV) * MODULATE;\n}";
+ RS::get_singleton()->shader_set_code(shader_cache[index], code);
+ }
+}
+
+void BlitMaterial::set_blend_mode(BlendMode p_blend_mode) {
+ blend_mode = p_blend_mode;
+ _update_shader(blend_mode);
+ if (shader_set) {
+ RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(blend_mode)]);
+ }
+}
+
+BlitMaterial::BlendMode BlitMaterial::get_blend_mode() const {
+ return blend_mode;
+}
+
+RID BlitMaterial::get_shader_rid() const {
+ _update_shader(blend_mode);
+ return shader_cache[int(blend_mode)];
+}
+
+Shader::Mode BlitMaterial::get_shader_mode() const {
+ return Shader::MODE_TEXTURE_BLIT;
+}
+
+RID BlitMaterial::get_rid() const {
+ _update_shader(blend_mode);
+ if (!shader_set) {
+ RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(blend_mode)]);
+ shader_set = true;
+ }
+ return _get_material();
+}
+
+Mutex BlitMaterial::shader_mutex;
+RID BlitMaterial::shader_cache[5];
+
+void BlitMaterial::cleanup_shader() {
+ for (int i = 0; i < 5; i++) {
+ if (shader_cache[i].is_valid()) {
+ RS::get_singleton()->free_rid(shader_cache[i]);
+ }
+ }
+}
+
+void BlitMaterial::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_blend_mode", "blend_mode"), &BlitMaterial::set_blend_mode);
+ ClassDB::bind_method(D_METHOD("get_blend_mode"), &BlitMaterial::get_blend_mode);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply,Disabled"), "set_blend_mode", "get_blend_mode");
+
+ BIND_ENUM_CONSTANT(BLEND_MODE_MIX);
+ BIND_ENUM_CONSTANT(BLEND_MODE_ADD);
+ BIND_ENUM_CONSTANT(BLEND_MODE_SUB);
+ BIND_ENUM_CONSTANT(BLEND_MODE_MUL);
+ BIND_ENUM_CONSTANT(BLEND_MODE_DISABLED);
+}
+
+BlitMaterial::BlitMaterial() {
+ _set_material(RS::get_singleton()->material_create());
+ set_blend_mode(BLEND_MODE_MIX);
+}
+
+BlitMaterial::~BlitMaterial() {
+}
diff --git a/scene/resources/blit_material.h b/scene/resources/blit_material.h
new file mode 100644
index 0000000000..aeb7f22d14
--- /dev/null
+++ b/scene/resources/blit_material.h
@@ -0,0 +1,72 @@
+/**************************************************************************/
+/* blit_material.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#pragma once
+
+#include "scene/resources/material.h"
+
+class BlitMaterial : public Material {
+ GDCLASS(BlitMaterial, Material);
+
+public:
+ enum BlendMode {
+ BLEND_MODE_MIX,
+ BLEND_MODE_ADD,
+ BLEND_MODE_SUB,
+ BLEND_MODE_MUL,
+ BLEND_MODE_DISABLED
+ };
+
+private:
+ static Mutex shader_mutex;
+ static RID shader_cache[5];
+ static void _update_shader(BlendMode p_blend);
+ mutable bool shader_set = false;
+
+ BlendMode blend_mode = BLEND_MODE_MIX;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_blend_mode(BlendMode p_blend_mode);
+ BlendMode get_blend_mode() const;
+
+ virtual Shader::Mode get_shader_mode() const override;
+ virtual RID get_shader_rid() const override;
+ virtual RID get_rid() const override;
+
+ static void cleanup_shader();
+
+ BlitMaterial();
+ virtual ~BlitMaterial();
+};
+
+VARIANT_ENUM_CAST(BlitMaterial::BlendMode);
diff --git a/scene/resources/drawable_texture_2d.cpp b/scene/resources/drawable_texture_2d.cpp
new file mode 100644
index 0000000000..8cb9cf8899
--- /dev/null
+++ b/scene/resources/drawable_texture_2d.cpp
@@ -0,0 +1,241 @@
+/**************************************************************************/
+/* drawable_texture_2d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "drawable_texture_2d.h"
+
+DrawableTexture2D::DrawableTexture2D() {
+ default_material = RS::get_singleton()->texture_drawable_get_default_material();
+}
+
+DrawableTexture2D::~DrawableTexture2D() {
+ if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RenderingServer::get_singleton()->free_rid(texture);
+ }
+}
+
+// Initialize Texture Resource with a call to rendering server. Overwrite existing.
+void DrawableTexture2D::_initialize() {
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_drawable_create(width, height, (RS::TextureDrawableFormat)format, base_color, mipmaps);
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_drawable_create(width, height, (RS::TextureDrawableFormat)format, base_color, mipmaps);
+ }
+}
+
+// Setup basic parameters on the Drawable Texture
+void DrawableTexture2D::setup(int p_width, int p_height, DrawableFormat p_format, const Color &p_color, bool p_use_mipmaps) {
+ ERR_FAIL_COND_MSG(p_width <= 0 || p_width > 16384, "Texture dimensions have to be in the 1 to 16384 range.");
+ ERR_FAIL_COND_MSG(p_height <= 0 || p_height > 16384, "Texture dimensions have to be in the 1 to 16384 range.");
+ width = p_width;
+ height = p_height;
+ format = p_format;
+ mipmaps = p_use_mipmaps;
+ base_color = p_color;
+ _initialize();
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void DrawableTexture2D::set_width(int p_width) {
+ ERR_FAIL_COND_MSG(p_width <= 0 || p_width > 16384, "Texture dimensions have to be in the 1 to 16384 range.");
+ if (width == p_width) {
+ return;
+ }
+ width = p_width;
+ notify_property_list_changed();
+ emit_changed();
+}
+
+int DrawableTexture2D::get_width() const {
+ return width;
+}
+
+void DrawableTexture2D::set_height(int p_height) {
+ ERR_FAIL_COND_MSG(p_height <= 0 || p_height > 16384, "Texture dimensions have to be in the 1 to 16384 range.");
+ if (height == p_height) {
+ return;
+ }
+ height = p_height;
+ notify_property_list_changed();
+ emit_changed();
+}
+
+int DrawableTexture2D::get_height() const {
+ return height;
+}
+
+void DrawableTexture2D::set_format(DrawableFormat p_format) {
+ if (format == p_format) {
+ return;
+ }
+ format = p_format;
+ notify_property_list_changed();
+ emit_changed();
+}
+
+DrawableTexture2D::DrawableFormat DrawableTexture2D::get_format() const {
+ return format;
+}
+
+void DrawableTexture2D::set_use_mipmaps(bool p_mipmaps) {
+ if (mipmaps == p_mipmaps) {
+ return;
+ }
+ mipmaps = p_mipmaps;
+ notify_property_list_changed();
+ emit_changed();
+}
+
+bool DrawableTexture2D::get_use_mipmaps() const {
+ return mipmaps;
+}
+
+RID DrawableTexture2D::get_rid() const {
+ if (texture.is_null()) {
+ // We are in trouble, create something temporary.
+ // 4, 4, false, Image::FORMAT_RGBA8
+ texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+ return texture;
+}
+
+void DrawableTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
+ if ((width | height) == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(width, height)), texture, false, p_modulate, p_transpose);
+}
+
+void DrawableTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
+ if ((width | height) == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
+}
+
+void DrawableTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
+ if ((width | height) == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
+}
+
+// Perform a blit operation from the given source to the given rect on self.
+void DrawableTexture2D::blit_rect(const Rect2i p_rect, const Ref &p_source, const Color &p_modulate, int p_mipmap, const Ref &p_material) {
+ // Use user Shader if exists.
+ RID material = default_material;
+ if (p_material.is_valid()) {
+ material = p_material->get_rid();
+ if (p_material->get_shader_mode() != Shader::MODE_TEXTURE_BLIT) {
+ WARN_PRINT("ShaderMaterial passed to blit_rect() is not a texture_blit shader. Using default instead.");
+ }
+ }
+
+ // Rendering server expects textureParameters as a TypedArray[RID]
+ Array textures;
+ textures.push_back(texture);
+
+ if (p_source.is_valid()) {
+ ERR_FAIL_COND_MSG(texture == p_source->get_rid(), "Cannot use self as a source.");
+ }
+ Array src_textures;
+ if (Ref(p_source).is_valid()) {
+ WARN_PRINT("AtlasTexture not supported as a source for blit_rect. Using default White.");
+ src_textures.push_back(RID());
+ } else {
+ src_textures.push_back(p_source);
+ }
+
+ RS::get_singleton()->texture_drawable_blit_rect(textures, p_rect, material, p_modulate, src_textures, p_mipmap);
+ notify_property_list_changed();
+}
+
+// Perform a blit operation from the given sources to the given rect on self and extra targets
+void DrawableTexture2D::blit_rect_multi(const Rect2i p_rect, const TypedArray &p_sources, const TypedArray &p_extra_targets, const Color &p_modulate, int p_mipmap, const Ref &p_material) {
+ RID material = default_material;
+ if (p_material.is_valid()) {
+ material = p_material->get_rid();
+ if (p_material->get_shader_mode() != Shader::MODE_TEXTURE_BLIT) {
+ WARN_PRINT("ShaderMaterial passed to blit_rect_multi() is not a texture_blit shader. Using default instead.");
+ }
+ }
+
+ // Rendering server expects textureParameters as a TypedArray[RID]
+ Array textures;
+ textures.push_back(texture);
+ int i = 0;
+ while (i < p_extra_targets.size()) {
+ textures.push_back(RID(p_extra_targets[i]));
+ i += 1;
+ }
+ i = 0;
+ Array src_textures;
+ while (i < p_sources.size()) {
+ if (Ref(p_sources[i]).is_valid()) {
+ WARN_PRINT("AtlasTexture not supported as a source for blit_rect. Using default White.");
+ src_textures.push_back(RID());
+ } else {
+ src_textures.push_back(RID(p_sources[i]));
+ }
+ ERR_FAIL_COND_MSG(textures.has(RID(src_textures[i])), "Cannot use self as a source.");
+ i += 1;
+ }
+
+ RS::get_singleton()->texture_drawable_blit_rect(textures, p_rect, material, p_modulate, src_textures, p_mipmap);
+ notify_property_list_changed();
+}
+
+Ref DrawableTexture2D::get_image() const {
+ if (texture.is_valid()) {
+ return RS::get_singleton()->texture_2d_get(texture);
+ } else {
+ return Ref();
+ }
+}
+
+void DrawableTexture2D::generate_mipmaps() {
+ if (texture.is_valid()) {
+ RS::get_singleton()->texture_drawable_generate_mipmaps(texture);
+ }
+}
+
+void DrawableTexture2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("setup", "width", "height", "format", "color", "use_mipmaps"), &DrawableTexture2D::setup, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("blit_rect", "rect", "source", "modulate", "mipmap", "material"), &DrawableTexture2D::blit_rect, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(0), DEFVAL(Ref()));
+ ClassDB::bind_method(D_METHOD("blit_rect_multi", "rect", "sources", "extra_targets", "modulate", "mipmap", "material"), &DrawableTexture2D::blit_rect_multi, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(0), DEFVAL(Ref()));
+ ClassDB::bind_method(D_METHOD("generate_mipmaps"), &DrawableTexture2D::generate_mipmaps);
+
+ BIND_ENUM_CONSTANT(DRAWABLE_FORMAT_RGBA8);
+ BIND_ENUM_CONSTANT(DRAWABLE_FORMAT_RGBA8_SRGB);
+ BIND_ENUM_CONSTANT(DRAWABLE_FORMAT_RGBAH);
+ BIND_ENUM_CONSTANT(DRAWABLE_FORMAT_RGBAF);
+}
diff --git a/scene/resources/drawable_texture_2d.h b/scene/resources/drawable_texture_2d.h
new file mode 100644
index 0000000000..38616f703c
--- /dev/null
+++ b/scene/resources/drawable_texture_2d.h
@@ -0,0 +1,95 @@
+/**************************************************************************/
+/* drawable_texture_2d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#pragma once
+
+#include "scene/resources/atlas_texture.h"
+#include "scene/resources/image_texture.h"
+#include "scene/resources/material.h"
+
+class DrawableTexture2D : public Texture2D {
+ GDCLASS(DrawableTexture2D, Texture2D);
+ RES_BASE_EXTENSION("tex");
+
+public:
+ enum DrawableFormat {
+ DRAWABLE_FORMAT_RGBA8,
+ DRAWABLE_FORMAT_RGBA8_SRGB,
+ DRAWABLE_FORMAT_RGBAH,
+ DRAWABLE_FORMAT_RGBAF,
+ };
+
+private:
+ mutable RID texture;
+ int width = 64;
+ int height = 64;
+ bool mipmaps = false;
+ DrawableFormat format = DRAWABLE_FORMAT_RGBA8;
+
+ Color base_color = Color(1, 1, 1, 1);
+
+ RID default_material;
+
+ void _initialize();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_width(int p_width);
+ int get_width() const override;
+ void set_height(int p_height);
+ int get_height() const override;
+
+ void set_format(DrawableFormat p_format);
+ DrawableFormat get_format() const;
+ void set_use_mipmaps(bool p_mipmaps);
+ bool get_use_mipmaps() const;
+
+ virtual RID get_rid() const override;
+
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
+
+ void setup(int p_width, int p_height, DrawableFormat p_format, const Color &p_modulate = Color(1, 1, 1, 1), bool p_use_mipmaps = false);
+
+ void blit_rect(const Rect2i p_rect, const Ref &p_source, const Color &p_modulate = Color(1, 1, 1, 1), int p_mipmap = 0, const Ref &p_material = Ref());
+ void blit_rect_multi(const Rect2i p_rect, const TypedArray &p_sources, const TypedArray &p_extra_targets, const Color &p_modulate = Color(1, 1, 1, 1), int p_mipmap = 0, const Ref &p_material = Ref());
+
+ virtual Ref get_image() const override;
+
+ void generate_mipmaps();
+
+ DrawableTexture2D();
+ ~DrawableTexture2D();
+};
+
+VARIANT_ENUM_CAST(DrawableTexture2D::DrawableFormat)
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 0e8b41fd5b..c650f92ee5 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -118,6 +118,8 @@ void Shader::set_code(const String &p_code) {
mode = MODE_SKY;
} else if (type == "fog") {
mode = MODE_FOG;
+ } else if (type == "texture_blit") {
+ mode = MODE_TEXTURE_BLIT;
} else {
mode = MODE_SPATIAL;
}
@@ -290,6 +292,7 @@ void Shader::_bind_methods() {
BIND_ENUM_CONSTANT(MODE_PARTICLES);
BIND_ENUM_CONSTANT(MODE_SKY);
BIND_ENUM_CONSTANT(MODE_FOG);
+ BIND_ENUM_CONSTANT(MODE_TEXTURE_BLIT);
}
Shader::Shader() {
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index a48aaa175c..5d2395114e 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -48,6 +48,7 @@ public:
MODE_PARTICLES,
MODE_SKY,
MODE_FOG,
+ MODE_TEXTURE_BLIT,
MODE_MAX
};
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index dba5527174..1ab06d2ec2 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -37,7 +37,7 @@
#include "visual_shader_particle_nodes.h"
String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name) {
- static const char *typepf[VisualShader::TYPE_MAX] = { "vtx", "frg", "lgt", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" };
+ static const char *typepf[VisualShader::TYPE_MAX] = { "vtx", "frg", "lgt", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog", "texture_blit" };
return p_name + "_" + String(typepf[p_type]) + "_" + itos(p_id);
}
@@ -1718,6 +1718,7 @@ static const char *type_string[VisualShader::TYPE_MAX] = {
"process_custom",
"sky",
"fog",
+ "texture_blit",
};
bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
@@ -1958,7 +1959,7 @@ void VisualShader::reset_state() {
void VisualShader::_get_property_list(List *p_list) const {
//mode
- p_list->push_back(PropertyInfo(Variant::INT, PNAME("mode"), PROPERTY_HINT_ENUM, "Spatial,CanvasItem,Particles,Sky,Fog"));
+ p_list->push_back(PropertyInfo(Variant::INT, PNAME("mode"), PROPERTY_HINT_ENUM, "Spatial,CanvasItem,Particles,Sky,Fog,TextureBlit"));
//render modes
HashMap blend_mode_enums;
@@ -2683,7 +2684,7 @@ void VisualShader::_update_shader() const {
Vector default_tex_params;
HashSet classes;
HashMap insertion_pos;
- static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles", "sky", "fog" };
+ static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles", "sky", "fog", "texture_blit" };
global_code += String() + "shader_type " + shader_mode_str[shader_mode] + ";\n";
@@ -2786,7 +2787,7 @@ void VisualShader::_update_shader() const {
global_code += "stencil_mode " + stencil_mode + ";\n\n";
}
- static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" };
+ static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog", "blit" };
String global_expressions;
HashSet used_parameter_names;
@@ -2794,6 +2795,13 @@ void VisualShader::_update_shader() const {
HashMap> emitters;
HashMap> varying_setters;
+ if (shader_mode == Shader::MODE_TEXTURE_BLIT) {
+ global_code += "uniform sampler2D source_texture0 : hint_blit_source0;\n";
+ global_code += "uniform sampler2D source_texture1 : hint_blit_source1;\n";
+ global_code += "uniform sampler2D source_texture2 : hint_blit_source2;\n";
+ global_code += "uniform sampler2D source_texture3 : hint_blit_source3;\n\n";
+ }
+
for (int i = 0, index = 0; i < TYPE_MAX; i++) {
if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) {
continue;
@@ -2905,7 +2913,7 @@ void VisualShader::_update_shader() const {
HashSet processed;
bool is_empty_func = false;
- if (shader_mode != Shader::MODE_PARTICLES && shader_mode != Shader::MODE_SKY && shader_mode != Shader::MODE_FOG) {
+ if (shader_mode != Shader::MODE_PARTICLES && shader_mode != Shader::MODE_SKY && shader_mode != Shader::MODE_FOG && shader_mode != Shader::MODE_TEXTURE_BLIT) {
is_empty_func = true;
}
@@ -3221,6 +3229,7 @@ void VisualShader::_bind_methods() {
BIND_ENUM_CONSTANT(TYPE_PROCESS_CUSTOM);
BIND_ENUM_CONSTANT(TYPE_SKY);
BIND_ENUM_CONSTANT(TYPE_FOG);
+ BIND_ENUM_CONSTANT(TYPE_TEXTURE_BLIT);
BIND_ENUM_CONSTANT(TYPE_MAX);
BIND_ENUM_CONSTANT(VARYING_MODE_VERTEX_TO_FRAG_LIGHT);
@@ -3550,6 +3559,15 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "uvw", "UVW" },
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "world_position", "WORLD_POSITION" },
+ // Blit, Blit
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" },
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "modulate", "MODULATE" },
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_SAMPLER, "source_texture", "source_texture" },
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_SAMPLER, "source_texture2", "source_texture2" },
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_SAMPLER, "source_texture3", "source_texture3" },
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_SAMPLER, "source_texture4", "source_texture4" },
+
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
@@ -3630,6 +3648,11 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Blit
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "modulate", "MODULATE" },
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_SAMPLER, "source_texture", "source_texture" },
+
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
@@ -4265,6 +4288,14 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Albedo", "ALBEDO" },
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Emission", "EMISSION" },
+ ////////////////////////////////////////////////////////////////////////
+ // Blit, Blit.
+ ////////////////////////////////////////////////////////////////////////
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color0", "COLOR0" },
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color1", "COLOR1" },
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color2", "COLOR2" },
+ { Shader::MODE_TEXTURE_BLIT, VisualShader::TYPE_TEXTURE_BLIT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color3", "COLOR3" },
+
////////////////////////////////////////////////////////////////////////
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index d3312816ef..5375fef1e9 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -53,6 +53,7 @@ public:
TYPE_PROCESS_CUSTOM,
TYPE_SKY,
TYPE_FOG,
+ TYPE_TEXTURE_BLIT,
TYPE_MAX
};
diff --git a/servers/rendering/dummy/storage/material_storage.cpp b/servers/rendering/dummy/storage/material_storage.cpp
index 8276eba136..eaf517b9f9 100644
--- a/servers/rendering/dummy/storage/material_storage.cpp
+++ b/servers/rendering/dummy/storage/material_storage.cpp
@@ -178,6 +178,8 @@ void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) {
new_mode = RS::SHADER_SKY;
} else if (mode_string == "fog") {
new_mode = RS::SHADER_FOG;
+ } else if (mode_string == "texture_blit") {
+ new_mode = RS::SHADER_TEXTURE_BLIT;
} else {
new_mode = RS::SHADER_MAX;
ERR_FAIL_MSG("Shader type " + mode_string + " not supported in Dummy renderer.");
diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h
index 0d5d38c919..5b3e024cbe 100644
--- a/servers/rendering/dummy/storage/texture_storage.h
+++ b/servers/rendering/dummy/storage/texture_storage.h
@@ -88,6 +88,7 @@ public:
virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector][> &p_data) override {}
virtual void texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override {}
virtual void texture_proxy_initialize(RID p_texture, RID p_base) override {} //all slices, then all the mipmaps, must be coherent
+ virtual void texture_drawable_initialize(RID p_texture, int p_width, int p_height, RS::TextureDrawableFormat p_format, const Color &p_color, bool p_with_mipmaps) override {}
virtual RID texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY) override { return RID(); }
@@ -96,6 +97,8 @@ public:
virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override {}
virtual void texture_proxy_update(RID p_proxy, RID p_base) override {}
+ virtual void texture_drawable_blit_rect(const TypedArray &p_textures, const Rect2i &p_rect, RID p_material, const Color &p_modulate, const TypedArray &p_source_textures, int p_to_mipmap) override {}
+
//these two APIs can be used together or in combination with the others.
virtual void texture_2d_placeholder_initialize(RID p_texture) override {}
virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override {}
@@ -109,6 +112,9 @@ public:
virtual Ref texture_2d_layer_get(RID p_texture, int p_layer) const override { return Ref(); }
virtual Vector][> texture_3d_get(RID p_texture) const override { return Vector][>(); }
+ virtual void texture_drawable_generate_mipmaps(RID p_texture) override {}
+ virtual RID texture_drawable_get_default_material() const override { return RID(); }
+
virtual void texture_replace(RID p_texture, RID p_by_texture) override { texture_free(p_by_texture); }
virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) override {}
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
index 517d833729..ac91d196d4 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
@@ -164,6 +164,7 @@ void RendererCompositorRD::initialize() {
uint64_t RendererCompositorRD::frame = 1;
void RendererCompositorRD::finalize() {
+ texture_storage->_tex_blit_shader_free();
memdelete(scene);
memdelete(canvas);
memdelete(fog);
@@ -318,6 +319,7 @@ RendererCompositorRD::RendererCompositorRD() {
particles_storage = memnew(RendererRD::ParticlesStorage);
fog = memnew(RendererRD::Fog);
canvas = memnew(RendererCanvasRenderRD());
+ texture_storage->_tex_blit_shader_initialize();
String rendering_method = OS::get_singleton()->get_current_rendering_method();
uint64_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE);
diff --git a/servers/rendering/renderer_rd/shaders/tex_blit.glsl b/servers/rendering/renderer_rd/shaders/tex_blit.glsl
new file mode 100644
index 0000000000..99f06b64e9
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/tex_blit.glsl
@@ -0,0 +1,128 @@
+/* clang-format off */
+
+#[vertex]
+
+#version 450
+
+// Has to be same push_constant for vertex & frag
+layout(push_constant, std430) uniform TexBlitData {
+ vec2 offset;
+ vec2 size;
+ vec4 modulate;
+ vec2 pad;
+ int convert_to_srgb;
+ float time;
+} data;
+
+layout(location = 0) out vec2 uv;
+
+void main() {
+ vec2 base_arr[6] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(0.0), vec2(1.0, 0.0), vec2(1.0, 1.0));
+ uv = base_arr[gl_VertexIndex];
+ // gl_Position = vec4(uv * 2.0 - 1.0, 0.0, 1.0);
+
+ gl_Position = vec4( (data.offset + (uv * data.size)) * 2.0 - 1.0, 1.0, 1.0);
+}
+
+#[fragment]
+
+#version 450
+
+#VERSION_DEFINES
+
+#include "samplers_inc.glsl"
+
+#define OUTPUT0_SRGB uint(1)
+#define OUTPUT1_SRGB uint(2)
+#define OUTPUT2_SRGB uint(4)
+#define OUTPUT3_SRGB uint(8)
+
+layout(push_constant, std430) uniform TexBlitData {
+ vec2 offset;
+ vec2 size;
+ vec4 modulate;
+ vec2 pad;
+ int convert_to_srgb;
+ float time;
+} data;
+
+layout(set = 0, binding = 0) uniform texture2D source0;
+
+layout(set = 0, binding = 1) uniform texture2D source1;
+
+layout(set = 0, binding = 2) uniform texture2D source2;
+
+layout(set = 0, binding = 3) uniform texture2D source3;
+
+layout(location = 0) in vec2 uv;
+
+layout (location = 0) out vec4 out_color0;
+
+#ifdef USE_OUTPUT1
+layout (location = 1) out vec4 out_color1;
+#endif
+
+#ifdef USE_OUTPUT2
+layout (location = 2) out vec4 out_color2;
+#endif
+
+#ifdef USE_OUTPUT3
+layout (location = 3) out vec4 out_color3;
+#endif
+
+#ifdef MATERIAL_UNIFORMS_USED
+layout(set = 1, binding = 0, std140) uniform MaterialUniforms {
+#MATERIAL_UNIFORMS
+} material;
+#endif
+
+#GLOBALS
+
+vec3 linear_to_srgb(vec3 color) {
+ // If going to srgb, clamp from 0 to 1.
+ color = clamp(color, vec3(0.0), vec3(1.0));
+ const vec3 a = vec3(0.055f);
+ return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
+}
+
+void main() {
+ // Handles the case where user code uses extra outputs, but extra output targets were not bound
+ vec4 color0 = vec4(0.0, 0.0, 0.0, 1.0);
+ vec4 color1 = vec4(0.0, 0.0, 0.0, 1.0);
+ vec4 color2 = vec4(0.0, 0.0, 0.0, 1.0);
+ vec4 color3 = vec4(0.0, 0.0, 0.0, 1.0);
+
+#CODE : BLIT
+
+ // Discards extra outputs if extra output targets were not bound
+ out_color0 = color0;
+
+#ifdef USE_OUTPUT1
+ out_color1 = color1;
+#endif
+#ifdef USE_OUTPUT2
+ out_color2 = color2;
+#endif
+#ifdef USE_OUTPUT3
+ out_color3 = color3;
+#endif
+
+ if (bool(data.convert_to_srgb & OUTPUT0_SRGB)) {
+ out_color0.rgb = linear_to_srgb(out_color0.rgb); // Regular linear -> SRGB conversion.
+ }
+#ifdef USE_OUTPUT1
+ if (bool(data.convert_to_srgb & OUTPUT1_SRGB)) {
+ out_color1.rgb = linear_to_srgb(out_color1.rgb);
+ }
+#endif
+#ifdef USE_OUTPUT2
+ if (bool(data.convert_to_srgb & OUTPUT2_SRGB)) {
+ out_color2.rgb = linear_to_srgb(out_color2.rgb);
+ }
+#endif
+#ifdef USE_OUTPUT3
+ if (bool(data.convert_to_srgb & OUTPUT3_SRGB)) {
+ out_color3.rgb = linear_to_srgb(out_color3.rgb);
+ }
+#endif
+}
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index bf117eea1e..87ddff0579 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -1243,6 +1243,178 @@ void MaterialStorage::MaterialData::set_as_used() {
}
}
+/* TextureBlit SHADER */
+
+void MaterialStorage::TexBlitShaderData::set_code(const String &p_code) {
+ TextureStorage *texture_storage = TextureStorage::get_singleton();
+ // Initialize and compile the shader.
+
+ code = p_code;
+ valid = false;
+ ubo_size = 0;
+ uniforms.clear();
+
+ if (code.is_empty()) {
+ return; // Just invalid, but no error.
+ }
+
+ ShaderCompiler::GeneratedCode gen_code;
+
+ // Actual enum set further down after compilation.
+ int blend_modei = BLEND_MODE_DISABLED;
+
+ ShaderCompiler::IdentifierActions actions;
+ actions.entry_point_stages["blit"] = ShaderCompiler::STAGE_FRAGMENT;
+
+ actions.render_mode_values["blend_add"] = Pair(&blend_modei, BLEND_MODE_ADD);
+ actions.render_mode_values["blend_mix"] = Pair(&blend_modei, BLEND_MODE_MIX);
+ actions.render_mode_values["blend_sub"] = Pair(&blend_modei, BLEND_MODE_SUB);
+ actions.render_mode_values["blend_mul"] = Pair(&blend_modei, BLEND_MODE_MUL);
+ actions.render_mode_values["blend_disabled"] = Pair(&blend_modei, BLEND_MODE_DISABLED);
+
+ actions.uniforms = &uniforms;
+ Error err = texture_storage->tex_blit_shader.compiler.compile(RS::SHADER_TEXTURE_BLIT, code, &actions, path, gen_code);
+ ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
+
+ if (version.is_null()) {
+ version = texture_storage->tex_blit_shader.shader.version_create();
+ }
+
+ blend_mode = BlendMode(blend_modei);
+
+#if 0
+ print_line("**compiling shader:");
+ print_line("**defines:\n");
+ for (int i = 0; i < gen_code.defines.size(); i++) {
+ print_line(gen_code.defines[i]);
+ }
+
+ HashMap::Iterator el = gen_code.code.begin();
+ while (el) {
+ print_line("\n**code " + el->key + ":\n" + el->value);
+ ++el;
+ }
+
+ print_line("\n**uniforms:\n" + gen_code.uniforms);
+ print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]);
+ print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]);
+#endif
+
+ texture_storage->tex_blit_shader.shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines);
+ ERR_FAIL_COND(!texture_storage->tex_blit_shader.shader.version_is_valid(version));
+
+ ubo_size = gen_code.uniform_total_size;
+ ubo_offsets = gen_code.uniform_offsets;
+ texture_uniforms = gen_code.texture_uniforms;
+
+ RD::PipelineColorBlendState blend_state_color_blend;
+ RD::PipelineColorBlendState::Attachment attachment;
+
+ // blend_mode_to_blend_attachment(blend_mode) does not work
+ // Because we want BlendAdd and BlendSub to behave differently for Texture_Blit
+ switch (blend_mode) {
+ case BLEND_MODE_MIX: {
+ attachment.enable_blend = true;
+ attachment.alpha_blend_op = RD::BLEND_OP_ADD;
+ attachment.color_blend_op = RD::BLEND_OP_ADD;
+ attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
+ attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ } break;
+ case BLEND_MODE_ADD: {
+ attachment.enable_blend = true;
+ attachment.alpha_blend_op = RD::BLEND_OP_ADD;
+ attachment.color_blend_op = RD::BLEND_OP_ADD;
+ attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ } break;
+ case BLEND_MODE_SUB: {
+ attachment.enable_blend = true;
+ attachment.alpha_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
+ attachment.color_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
+ attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ } break;
+ case BLEND_MODE_MUL: {
+ attachment.enable_blend = true;
+ attachment.alpha_blend_op = RD::BLEND_OP_ADD;
+ attachment.color_blend_op = RD::BLEND_OP_ADD;
+ attachment.src_color_blend_factor = RD::BLEND_FACTOR_DST_COLOR;
+ attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ZERO;
+ attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_DST_ALPHA;
+ attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
+ } break;
+ case BLEND_MODE_DISABLED:
+ default: {
+ // Use default attachment values.
+ } break;
+ }
+
+ blend_state_color_blend.attachments = { attachment, attachment, attachment, attachment };
+
+ // Update Pipelines
+ for (int i = 0; i < 4; i++) {
+ RID shader_variant = texture_storage->tex_blit_shader.shader.version_get_shader(version, i);
+
+ pipelines[i].setup(shader_variant, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state_color_blend, 0);
+ }
+
+ valid = true;
+}
+
+bool MaterialStorage::TexBlitShaderData::is_animated() const {
+ return false;
+}
+
+bool MaterialStorage::TexBlitShaderData::casts_shadows() const {
+ return false;
+}
+
+RS::ShaderNativeSourceCode MaterialStorage::TexBlitShaderData::get_native_source_code() const {
+ return TextureStorage::get_singleton()->tex_blit_shader.shader.version_get_native_source_code(version);
+}
+
+Pair MaterialStorage::TexBlitShaderData::get_native_shader_and_version() const {
+ return { &TextureStorage::get_singleton()->tex_blit_shader.shader, version };
+}
+
+MaterialStorage::TexBlitShaderData::TexBlitShaderData() {
+ valid = false;
+}
+
+MaterialStorage::TexBlitShaderData::~TexBlitShaderData() {
+ if (version.is_valid()) {
+ TextureStorage::get_singleton()->tex_blit_shader.shader.version_free(version);
+ }
+}
+
+MaterialStorage::ShaderData *MaterialStorage::_create_tex_blit_shader_func() {
+ MaterialStorage::TexBlitShaderData *shader_data = memnew(MaterialStorage::TexBlitShaderData);
+ return shader_data;
+}
+
+bool MaterialStorage::TexBlitMaterialData::update_parameters(const HashMap &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
+ uniform_set_updated = true;
+
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, TextureStorage::get_singleton()->tex_blit_shader.shader.version_get_shader(shader_data->version, 0), 1, true, false);
+}
+
+MaterialStorage::TexBlitMaterialData::~TexBlitMaterialData() {
+ free_parameters_uniform_set(uniform_set);
+}
+
+MaterialStorage::MaterialData *MaterialStorage::_create_tex_blit_material_func(MaterialStorage::ShaderData *p_shader) {
+ MaterialStorage::TexBlitMaterialData *material_data = memnew(TexBlitMaterialData);
+ material_data->shader_data = static_cast(p_shader);
+ //update will happen later anyway so do nothing.
+ return material_data;
+}
+
///////////////////////////////////////////////////////////////////////////
// MaterialStorage::Samplers
@@ -2033,6 +2205,8 @@ void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) {
new_type = SHADER_TYPE_SKY;
} else if (mode_string == "fog") {
new_type = SHADER_TYPE_FOG;
+ } else if (mode_string == "texture_blit") {
+ new_type = SHADER_TYPE_TEXTURE_BLIT;
} else {
new_type = SHADER_TYPE_MAX;
}
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h
index 3ca150646c..4ccff36955 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h
@@ -36,6 +36,7 @@
#include "core/templates/local_vector.h"
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
+#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
#include "servers/rendering/shader_compiler.h"
#include "servers/rendering/shader_language.h"
#include "servers/rendering/storage/material_storage.h"
@@ -51,6 +52,7 @@ public:
SHADER_TYPE_PARTICLES,
SHADER_TYPE_SKY,
SHADER_TYPE_FOG,
+ SHADER_TYPE_TEXTURE_BLIT,
SHADER_TYPE_MAX
};
@@ -134,6 +136,53 @@ public:
bool is_null() const;
};
+ /* Texture Blit Shader */
+
+ struct TexBlitShaderData : public ShaderData {
+ bool valid;
+ RID version;
+
+ PipelineCacheRD pipelines[4];
+ Vector texture_uniforms;
+
+ Vector ubo_offsets;
+ uint32_t ubo_size;
+
+ String code;
+
+ BlendMode blend_mode;
+
+ virtual void set_code(const String &p_Code);
+ virtual bool is_animated() const;
+ virtual bool casts_shadows() const;
+ virtual RS::ShaderNativeSourceCode get_native_source_code() const;
+ virtual Pair get_native_shader_and_version() const;
+
+ TexBlitShaderData();
+ virtual ~TexBlitShaderData();
+ };
+
+ ShaderData *_create_tex_blit_shader_func();
+ static MaterialStorage::ShaderData *_create_tex_blit_shader_funcs() {
+ return get_singleton()->_create_tex_blit_shader_func();
+ }
+
+ struct TexBlitMaterialData : public MaterialData {
+ TexBlitShaderData *shader_data = nullptr;
+ RID uniform_set;
+ bool uniform_set_updated;
+
+ virtual void set_render_priority(int p_priority) {}
+ virtual void set_next_pass(RID p_pass) {}
+ virtual bool update_parameters(const HashMap &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
+ virtual ~TexBlitMaterialData();
+ };
+
+ MaterialData *_create_tex_blit_material_func(ShaderData *p_shader);
+ static MaterialStorage::MaterialData *_create_tex_blit_material_funcs(MaterialStorage::ShaderData *p_shader) {
+ return get_singleton()->_create_tex_blit_material_func(static_cast(p_shader));
+ }
+
private:
static MaterialStorage *singleton;
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index 1cfb44e133..a60815a690 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -32,7 +32,9 @@
#include "../effects/copy_effects.h"
#include "../framebuffer_cache_rd.h"
+#include "../uniform_set_cache_rd.h"
#include "material_storage.h"
+#include "render_scene_buffers_rd.h"
#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
using namespace RendererRD;
@@ -633,6 +635,92 @@ TextureStorage::~TextureStorage() {
singleton = nullptr;
}
+// Has to be a separate call from TextureStorage initialization due to interacting with Material Storage
+void TextureStorage::_tex_blit_shader_initialize() {
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+
+ {
+ Vector tex_blit_modes;
+ tex_blit_modes.push_back(""); // Only 1 Output
+ tex_blit_modes.push_back("\n#define USE_OUTPUT1\n"); // 2 Outputs
+ tex_blit_modes.push_back("\n#define USE_OUTPUT1\n#define USE_OUTPUT2\n"); // 3 Outputs
+ tex_blit_modes.push_back("\n#define USE_OUTPUT1\n#define USE_OUTPUT2\n#define USE_OUTPUT3\n"); // 4 Outputs
+ String global_defines;
+ global_defines += "\n#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n";
+ global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now
+ tex_blit_shader.shader.initialize(tex_blit_modes, global_defines);
+ }
+ material_storage->shader_set_data_request_function(MaterialStorage::SHADER_TYPE_TEXTURE_BLIT, MaterialStorage::_create_tex_blit_shader_funcs);
+ material_storage->material_set_data_request_function(MaterialStorage::SHADER_TYPE_TEXTURE_BLIT, MaterialStorage::_create_tex_blit_material_funcs);
+
+ {
+ // Setup TextureBlit compiler
+ ShaderCompiler::DefaultIdentifierActions actions;
+
+ actions.renames["TIME"] = "data.time";
+ actions.renames["PI"] = _MKSTR(Math_PI);
+ actions.renames["TAU"] = _MKSTR(Math_TAU);
+ actions.renames["E"] = _MKSTR(Math_E);
+
+ actions.renames["FRAGCOORD"] = "gl_FragCoord";
+
+ actions.renames["UV"] = "uv";
+ actions.renames["MODULATE"] = "data.modulate";
+
+ actions.renames["COLOR0"] = "color0";
+ actions.renames["COLOR1"] = "color1";
+ actions.renames["COLOR2"] = "color2";
+ actions.renames["COLOR3"] = "color3";
+
+ actions.base_uniform_string = "material.";
+ actions.base_texture_binding_index = 1;
+ actions.texture_layout_set = 1;
+
+ tex_blit_shader.compiler.initialize(actions);
+ }
+
+ {
+ // default material and shader for Texture Blit shader
+ tex_blit_shader.default_shader = material_storage->shader_allocate();
+ material_storage->shader_initialize(tex_blit_shader.default_shader);
+ material_storage->shader_set_code(tex_blit_shader.default_shader, R"(
+// Default Texture Blit shader.
+
+shader_type texture_blit;
+render_mode blend_mix;
+
+uniform sampler2D source_texture0 : hint_blit_source0;
+uniform sampler2D source_texture1 : hint_blit_source1;
+uniform sampler2D source_texture2 : hint_blit_source2;
+uniform sampler2D source_texture3 : hint_blit_source3;
+
+void blit() {
+ // Copies from each whole source texture to a rect on each output texture.
+ COLOR0 = texture(source_texture0, UV) * MODULATE;
+ COLOR1 = texture(source_texture1, UV) * MODULATE;
+ COLOR2 = texture(source_texture2, UV) * MODULATE;
+ COLOR3 = texture(source_texture3, UV) * MODULATE;
+}
+)");
+ tex_blit_shader.default_material = material_storage->material_allocate();
+ material_storage->material_initialize(tex_blit_shader.default_material);
+ material_storage->material_set_shader(tex_blit_shader.default_material, tex_blit_shader.default_shader);
+ }
+
+ tex_blit_shader.initialized = true;
+}
+
+// Has to be a separate call from TextureStorage destruction due to interacting with Material Storage
+void TextureStorage::_tex_blit_shader_free() {
+ if (tex_blit_shader.initialized) {
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+
+ print_verbose("Freeing Default Tex_Blit Shader");
+ material_storage->material_free(tex_blit_shader.default_material);
+ material_storage->shader_free(tex_blit_shader.default_shader);
+ }
+}
+
bool TextureStorage::free(RID p_rid) {
if (owns_texture(p_rid)) {
texture_free(p_rid);
@@ -1164,6 +1252,106 @@ void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) {
tex->proxies.push_back(p_texture);
}
+void TextureStorage::texture_drawable_initialize(RID p_texture, int p_width, int p_height, RS::TextureDrawableFormat p_format, const Color &p_color, bool p_with_mipmaps) {
+ // Near identical to Texture_2D_initialize, Generates an empty white image based on parameters
+
+ // GUARDRAIL: Bad Widths/Heights
+ ERR_FAIL_COND_MSG(p_width <= 0 || p_height <= 0, "Drawable Texture Width or Height cannot be less than 1.");
+ ERR_FAIL_COND_MSG(p_width >= 16384 || p_height >= 16384, "Drawable Texture Width or Height cannot be greater than 16383.");
+
+ Image::Format format;
+ switch (p_format) {
+ case RS::TEXTURE_DRAWABLE_FORMAT_RGBA8:
+ format = Image::FORMAT_RGBA8;
+ break;
+ case RS::TEXTURE_DRAWABLE_FORMAT_RGBA8_SRGB:
+ format = Image::FORMAT_RGBA8;
+ break;
+ case RS::TEXTURE_DRAWABLE_FORMAT_RGBAH:
+ format = Image::FORMAT_RGBAH;
+ break;
+ case RS::TEXTURE_DRAWABLE_FORMAT_RGBAF:
+ format = Image::FORMAT_RGBAF;
+ break;
+ default:
+ format = Image::FORMAT_RGBA8;
+ }
+
+ Ref image = Image::create_empty(p_width, p_height, p_with_mipmaps, format);
+ image->fill(p_color);
+ TextureToRDFormat ret_format;
+
+ Ref valid_image = _validate_texture_format(image, ret_format);
+ Texture texture;
+
+ texture.type = TextureStorage::TYPE_2D;
+
+ texture.width = p_width;
+ texture.height = p_height;
+ texture.layers = 1;
+ texture.mipmaps = image->get_mipmap_count() + 1;
+ texture.depth = 1;
+ texture.format = image->get_format();
+ texture.validated_format = image->get_format();
+
+ texture.rd_type = RD::TEXTURE_TYPE_2D;
+ texture.rd_format = ret_format.format;
+ texture.rd_format_srgb = ret_format.format_srgb;
+
+ RD::TextureFormat rd_format;
+ RD::TextureView rd_view;
+ { //attempt register
+ rd_format.format = texture.rd_format;
+ rd_format.width = texture.width;
+ rd_format.height = texture.height;
+ rd_format.depth = 1;
+ rd_format.array_layers = 1;
+ rd_format.mipmaps = texture.mipmaps;
+ rd_format.texture_type = texture.rd_type;
+ rd_format.samples = RD::TEXTURE_SAMPLES_1;
+ // The Color Attachment Usage bit here is what differentiates a DrawableTexture from a regular Texture2D
+ rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) {
+ rd_format.shareable_formats.push_back(texture.rd_format);
+ rd_format.shareable_formats.push_back(texture.rd_format_srgb);
+ }
+ }
+ {
+ rd_view.swizzle_r = ret_format.swizzle_r;
+ rd_view.swizzle_g = ret_format.swizzle_g;
+ rd_view.swizzle_b = ret_format.swizzle_b;
+ rd_view.swizzle_a = ret_format.swizzle_a;
+ }
+
+ Vector data = image->get_data(); //use image data
+ Vector> data_slices;
+ data_slices.push_back(data);
+ texture.rd_texture = RD::get_singleton()->texture_create(rd_format, rd_view, data_slices);
+ ERR_FAIL_COND(texture.rd_texture.is_null());
+ if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) {
+ rd_view.format_override = texture.rd_format_srgb;
+ texture.rd_texture_srgb = RD::get_singleton()->texture_create_shared(rd_view, texture.rd_texture);
+ if (texture.rd_texture_srgb.is_null()) {
+ RD::get_singleton()->free_rid(texture.rd_texture);
+ ERR_FAIL_COND(texture.rd_texture_srgb.is_null());
+ }
+ }
+
+ // Used for Drawable Textures.
+ for (int i = 0; i < texture.mipmaps; i++) {
+ texture.cached_rd_slices.append(RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), texture.rd_texture, 0, i));
+ }
+
+ //used for 2D, overridable
+ texture.width_2d = texture.width;
+ texture.height_2d = texture.height;
+ texture.is_render_target = false;
+ texture.rd_view = rd_view;
+ texture.is_proxy = false;
+
+ texture_owner.initialize_rid(p_texture, texture);
+}
+
// Note: We make some big assumptions about format and usage. If developers need more control,
// they should use RD::texture_create_from_extension() instead.
RID TextureStorage::texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers, RS::TextureLayeredType p_layered_type) {
@@ -1474,6 +1662,145 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) {
}
}
+// Output textures in p_textures must ALL BE THE SAME SIZE
+void TextureStorage::texture_drawable_blit_rect(const TypedArray &p_textures, const Rect2i &p_rect, RID p_material, const Color &p_modulate, const TypedArray &p_source_textures, int p_to_mipmap) {
+ ERR_FAIL_COND_MSG(!tex_blit_shader.initialized, "Texture Blit shader & materials not yet initialized.");
+ ERR_FAIL_COND_MSG(p_textures.is_empty() || p_source_textures.is_empty(), "Blit Rect texture output and source arrays must contain at least 1 texture.");
+ const RID default_tex_rid = texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE);
+ RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
+
+ RendererRD::MaterialStorage::TexBlitMaterialData *m = static_cast(material_storage->material_get_data(p_material, RendererRD::MaterialStorage::SHADER_TYPE_TEXTURE_BLIT));
+ if (!m) {
+ m = static_cast(material_storage->material_get_data(tex_blit_shader.default_material, RendererRD::MaterialStorage::SHADER_TYPE_TEXTURE_BLIT));
+ }
+ // GUARDRAIL:: p_material MUST BE ShaderType TextureBlit
+ ERR_FAIL_NULL(m);
+
+ RendererRD::MaterialStorage::TexBlitShaderData *shader_data = m->shader_data;
+ ERR_FAIL_NULL(shader_data);
+ material_storage->_update_queued_materials();
+ RID shaderRD = tex_blit_shader.shader.version_get_shader(shader_data->version, p_source_textures.size() - 1);
+
+ RID tar_textures[4];
+ Texture *src_textures[4];
+
+ RID uniform_texture_set;
+ int TEX_BLIT_MATERIAL_SET = 1;
+ int TEX_BLIT_TEXTURE_SET = 0;
+ int srgb_mask = 0;
+ const int srgbMaskArray[4] = { 1, 2, 4, 8 };
+ LocalVector texture_uniforms;
+
+ int i = 0;
+ while (i < 4) {
+ // Load Target Textures
+ if (i < p_textures.size()) {
+ Texture *tex = get_texture(p_textures[i]);
+ srgb_mask += get_texture(p_textures[i])->drawable_type == RS::TEXTURE_DRAWABLE_FORMAT_RGBA8_SRGB ? srgbMaskArray[i] : 0;
+ ERR_FAIL_NULL_MSG(tex, "Drawable Texture target cannot be null.");
+ ERR_FAIL_COND_MSG(p_to_mipmap >= tex->mipmaps || p_to_mipmap >= tex->cached_rd_slices.size(), vformat("Drawable Texture Target does not have mipmap level %d.", p_to_mipmap));
+ if (i > 0) {
+ ERR_FAIL_COND_MSG(texture_2d_get_size(p_textures[i - 1]) != texture_2d_get_size(p_textures[i]), "All Blit_Rect output textures must be same size.");
+ }
+ tar_textures[i] = tex->cached_rd_slices[p_to_mipmap];
+ }
+
+ // Load and bind source textures, load default Black if source is bad.
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = i;
+ if (i < p_source_textures.size()) {
+ src_textures[i] = get_texture(p_source_textures[i]);
+ if (!src_textures[i]) {
+ u.append_id(default_tex_rid);
+ } else {
+ u.append_id(src_textures[i]->rd_texture);
+ }
+ } else {
+ u.append_id(default_tex_rid);
+ }
+ texture_uniforms.push_back(u);
+
+ i += 1;
+ }
+
+ // Calculates the Rects Offset & Size in UV space for Shader to scale Vertex Quad correctly
+ Vector2i size = texture_2d_get_size(p_textures[0]);
+ Vector2 offset = Vector2(float(p_rect.position.x) / size.x, float(p_rect.position.y) / size.y);
+ Vector2 rect_size = Vector2(float(p_rect.size.x) / size.x, float(p_rect.size.y) / size.y);
+
+ // Select Pipeline based on # of targets.
+ RID tex_blit_fb;
+ PipelineCacheRD *pipeline;
+ switch (p_textures.size()) {
+ case 1:
+ tex_blit_fb = FramebufferCacheRD::get_singleton()->get_cache(tar_textures[0]);
+ pipeline = &shader_data->pipelines[0];
+ break;
+ case 2:
+ tex_blit_fb = FramebufferCacheRD::get_singleton()->get_cache(tar_textures[0], tar_textures[1]);
+ pipeline = &shader_data->pipelines[1];
+ break;
+ case 3:
+ tex_blit_fb = FramebufferCacheRD::get_singleton()->get_cache(tar_textures[0], tar_textures[1], tar_textures[2]);
+ pipeline = &shader_data->pipelines[2];
+ break;
+ case 4:
+ tex_blit_fb = FramebufferCacheRD::get_singleton()->get_cache(tar_textures[0], tar_textures[1], tar_textures[2], tar_textures[3]);
+ pipeline = &shader_data->pipelines[3];
+ break;
+ default:
+ tex_blit_fb = FramebufferCacheRD::get_singleton()->get_cache(tar_textures[0], tar_textures[1], tar_textures[2], tar_textures[3]);
+ pipeline = &shader_data->pipelines[3];
+ }
+
+ // Bind uniforms via push_constant
+ TexBlitPushConstant push_constant;
+ memset(&push_constant, 0, sizeof(TexBlitPushConstant));
+
+ push_constant.offset[0] = offset.x;
+ push_constant.offset[1] = offset.y;
+ push_constant.size[0] = rect_size.x;
+ push_constant.size[1] = rect_size.y;
+ push_constant.modulate[0] = p_modulate.r;
+ push_constant.modulate[1] = p_modulate.g;
+ push_constant.modulate[2] = p_modulate.b;
+ push_constant.modulate[3] = p_modulate.a;
+ push_constant.convert_to_srgb = srgb_mask;
+ push_constant.time = RSG::rasterizer->get_total_time();
+
+ Rect2i tex_blit_rr;
+
+ RD::get_singleton()->draw_command_begin_label("Blit Rect");
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(tex_blit_fb, RD::DRAW_DEFAULT_ALL, Vector(), 1.0f, 0u, tex_blit_rr);
+
+ RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(tex_blit_fb);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline->get_render_pipeline(RD::INVALID_ID, fb_format, false, 0));
+
+ material_storage->samplers_rd_get_default().append_uniforms(texture_uniforms, 4);
+
+ uniform_texture_set = UniformSetCacheRD::get_singleton()->get_cache_vec(shaderRD, TEX_BLIT_TEXTURE_SET, texture_uniforms);
+
+ {
+ // Push Constants
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(TexBlitPushConstant));
+
+ // Material Uniforms
+ if (m->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(m->uniform_set)) { // Material may not have a uniform set.
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, m->uniform_set, TEX_BLIT_MATERIAL_SET);
+ }
+
+ // Texture Uniforms
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_texture_set, TEX_BLIT_TEXTURE_SET);
+ }
+
+ // DRAW!!
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 6u);
+
+ RD::get_singleton()->draw_list_end();
+ RD::get_singleton()->draw_command_end_label();
+}
+
//these two APIs can be used together or in combination with the others.
void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) {
texture_2d_initialize(p_texture, texture_2d_placeholder);
@@ -1599,6 +1926,38 @@ Vector][> TextureStorage::texture_3d_get(RID p_texture) const {
return ret;
}
+void TextureStorage::texture_drawable_generate_mipmaps(RID p_texture) {
+ Texture *tex = get_texture(p_texture);
+ CopyEffects *copy_effects = CopyEffects::get_singleton();
+ ERR_FAIL_NULL(copy_effects);
+
+ uint32_t mipmaps = tex->mipmaps;
+ int width = tex->width;
+ int height = tex->height;
+
+ RID source = tex->rd_texture;
+ RID dest = tex->cached_rd_slices[0];
+
+ for (uint32_t m = 1; m < mipmaps; m++) {
+ width = MAX(1, width >> 1);
+ height = MAX(1, height >> 1);
+
+ source = dest;
+ dest = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), source, 0, m, 1, RD::TEXTURE_SLICE_2D);
+
+ if (copy_effects->get_raster_effects().has_flag(CopyEffects::RASTER_EFFECT_COPY)) {
+ copy_effects->make_mipmap_raster(source, dest, Size2i(width, height));
+ } else {
+ copy_effects->make_mipmap(source, dest, Size2i(width, height));
+ }
+ }
+}
+
+RID TextureStorage::texture_drawable_get_default_material() const {
+ // Return a material with a default Texture_Blit shader for DrawableTexture2D to use
+ return tex_blit_shader.default_material;
+}
+
void TextureStorage::texture_replace(RID p_texture, RID p_by_texture) {
Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL(tex);
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
index 4a4767f911..2f6cf38806 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
@@ -33,8 +33,10 @@
#include "core/templates/paged_array.h"
#include "core/templates/rid_owner.h"
#include "servers/rendering/renderer_rd/shaders/canvas_sdf.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/tex_blit.glsl.gen.h"
#include "servers/rendering/renderer_rd/storage_rd/forward_id_storage.h"
#include "servers/rendering/rendering_server_default.h"
+#include "servers/rendering/shader_compiler.h"
#include "servers/rendering/storage/texture_storage.h"
#include "servers/rendering/storage/utilities.h"
@@ -45,6 +47,8 @@ class MaterialStorage;
class TextureStorage : public RendererTextureStorage {
public:
+ const int SAMPLERS_BINDING_FIRST_INDEX = 4;
+
enum DefaultRDTexture {
DEFAULT_RD_TEXTURE_WHITE,
DEFAULT_RD_TEXTURE_BLACK,
@@ -144,12 +148,14 @@ private:
public:
TextureType type;
RS::TextureLayeredType layered_type = RS::TEXTURE_LAYERED_2D_ARRAY;
+ RS::TextureDrawableFormat drawable_type = RS::TEXTURE_DRAWABLE_FORMAT_RGBA8;
RenderingDevice::TextureType rd_type;
RID rd_texture;
RID rd_texture_srgb;
RenderingDevice::DataFormat rd_format;
RenderingDevice::DataFormat rd_format_srgb;
+ Vector cached_rd_slices;
RD::TextureView rd_view;
@@ -482,9 +488,32 @@ private:
RID pipelines[SHADER_MAX];
} rt_sdf;
+ struct TextureBlitShader {
+ TexBlitShaderRD shader;
+ ShaderCompiler compiler;
+
+ bool initialized = false;
+ RID default_shader;
+ RID default_material;
+ RID default_shader_version;
+ } tex_blit_shader;
+
+ struct TexBlitPushConstant {
+ float offset[2]; // 8 - 8
+ float size[2]; // 8 - 16
+ float modulate[4]; // 16 - 32
+ float pad[2]; // 8 - 40
+ uint32_t convert_to_srgb; // 4 - 44
+ float time; // 4 - 48
+ // 128 is the max size of a push constant. We can replace "pad" but we can't add any more.
+ };
+
public:
static TextureStorage *get_singleton();
+ void _tex_blit_shader_initialize();
+ void _tex_blit_shader_free();
+
_FORCE_INLINE_ RID texture_rd_get_default(DefaultRDTexture p_texture) {
return default_rd_textures[p_texture];
}
@@ -523,6 +552,7 @@ public:
virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector][> &p_data) override;
virtual void texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent
+ virtual void texture_drawable_initialize(RID p_texture, int p_width, int p_height, RS::TextureDrawableFormat p_format, const Color &p_color, bool p_with_mipmaps) override;
virtual RID texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY) override;
@@ -531,6 +561,8 @@ public:
virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_update(RID p_proxy, RID p_base) override;
+ virtual void texture_drawable_blit_rect(const TypedArray &p_textures, const Rect2i &p_rect, RID p_material, const Color &p_modulate, const TypedArray &p_source_textures, int p_to_mipmap) override;
+
Ref texture_2d_placeholder;
Vector][> texture_2d_array_placeholder;
Vector][> cubemap_placeholder;
@@ -545,6 +577,9 @@ public:
virtual Ref texture_2d_layer_get(RID p_texture, int p_layer) const override;
virtual Vector][> texture_3d_get(RID p_texture) const override;
+ virtual void texture_drawable_generate_mipmaps(RID p_texture) override;
+ virtual RID texture_drawable_get_default_material() const override;
+
virtual void texture_replace(RID p_texture, RID p_by_texture) override;
virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) override;
diff --git a/servers/rendering/rendering_server.cpp b/servers/rendering/rendering_server.cpp
index 50533260ec..2379d8a136 100644
--- a/servers/rendering/rendering_server.cpp
+++ b/servers/rendering/rendering_server.cpp
@@ -2329,11 +2329,14 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("texture_3d_create", "format", "width", "height", "depth", "mipmaps", "data"), &RenderingServer::_texture_3d_create);
ClassDB::bind_method(D_METHOD("texture_proxy_create", "base"), &RenderingServer::texture_proxy_create);
ClassDB::bind_method(D_METHOD("texture_create_from_native_handle", "type", "format", "native_handle", "width", "height", "depth", "layers", "layered_type"), &RenderingServer::texture_create_from_native_handle, DEFVAL(1), DEFVAL(TEXTURE_LAYERED_2D_ARRAY));
+ ClassDB::bind_method(D_METHOD("texture_drawable_create", "width", "height", "format", "color", "with_mipmaps"), &RenderingServer::texture_drawable_create, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false));
ClassDB::bind_method(D_METHOD("texture_2d_update", "texture", "image", "layer"), &RenderingServer::texture_2d_update);
ClassDB::bind_method(D_METHOD("texture_3d_update", "texture", "data"), &RenderingServer::_texture_3d_update);
ClassDB::bind_method(D_METHOD("texture_proxy_update", "texture", "proxy_to"), &RenderingServer::texture_proxy_update);
+ ClassDB::bind_method(D_METHOD("texture_drawable_blit_rect", "textures", "rect", "material", "modulate", "source_textures", "to_mipmap"), &RenderingServer::texture_drawable_blit_rect, DEFVAL(0));
+
ClassDB::bind_method(D_METHOD("texture_2d_placeholder_create"), &RenderingServer::texture_2d_placeholder_create);
ClassDB::bind_method(D_METHOD("texture_2d_layered_placeholder_create", "layered_type"), &RenderingServer::texture_2d_layered_placeholder_create);
ClassDB::bind_method(D_METHOD("texture_3d_placeholder_create"), &RenderingServer::texture_3d_placeholder_create);
@@ -2342,6 +2345,9 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("texture_2d_layer_get", "texture", "layer"), &RenderingServer::texture_2d_layer_get);
ClassDB::bind_method(D_METHOD("texture_3d_get", "texture"), &RenderingServer::_texture_3d_get);
+ ClassDB::bind_method(D_METHOD("texture_drawable_generate_mipmaps", "texture"), &RenderingServer::texture_drawable_generate_mipmaps);
+ ClassDB::bind_method(D_METHOD("texture_drawable_get_default_material"), &RenderingServer::texture_drawable_get_default_material);
+
ClassDB::bind_method(D_METHOD("texture_replace", "texture", "by_texture"), &RenderingServer::texture_replace);
ClassDB::bind_method(D_METHOD("texture_set_size_override", "texture", "width", "height"), &RenderingServer::texture_set_size_override);
@@ -2370,6 +2376,11 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(CUBEMAP_LAYER_FRONT);
BIND_ENUM_CONSTANT(CUBEMAP_LAYER_BACK);
+ BIND_ENUM_CONSTANT(TEXTURE_DRAWABLE_FORMAT_RGBA8);
+ BIND_ENUM_CONSTANT(TEXTURE_DRAWABLE_FORMAT_RGBA8_SRGB);
+ BIND_ENUM_CONSTANT(TEXTURE_DRAWABLE_FORMAT_RGBAH);
+ BIND_ENUM_CONSTANT(TEXTURE_DRAWABLE_FORMAT_RGBAF);
+
/* SHADER */
ClassDB::bind_method(D_METHOD("shader_create"), &RenderingServer::shader_create);
@@ -2387,6 +2398,7 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(SHADER_PARTICLES);
BIND_ENUM_CONSTANT(SHADER_SKY);
BIND_ENUM_CONSTANT(SHADER_FOG);
+ BIND_ENUM_CONSTANT(SHADER_TEXTURE_BLIT);
BIND_ENUM_CONSTANT(SHADER_MAX);
/* MATERIAL */
diff --git a/servers/rendering/rendering_server.h b/servers/rendering/rendering_server.h
index 9ced1297f1..34894fe38b 100644
--- a/servers/rendering/rendering_server.h
+++ b/servers/rendering/rendering_server.h
@@ -136,11 +136,19 @@ public:
CUBEMAP_LAYER_BACK
};
+ enum TextureDrawableFormat {
+ TEXTURE_DRAWABLE_FORMAT_RGBA8,
+ TEXTURE_DRAWABLE_FORMAT_RGBA8_SRGB, // Use this if you want to read the result from both 2D (non-hdr) and 3D.
+ TEXTURE_DRAWABLE_FORMAT_RGBAH,
+ TEXTURE_DRAWABLE_FORMAT_RGBAF,
+ };
+
virtual RID texture_2d_create(const Ref &p_image) = 0;
virtual RID texture_2d_layered_create(const Vector][> &p_layers, TextureLayeredType p_layered_type) = 0;
virtual RID texture_3d_create(Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector][> &p_data) = 0; //all slices, then all the mipmaps, must be coherent
virtual RID texture_external_create(int p_width, int p_height, uint64_t p_external_buffer = 0) = 0;
virtual RID texture_proxy_create(RID p_base) = 0;
+ virtual RID texture_drawable_create(int p_width, int p_height, TextureDrawableFormat p_format, const Color &p_color = Color(1, 1, 1, 1), bool p_with_mipmaps = false) = 0;
virtual RID texture_create_from_native_handle(TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, TextureLayeredType p_layered_type = TEXTURE_LAYERED_2D_ARRAY) = 0;
@@ -149,6 +157,8 @@ public:
virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer = 0) = 0;
virtual void texture_proxy_update(RID p_texture, RID p_proxy_to) = 0;
+ virtual void texture_drawable_blit_rect(const TypedArray &p_textures, const Rect2i &p_rect, RID p_material, const Color &p_modulate, const TypedArray &p_source_textures, int p_to_mipmap = 0) = 0;
+
// These two APIs can be used together or in combination with the others.
virtual RID texture_2d_placeholder_create() = 0;
virtual RID texture_2d_layered_placeholder_create(TextureLayeredType p_layered_type) = 0;
@@ -164,6 +174,9 @@ public:
virtual void texture_set_path(RID p_texture, const String &p_path) = 0;
virtual String texture_get_path(RID p_texture) const = 0;
+ virtual void texture_drawable_generate_mipmaps(RID p_texture) = 0; // Update mipmaps if modified
+ virtual RID texture_drawable_get_default_material() const = 0; // To use with simplified functions in DrawableTexture2D
+
virtual Image::Format texture_get_format(RID p_texture) const = 0;
typedef void (*TextureDetectCallback)(void *);
@@ -221,6 +234,7 @@ public:
SHADER_PARTICLES,
SHADER_SKY,
SHADER_FOG,
+ SHADER_TEXTURE_BLIT,
SHADER_MAX
};
@@ -1947,6 +1961,7 @@ private:
VARIANT_ENUM_CAST(RenderingServer::TextureType);
VARIANT_ENUM_CAST(RenderingServer::TextureLayeredType);
VARIANT_ENUM_CAST(RenderingServer::CubeMapLayer);
+VARIANT_ENUM_CAST(RenderingServer::TextureDrawableFormat);
VARIANT_ENUM_CAST(RenderingServer::PipelineSource);
VARIANT_ENUM_CAST(RenderingServer::ShaderMode);
VARIANT_ENUM_CAST(RenderingServer::ArrayType);
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 5ddde49c11..966194bc83 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -174,6 +174,28 @@ public:
return ret; \
}
+#define FUNCRIDTEX4(m_type, m_type1, m_type2, m_type3, m_type4) \
+ virtual RID m_type##_create(m_type1 p1, m_type2 p2, m_type3 p3, m_type4 p4) override { \
+ RID ret = RSG::texture_storage->texture_allocate(); \
+ if (Thread::get_caller_id() == server_thread || RSG::rasterizer->can_create_resources_async()) { \
+ RSG::texture_storage->m_type##_initialize(ret, p1, p2, p3, p4); \
+ } else { \
+ command_queue.push(RSG::texture_storage, &RendererTextureStorage::m_type##_initialize, ret, p1, p2, p3, p4); \
+ } \
+ return ret; \
+ }
+
+#define FUNCRIDTEX5(m_type, m_type1, m_type2, m_type3, m_type4, m_type5) \
+ virtual RID m_type##_create(m_type1 p1, m_type2 p2, m_type3 p3, m_type4 p4, m_type5 p5) override { \
+ RID ret = RSG::texture_storage->texture_allocate(); \
+ if (Thread::get_caller_id() == server_thread || RSG::rasterizer->can_create_resources_async()) { \
+ RSG::texture_storage->m_type##_initialize(ret, p1, p2, p3, p4, p5); \
+ } else { \
+ command_queue.push(RSG::texture_storage, &RendererTextureStorage::m_type##_initialize, ret, p1, p2, p3, p4, p5); \
+ } \
+ return ret; \
+ }
+
#define FUNCRIDTEX6(m_type, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \
virtual RID m_type##_create(m_type1 p1, m_type2 p2, m_type3 p3, m_type4 p4, m_type5 p5, m_type6 p6) override { \
RID ret = RSG::texture_storage->texture_allocate(); \
@@ -191,6 +213,7 @@ public:
FUNCRIDTEX6(texture_3d, Image::Format, int, int, int, bool, const Vector][> &)
FUNCRIDTEX3(texture_external, int, int, uint64_t)
FUNCRIDTEX1(texture_proxy, RID)
+ FUNCRIDTEX5(texture_drawable, int, int, TextureDrawableFormat, const Color &, bool)
// Called directly, not through the command queue.
virtual RID texture_create_from_native_handle(TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, TextureLayeredType p_layered_type = TEXTURE_LAYERED_2D_ARRAY) override {
@@ -203,6 +226,8 @@ public:
FUNC4(texture_external_update, RID, int, int, uint64_t)
FUNC2(texture_proxy_update, RID, RID)
+ FUNC6(texture_drawable_blit_rect, const TypedArray &, const Rect2i &, RID, const Color &, const TypedArray &, int)
+
//these also go pass-through
FUNCRIDTEX0(texture_2d_placeholder)
FUNCRIDTEX1(texture_2d_layered_placeholder, TextureLayeredType)
@@ -212,6 +237,9 @@ public:
FUNC2RC(Ref, texture_2d_layer_get, RID, int)
FUNC1RC(Vector][>, texture_3d_get, RID)
+ FUNC1(texture_drawable_generate_mipmaps, RID)
+ FUNC0RC(RID, texture_drawable_get_default_material)
+
FUNC2(texture_replace, RID, RID)
FUNC3(texture_set_size_override, RID, int, int)
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 9324d4f71e..70530784b7 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -527,7 +527,11 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
if (SL::is_sampler_type(E.value.type)) {
if (E.value.hint == SL::ShaderNode::Uniform::HINT_SCREEN_TEXTURE ||
E.value.hint == SL::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE ||
- E.value.hint == SL::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) {
+ E.value.hint == SL::ShaderNode::Uniform::HINT_DEPTH_TEXTURE ||
+ E.value.hint == SL::ShaderNode::Uniform::HINT_BLIT_SOURCE0 ||
+ E.value.hint == SL::ShaderNode::Uniform::HINT_BLIT_SOURCE1 ||
+ E.value.hint == SL::ShaderNode::Uniform::HINT_BLIT_SOURCE2 ||
+ E.value.hint == SL::ShaderNode::Uniform::HINT_BLIT_SOURCE3) {
continue; // Don't create uniforms in the generated code for these.
}
max_texture_uniforms++;
@@ -572,7 +576,11 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
if (uniform.hint == SL::ShaderNode::Uniform::HINT_SCREEN_TEXTURE ||
uniform.hint == SL::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE ||
- uniform.hint == SL::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) {
+ uniform.hint == SL::ShaderNode::Uniform::HINT_DEPTH_TEXTURE ||
+ uniform.hint == SL::ShaderNode::Uniform::HINT_BLIT_SOURCE0 ||
+ uniform.hint == SL::ShaderNode::Uniform::HINT_BLIT_SOURCE1 ||
+ uniform.hint == SL::ShaderNode::Uniform::HINT_BLIT_SOURCE2 ||
+ uniform.hint == SL::ShaderNode::Uniform::HINT_BLIT_SOURCE3) {
continue; // Don't create uniforms in the generated code for these.
}
@@ -937,6 +945,14 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
} else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) {
name = "depth_buffer";
r_gen_code.uses_depth_texture = true;
+ } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_BLIT_SOURCE0) {
+ name = "source0";
+ } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_BLIT_SOURCE1) {
+ name = "source1";
+ } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_BLIT_SOURCE2) {
+ name = "source2";
+ } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_BLIT_SOURCE3) {
+ name = "source3";
} else {
name = _mkid(vnode->name); //texture, use as is
}
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index c577e5c786..5809236233 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -220,6 +220,10 @@ const char *ShaderLanguage::token_names[TK_MAX] = {
"HINT_SCREEN_TEXTURE",
"HINT_NORMAL_ROUGHNESS_TEXTURE",
"HINT_DEPTH_TEXTURE",
+ "HINT_BLIT_SOURCE0",
+ "HINT_BLIT_SOURCE1",
+ "HINT_BLIT_SOURCE2",
+ "HINT_BLIT_SOURCE3",
"FILTER_NEAREST",
"FILTER_LINEAR",
"FILTER_NEAREST_MIPMAP",
@@ -392,6 +396,11 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
{ TK_HINT_NORMAL_ROUGHNESS_TEXTURE, "hint_normal_roughness_texture", CF_UNSPECIFIED, {}, {} },
{ TK_HINT_DEPTH_TEXTURE, "hint_depth_texture", CF_UNSPECIFIED, {}, {} },
+ { TK_HINT_BLIT_SOURCE0, "hint_blit_source0", CF_UNSPECIFIED, {}, {} },
+ { TK_HINT_BLIT_SOURCE1, "hint_blit_source1", CF_UNSPECIFIED, {}, {} },
+ { TK_HINT_BLIT_SOURCE2, "hint_blit_source2", CF_UNSPECIFIED, {}, {} },
+ { TK_HINT_BLIT_SOURCE3, "hint_blit_source3", CF_UNSPECIFIED, {}, {} },
+
{ TK_FILTER_NEAREST, "filter_nearest", CF_UNSPECIFIED, {}, {} },
{ TK_FILTER_LINEAR, "filter_linear", CF_UNSPECIFIED, {}, {} },
{ TK_FILTER_NEAREST_MIPMAP, "filter_nearest_mipmap", CF_UNSPECIFIED, {}, {} },
@@ -1236,6 +1245,18 @@ String ShaderLanguage::get_uniform_hint_name(ShaderNode::Uniform::Hint p_hint) {
case ShaderNode::Uniform::HINT_DEPTH_TEXTURE: {
result = "hint_depth_texture";
} break;
+ case ShaderNode::Uniform::HINT_BLIT_SOURCE0: {
+ result = "hint_blit_source0";
+ } break;
+ case ShaderNode::Uniform::HINT_BLIT_SOURCE1: {
+ result = "hint_blit_source1";
+ } break;
+ case ShaderNode::Uniform::HINT_BLIT_SOURCE2: {
+ result = "hint_blit_source2";
+ } break;
+ case ShaderNode::Uniform::HINT_BLIT_SOURCE3: {
+ result = "hint_blit_source3";
+ } break;
default:
break;
}
@@ -10029,6 +10050,42 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f
return ERR_PARSE_ERROR;
}
} break;
+ case TK_HINT_BLIT_SOURCE0: {
+ new_hint = ShaderNode::Uniform::HINT_BLIT_SOURCE0;
+ --texture_uniforms;
+ --texture_binding;
+ if (shader_type_identifier != StringName() && String(shader_type_identifier) != "texture_blit") {
+ _set_error(vformat(RTR("'hint_blit_source' is not supported in '%s' shaders."), shader_type_identifier));
+ return ERR_PARSE_ERROR;
+ }
+ } break;
+ case TK_HINT_BLIT_SOURCE1: {
+ new_hint = ShaderNode::Uniform::HINT_BLIT_SOURCE1;
+ --texture_uniforms;
+ --texture_binding;
+ if (shader_type_identifier != StringName() && String(shader_type_identifier) != "texture_blit") {
+ _set_error(vformat(RTR("'hint_blit_source' is not supported in '%s' shaders."), shader_type_identifier));
+ return ERR_PARSE_ERROR;
+ }
+ } break;
+ case TK_HINT_BLIT_SOURCE2: {
+ new_hint = ShaderNode::Uniform::HINT_BLIT_SOURCE2;
+ --texture_uniforms;
+ --texture_binding;
+ if (shader_type_identifier != StringName() && String(shader_type_identifier) != "texture_blit") {
+ _set_error(vformat(RTR("'hint_blit_source' is not supported in '%s' shaders."), shader_type_identifier));
+ return ERR_PARSE_ERROR;
+ }
+ } break;
+ case TK_HINT_BLIT_SOURCE3: {
+ new_hint = ShaderNode::Uniform::HINT_BLIT_SOURCE3;
+ --texture_uniforms;
+ --texture_binding;
+ if (shader_type_identifier != StringName() && String(shader_type_identifier) != "texture_blit") {
+ _set_error(vformat(RTR("'hint_blit_source' is not supported in '%s' shaders."), shader_type_identifier));
+ return ERR_PARSE_ERROR;
+ }
+ } break;
case TK_FILTER_NEAREST: {
new_filter = FILTER_NEAREST;
} break;
@@ -12072,6 +12129,10 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
options.push_back("hint_screen_texture");
options.push_back("hint_normal_roughness_texture");
options.push_back("hint_depth_texture");
+ options.push_back("hint_blit_source0");
+ options.push_back("hint_blit_source1");
+ options.push_back("hint_blit_source2");
+ options.push_back("hint_blit_source3");
options.push_back("source_color");
}
}
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index ce9244d952..164a1ef467 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -184,6 +184,10 @@ public:
TK_HINT_SCREEN_TEXTURE,
TK_HINT_NORMAL_ROUGHNESS_TEXTURE,
TK_HINT_DEPTH_TEXTURE,
+ TK_HINT_BLIT_SOURCE0,
+ TK_HINT_BLIT_SOURCE1,
+ TK_HINT_BLIT_SOURCE2,
+ TK_HINT_BLIT_SOURCE3,
TK_FILTER_NEAREST,
TK_FILTER_LINEAR,
TK_FILTER_NEAREST_MIPMAP,
@@ -676,6 +680,10 @@ public:
HINT_DEFAULT_TRANSPARENT,
HINT_ANISOTROPY,
HINT_SCREEN_TEXTURE,
+ HINT_BLIT_SOURCE0,
+ HINT_BLIT_SOURCE1,
+ HINT_BLIT_SOURCE2,
+ HINT_BLIT_SOURCE3,
HINT_NORMAL_ROUGHNESS_TEXTURE,
HINT_DEPTH_TEXTURE,
HINT_MAX
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index af6948c45d..a8e9f17f5b 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -519,12 +519,37 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_FOG].functions["fog"].built_ins["EMISSION"] = ShaderLanguage::TYPE_VEC3;
shader_modes[RS::SHADER_FOG].functions["fog"].main_function = true;
+ /************ TEXTURE_BLIT **************************/
+
+ shader_modes[RS::SHADER_TEXTURE_BLIT].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_TEXTURE_BLIT].functions["constants"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_TEXTURE_BLIT].functions["constants"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_TEXTURE_BLIT].functions["constants"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
+
+ shader_modes[RS::SHADER_TEXTURE_BLIT].functions["blit"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4);
+
+ shader_modes[RS::SHADER_TEXTURE_BLIT].functions["blit"].built_ins["UV"] = constt(ShaderLanguage::TYPE_VEC2);
+ shader_modes[RS::SHADER_TEXTURE_BLIT].functions["blit"].built_ins["MODULATE"] = constt(ShaderLanguage::TYPE_VEC4);
+
+ shader_modes[RS::SHADER_TEXTURE_BLIT].functions["blit"].built_ins["COLOR0"] = ShaderLanguage::TYPE_VEC4;
+ shader_modes[RS::SHADER_TEXTURE_BLIT].functions["blit"].built_ins["COLOR1"] = ShaderLanguage::TYPE_VEC4;
+ shader_modes[RS::SHADER_TEXTURE_BLIT].functions["blit"].built_ins["COLOR2"] = ShaderLanguage::TYPE_VEC4;
+ shader_modes[RS::SHADER_TEXTURE_BLIT].functions["blit"].built_ins["COLOR3"] = ShaderLanguage::TYPE_VEC4;
+
+ shader_modes[RS::SHADER_TEXTURE_BLIT].functions["blit"].main_function = true;
+
+ // Texture Blit Modes
+ {
+ shader_modes[RS::SHADER_TEXTURE_BLIT].modes.push_back({ PNAME("blend"), "mix", "add", "sub", "mul", "disabled" });
+ }
+
// Must be kept in sync with the Shader::Mode enum.
shader_types_list.push_back("spatial");
shader_types_list.push_back("canvas_item");
shader_types_list.push_back("particles");
shader_types_list.push_back("sky");
shader_types_list.push_back("fog");
+ shader_types_list.push_back("texture_blit");
DEV_ASSERT(shader_types_list.size() == Shader::MODE_MAX);
for (const String &type : shader_types_list) {
diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h
index cef1ae2b1a..77a31cc8b3 100644
--- a/servers/rendering/storage/texture_storage.h
+++ b/servers/rendering/storage/texture_storage.h
@@ -69,6 +69,7 @@ public:
virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector][> &p_data) = 0;
virtual void texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) = 0;
virtual void texture_proxy_initialize(RID p_texture, RID p_base) = 0; //all slices, then all the mipmaps, must be coherent
+ virtual void texture_drawable_initialize(RID p_texture, int p_width, int p_height, RS::TextureDrawableFormat p_format, const Color &p_color, bool p_with_mipmaps) = 0;
virtual RID texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY) = 0;
@@ -77,6 +78,8 @@ public:
virtual void texture_external_update(RID p_proxy, int p_width, int p_height, uint64_t p_external_buffer) = 0;
virtual void texture_proxy_update(RID p_proxy, RID p_base) = 0;
+ virtual void texture_drawable_blit_rect(const TypedArray &p_textures, const Rect2i &p_rect, RID p_material, const Color &p_modulate, const TypedArray &p_source_textures, int p_to_mipmap) = 0;
+
//these two APIs can be used together or in combination with the others.
virtual void texture_2d_placeholder_initialize(RID p_texture) = 0;
virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) = 0;
@@ -86,6 +89,9 @@ public:
virtual Ref texture_2d_layer_get(RID p_texture, int p_layer) const = 0;
virtual Vector][> texture_3d_get(RID p_texture) const = 0;
+ virtual void texture_drawable_generate_mipmaps(RID p_texture) = 0;
+ virtual RID texture_drawable_get_default_material() const = 0;
+
virtual void texture_replace(RID p_texture, RID p_by_texture) = 0;
virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) = 0;
]