diff options
Diffstat (limited to 'servers/rendering/renderer_rd')
21 files changed, 499 insertions, 161 deletions
diff --git a/servers/rendering/renderer_rd/cluster_builder_rd.cpp b/servers/rendering/renderer_rd/cluster_builder_rd.cpp index d2a1a5ab9c..f99a5254bb 100644 --- a/servers/rendering/renderer_rd/cluster_builder_rd.cpp +++ b/servers/rendering/renderer_rd/cluster_builder_rd.cpp @@ -58,6 +58,7 @@ ClusterBuilderSharedDataRD::ClusterBuilderSharedDataRD() { afs.push_back(RD::AttachmentFormat()); afs.write[0].usage_flags = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; fb_format = RD::get_singleton()->framebuffer_format_create(afs); + blend_state = RD::PipelineColorBlendState::create_blend(); defines = "\n#define USE_ATTACHMENT\n"; } @@ -285,7 +286,7 @@ void ClusterBuilderRD::setup(Size2i p_screen_size, uint32_t p_max_elements, RID cluster_render_buffer = RD::get_singleton()->storage_buffer_create(cluster_render_buffer_size); cluster_buffer = RD::get_singleton()->storage_buffer_create(cluster_buffer_size); - render_elements = static_cast<RenderElementData *>(memalloc(sizeof(RenderElementData *) * render_element_max)); + render_elements = static_cast<RenderElementData *>(memalloc(sizeof(RenderElementData) * render_element_max)); render_element_count = 0; element_buffer = RD::get_singleton()->storage_buffer_create(sizeof(RenderElementData) * render_element_max); diff --git a/servers/rendering/renderer_rd/effects/debug_effects.cpp b/servers/rendering/renderer_rd/effects/debug_effects.cpp index 8cd3c22483..3d26a9a8df 100644 --- a/servers/rendering/renderer_rd/effects/debug_effects.cpp +++ b/servers/rendering/renderer_rd/effects/debug_effects.cpp @@ -51,6 +51,17 @@ DebugEffects::DebugEffects() { raster_state.wireframe = true; shadow_frustum.pipelines[SFP_WIREFRAME].setup(shadow_frustum.shader.version_get_shader(shadow_frustum.shader_version, 0), RD::RENDER_PRIMITIVE_LINES, raster_state, RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); } + + { + // Motion Vectors debug shader. + Vector<String> modes; + modes.push_back(""); + + motion_vectors.shader.initialize(modes); + motion_vectors.shader_version = motion_vectors.shader.version_create(); + + motion_vectors.pipeline.setup(motion_vectors.shader.version_get_shader(motion_vectors.shader_version, 0), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_blend(), 0); + } } void DebugEffects::_create_frustum_arrays() { @@ -163,6 +174,8 @@ DebugEffects::~DebugEffects() { if (frustum.lines_buffer.is_valid()) { RD::get_singleton()->free(frustum.lines_buffer); // Array gets freed as dependency. } + + motion_vectors.shader.version_free(motion_vectors.shader_version); } void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect) { @@ -326,3 +339,26 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj } } } + +void DebugEffects::draw_motion_vectors(RID p_velocity, RID p_dest_fb, Size2i p_velocity_size) { + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + ERR_FAIL_NULL(material_storage); + + UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); + ERR_FAIL_NULL(uniform_set_cache); + + RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + RD::Uniform u_source_velocity(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_velocity })); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, motion_vectors.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_fb), false, RD::get_singleton()->draw_list_get_current_pass())); + + motion_vectors.push_constant.velocity_resolution[0] = p_velocity_size.width; + motion_vectors.push_constant.velocity_resolution[1] = p_velocity_size.height; + + RID shader = motion_vectors.shader.version_get_shader(motion_vectors.shader_version, 0); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_velocity), 0); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &motion_vectors.push_constant, sizeof(MotionVectorsPushConstant)); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); + RD::get_singleton()->draw_list_end(); +} diff --git a/servers/rendering/renderer_rd/effects/debug_effects.h b/servers/rendering/renderer_rd/effects/debug_effects.h index 21b7b03f84..ae32d94912 100644 --- a/servers/rendering/renderer_rd/effects/debug_effects.h +++ b/servers/rendering/renderer_rd/effects/debug_effects.h @@ -32,6 +32,7 @@ #define DEBUG_EFFECTS_RD_H #include "servers/rendering/renderer_rd/pipeline_cache_rd.h" +#include "servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/effects/shadow_frustum.glsl.gen.h" #include "servers/rendering/renderer_scene_render.h" @@ -70,6 +71,18 @@ private: PipelineCacheRD pipelines[SFP_MAX]; } shadow_frustum; + struct MotionVectorsPushConstant { + float velocity_resolution[2]; + float pad[2]; + }; + + struct { + MotionVectorsShaderRD shader; + RID shader_version; + PipelineCacheRD pipeline; + MotionVectorsPushConstant push_constant; + } motion_vectors; + void _create_frustum_arrays(); protected: @@ -78,6 +91,7 @@ public: ~DebugEffects(); void draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect); + void draw_motion_vectors(RID p_velocity, RID p_dest_fb, Size2i p_velocity_size); }; } // namespace RendererRD diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index 991ccf984e..d623553273 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -395,6 +395,16 @@ Dependency *GI::voxel_gi_get_dependency(RID p_voxel_gi) const { //////////////////////////////////////////////////////////////////////////////// // SDFGI +static RID create_clear_texture(const RD::TextureFormat &p_format, const String &p_name) { + RID texture = RD::get_singleton()->texture_create(p_format, RD::TextureView()); + ERR_FAIL_COND_V_MSG(texture.is_null(), RID(), String("Cannot create texture: ") + p_name); + + RD::get_singleton()->set_resource_name(texture, p_name); + RD::get_singleton()->texture_clear(texture, Color(0, 0, 0, 0), 0, p_format.mipmaps, 0, p_format.array_layers); + + return texture; +} + void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, GI *p_gi) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); @@ -424,39 +434,31 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re { RD::TextureFormat tf_render = tf_sdf; tf_render.format = RD::DATA_FORMAT_R16_UINT; - render_albedo = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); - RD::get_singleton()->set_resource_name(render_albedo, "VoxelGI Render Albedo"); + render_albedo = create_clear_texture(tf_render, "SDFGI Render Albedo"); + tf_render.format = RD::DATA_FORMAT_R32_UINT; - render_emission = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); - RD::get_singleton()->set_resource_name(render_emission, "VoxelGI Render Emission"); - render_emission_aniso = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); - RD::get_singleton()->set_resource_name(render_emission_aniso, "VoxelGI Render Emission Aniso"); + render_emission = create_clear_texture(tf_render, "SDFGI Render Emission"); + render_emission_aniso = create_clear_texture(tf_render, "SDFGI Render Emission Aniso"); tf_render.format = RD::DATA_FORMAT_R8_UNORM; //at least its easy to visualize for (int i = 0; i < 8; i++) { - render_occlusion[i] = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); - RD::get_singleton()->set_resource_name(render_occlusion[i], String("VoxelGI Render Occlusion ") + itos(i)); + render_occlusion[i] = create_clear_texture(tf_render, String("SDFGI Render Occlusion ") + itos(i)); } tf_render.format = RD::DATA_FORMAT_R32_UINT; - render_geom_facing = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); - RD::get_singleton()->set_resource_name(render_geom_facing, "VoxelGI Render Geometry Facing"); + render_geom_facing = create_clear_texture(tf_render, "SDFGI Render Geometry Facing"); tf_render.format = RD::DATA_FORMAT_R8G8B8A8_UINT; - render_sdf[0] = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); - RD::get_singleton()->set_resource_name(render_sdf[0], "VoxelGI Render SDF 0"); - render_sdf[1] = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); - RD::get_singleton()->set_resource_name(render_sdf[1], "VoxelGI Render SDF 1"); + render_sdf[0] = create_clear_texture(tf_render, "SDFGI Render SDF 0"); + render_sdf[1] = create_clear_texture(tf_render, "SDFGI Render SDF 1"); tf_render.width /= 2; tf_render.height /= 2; tf_render.depth /= 2; - render_sdf_half[0] = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); - RD::get_singleton()->set_resource_name(render_sdf_half[0], "VoxelGI Render SDF Half 0"); - render_sdf_half[1] = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); - RD::get_singleton()->set_resource_name(render_sdf_half[1], "VoxelGI Render SDF Half 1"); + render_sdf_half[0] = create_clear_texture(tf_render, "SDFGI Render SDF Half 0"); + render_sdf_half[1] = create_clear_texture(tf_render, "SDFGI Render SDF Half 1"); } RD::TextureFormat tf_occlusion = tf_sdf; @@ -496,10 +498,8 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re tf_probe_average.format = RD::DATA_FORMAT_R32G32B32A32_SINT; //signed integer because SH are signed tf_probe_average.texture_type = RD::TEXTURE_TYPE_2D; - lightprobe_history_scroll = RD::get_singleton()->texture_create(tf_probe_history, RD::TextureView()); - RD::get_singleton()->set_resource_name(lightprobe_history_scroll, "VoxelGI LightProbe History Scroll"); - lightprobe_average_scroll = RD::get_singleton()->texture_create(tf_probe_average, RD::TextureView()); - RD::get_singleton()->set_resource_name(lightprobe_average_scroll, "VoxelGI LightProbe Average Scroll"); + lightprobe_history_scroll = create_clear_texture(tf_probe_history, "SDFGI LightProbe History Scroll"); + lightprobe_average_scroll = create_clear_texture(tf_probe_average, "SDFGI LightProbe Average Scroll"); { //octahedral lightprobes @@ -512,8 +512,7 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re tf_octprobes.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32); //lightprobe texture is an octahedral texture - lightprobe_data = RD::get_singleton()->texture_create(tf_octprobes, RD::TextureView()); - RD::get_singleton()->set_resource_name(lightprobe_data, "VoxelGI LightProbe Data"); + lightprobe_data = create_clear_texture(tf_octprobes, "SDFGI LightProbe Data"); RD::TextureView tv; tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; lightprobe_texture = RD::get_singleton()->texture_create_shared(tv, lightprobe_data); @@ -526,14 +525,12 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re tf_ambient.height = probe_axis_count; tf_ambient.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; //lightprobe texture is an octahedral texture - ambient_texture = RD::get_singleton()->texture_create(tf_ambient, RD::TextureView()); - RD::get_singleton()->set_resource_name(ambient_texture, "VoxelGI Ambient Texture"); + ambient_texture = create_clear_texture(tf_ambient, "SDFGI Ambient Texture"); } cascades_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES); - occlusion_data = RD::get_singleton()->texture_create(tf_occlusion, RD::TextureView()); - RD::get_singleton()->set_resource_name(occlusion_data, "VoxelGI Occlusion Data"); + occlusion_data = create_clear_texture(tf_occlusion, "SDFGI Occlusion Data"); { RD::TextureView tv; tv.format_override = RD::DATA_FORMAT_R4G4B4A4_UNORM_PACK16; @@ -543,25 +540,17 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re for (SDFGI::Cascade &cascade : cascades) { /* 3D Textures */ - cascade.sdf_tex = RD::get_singleton()->texture_create(tf_sdf, RD::TextureView()); - RD::get_singleton()->set_resource_name(cascade.sdf_tex, "VoxelGI Cascade SDF Texture"); + cascade.sdf_tex = create_clear_texture(tf_sdf, "SDFGI Cascade SDF Texture"); - cascade.light_data = RD::get_singleton()->texture_create(tf_light, RD::TextureView()); - RD::get_singleton()->set_resource_name(cascade.light_data, "VoxelGI Cascade Light Data"); + cascade.light_data = create_clear_texture(tf_light, "SDFGI Cascade Light Data"); - cascade.light_aniso_0_tex = RD::get_singleton()->texture_create(tf_aniso0, RD::TextureView()); - RD::get_singleton()->set_resource_name(cascade.light_aniso_0_tex, "VoxelGI Cascade Light Aniso 0 Texture"); - cascade.light_aniso_1_tex = RD::get_singleton()->texture_create(tf_aniso1, RD::TextureView()); - RD::get_singleton()->set_resource_name(cascade.light_aniso_1_tex, "VoxelGI Cascade Light Aniso 1 Texture"); + cascade.light_aniso_0_tex = create_clear_texture(tf_aniso0, "SDFGI Cascade Light Aniso 0 Texture"); + cascade.light_aniso_1_tex = create_clear_texture(tf_aniso1, "SDFGI Cascade Light Aniso 1 Texture"); { RD::TextureView tv; tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; cascade.light_tex = RD::get_singleton()->texture_create_shared(tv, cascade.light_data); - - RD::get_singleton()->texture_clear(cascade.light_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); - RD::get_singleton()->texture_clear(cascade.light_aniso_0_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); - RD::get_singleton()->texture_clear(cascade.light_aniso_1_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); } cascade.cell_size = base_cell_size; @@ -579,11 +568,11 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re /* Probe History */ cascade.lightprobe_history_tex = RD::get_singleton()->texture_create(tf_probe_history, RD::TextureView()); - RD::get_singleton()->set_resource_name(cascade.lightprobe_history_tex, "VoxelGI Cascade LightProbe History Texture"); + RD::get_singleton()->set_resource_name(cascade.lightprobe_history_tex, "SDFGI Cascade LightProbe History Texture"); RD::get_singleton()->texture_clear(cascade.lightprobe_history_tex, Color(0, 0, 0, 0), 0, 1, 0, tf_probe_history.array_layers); //needs to be cleared for average to work cascade.lightprobe_average_tex = RD::get_singleton()->texture_create(tf_probe_average, RD::TextureView()); - RD::get_singleton()->set_resource_name(cascade.lightprobe_average_tex, "VoxelGI Cascade LightProbe Average Texture"); + RD::get_singleton()->set_resource_name(cascade.lightprobe_average_tex, "SDFGI Cascade LightProbe Average Texture"); RD::get_singleton()->texture_clear(cascade.lightprobe_average_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); //needs to be cleared for average to work /* Buffers */ diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 2974e9c4a3..2e64946fd2 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -252,6 +252,7 @@ bool RenderForwardClustered::free(RID p_rid) { template <RenderForwardClustered::PassMode p_pass_mode, uint32_t p_color_pass_flags> void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element) { RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); + RendererRD::ParticlesStorage *particles_storage = RendererRD::ParticlesStorage::get_singleton(); RD::DrawListID draw_list = p_draw_list; RD::FramebufferFormatID framebuffer_format = p_framebuffer_Format; @@ -433,10 +434,11 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p RID index_array_rd; //skeleton and blend shape + bool pipeline_motion_vectors = pipeline_color_pass_flags & SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS; if (surf->owner->mesh_instance.is_valid()) { - mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, pipeline->get_vertex_input_mask(), vertex_array_rd, vertex_format); + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, pipeline->get_vertex_input_mask(), pipeline_motion_vectors, vertex_array_rd, vertex_format); } else { - mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, pipeline->get_vertex_input_mask(), vertex_array_rd, vertex_format); + mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, pipeline->get_vertex_input_mask(), pipeline_motion_vectors, vertex_array_rd, vertex_format); } index_array_rd = mesh_storage->mesh_surface_get_index_array(mesh_surface, element_info.lod_index); @@ -476,7 +478,9 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p prev_material_uniform_set = material_uniform_set; } - if ((surf->owner->base_flags & (INSTANCE_DATA_FLAG_MULTIMESH | INSTANCE_DATA_FLAG_PARTICLES)) == INSTANCE_DATA_FLAG_MULTIMESH) { + if (surf->owner->base_flags & INSTANCE_DATA_FLAG_PARTICLES) { + particles_storage->particles_get_instance_buffer_motion_vectors_offsets(surf->owner->data->base, push_constant.multimesh_motion_vectors_current_offset, push_constant.multimesh_motion_vectors_previous_offset); + } else if (surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH) { mesh_storage->_multimesh_get_motion_vectors_offsets(surf->owner->data->base, push_constant.multimesh_motion_vectors_current_offset, push_constant.multimesh_motion_vectors_previous_offset); } else { push_constant.multimesh_motion_vectors_current_offset = 0; @@ -3715,6 +3719,10 @@ void RenderForwardClustered::_geometry_instance_update(RenderGeometryInstance *p // Particles haven't been cleared or updated, update once now to ensure they are ready to render. particles_storage->update_particles(); } + + if (ginstance->data->dirty_dependencies) { + particles_storage->particles_update_dependency(ginstance->data->base, &ginstance->data->dependency_tracker); + } } else if (ginstance->data->base_type == RS::INSTANCE_MESH) { if (mesh_storage->skeleton_is_valid(ginstance->data->skeleton)) { ginstance->transforms_uniform_set = mesh_storage->skeleton_get_3d_uniform_set(ginstance->data->skeleton, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); @@ -3754,6 +3762,7 @@ void RenderForwardClustered::_geometry_instance_dependency_changed(Dependency::D case Dependency::DEPENDENCY_CHANGED_MATERIAL: case Dependency::DEPENDENCY_CHANGED_MESH: case Dependency::DEPENDENCY_CHANGED_PARTICLES: + case Dependency::DEPENDENCY_CHANGED_PARTICLES_INSTANCES: case Dependency::DEPENDENCY_CHANGED_MULTIMESH: case Dependency::DEPENDENCY_CHANGED_SKELETON_DATA: { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index befb2c5504..3f01ffa3c9 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -752,7 +752,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color bool draw_sky = false; bool draw_sky_fog_only = false; // We invert luminance_multiplier for sky so that we can combine it with exposure value. - float sky_energy_multiplier = 1.0 / _render_buffers_get_luminance_multiplier(); + float inverse_luminance_multiplier = 1.0 / _render_buffers_get_luminance_multiplier(); + float sky_energy_multiplier = inverse_luminance_multiplier; Color clear_color = p_default_bg_color; bool keep_color = false; @@ -899,14 +900,14 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color // regular forward for now Vector<Color> c; { - Color cc = clear_color.srgb_to_linear(); + Color cc = clear_color.srgb_to_linear() * inverse_luminance_multiplier; if (rb_data.is_valid()) { cc.a = 0; // For transparent viewport backgrounds. } c.push_back(cc); // Our render buffer. if (rb_data.is_valid()) { if (p_render_data->render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { - c.push_back(clear_color.srgb_to_linear()); // Our resolve buffer. + c.push_back(clear_color.srgb_to_linear() * inverse_luminance_multiplier); // Our resolve buffer. } if (using_subpass_post_process) { c.push_back(Color()); // Our 2D buffer we're copying into. @@ -2139,9 +2140,9 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr //skeleton and blend shape if (surf->owner->mesh_instance.is_valid()) { - mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, pipeline->get_vertex_input_mask(), vertex_array_rd, vertex_format); + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, pipeline->get_vertex_input_mask(), false, vertex_array_rd, vertex_format); } else { - mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, pipeline->get_vertex_input_mask(), vertex_array_rd, vertex_format); + mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, pipeline->get_vertex_input_mask(), false, vertex_array_rd, vertex_format); } index_array_rd = mesh_storage->mesh_surface_get_index_array(mesh_surface, element_info.lod_index); @@ -2614,6 +2615,10 @@ void RenderForwardMobile::_geometry_instance_update(RenderGeometryInstance *p_ge // Particles haven't been cleared or updated, update once now to ensure they are ready to render. particles_storage->update_particles(); } + + if (ginstance->data->dirty_dependencies) { + particles_storage->particles_update_dependency(ginstance->data->base, &ginstance->data->dependency_tracker); + } } else if (ginstance->data->base_type == RS::INSTANCE_MESH) { if (mesh_storage->skeleton_is_valid(ginstance->data->skeleton)) { ginstance->transforms_uniform_set = mesh_storage->skeleton_get_3d_uniform_set(ginstance->data->skeleton, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 773ea9098a..d8c035a51c 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -348,7 +348,7 @@ void RendererCanvasRenderRD::free_polygon(PolygonID p_polygon) { //////////////////// -void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size) { +void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data) { if (p_texture == RID()) { p_texture = default_canvas_texture; } @@ -363,7 +363,7 @@ void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RI bool use_normal; bool use_specular; - bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(p_texture, p_base_filter, p_base_repeat, shader.default_version_rd_shader, CANVAS_TEXTURE_UNIFORM_SET, bool(push_constant.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR), uniform_set, size, specular_shininess, use_normal, use_specular); + bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(p_texture, p_base_filter, p_base_repeat, shader.default_version_rd_shader, CANVAS_TEXTURE_UNIFORM_SET, bool(push_constant.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR), uniform_set, size, specular_shininess, use_normal, use_specular, p_texture_is_data); //something odd happened if (!success) { _bind_canvas_texture(p_draw_list, default_canvas_texture, p_base_filter, p_base_repeat, r_last_texture, push_constant, r_texpixel_size); @@ -507,7 +507,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend //bind textures - _bind_canvas_texture(p_draw_list, rect->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size); + _bind_canvas_texture(p_draw_list, rect->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size, bool(rect->flags & CANVAS_RECT_MSDF)); Rect2 src_rect; Rect2 dst_rect; @@ -900,9 +900,9 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend RD::VertexFormatID vertex_format = RD::INVALID_FORMAT_ID; if (mesh_instance.is_valid()) { - mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array, vertex_format); + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, false, vertex_array, vertex_format); } else { - mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, vertex_array, vertex_format); + mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, false, vertex_array, vertex_format); } RID pipeline = pipeline_variants->variants[light_mode][variant[primitive]].get_render_pipeline(vertex_format, p_framebuffer_format); @@ -2427,7 +2427,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() { actions.renames["LIGHT_VERTEX"] = "light_vertex"; actions.renames["SHADOW_VERTEX"] = "shadow_vertex"; actions.renames["UV"] = "uv"; - actions.renames["POINT_SIZE"] = "gl_PointSize"; + actions.renames["POINT_SIZE"] = "point_size"; actions.renames["MODEL_MATRIX"] = "model_matrix"; actions.renames["CANVAS_MATRIX"] = "canvas_data.canvas_transform"; @@ -2475,6 +2475,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() { actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n"; actions.usage_defines["LIGHT"] = "#define LIGHT_SHADER_CODE_USED\n"; actions.usage_defines["SPECULAR_SHININESS"] = "#define SPECULAR_SHININESS_USED\n"; + actions.usage_defines["POINT_SIZE"] = "#define USE_POINT_SIZE\n"; actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n"; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index af8736a445..4c8cbd1c9f 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -418,7 +418,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { RID _create_base_uniform_set(RID p_to_render_target, bool p_backbuffer); - inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size); //recursive, so regular inline used instead. + inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead. void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used); void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 9d4d266a7a..20e24dba0e 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -745,8 +745,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(const RenderDataRD *p_ren } if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS && _render_buffers_get_velocity_texture(rb).is_valid()) { - Size2i rtsize = texture_storage->render_target_get_size(render_target); - copy_effects->copy_to_fb_rect(_render_buffers_get_velocity_texture(rb), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false); + debug_effects->draw_motion_vectors(_render_buffers_get_velocity_texture(rb), texture_storage->render_target_get_rd_framebuffer(render_target), rb->get_internal_size()); } } diff --git a/servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl b/servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl new file mode 100644 index 0000000000..80e4f51565 --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl @@ -0,0 +1,80 @@ +#[vertex] + +#version 450 + +#VERSION_DEFINES + +layout(location = 0) out vec2 uv_interp; + +void main() { + vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 +} + +#[fragment] + +#version 450 + +#VERSION_DEFINES + +layout(location = 0) in vec2 uv_interp; + +layout(set = 0, binding = 0) uniform sampler2D source_velocity; + +layout(location = 0) out vec4 frag_color; + +layout(push_constant, std430) uniform Params { + vec2 resolution; +} +params; + +// Based on distance to line segment from https://www.shadertoy.com/view/3tdSDj + +float line_segment(in vec2 p, in vec2 a, in vec2 b) { + vec2 aspect = vec2(params.resolution.x / params.resolution.y, 1.0f); + vec2 ba = (b - a) * aspect; + vec2 pa = (p - a) * aspect; + float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0f, 1.0f); + return length(pa - h * ba) * (params.resolution.y / 2.0f); +} + +void main() { + // Retrieve motion vector data. + float cell_size = 32.0f; + float circle_radius = 2.0f; + vec3 nan_color = vec3(1.0f, 0.0f, 0.0f); + vec3 active_color = vec3(1.0f, 0.8f, 0.1f); + vec3 inactive_color = vec3(0.5f, 0.5f, 0.5f); + vec2 pos_pixel = uv_interp * params.resolution; + vec2 cell_pos_pixel = floor(pos_pixel / cell_size) * cell_size + (cell_size * 0.5f); + vec2 cell_pos_uv = cell_pos_pixel / params.resolution; + vec2 cell_pos_previous_uv = cell_pos_uv + textureLod(source_velocity, cell_pos_uv, 0.0f).xy; + + // Draw the shapes. + float epsilon = 1e-6f; + vec2 cell_pos_delta_uv = cell_pos_uv - cell_pos_previous_uv; + bool motion_active = length(cell_pos_delta_uv) > epsilon; + vec3 color; + if (any(isnan(cell_pos_delta_uv))) { + color = nan_color; + } else if (motion_active) { + color = active_color; + } else { + color = inactive_color; + } + + float alpha; + if (length(cell_pos_pixel - pos_pixel) <= circle_radius) { + // Circle center. + alpha = 1.0f; + } else if (motion_active) { + // Motion vector line. + alpha = 1.0f - line_segment(uv_interp, cell_pos_uv, cell_pos_previous_uv); + } else { + // Ignore pixel. + alpha = 0.0f; + } + + frag_color = vec4(color, alpha); +} diff --git a/servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl b/servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl index 02566d8e35..e50397cc5d 100644 --- a/servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl @@ -320,7 +320,7 @@ vec3 temporal_antialiasing(uvec2 pos_group_top_left, uvec2 pos_group, uvec2 pos_ vec2 velocity = imageLoad(velocity_buffer, ivec2(pos_screen)).xy; // Get reprojected uv - vec2 uv_reprojected = uv - velocity; + vec2 uv_reprojected = uv + velocity; // Get input color vec3 color_input = load_color(pos_group); diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 9214a953aa..8f3c704afe 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -18,7 +18,11 @@ layout(location = 0) in vec3 vertex_attrib; layout(location = 1) in vec2 normal_attrib; #endif -#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) +#if !defined(TANGENT_USED) && (defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)) +#define TANGENT_USED +#endif + +#ifdef TANGENT_USED layout(location = 2) in vec2 tangent_attrib; #endif @@ -58,6 +62,18 @@ layout(location = 10) in uvec4 bone_attrib; layout(location = 11) in vec4 weight_attrib; #endif +#ifdef MOTION_VECTORS +layout(location = 12) in vec3 previous_vertex_attrib; + +#ifdef NORMAL_USED +layout(location = 13) in vec2 previous_normal_attrib; +#endif + +#ifdef TANGENT_USED +layout(location = 14) in vec2 previous_tangent_attrib; +#endif +#endif // MOTION_VECTORS + vec3 oct_to_vec3(vec2 e) { vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y)); float t = max(-v.z, 0.0); @@ -85,7 +101,7 @@ layout(location = 3) out vec2 uv_interp; layout(location = 4) out vec2 uv2_interp; #endif -#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) +#ifdef TANGENT_USED layout(location = 5) out vec3 tangent_interp; layout(location = 6) out vec3 binormal_interp; #endif @@ -161,7 +177,14 @@ vec3 double_add_vec3(vec3 base_a, vec3 prec_a, vec3 base_b, vec3 prec_b, out vec } #endif -void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multimesh_offset, in SceneData scene_data, in mat4 model_matrix, out vec4 screen_pos) { +void vertex_shader(vec3 vertex_input, +#ifdef NORMAL_USED + in vec2 normal_input, +#endif +#ifdef TANGENT_USED + in vec2 tangent_input, +#endif + in uint instance_index, in bool is_multimesh, in uint multimesh_offset, in SceneData scene_data, in mat4 model_matrix, out vec4 screen_pos) { vec4 instance_custom = vec4(0.0); #if defined(COLOR_USED) color_interp = color_attrib; @@ -289,15 +312,15 @@ void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multime model_normal_matrix = model_normal_matrix * mat3(matrix); } - vec3 vertex = vertex_attrib; + vec3 vertex = vertex_input; #ifdef NORMAL_USED - vec3 normal = oct_to_vec3(normal_attrib * 2.0 - 1.0); + vec3 normal = oct_to_vec3(normal_input * 2.0 - 1.0); #endif -#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) - vec2 signed_tangent_attrib = tangent_attrib * 2.0 - 1.0; - vec3 tangent = oct_to_vec3(vec2(signed_tangent_attrib.x, abs(signed_tangent_attrib.y) * 2.0 - 1.0)); - float binormalf = sign(signed_tangent_attrib.y); +#ifdef TANGENT_USED + vec2 signed_tangent_input = tangent_input * 2.0 - 1.0; + vec3 tangent = oct_to_vec3(vec2(signed_tangent_input.x, abs(signed_tangent_input.y) * 2.0 - 1.0)); + float binormalf = sign(signed_tangent_input.y); vec3 binormal = normalize(cross(normal, tangent) * binormalf); #endif @@ -333,7 +356,7 @@ void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multime normal = model_normal_matrix * normal; #endif -#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) +#ifdef TANGENT_USED tangent = model_normal_matrix * tangent; binormal = model_normal_matrix * binormal; @@ -377,7 +400,7 @@ void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multime #endif -#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) +#ifdef TANGENT_USED binormal = modelview_normal * binormal; tangent = modelview_normal * tangent; @@ -391,7 +414,7 @@ void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multime normal = (scene_data.view_matrix * vec4(normal, 0.0)).xyz; #endif -#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) +#ifdef TANGENT_USED binormal = (scene_data.view_matrix * vec4(binormal, 0.0)).xyz; tangent = (scene_data.view_matrix * vec4(tangent, 0.0)).xyz; #endif @@ -403,7 +426,7 @@ void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multime normal_interp = normal; #endif -#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) +#ifdef TANGENT_USED tangent_interp = tangent; binormal_interp = binormal; #endif @@ -472,16 +495,33 @@ void main() { instance_index_interp = instance_index; mat4 model_matrix = instances.data[instance_index].transform; -#if defined(MOTION_VECTORS) + +#ifdef MOTION_VECTORS + // Previous vertex. global_time = scene_data_block.prev_data.time; - vertex_shader(instance_index, is_multimesh, draw_call.multimesh_motion_vectors_previous_offset, scene_data_block.prev_data, instances.data[instance_index].prev_transform, prev_screen_position); - global_time = scene_data_block.data.time; - vertex_shader(instance_index, is_multimesh, draw_call.multimesh_motion_vectors_current_offset, scene_data_block.data, model_matrix, screen_position); + vertex_shader(previous_vertex_attrib, +#ifdef NORMAL_USED + previous_normal_attrib, +#endif +#ifdef TANGENT_USED + previous_tangent_attrib, +#endif + instance_index, is_multimesh, draw_call.multimesh_motion_vectors_previous_offset, scene_data_block.prev_data, instances.data[instance_index].prev_transform, prev_screen_position); #else - global_time = scene_data_block.data.time; + // Unused output. vec4 screen_position; - vertex_shader(instance_index, is_multimesh, draw_call.multimesh_motion_vectors_current_offset, scene_data_block.data, model_matrix, screen_position); #endif + + // Current vertex. + global_time = scene_data_block.data.time; + vertex_shader(vertex_attrib, +#ifdef NORMAL_USED + normal_attrib, +#endif +#ifdef TANGENT_USED + tangent_attrib, +#endif + instance_index, is_multimesh, draw_call.multimesh_motion_vectors_current_offset, scene_data_block.data, model_matrix, screen_position); } #[fragment] @@ -535,7 +575,11 @@ layout(location = 3) in vec2 uv_interp; layout(location = 4) in vec2 uv2_interp; #endif -#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) +#if !defined(TANGENT_USED) && (defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)) +#define TANGENT_USED +#endif + +#ifdef TANGENT_USED layout(location = 5) in vec3 tangent_interp; layout(location = 6) in vec3 binormal_interp; #endif @@ -771,7 +815,7 @@ void fragment_shader(in SceneData scene_data) { float alpha = float(instances.data[instance_index].flags >> INSTANCE_FLAGS_FADE_SHIFT) / float(255.0); -#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) +#ifdef TANGENT_USED vec3 binormal = normalize(binormal_interp); vec3 tangent = normalize(tangent_interp); #else @@ -2249,7 +2293,7 @@ void fragment_shader(in SceneData scene_data) { vec2 position_uv = position_clip * vec2(0.5, 0.5); vec2 prev_position_uv = prev_position_clip * vec2(0.5, 0.5); - motion_vector = position_uv - prev_position_uv; + motion_vector = prev_position_uv - position_uv; #endif } diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index b63eea1401..0283482d76 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -899,6 +899,10 @@ void main() { break; } + if (!bool(decals.data[decal_index].mask & draw_call.layer_mask)) { + continue; //not masked + } + vec3 uv_local = (decals.data[decal_index].xform * vec4(vertex, 1.0)).xyz; if (any(lessThan(uv_local, vec3(0.0, -1.0, 0.0))) || any(greaterThan(uv_local, vec3(1.0)))) { continue; //out of decal diff --git a/servers/rendering/renderer_rd/shaders/particles_copy.glsl b/servers/rendering/renderer_rd/shaders/particles_copy.glsl index 67a894b86f..6a06979c2c 100644 --- a/servers/rendering/renderer_rd/shaders/particles_copy.glsl +++ b/servers/rendering/renderer_rd/shaders/particles_copy.glsl @@ -45,6 +45,9 @@ layout(set = 2, binding = 0, std430) restrict readonly buffer TrailBindPoses { } trail_bind_poses; +#define PARAMS_FLAG_ORDER_BY_LIFETIME 1 +#define PARAMS_FLAG_COPY_MODE_2D 2 + layout(push_constant, std430) uniform Params { vec3 sort_direction; uint total_particles; @@ -57,10 +60,10 @@ layout(push_constant, std430) uniform Params { vec3 align_up; uint align_mode; - bool order_by_lifetime; uint lifetime_split; bool lifetime_reverse; - bool copy_mode_2d; + uint motion_vectors_current_offset; + uint flags; mat4 inv_emission_transform; } @@ -103,7 +106,7 @@ void main() { particle = uint(sort_buffer.data[particle].y); //use index from sort buffer } #else - if (params.order_by_lifetime) { + if (bool(params.flags & PARAMS_FLAG_ORDER_BY_LIFETIME)) { if (params.trail_size > 1) { uint limit = (params.total_particles / params.trail_size) - params.lifetime_split; @@ -201,7 +204,7 @@ void main() { txform = txform * trail_bind_poses.data[part_ofs]; } - if (params.copy_mode_2d) { + if (bool(params.flags & PARAMS_FLAG_COPY_MODE_2D)) { // In global mode, bring 2D particles to local coordinates // as they will be drawn with the node position as origin. txform = params.inv_emission_transform * txform; @@ -213,15 +216,16 @@ void main() { } txform = transpose(txform); - if (params.copy_mode_2d) { - uint write_offset = gl_GlobalInvocationID.x * (2 + 1 + 1); //xform + color + custom + uint instance_index = gl_GlobalInvocationID.x + params.motion_vectors_current_offset; + if (bool(params.flags & PARAMS_FLAG_COPY_MODE_2D)) { + uint write_offset = instance_index * (2 + 1 + 1); //xform + color + custom instances.data[write_offset + 0] = txform[0]; instances.data[write_offset + 1] = txform[1]; instances.data[write_offset + 2] = particles.data[particle].color; instances.data[write_offset + 3] = particles.data[particle].custom; } else { - uint write_offset = gl_GlobalInvocationID.x * (3 + 1 + 1); //xform + color + custom + uint write_offset = instance_index * (3 + 1 + 1); //xform + color + custom instances.data[write_offset + 0] = txform[0]; instances.data[write_offset + 1] = txform[1]; diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index a96893570e..fda341bbc9 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -1821,14 +1821,16 @@ void MaterialStorage::global_shader_parameters_load_settings(bool p_load_texture if (gvtype >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) { //textire if (!p_load_textures) { - value = RID(); continue; } String path = value; - Ref<Resource> resource = ResourceLoader::load(path); - ERR_CONTINUE(resource.is_null()); - value = resource; + if (path.is_empty()) { + value = RID(); + } else { + Ref<Resource> resource = ResourceLoader::load(path); + value = resource; + } } if (global_shader_uniforms.variables.has(name)) { diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 6888af7af9..439d0702f5 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -29,7 +29,6 @@ /**************************************************************************/ #include "mesh_storage.h" -#include "../../rendering_server_globals.h" using namespace RendererRD; @@ -854,8 +853,11 @@ void MeshStorage::_mesh_instance_clear(MeshInstance *mi) { } memfree(surface.versions); } - if (surface.vertex_buffer.is_valid()) { - RD::get_singleton()->free(surface.vertex_buffer); + + for (uint32_t i = 0; i < 2; i++) { + if (surface.vertex_buffer[i].is_valid()) { + RD::get_singleton()->free(surface.vertex_buffer[i]); + } } } mi->surfaces.clear(); @@ -881,35 +883,38 @@ void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint3 MeshInstance::Surface s; if ((mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) && mesh->surfaces[p_surface]->vertex_buffer_size > 0) { - //surface warrants transform - s.vertex_buffer = RD::get_singleton()->vertex_buffer_create(mesh->surfaces[p_surface]->vertex_buffer_size, Vector<uint8_t>(), true); - - Vector<RD::Uniform> uniforms; - { - RD::Uniform u; - u.binding = 1; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.append_id(s.vertex_buffer); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 2; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - if (mi->blend_weights_buffer.is_valid()) { - u.append_id(mi->blend_weights_buffer); - } else { - u.append_id(default_rd_storage_buffer); - } - uniforms.push_back(u); - } - s.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, skeleton_shader.version_shader[0], SkeletonShader::UNIFORM_SET_INSTANCE); + _mesh_instance_add_surface_buffer(mi, mesh, &s, p_surface, 0); } mi->surfaces.push_back(s); mi->dirty = true; } +void MeshStorage::_mesh_instance_add_surface_buffer(MeshInstance *mi, Mesh *mesh, MeshInstance::Surface *s, uint32_t p_surface, uint32_t p_buffer_index) { + s->vertex_buffer[p_buffer_index] = RD::get_singleton()->vertex_buffer_create(mesh->surfaces[p_surface]->vertex_buffer_size, Vector<uint8_t>(), true); + + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.binding = 1; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.append_id(s->vertex_buffer[p_buffer_index]); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 2; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + if (mi->blend_weights_buffer.is_valid()) { + u.append_id(mi->blend_weights_buffer); + } else { + u.append_id(default_rd_storage_buffer); + } + uniforms.push_back(u); + } + s->uniform_set[p_buffer_index] = RD::get_singleton()->uniform_set_create(uniforms, skeleton_shader.version_shader[0], SkeletonShader::UNIFORM_SET_INSTANCE); +} + void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) { MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); @@ -956,6 +961,8 @@ void MeshStorage::update_mesh_instances() { } //process skeletons and blend shapes + uint64_t frame = RSG::rasterizer->get_frame_number(); + bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0); RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); while (dirty_mesh_instance_arrays.first()) { @@ -964,7 +971,29 @@ void MeshStorage::update_mesh_instances() { Skeleton *sk = skeleton_owner.get_or_null(mi->skeleton); for (uint32_t i = 0; i < mi->surfaces.size(); i++) { - if (mi->surfaces[i].uniform_set == RID() || mi->mesh->surfaces[i]->uniform_set == RID()) { + if (mi->surfaces[i].uniform_set[0].is_null() || mi->mesh->surfaces[i]->uniform_set.is_null()) { + // Skip over mesh instances that don't require their own uniform buffers. + continue; + } + + mi->surfaces[i].previous_buffer = mi->surfaces[i].current_buffer; + + if (uses_motion_vectors && (frame - mi->surfaces[i].last_change) == 1) { + // Previous buffer's data can only be one frame old to be able to use motion vectors. + uint32_t new_buffer_index = mi->surfaces[i].current_buffer ^ 1; + + if (mi->surfaces[i].uniform_set[new_buffer_index].is_null()) { + // Create the new vertex buffer on demand where the result for the current frame will be stored. + _mesh_instance_add_surface_buffer(mi, mi->mesh, &mi->surfaces[i], i, new_buffer_index); + } + + mi->surfaces[i].current_buffer = new_buffer_index; + } + + mi->surfaces[i].last_change = frame; + + RID mi_surface_uniform_set = mi->surfaces[i].uniform_set[mi->surfaces[i].current_buffer]; + if (mi_surface_uniform_set.is_null()) { continue; } @@ -972,7 +1001,7 @@ void MeshStorage::update_mesh_instances() { RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, skeleton_shader.pipeline[array_is_2d ? SkeletonShader::SHADER_MODE_2D : SkeletonShader::SHADER_MODE_3D]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, mi->surfaces[i].uniform_set, SkeletonShader::UNIFORM_SET_INSTANCE); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, mi_surface_uniform_set, SkeletonShader::UNIFORM_SET_INSTANCE); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, mi->mesh->surfaces[i]->uniform_set, SkeletonShader::UNIFORM_SET_SURFACE); if (sk && sk->uniform_set_mi.is_valid()) { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sk->uniform_set_mi, SkeletonShader::UNIFORM_SET_SKELETON); @@ -1032,7 +1061,7 @@ void MeshStorage::update_mesh_instances() { RD::get_singleton()->compute_list_end(); } -void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, MeshInstance::Surface *mis) { +void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis) { Vector<RD::VertexAttribute> attributes; Vector<RID> buffers; @@ -1105,7 +1134,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V } if (mis) { - buffer = mis->vertex_buffer; + buffer = mis->vertex_buffer[mis->current_buffer]; } else { buffer = s->vertex_buffer; } @@ -1117,7 +1146,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V stride += sizeof(uint16_t) * 2; if (mis) { - buffer = mis->vertex_buffer; + buffer = mis->vertex_buffer[mis->current_buffer]; } else { buffer = s->vertex_buffer; } @@ -1128,7 +1157,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V stride += sizeof(uint16_t) * 2; if (mis) { - buffer = mis->vertex_buffer; + buffer = mis->vertex_buffer[mis->current_buffer]; } else { buffer = s->vertex_buffer; } @@ -1193,6 +1222,32 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V attributes.push_back(vd); buffers.push_back(buffer); + + if (p_input_motion_vectors) { + // Since the previous vertex, normal and tangent can't be part of the vertex format but they are required when motion + // vectors are enabled, we opt to push a copy of the vertex attribute with a different location and buffer (if it's + // part of an instance that has one). + switch (i) { + case RS::ARRAY_VERTEX: { + vd.location = ATTRIBUTE_LOCATION_PREV_VERTEX; + } break; + case RS::ARRAY_NORMAL: { + vd.location = ATTRIBUTE_LOCATION_PREV_NORMAL; + } break; + case RS::ARRAY_TANGENT: { + vd.location = ATTRIBUTE_LOCATION_PREV_TANGENT; + } break; + } + + if (int(vd.location) != i) { + if (mis && buffer != mesh_default_rd_buffers[i]) { + buffer = mis->vertex_buffer[mis->previous_buffer]; + } + + attributes.push_back(vd); + buffers.push_back(buffer); + } + } } //update final stride @@ -1202,7 +1257,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V } int loc = attributes[i].location; - if (loc < RS::ARRAY_COLOR) { + if ((loc < RS::ARRAY_COLOR) || ((loc >= ATTRIBUTE_LOCATION_PREV_VERTEX) && (loc <= ATTRIBUTE_LOCATION_PREV_TANGENT))) { attributes.write[i].stride = stride; } else if (loc < RS::ARRAY_BONES) { attributes.write[i].stride = attribute_stride; @@ -1212,6 +1267,9 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V } v.input_mask = p_input_mask; + v.current_buffer = mis ? mis->current_buffer : 0; + v.previous_buffer = mis ? mis->previous_buffer : 0; + v.input_motion_vectors = p_input_motion_vectors; v.vertex_format = RD::get_singleton()->vertex_format_create(attributes); v.vertex_array = RD::get_singleton()->vertex_array_create(s->vertex_count, v.vertex_format, buffers); } @@ -1309,13 +1367,10 @@ void MeshStorage::_multimesh_enable_motion_vectors(MultiMesh *multimesh) { RID new_buffer = RD::get_singleton()->storage_buffer_create(new_buffer_size); if (multimesh->buffer_set && multimesh->data_cache.is_empty()) { - // If the buffer was set but there's no data cached in the CPU, we must download it from the GPU and - // upload it because RD does not provide a way to copy the buffer directly yet. + // If the buffer was set but there's no data cached in the CPU, we copy the buffer directly on the GPU. RD::get_singleton()->barrier(); - Vector<uint8_t> buffer_data = RD::get_singleton()->buffer_get_data(multimesh->buffer); - ERR_FAIL_COND(buffer_data.size() != int(buffer_size)); - RD::get_singleton()->buffer_update(new_buffer, 0, buffer_size, buffer_data.ptr(), RD::BARRIER_MASK_NO_BARRIER); - RD::get_singleton()->buffer_update(new_buffer, buffer_size, buffer_size, buffer_data.ptr()); + RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, 0, buffer_size, RD::BARRIER_MASK_NO_BARRIER); + RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, buffer_size, buffer_size); } else if (!multimesh->data_cache.is_empty()) { // Simply upload the data cached in the CPU, which should already be doubled in size. ERR_FAIL_COND(multimesh->data_cache.size() * sizeof(float) != size_t(new_buffer_size)); diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index c23a5b1449..99ba69f98a 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -31,6 +31,7 @@ #ifndef MESH_STORAGE_RD_H #define MESH_STORAGE_RD_H +#include "../../rendering_server_globals.h" #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" @@ -90,6 +91,9 @@ private: struct Version { uint32_t input_mask = 0; + uint32_t current_buffer = 0; + uint32_t previous_buffer = 0; + bool input_motion_vectors = false; RD::VertexFormatID vertex_format = 0; RID vertex_array; }; @@ -162,8 +166,11 @@ private: Mesh *mesh = nullptr; RID skeleton; struct Surface { - RID vertex_buffer; - RID uniform_set; + RID vertex_buffer[2]; + RID uniform_set[2]; + uint32_t current_buffer = 0; + uint32_t previous_buffer = 0; + uint64_t last_change = 0; Mesh::Surface::Version *versions = nullptr; //allocated on demand uint32_t version_count = 0; @@ -183,10 +190,11 @@ private: weight_update_list(this), array_update_list(this) {} }; - void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, MeshInstance::Surface *mis = nullptr); + void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis = nullptr); void _mesh_instance_clear(MeshInstance *mi); void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface); + void _mesh_instance_add_surface_buffer(MeshInstance *mi, Mesh *mesh, MeshInstance::Surface *s, uint32_t p_surface, uint32_t p_buffer_index); mutable RID_Owner<MeshInstance> mesh_instance_owner; @@ -311,6 +319,12 @@ private: Skeleton *skeleton_dirty_list = nullptr; + enum AttributeLocation { + ATTRIBUTE_LOCATION_PREV_VERTEX = 12, + ATTRIBUTE_LOCATION_PREV_NORMAL = 13, + ATTRIBUTE_LOCATION_PREV_TANGENT = 14 + }; + public: static MeshStorage *get_singleton(); @@ -437,7 +451,7 @@ public: } } - _FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint32_t p_input_mask, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) { + _FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint32_t p_input_mask, bool p_input_motion_vectors, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) { Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); s->version_lock.lock(); @@ -445,9 +459,11 @@ public: //there will never be more than, at much, 3 or 4 versions, so iterating is the fastest way for (uint32_t i = 0; i < s->version_count; i++) { - if (s->versions[i].input_mask != p_input_mask) { + if (s->versions[i].input_mask != p_input_mask || s->versions[i].input_motion_vectors != p_input_motion_vectors) { + // Find the version that matches the inputs required. continue; } + //we have this version, hooray r_vertex_format = s->versions[i].vertex_format; r_vertex_array_rd = s->versions[i].vertex_array; @@ -459,7 +475,7 @@ public: s->version_count++; s->versions = (Mesh::Surface::Version *)memrealloc(s->versions, sizeof(Mesh::Surface::Version) * s->version_count); - _mesh_surface_generate_version_for_input_mask(s->versions[version], s, p_input_mask); + _mesh_surface_generate_version_for_input_mask(s->versions[version], s, p_input_mask, p_input_motion_vectors); r_vertex_format = s->versions[version].vertex_format; r_vertex_array_rd = s->versions[version].vertex_array; @@ -467,7 +483,7 @@ public: s->version_lock.unlock(); } - _FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint32_t p_input_mask, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) { + _FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint32_t p_input_mask, bool p_input_motion_vectors, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) { MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); ERR_FAIL_COND(!mi); Mesh *mesh = mi->mesh; @@ -475,15 +491,26 @@ public: MeshInstance::Surface *mis = &mi->surfaces[p_surface_index]; Mesh::Surface *s = mesh->surfaces[p_surface_index]; + uint32_t current_buffer = mis->current_buffer; + + // Using the previous buffer is only allowed if the surface was updated this frame and motion vectors are required. + uint32_t previous_buffer = p_input_motion_vectors && (RSG::rasterizer->get_frame_number() == mis->last_change) ? mis->previous_buffer : current_buffer; s->version_lock.lock(); //there will never be more than, at much, 3 or 4 versions, so iterating is the fastest way for (uint32_t i = 0; i < mis->version_count; i++) { - if (mis->versions[i].input_mask != p_input_mask) { + if (mis->versions[i].input_mask != p_input_mask || mis->versions[i].input_motion_vectors != p_input_motion_vectors) { + // Find the version that matches the inputs required. continue; } + + if (mis->versions[i].current_buffer != current_buffer || mis->versions[i].previous_buffer != previous_buffer) { + // Find the version that corresponds to the correct buffers that should be used. + continue; + } + //we have this version, hooray r_vertex_format = mis->versions[i].vertex_format; r_vertex_array_rd = mis->versions[i].vertex_array; @@ -495,7 +522,7 @@ public: mis->version_count++; mis->versions = (Mesh::Surface::Version *)memrealloc(mis->versions, sizeof(Mesh::Surface::Version) * mis->version_count); - _mesh_surface_generate_version_for_input_mask(mis->versions[version], s, p_input_mask, mis); + _mesh_surface_generate_version_for_input_mask(mis->versions[version], s, p_input_mask, p_input_motion_vectors, mis); r_vertex_format = mis->versions[version].vertex_format; r_vertex_array_rd = mis->versions[version].vertex_array; diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index 0a91672544..3a415e97e0 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -666,6 +666,19 @@ RID ParticlesStorage::particles_get_draw_pass_mesh(RID p_particles, int p_pass) return particles->draw_passes[p_pass]; } +void ParticlesStorage::particles_update_dependency(RID p_particles, DependencyTracker *p_instance) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + p_instance->update_dependency(&particles->dependency); +} + +void ParticlesStorage::particles_get_instance_buffer_motion_vectors_offsets(RID p_particles, uint32_t &r_current_offset, uint32_t &r_prev_offset) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + r_current_offset = particles->instance_motion_vectors_current_offset; + r_prev_offset = particles->instance_motion_vectors_previous_offset; +} + void ParticlesStorage::particles_add_collision(RID p_particles, RID p_particles_collision_instance) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); @@ -1185,6 +1198,7 @@ void ParticlesStorage::particles_set_view_axis(RID p_particles, const Vector3 &p copy_push_constant.order_by_lifetime = (particles->draw_order == RS::PARTICLES_DRAW_ORDER_LIFETIME || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME); copy_push_constant.lifetime_split = (MIN(int(particles->amount * particles->phase), particles->amount - 1) + 1) % particles->amount; copy_push_constant.lifetime_reverse = particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME; + copy_push_constant.motion_vectors_current_offset = particles->instance_motion_vectors_current_offset; copy_push_constant.frame_remainder = particles->interpolate ? particles->frame_remainder : 0.0; copy_push_constant.total_particles = particles->amount; @@ -1252,28 +1266,50 @@ void ParticlesStorage::_particles_update_buffers(Particles *particles) { userdata_count = particle_shader_data->userdata_count; } + bool uses_motion_vectors = RSG::viewport->get_num_viewports_with_motion_vectors() > 0; + bool index_draw_order = particles->draw_order == RS::ParticlesDrawOrder::PARTICLES_DRAW_ORDER_INDEX; + bool enable_motion_vectors = uses_motion_vectors && index_draw_order && !particles->instance_motion_vectors_enabled; + bool only_instances_changed = false; + if (userdata_count != particles->userdata_count) { - // Mismatch userdata, re-create buffers. + // Mismatch userdata, re-create all buffers. _particles_free_data(particles); + } else if (enable_motion_vectors) { + // Only motion vectors are required, release the transforms buffer and uniform set. + if (particles->particle_instance_buffer.is_valid()) { + RD::get_singleton()->free(particles->particle_instance_buffer); + particles->particle_instance_buffer = RID(); + } + + particles->particles_transforms_buffer_uniform_set = RID(); + only_instances_changed = true; + } else if (!particles->particle_buffer.is_null()) { + // No operation is required because a buffer already exists, return early. + return; } - if (particles->amount > 0 && particles->particle_buffer.is_null()) { + if (particles->amount > 0) { int total_amount = particles->amount; if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { total_amount *= particles->trail_bind_poses.size(); } uint32_t xform_size = particles->mode == RS::PARTICLES_MODE_2D ? 2 : 3; - - particles->particle_buffer = RD::get_singleton()->storage_buffer_create((sizeof(ParticleData) + userdata_count * sizeof(float) * 4) * total_amount); - - particles->userdata_count = userdata_count; + if (particles->particle_buffer.is_null()) { + particles->particle_buffer = RD::get_singleton()->storage_buffer_create((sizeof(ParticleData) + userdata_count * sizeof(float) * 4) * total_amount); + particles->userdata_count = userdata_count; + } PackedByteArray data; - data.resize_zeroed(sizeof(float) * 4 * (xform_size + 1 + 1) * total_amount); + uint32_t particle_instance_buffer_size = total_amount * (xform_size + 1 + 1) * sizeof(float) * 4; + if (uses_motion_vectors) { + particle_instance_buffer_size *= 2; + particles->instance_motion_vectors_enabled = true; + } - particles->particle_instance_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * 4 * (xform_size + 1 + 1) * total_amount, data); - //needs to clear it + data.resize_zeroed(particle_instance_buffer_size); + + particles->particle_instance_buffer = RD::get_singleton()->storage_buffer_create(particle_instance_buffer_size, data); { Vector<RD::Uniform> uniforms; @@ -1295,9 +1331,20 @@ void ParticlesStorage::_particles_update_buffers(Particles *particles) { particles->particles_copy_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.copy_shader.version_get_shader(particles_shader.copy_shader_version, 0), 0); } + + particles->instance_motion_vectors_current_offset = 0; + particles->instance_motion_vectors_previous_offset = 0; + particles->instance_motion_vectors_last_change = -1; + + if (only_instances_changed) { + // Notify the renderer the instances uniform must be retrieved again, as it's the only element that has been changed because motion vectors were enabled. + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES_INSTANCES); + } } } void ParticlesStorage::update_particles() { + uint32_t frame = RSG::rasterizer->get_frame_number(); + bool uses_motion_vectors = RSG::viewport->get_num_viewports_with_motion_vectors() > 0; while (particle_update_list) { //use transform feedback to process particles @@ -1461,16 +1508,25 @@ void ParticlesStorage::update_particles() { // Ensure that memory is initialized (the code above should ensure that _particles_process is always called at least once upon clearing). DEV_ASSERT(!particles->clear); + int total_amount = particles->amount; + if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { + total_amount *= particles->trail_bind_poses.size(); + } + + // Swap offsets for motion vectors. Motion vectors can only be used when the draw order keeps the indices consistent across frames. + bool index_draw_order = particles->draw_order == RS::ParticlesDrawOrder::PARTICLES_DRAW_ORDER_INDEX; + particles->instance_motion_vectors_previous_offset = particles->instance_motion_vectors_current_offset; + if (uses_motion_vectors && index_draw_order && particles->instance_motion_vectors_enabled && (frame - particles->instance_motion_vectors_last_change) == 1) { + particles->instance_motion_vectors_current_offset = total_amount - particles->instance_motion_vectors_current_offset; + } + + particles->instance_motion_vectors_last_change = frame; + // Copy particles to instance buffer. if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { //does not need view dependent operation, do copy here ParticlesShader::CopyPushConstant copy_push_constant; - int total_amount = particles->amount; - if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { - total_amount *= particles->trail_bind_poses.size(); - } - // Affect 2D only. if (particles->use_local_coords) { // In local mode, particle positions are calculated locally (relative to the node position) @@ -1506,6 +1562,7 @@ void ParticlesStorage::update_particles() { copy_push_constant.order_by_lifetime = (particles->draw_order == RS::PARTICLES_DRAW_ORDER_LIFETIME || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME); copy_push_constant.lifetime_split = (MIN(int(particles->amount * particles->phase), particles->amount - 1) + 1) % particles->amount; copy_push_constant.lifetime_reverse = particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME; + copy_push_constant.motion_vectors_current_offset = particles->instance_motion_vectors_current_offset; RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); copy_push_constant.copy_mode_2d = particles->mode == RS::PARTICLES_MODE_2D ? 1 : 0; diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h index 612420a36c..7cea706a8c 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h @@ -225,6 +225,11 @@ private: double frame_remainder = 0; real_t collision_base_size = 0.01; + uint32_t instance_motion_vectors_current_offset = 0; + uint32_t instance_motion_vectors_previous_offset = 0; + uint64_t instance_motion_vectors_last_change = -1; + bool instance_motion_vectors_enabled = false; + bool clear = true; bool force_sub_emit = false; @@ -288,10 +293,13 @@ private: float align_up[3]; uint32_t align_mode; - uint32_t order_by_lifetime; uint32_t lifetime_split; uint32_t lifetime_reverse; - uint32_t copy_mode_2d; + uint32_t motion_vectors_current_offset; + struct { + uint32_t order_by_lifetime : 1; + uint32_t copy_mode_2d : 1; + }; float inv_emission_transform[16]; }; @@ -521,12 +529,15 @@ public: return particles->particles_transforms_buffer_uniform_set; } + void particles_get_instance_buffer_motion_vectors_offsets(RID p_particles, uint32_t &r_current_offset, uint32_t &r_prev_offset); + virtual void particles_add_collision(RID p_particles, RID p_particles_collision_instance) override; virtual void particles_remove_collision(RID p_particles, RID p_particles_collision_instance) override; void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture); virtual void update_particles() override; + void particles_update_dependency(RID p_particles, DependencyTracker *p_instance); Dependency *particles_get_dependency(RID p_particles) const; /* Particles Collision */ diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index f009318d24..286a9528fc 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -643,7 +643,7 @@ void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS: ct->clear_sets(); } -bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular) { +bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular, bool p_texture_is_data) { MaterialStorage *material_storage = MaterialStorage::get_singleton(); CanvasTexture *ct = nullptr; @@ -690,7 +690,7 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte u.append_id(texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE)); ct->size_cache = Size2i(1, 1); } else { - u.append_id(t->rd_texture_srgb.is_valid() && (p_use_srgb) ? t->rd_texture_srgb : t->rd_texture); + u.append_id(t->rd_texture_srgb.is_valid() && p_use_srgb && !p_texture_is_data ? t->rd_texture_srgb : t->rd_texture); ct->size_cache = Size2i(t->width_2d, t->height_2d); if (t->render_target) { t->render_target->was_used = true; diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h index 2137c5f383..276c8c4ce2 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h @@ -475,7 +475,7 @@ public: virtual void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override; virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override; - bool canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular); + bool canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular, bool p_texture_is_data); /* Texture API */ |
