From 63f6e3691cc1058dd89188e0a02178a53e5b7a94 Mon Sep 17 00:00:00 2001 From: Colin O'Rourke Date: Sun, 9 Feb 2025 02:20:35 -0800 Subject: [PATCH] DrawableTextures Implementing DrawableTextures based on: https://github.com/godotengine/godot-proposals/issues/7379 --- doc/classes/BlitMaterial.xml | 33 ++ doc/classes/DrawableTexture2D.xml | 71 ++++ doc/classes/RenderingServer.xml | 57 ++- doc/classes/Shader.xml | 3 + doc/classes/VisualShader.xml | 5 +- drivers/gles3/rasterizer_gles3.cpp | 4 + drivers/gles3/shaders/SCsub | 1 + drivers/gles3/shaders/tex_blit.glsl | 121 ++++++ drivers/gles3/storage/material_storage.cpp | 141 +++++++ drivers/gles3/storage/material_storage.h | 49 +++ drivers/gles3/storage/texture_storage.cpp | 273 +++++++++++++ drivers/gles3/storage/texture_storage.h | 23 ++ editor/editor_node.cpp | 4 + editor/icons/BlitMaterial.svg | 1 + editor/icons/DrawableTexture2D.svg | 1 + editor/scene/material_editor_plugin.cpp | 40 ++ editor/scene/material_editor_plugin.h | 9 + editor/shader/text_shader_editor.cpp | 2 + editor/shader/text_shader_language_plugin.cpp | 7 + editor/shader/visual_shader_editor_plugin.cpp | 46 ++- editor/shader/visual_shader_editor_plugin.h | 6 + scene/register_scene_types.cpp | 6 + scene/resources/blit_material.cpp | 140 +++++++ scene/resources/blit_material.h | 72 ++++ scene/resources/drawable_texture_2d.cpp | 241 ++++++++++++ scene/resources/drawable_texture_2d.h | 95 +++++ scene/resources/shader.cpp | 3 + scene/resources/shader.h | 1 + scene/resources/visual_shader.cpp | 41 +- scene/resources/visual_shader.h | 1 + .../dummy/storage/material_storage.cpp | 2 + .../rendering/dummy/storage/texture_storage.h | 6 + .../renderer_rd/renderer_compositor_rd.cpp | 2 + .../renderer_rd/shaders/tex_blit.glsl | 128 +++++++ .../storage_rd/material_storage.cpp | 174 +++++++++ .../renderer_rd/storage_rd/material_storage.h | 49 +++ .../storage_rd/texture_storage.cpp | 359 ++++++++++++++++++ .../renderer_rd/storage_rd/texture_storage.h | 35 ++ servers/rendering/rendering_server.cpp | 12 + servers/rendering/rendering_server.h | 15 + servers/rendering/rendering_server_default.h | 28 ++ servers/rendering/shader_compiler.cpp | 20 +- servers/rendering/shader_language.cpp | 61 +++ servers/rendering/shader_language.h | 8 + servers/rendering/shader_types.cpp | 25 ++ servers/rendering/storage/texture_storage.h | 6 + 46 files changed, 2417 insertions(+), 10 deletions(-) create mode 100644 doc/classes/BlitMaterial.xml create mode 100644 doc/classes/DrawableTexture2D.xml create mode 100644 drivers/gles3/shaders/tex_blit.glsl create mode 100644 editor/icons/BlitMaterial.svg create mode 100644 editor/icons/DrawableTexture2D.svg create mode 100644 scene/resources/blit_material.cpp create mode 100644 scene/resources/blit_material.h create mode 100644 scene/resources/drawable_texture_2d.cpp create mode 100644 scene/resources/drawable_texture_2d.h create mode 100644 servers/rendering/renderer_rd/shaders/tex_blit.glsl 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 50dbb3259c..e2625d5dd0 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -3804,6 +3804,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. + + @@ -4589,6 +4628,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. @@ -4604,7 +4656,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 757df717f2..a71d57954f 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -9202,6 +9202,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 da9d0fbc94..cbe425b188 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -112,6 +112,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" @@ -120,6 +121,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" @@ -886,8 +888,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 */ @@ -1054,6 +1058,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); @@ -1472,6 +1477,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 b7e6c81b81..c110d10233 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 ddaef0ecba..1623b044cb 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;