diff options
Diffstat (limited to 'servers/rendering')
29 files changed, 1369 insertions, 241 deletions
diff --git a/servers/rendering/renderer_rd/cluster_builder_rd.cpp b/servers/rendering/renderer_rd/cluster_builder_rd.cpp index 0fdd864d47..2669a73014 100644 --- a/servers/rendering/renderer_rd/cluster_builder_rd.cpp +++ b/servers/rendering/renderer_rd/cluster_builder_rd.cpp @@ -86,13 +86,13 @@ ClusterBuilderSharedDataRD::ClusterBuilderSharedDataRD() { Vector<uint8_t> vertex_data; vertex_data.resize(sizeof(float) * icosphere_vertex_count * 3); - copymem(vertex_data.ptrw(), icosphere_vertices, vertex_data.size()); + memcpy(vertex_data.ptrw(), icosphere_vertices, vertex_data.size()); sphere_vertex_buffer = RD::get_singleton()->vertex_buffer_create(vertex_data.size(), vertex_data); Vector<uint8_t> index_data; index_data.resize(sizeof(uint32_t) * icosphere_triangle_count * 3); - copymem(index_data.ptrw(), icosphere_triangle_indices, index_data.size()); + memcpy(index_data.ptrw(), icosphere_triangle_indices, index_data.size()); sphere_index_buffer = RD::get_singleton()->index_buffer_create(icosphere_triangle_count * 3, RD::INDEX_BUFFER_FORMAT_UINT32, index_data); @@ -130,13 +130,13 @@ ClusterBuilderSharedDataRD::ClusterBuilderSharedDataRD() { Vector<uint8_t> vertex_data; vertex_data.resize(sizeof(float) * cone_vertex_count * 3); - copymem(vertex_data.ptrw(), cone_vertices, vertex_data.size()); + memcpy(vertex_data.ptrw(), cone_vertices, vertex_data.size()); cone_vertex_buffer = RD::get_singleton()->vertex_buffer_create(vertex_data.size(), vertex_data); Vector<uint8_t> index_data; index_data.resize(sizeof(uint32_t) * cone_triangle_count * 3); - copymem(index_data.ptrw(), cone_triangle_indices, index_data.size()); + memcpy(index_data.ptrw(), cone_triangle_indices, index_data.size()); cone_index_buffer = RD::get_singleton()->index_buffer_create(cone_triangle_count * 3, RD::INDEX_BUFFER_FORMAT_UINT32, index_data); @@ -184,13 +184,13 @@ ClusterBuilderSharedDataRD::ClusterBuilderSharedDataRD() { Vector<uint8_t> vertex_data; vertex_data.resize(sizeof(float) * box_vertex_count * 3); - copymem(vertex_data.ptrw(), box_vertices, vertex_data.size()); + memcpy(vertex_data.ptrw(), box_vertices, vertex_data.size()); box_vertex_buffer = RD::get_singleton()->vertex_buffer_create(vertex_data.size(), vertex_data); Vector<uint8_t> index_data; index_data.resize(sizeof(uint32_t) * box_triangle_count * 3); - copymem(index_data.ptrw(), box_triangle_indices, index_data.size()); + memcpy(index_data.ptrw(), box_triangle_indices, index_data.size()); box_index_buffer = RD::get_singleton()->index_buffer_create(box_triangle_count * 3, RD::INDEX_BUFFER_FORMAT_UINT32, index_data); diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp index bc304aedd8..563e08fdcb 100644 --- a/servers/rendering/renderer_rd/effects_rd.cpp +++ b/servers/rendering/renderer_rd/effects_rd.cpp @@ -226,7 +226,7 @@ RID EffectsRD::_get_compute_uniform_set_from_image_pair(RID p_texture1, RID p_te } void EffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y, bool p_panorama) { - zeromem(©_to_fb.push_constant, sizeof(CopyToFbPushConstant)); + memset(©_to_fb.push_constant, 0, sizeof(CopyToFbPushConstant)); copy_to_fb.push_constant.use_section = true; copy_to_fb.push_constant.section[0] = p_uv_rect.position.x; @@ -247,7 +247,7 @@ void EffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer } void EffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero, bool p_srgb, RID p_secondary) { - zeromem(©_to_fb.push_constant, sizeof(CopyToFbPushConstant)); + memset(©_to_fb.push_constant, 0, sizeof(CopyToFbPushConstant)); if (p_flip_y) { copy_to_fb.push_constant.flip_y = true; @@ -275,7 +275,7 @@ void EffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, } void EffectsRD::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_all_source, bool p_8_bit_dst, bool p_alpha_to_one) { - zeromem(©.push_constant, sizeof(CopyPushConstant)); + memset(©.push_constant, 0, sizeof(CopyPushConstant)); if (p_flip_y) { copy.push_constant.flags |= COPY_FLAG_FLIP_Y; } @@ -309,7 +309,7 @@ void EffectsRD::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const } void EffectsRD::copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array) { - zeromem(©.push_constant, sizeof(CopyPushConstant)); + memset(©.push_constant, 0, sizeof(CopyPushConstant)); copy.push_constant.section[0] = 0; copy.push_constant.section[1] = 0; @@ -329,7 +329,7 @@ void EffectsRD::copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, } void EffectsRD::copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far) { - zeromem(©.push_constant, sizeof(CopyPushConstant)); + memset(©.push_constant, 0, sizeof(CopyPushConstant)); if (p_flip_y) { copy.push_constant.flags |= COPY_FLAG_FLIP_Y; } @@ -353,7 +353,7 @@ void EffectsRD::copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_ } void EffectsRD::copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y) { - zeromem(©.push_constant, sizeof(CopyPushConstant)); + memset(©.push_constant, 0, sizeof(CopyPushConstant)); if (p_flip_y) { copy.push_constant.flags |= COPY_FLAG_FLIP_Y; } @@ -375,7 +375,7 @@ void EffectsRD::copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_texture, } void EffectsRD::set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst) { - zeromem(©.push_constant, sizeof(CopyPushConstant)); + memset(©.push_constant, 0, sizeof(CopyPushConstant)); copy.push_constant.section[0] = 0; copy.push_constant.section[1] = 0; @@ -397,7 +397,7 @@ void EffectsRD::set_color(RID p_dest_texture, const Color &p_color, const Rect2i } void EffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst) { - zeromem(©.push_constant, sizeof(CopyPushConstant)); + memset(©.push_constant, 0, sizeof(CopyPushConstant)); uint32_t base_flags = 0; copy.push_constant.section[0] = p_region.position.x; @@ -430,7 +430,7 @@ void EffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back } void EffectsRD::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_treshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) { - zeromem(©.push_constant, sizeof(CopyPushConstant)); + memset(©.push_constant, 0, sizeof(CopyPushConstant)); CopyMode copy_mode = p_first_pass && p_auto_exposure.is_valid() ? COPY_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE : COPY_MODE_GAUSSIAN_GLOW; uint32_t base_flags = 0; @@ -657,7 +657,7 @@ void EffectsRD::merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_bas } void EffectsRD::make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size) { - zeromem(©.push_constant, sizeof(CopyPushConstant)); + memset(©.push_constant, 0, sizeof(CopyPushConstant)); copy.push_constant.section[0] = 0; copy.push_constant.section[1] = 0; @@ -694,7 +694,7 @@ void EffectsRD::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffe } void EffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings) { - zeromem(&tonemap.push_constant, sizeof(TonemapPushConstant)); + memset(&tonemap.push_constant, 0, sizeof(TonemapPushConstant)); tonemap.push_constant.use_bcs = p_settings.use_bcs; tonemap.push_constant.bcs[0] = p_settings.brightness; @@ -1294,7 +1294,7 @@ void EffectsRD::roughness_limit(RID p_source_normal, RID p_roughness, const Size } void EffectsRD::cubemap_roughness(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size) { - zeromem(&roughness.push_constant, sizeof(CubemapRoughnessPushConstant)); + memset(&roughness.push_constant, 0, sizeof(CubemapRoughnessPushConstant)); roughness.push_constant.face_id = p_face_id > 9 ? 0 : p_face_id; roughness.push_constant.roughness = p_roughness; @@ -1368,7 +1368,7 @@ void EffectsRD::cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, void EffectsRD::render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_fog, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const CameraMatrix &p_camera, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position) { SkyPushConstant sky_push_constant; - zeromem(&sky_push_constant, sizeof(SkyPushConstant)); + memset(&sky_push_constant, 0, sizeof(SkyPushConstant)); sky_push_constant.proj[0] = p_camera.matrix[2][0]; sky_push_constant.proj[1] = p_camera.matrix[0][0]; @@ -1510,7 +1510,7 @@ EffectsRD::EffectsRD() { copy_modes.push_back("\n#define MODE_CUBEMAP_ARRAY_TO_PANORAMA\n"); copy.shader.initialize(copy_modes); - zeromem(©.push_constant, sizeof(CopyPushConstant)); + memset(©.push_constant, 0, sizeof(CopyPushConstant)); copy.shader_version = copy.shader.version_create(); for (int i = 0; i < COPY_MODE_MAX; i++) { 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 9be0dc0d15..89e88f23b3 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -466,6 +466,10 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(SceneState::PushConstant)); uint32_t instance_count = surf->owner->instance_count > 1 ? surf->owner->instance_count : element_info.repeat; + if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_PARTICLE_TRAILS) { + instance_count /= surf->owner->trail_steps; + } + RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count); i += element_info.repeat - 1; //skip equal elements } @@ -2379,9 +2383,13 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW; } + if (p_material->shader_data->uses_particle_trails) { + flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_PARTICLE_TRAILS; + } + SceneShaderForwardClustered::MaterialData *material_shadow = nullptr; void *surface_shadow = nullptr; - if (!p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_pre_pass) { + if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_pre_pass) { flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_SHARED_SHADOW_MATERIAL; material_shadow = (SceneShaderForwardClustered::MaterialData *)storage->material_get_data(scene_shader.default_material, RendererStorageRD::SHADER_TYPE_3D); @@ -2550,7 +2558,7 @@ void RenderForwardClustered::_geometry_instance_update(GeometryInstance *p_geome } } - ginstance->instance_count = storage->particles_get_amount(ginstance->data->base); + ginstance->instance_count = storage->particles_get_amount(ginstance->data->base, ginstance->trail_steps); } break; @@ -2564,42 +2572,26 @@ void RenderForwardClustered::_geometry_instance_update(GeometryInstance *p_geome if (ginstance->data->base_type == RS::INSTANCE_MULTIMESH) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH; - uint32_t stride; if (storage->multimesh_get_transform_format(ginstance->data->base) == RS::MULTIMESH_TRANSFORM_2D) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D; - stride = 2; - } else { - stride = 3; } if (storage->multimesh_uses_colors(ginstance->data->base)) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR; - stride += 1; } if (storage->multimesh_uses_custom_data(ginstance->data->base)) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA; - stride += 1; } - ginstance->base_flags |= (stride << INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_SHIFT); ginstance->transforms_uniform_set = storage->multimesh_get_3d_uniform_set(ginstance->data->base, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); } else if (ginstance->data->base_type == RS::INSTANCE_PARTICLES) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH; - uint32_t stride; - if (false) { // 2D particles - ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D; - stride = 2; - } else { - stride = 3; - } ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR; - stride += 1; - ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA; - stride += 1; - ginstance->base_flags |= (stride << INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_SHIFT); + //for particles, stride is the trail size + ginstance->base_flags |= (ginstance->trail_steps << INSTANCE_DATA_FLAGS_PARTICLE_TRAIL_SHIFT); if (!storage->particles_is_using_local_coords(ginstance->data->base)) { store_transform = false; @@ -2608,7 +2600,6 @@ void RenderForwardClustered::_geometry_instance_update(GeometryInstance *p_geome } else if (ginstance->data->base_type == RS::INSTANCE_MESH) { if (storage->skeleton_is_valid(ginstance->data->skeleton)) { - ginstance->base_flags |= INSTANCE_DATA_FLAG_SKELETON; ginstance->transforms_uniform_set = storage->skeleton_get_3d_uniform_set(ginstance->data->skeleton, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); if (ginstance->data->dirty_dependencies) { storage->skeleton_update_dependency(ginstance->data->skeleton, &ginstance->data->dependency_tracker); @@ -2643,6 +2634,7 @@ void RenderForwardClustered::_geometry_instance_dependency_changed(RendererStora switch (p_notification) { case RendererStorage::DEPENDENCY_CHANGED_MATERIAL: case RendererStorage::DEPENDENCY_CHANGED_MESH: + case RendererStorage::DEPENDENCY_CHANGED_PARTICLES: case RendererStorage::DEPENDENCY_CHANGED_MULTIMESH: case RendererStorage::DEPENDENCY_CHANGED_SKELETON_DATA: { static_cast<RenderForwardClustered *>(singleton)->_geometry_instance_mark_dirty(static_cast<GeometryInstance *>(p_tracker->userdata)); @@ -2756,7 +2748,7 @@ void RenderForwardClustered::geometry_instance_set_lightmap_capture(GeometryInst ginstance->lightmap_sh = geometry_instance_lightmap_sh.alloc(); } - copymem(ginstance->lightmap_sh->sh, p_sh9, sizeof(Color) * 9); + memcpy(ginstance->lightmap_sh->sh, p_sh9, sizeof(Color) * 9); } else { if (ginstance->lightmap_sh != nullptr) { geometry_instance_lightmap_sh.free(ginstance->lightmap_sh); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 72e84a6f24..05de10ae28 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -196,9 +196,9 @@ class RenderForwardClustered : public RendererSceneRenderRD { INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D = 1 << 13, INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR = 1 << 14, INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA = 1 << 15, - INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_SHIFT = 16, - INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_MASK = 0x7, - INSTANCE_DATA_FLAG_SKELETON = 1 << 19, + INSTANCE_DATA_FLAGS_PARTICLE_TRAIL_SHIFT = 16, + INSTANCE_DATA_FLAGS_PARTICLE_TRAIL_MASK = 0xFF, + INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 24, }; struct SceneState { @@ -398,6 +398,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { FLAG_USES_DEPTH_TEXTURE = 8192, FLAG_USES_NORMAL_TEXTURE = 16384, FLAG_USES_DOUBLE_SIDED_SHADOWS = 32768, + FLAG_USES_PARTICLE_TRAILS = 65536, }; union { @@ -453,6 +454,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { uint32_t layer_mask = 1; RID transforms_uniform_set; uint32_t instance_count = 0; + uint32_t trail_steps = 1; RID mesh_instance; bool can_sdfgi = false; //used during setup diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index 15982b4b29..df2931126b 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -73,6 +73,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { uses_time = false; writes_modelview_or_projection = false; uses_world_coordinates = false; + uses_particle_trails = false; int depth_drawi = DEPTH_DRAW_OPAQUE; @@ -101,6 +102,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { actions.render_mode_flags["unshaded"] = &unshaded; actions.render_mode_flags["wireframe"] = &wireframe; + actions.render_mode_flags["particle_trails"] = &uses_particle_trails; actions.usage_flag_pointers["ALPHA"] = &uses_alpha; actions.render_mode_flags["depth_prepass_alpha"] = &uses_depth_pre_pass; @@ -714,6 +716,7 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin actions.render_mode_defines["ensure_correct_normals"] = "#define ENSURE_CORRECT_NORMALS\n"; actions.render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n"; actions.render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n"; + actions.render_mode_defines["particle_trails"] = "#define USE_PARTICLE_TRAILS\n"; bool force_lambert = GLOBAL_GET("rendering/shading/overrides/force_lambert_over_burley"); diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h index 953a5291c8..7c8879686b 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h @@ -126,6 +126,7 @@ public: bool uses_discard; bool uses_roughness; bool uses_normal; + bool uses_particle_trails; bool unshaded; bool uses_vertex; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 3c76c91a67..377b0fd72d 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -304,7 +304,7 @@ RendererCanvasRender::PolygonID RendererCanvasRenderRD::request_polygon(const Ve index_buffer.resize(p_indices.size() * sizeof(int32_t)); { uint8_t *w = index_buffer.ptrw(); - copymem(w, p_indices.ptr(), sizeof(int32_t) * p_indices.size()); + memcpy(w, p_indices.ptr(), sizeof(int32_t) * p_indices.size()); } pb.index_buffer = RD::get_singleton()->index_buffer_create(p_indices.size(), RD::INDEX_BUFFER_FORMAT_UINT32, index_buffer); pb.indices = RD::get_singleton()->index_array_create(pb.index_buffer, 0, p_indices.size()); diff --git a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp index 3856f38457..2b0e93f734 100644 --- a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp @@ -1564,7 +1564,7 @@ void RendererSceneGIRD::SDFGI::render_region(RID p_render_buffers, int p_region, //clear dispatch indirect data SDFGIShader::PreprocessPushConstant push_constant; - zeromem(&push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); + memset(&push_constant, 0, sizeof(SDFGIShader::PreprocessPushConstant)); RENDER_TIMESTAMP("Scroll SDF"); @@ -2602,7 +2602,7 @@ void RendererSceneGIRD::GIProbeInstance::update(bool p_update_light_instances, c p_scene_render->_render_material(to_world_xform * xform, cm, true, p_scene_render->cull_argument, dynamic_maps[0].fb, Rect2i(Vector2i(), rect.size)); GIProbeDynamicPushConstant push_constant; - zeromem(&push_constant, sizeof(GIProbeDynamicPushConstant)); + memset(&push_constant, 0, sizeof(GIProbeDynamicPushConstant)); push_constant.limits[0] = octree_size.x; push_constant.limits[1] = octree_size.y; push_constant.limits[2] = octree_size.z; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index a98f67f163..a742c4cc28 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -1873,7 +1873,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(RID p_rende storage->render_target_disable_clear_request(rb->render_target); } -void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas) { +void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) { EffectsRD *effects = storage->get_effects(); RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); @@ -1932,6 +1932,13 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID RID reflection_texture = rb->reflection_buffer; effects->copy_to_fb_rect(ambient_texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture); } + + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_OCCLUDERS) { + if (p_occlusion_buffer.is_valid()) { + Size2 rtsize = storage->render_target_get_size(rb->render_target); + effects->copy_to_fb_rect(storage->texture_get_rd_texture(p_occlusion_buffer), storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize), true, false); + } + } } void RendererSceneRenderRD::environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) { @@ -3516,7 +3523,7 @@ void RendererSceneRenderRD::_pre_opaque_render(bool p_use_ssao, bool p_use_gi, R } } -void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data) { +void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data) { // getting this here now so we can direct call a bunch of things more easily RenderBuffers *rb = nullptr; if (p_render_buffers.is_valid()) { @@ -3643,7 +3650,7 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform & RENDER_TIMESTAMP("Tonemap"); _render_buffers_post_process_and_tonemap(p_render_buffers, p_environment, p_camera_effects, p_cam_projection); - _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas); + _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex); if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb != nullptr && rb->sdfgi != nullptr) { rb->sdfgi->debug_draw(p_cam_projection, p_cam_transform, rb->width, rb->height, rb->render_target, rb->texture); } diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 4bf0818206..8c01b69b91 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -441,7 +441,7 @@ private: void _allocate_blur_textures(RenderBuffers *rb); void _allocate_luminance_textures(RenderBuffers *rb); - void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas); + void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer); void _render_buffers_post_process_and_tonemap(RID p_render_buffers, RID p_environment, RID p_camera_effects, const CameraMatrix &p_projection); /* Cluster */ @@ -1125,7 +1125,7 @@ public: float render_buffers_get_volumetric_fog_end(RID p_render_buffers); float render_buffers_get_volumetric_fog_detail_spread(RID p_render_buffers); - void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr); + void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr); void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region); diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index 189c5782f4..47b9e33ca6 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -756,7 +756,7 @@ void RendererStorageRD::texture_3d_initialize(RID p_texture, Image::Format p_for for (int i = 0; i < p_data.size(); i++) { uint32_t s = images[i]->get_data().size(); - copymem(&all_data.write[offset], images[i]->get_data().ptr(), s); + memcpy(&all_data.write[offset], images[i]->get_data().ptr(), s); { Texture::BufferSlice3D slice; slice.size.width = images[i]->get_width(); @@ -919,7 +919,7 @@ void RendererStorageRD::texture_3d_update(RID p_texture, const Vector<Ref<Image> for (int i = 0; i < p_data.size(); i++) { uint32_t s = images[i]->get_data().size(); - copymem(&all_data.write[offset], images[i]->get_data().ptr(), s); + memcpy(&all_data.write[offset], images[i]->get_data().ptr(), s); offset += s; } } @@ -2108,13 +2108,13 @@ _FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, case ShaderLanguage::TYPE_INT: case ShaderLanguage::TYPE_UINT: case ShaderLanguage::TYPE_FLOAT: { - zeromem(data, 4); + memset(data, 0, 4); } break; case ShaderLanguage::TYPE_BVEC2: case ShaderLanguage::TYPE_IVEC2: case ShaderLanguage::TYPE_UVEC2: case ShaderLanguage::TYPE_VEC2: { - zeromem(data, 8); + memset(data, 0, 8); } break; case ShaderLanguage::TYPE_BVEC3: case ShaderLanguage::TYPE_IVEC3: @@ -2124,16 +2124,16 @@ _FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, case ShaderLanguage::TYPE_IVEC4: case ShaderLanguage::TYPE_UVEC4: case ShaderLanguage::TYPE_VEC4: { - zeromem(data, 16); + memset(data, 0, 16); } break; case ShaderLanguage::TYPE_MAT2: { - zeromem(data, 32); + memset(data, 0, 32); } break; case ShaderLanguage::TYPE_MAT3: { - zeromem(data, 48); + memset(data, 0, 48); } break; case ShaderLanguage::TYPE_MAT4: { - zeromem(data, 64); + memset(data, 0, 64); } break; default: { @@ -3412,10 +3412,10 @@ void RendererStorageRD::_multimesh_make_local(MultiMesh *multimesh) const { Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); { const uint8_t *r = buffer.ptr(); - copymem(w, r, buffer.size()); + memcpy(w, r, buffer.size()); } } else { - zeromem(w, multimesh->instances * multimesh->stride_cache * sizeof(float)); + memset(w, 0, multimesh->instances * multimesh->stride_cache * sizeof(float)); } } uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; @@ -3771,7 +3771,7 @@ Vector<float> RendererStorageRD::multimesh_get_buffer(RID p_multimesh) const { { float *w = ret.ptrw(); const uint8_t *r = buffer.ptr(); - copymem(w, r, buffer.size()); + memcpy(w, r, buffer.size()); } return ret; @@ -3888,22 +3888,37 @@ bool RendererStorageRD::particles_get_emitting(RID p_particles) { } void RendererStorageRD::_particles_free_data(Particles *particles) { - if (!particles->particle_buffer.is_valid()) { - return; + if (particles->particle_buffer.is_valid()) { + RD::get_singleton()->free(particles->particle_buffer); + particles->particle_buffer = RID(); + RD::get_singleton()->free(particles->particle_instance_buffer); + particles->particle_instance_buffer = RID(); + } + + if (particles->frame_params_buffer.is_valid()) { + RD::get_singleton()->free(particles->frame_params_buffer); + particles->frame_params_buffer = RID(); } - RD::get_singleton()->free(particles->particle_buffer); - RD::get_singleton()->free(particles->frame_params_buffer); - RD::get_singleton()->free(particles->particle_instance_buffer); particles->particles_transforms_buffer_uniform_set = RID(); - particles->particle_buffer = RID(); + if (RD::get_singleton()->uniform_set_is_valid(particles->trail_bind_pose_uniform_set)) { + RD::get_singleton()->free(particles->trail_bind_pose_uniform_set); + } + particles->trail_bind_pose_uniform_set = RID(); + + if (particles->trail_bind_pose_buffer.is_valid()) { + RD::get_singleton()->free(particles->trail_bind_pose_buffer); + particles->trail_bind_pose_buffer = RID(); + } if (RD::get_singleton()->uniform_set_is_valid(particles->collision_textures_uniform_set)) { RD::get_singleton()->free(particles->collision_textures_uniform_set); } + particles->collision_textures_uniform_set = RID(); if (particles->particles_sort_buffer.is_valid()) { RD::get_singleton()->free(particles->particles_sort_buffer); particles->particles_sort_buffer = RID(); + particles->particles_sort_uniform_set = RID(); } if (particles->emission_buffer != nullptr) { @@ -3912,6 +3927,12 @@ void RendererStorageRD::_particles_free_data(Particles *particles) { RD::get_singleton()->free(particles->emission_storage_buffer); particles->emission_storage_buffer = RID(); } + + if (RD::get_singleton()->uniform_set_is_valid(particles->particles_material_uniform_set)) { + //will need to be re-created + RD::get_singleton()->free(particles->particles_material_uniform_set); + } + particles->particles_material_uniform_set = RID(); } void RendererStorageRD::particles_set_amount(RID p_particles, int p_amount) { @@ -3926,38 +3947,12 @@ void RendererStorageRD::particles_set_amount(RID p_particles, int p_amount) { particles->amount = p_amount; - if (particles->amount > 0) { - particles->particle_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticleData) * p_amount); - particles->frame_params_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticlesFrameParams) * 1); - particles->particle_instance_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * 4 * (3 + 1 + 1) * p_amount); - //needs to clear it - - { - Vector<RD::Uniform> uniforms; - - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 1; - u.ids.push_back(particles->particle_buffer); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 2; - u.ids.push_back(particles->particle_instance_buffer); - uniforms.push_back(u); - } - - 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->prev_ticks = 0; particles->phase = 0; particles->prev_phase = 0; particles->clear = true; + + particles->dependency.changed_notify(DEPENDENCY_CHANGED_PARTICLES); } void RendererStorageRD::particles_set_lifetime(RID p_particles, float p_lifetime) { @@ -4013,6 +4008,22 @@ void RendererStorageRD::particles_set_fixed_fps(RID p_particles, int p_fps) { ERR_FAIL_COND(!particles); particles->fixed_fps = p_fps; + + _particles_free_data(particles); + + particles->prev_ticks = 0; + particles->phase = 0; + particles->prev_phase = 0; + particles->clear = true; + + particles->dependency.changed_notify(DEPENDENCY_CHANGED_PARTICLES); +} + +void RendererStorageRD::particles_set_interpolate(RID p_particles, bool p_enable) { + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + + particles->interpolate = p_enable; } void RendererStorageRD::particles_set_fractional_delta(RID p_particles, bool p_enable) { @@ -4022,6 +4033,42 @@ void RendererStorageRD::particles_set_fractional_delta(RID p_particles, bool p_e particles->fractional_delta = p_enable; } +void RendererStorageRD::particles_set_trails(RID p_particles, bool p_enable, float p_length) { + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + ERR_FAIL_COND(p_length < 0.1); + p_length = MIN(10.0, p_length); + + particles->trails_enabled = p_enable; + particles->trail_length = p_length; + + _particles_free_data(particles); + + particles->prev_ticks = 0; + particles->phase = 0; + particles->prev_phase = 0; + particles->clear = true; + + particles->dependency.changed_notify(DEPENDENCY_CHANGED_PARTICLES); +} + +void RendererStorageRD::particles_set_trail_bind_poses(RID p_particles, const Vector<Transform> &p_bind_poses) { + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + if (particles->trail_bind_pose_buffer.is_valid() && particles->trail_bind_poses.size() != p_bind_poses.size()) { + _particles_free_data(particles); + + particles->prev_ticks = 0; + particles->phase = 0; + particles->prev_phase = 0; + particles->clear = true; + } + particles->trail_bind_poses = p_bind_poses; + particles->trail_bind_poses_dirty = true; + + particles->dependency.changed_notify(DEPENDENCY_CHANGED_PARTICLES); +} + void RendererStorageRD::particles_set_collision_base_size(RID p_particles, float p_size) { Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND(!particles); @@ -4029,6 +4076,13 @@ void RendererStorageRD::particles_set_collision_base_size(RID p_particles, float particles->collision_base_size = p_size; } +void RendererStorageRD::particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) { + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + + particles->transform_align = p_transform_align; +} + void RendererStorageRD::particles_set_process_material(RID p_particles, RID p_material) { Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND(!particles); @@ -4068,7 +4122,7 @@ void RendererStorageRD::_particles_allocate_emission_buffer(Particles *particles ERR_FAIL_COND(particles->emission_buffer != nullptr); particles->emission_buffer_data.resize(sizeof(ParticleEmissionBuffer::Data) * particles->amount + sizeof(uint32_t) * 4); - zeromem(particles->emission_buffer_data.ptrw(), particles->emission_buffer_data.size()); + memset(particles->emission_buffer_data.ptrw(), 0, particles->emission_buffer_data.size()); particles->emission_buffer = (ParticleEmissionBuffer *)particles->emission_buffer_data.ptrw(); particles->emission_buffer->particle_max = particles->amount; @@ -4152,8 +4206,13 @@ AABB RendererStorageRD::particles_get_current_aabb(RID p_particles) { const Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND_V(!particles, AABB()); + int total_amount = particles->amount; + if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { + total_amount *= particles->trail_bind_poses.size(); + } + Vector<ParticleData> data; - data.resize(particles->amount); + data.resize(total_amount); Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(particles->particle_buffer); @@ -4162,8 +4221,9 @@ AABB RendererStorageRD::particles_get_current_aabb(RID p_particles) { AABB aabb; if (buffer.size()) { bool first = true; + const ParticleData *particle_data = (const ParticleData *)data.ptr(); - for (int i = 0; i < particles->amount; i++) { + for (int i = 0; i < total_amount; i++) { if (particle_data[i].active) { Vector3 pos = Vector3(particle_data[i].xform[12], particle_data[i].xform[13], particle_data[i].xform[14]); if (!particles->use_local_coords) { @@ -4224,14 +4284,12 @@ RID RendererStorageRD::particles_get_draw_pass_mesh(RID p_particles, int p_pass) void RendererStorageRD::particles_add_collision(RID p_particles, RID p_particles_collision_instance) { Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND(!particles); - particles->collisions.insert(p_particles_collision_instance); } void RendererStorageRD::particles_remove_collision(RID p_particles, RID p_particles_collision_instance) { Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND(!particles); - particles->collisions.erase(p_particles_collision_instance); } @@ -4286,7 +4344,12 @@ void RendererStorageRD::_particles_process(Particles *p_particles, float p_delta float new_phase = Math::fmod((float)p_particles->phase + (p_delta / p_particles->lifetime) * p_particles->speed_scale, (float)1.0); - ParticlesFrameParams &frame_params = p_particles->frame_params; + //move back history (if there is any) + for (uint32_t i = p_particles->frame_history.size() - 1; i > 0; i--) { + p_particles->frame_history[i] = p_particles->frame_history[i - 1]; + } + //update current frame + ParticlesFrameParams &frame_params = p_particles->frame_history[0]; if (p_particles->clear) { p_particles->cycle_number = 0; @@ -4317,6 +4380,10 @@ void RendererStorageRD::_particles_process(Particles *p_particles, float p_delta } frame_params.cycle = p_particles->cycle_number; + frame_params.frame = p_particles->frame_counter++; + frame_params.pad0 = 0; + frame_params.pad1 = 0; + frame_params.pad2 = 0; { //collision and attractors @@ -4515,12 +4582,18 @@ void RendererStorageRD::_particles_process(Particles *p_particles, float p_delta ParticlesShader::PushConstant push_constant; + int process_amount = p_particles->amount; + + if (p_particles->trails_enabled && p_particles->trail_bind_poses.size() > 1) { + process_amount *= p_particles->trail_bind_poses.size(); + } push_constant.clear = p_particles->clear; push_constant.total_particles = p_particles->amount; push_constant.lifetime = p_particles->lifetime; - push_constant.trail_size = 1; + push_constant.trail_size = p_particles->trail_params.size(); push_constant.use_fractional_delta = p_particles->fractional_delta; push_constant.sub_emitter_mode = !p_particles->emitting && p_particles->emission_buffer && (p_particles->emission_buffer->particle_count > 0 || p_particles->force_sub_emit); + push_constant.trail_pass = false; p_particles->force_sub_emit = false; //reset @@ -4553,7 +4626,17 @@ void RendererStorageRD::_particles_process(Particles *p_particles, float p_delta p_particles->clear = false; - RD::get_singleton()->buffer_update(p_particles->frame_params_buffer, 0, sizeof(ParticlesFrameParams), &frame_params); + if (p_particles->trail_params.size() > 1) { + //fill the trail params + for (uint32_t i = 0; i < p_particles->trail_params.size(); i++) { + uint32_t src_idx = i * p_particles->frame_history.size() / p_particles->trail_params.size(); + p_particles->trail_params[i] = p_particles->frame_history[src_idx]; + } + } else { + p_particles->trail_params[0] = p_particles->frame_history[0]; + } + + RD::get_singleton()->buffer_update(p_particles->frame_params_buffer, 0, sizeof(ParticlesFrameParams) * p_particles->trail_params.size(), p_particles->trail_params.ptr()); ParticlesMaterialData *m = (ParticlesMaterialData *)material_get_data(p_particles->process_material, SHADER_TYPE_PARTICLES); if (!m) { @@ -4575,27 +4658,45 @@ void RendererStorageRD::_particles_process(Particles *p_particles, float p_delta RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ParticlesShader::PushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_particles->amount, 1, 1); + if (p_particles->trails_enabled && p_particles->trail_bind_poses.size() > 1) { + //trails requires two passes in order to catch particle starts + RD::get_singleton()->compute_list_dispatch_threads(compute_list, process_amount / p_particles->trail_bind_poses.size(), 1, 1); + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + push_constant.trail_pass = true; + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ParticlesShader::PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, process_amount - p_particles->amount, 1, 1); + } else { + RD::get_singleton()->compute_list_dispatch_threads(compute_list, process_amount, 1, 1); + } RD::get_singleton()->compute_list_end(); } -void RendererStorageRD::particles_set_view_axis(RID p_particles, const Vector3 &p_axis) { +void RendererStorageRD::particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND(!particles); - if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH) { - return; //uninteresting for other modes + 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) { + return; + } + + if (particles->particle_buffer.is_null()) { + return; //particles have not processed yet } + bool do_sort = particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH; + //copy to sort buffer - if (particles->particles_sort_buffer == RID()) { + if (do_sort && particles->particles_sort_buffer == RID()) { uint32_t size = particles->amount; if (size & 1) { size++; //make multiple of 16 } size *= sizeof(float) * 2; particles->particles_sort_buffer = RD::get_singleton()->storage_buffer_create(size); + { Vector<RD::Uniform> uniforms; @@ -4611,41 +4712,105 @@ void RendererStorageRD::particles_set_view_axis(RID p_particles, const Vector3 & } } + ParticlesShader::CopyPushConstant copy_push_constant; + + if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { + int fixed_fps = 60.0; + if (particles->fixed_fps > 0) { + fixed_fps = particles->fixed_fps; + } + + copy_push_constant.trail_size = particles->trail_bind_poses.size(); + copy_push_constant.trail_total = particles->frame_history.size(); + copy_push_constant.frame_delta = 1.0 / fixed_fps; + } else { + copy_push_constant.trail_size = 1; + copy_push_constant.trail_total = 1; + copy_push_constant.frame_delta = 0.0; + } + copy_push_constant.frame_remainder = particles->interpolate ? particles->frame_remainder : 0.0; + copy_push_constant.total_particles = particles->amount; + Vector3 axis = -p_axis; // cameras look to z negative if (particles->use_local_coords) { axis = particles->emission_transform.basis.xform_inv(axis).normalized(); } - ParticlesShader::CopyPushConstant copy_push_constant; - copy_push_constant.total_particles = particles->amount; copy_push_constant.sort_direction[0] = axis.x; copy_push_constant.sort_direction[1] = axis.y; copy_push_constant.sort_direction[2] = axis.z; - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[ParticlesShader::COPY_MODE_FILL_SORT_BUFFER]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_sort_uniform_set, 1); - RD::get_singleton()->compute_list_set_push_constant(compute_list, ©_push_constant, sizeof(ParticlesShader::CopyPushConstant)); + copy_push_constant.align_up[0] = p_up_axis.x; + copy_push_constant.align_up[1] = p_up_axis.y; + copy_push_constant.align_up[2] = p_up_axis.z; - RD::get_singleton()->compute_list_dispatch_threads(compute_list, particles->amount, 1, 1); + copy_push_constant.align_mode = particles->transform_align; - RD::get_singleton()->compute_list_end(); + if (do_sort) { + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[ParticlesShader::COPY_MODE_FILL_SORT_BUFFER]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_sort_uniform_set, 1); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->trail_bind_pose_uniform_set, 2); + RD::get_singleton()->compute_list_set_push_constant(compute_list, ©_push_constant, sizeof(ParticlesShader::CopyPushConstant)); + + RD::get_singleton()->compute_list_dispatch_threads(compute_list, particles->amount, 1, 1); + + RD::get_singleton()->compute_list_end(); + effects.sort_buffer(particles->particles_sort_uniform_set, particles->amount); + } - effects.sort_buffer(particles->particles_sort_uniform_set, particles->amount); + copy_push_constant.total_particles *= copy_push_constant.total_particles; - compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[ParticlesShader::COPY_MODE_FILL_INSTANCES_WITH_SORT_BUFFER]); + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[do_sort ? ParticlesShader::COPY_MODE_FILL_INSTANCES_WITH_SORT_BUFFER : ParticlesShader::COPY_MODE_FILL_INSTANCES]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_sort_uniform_set, 1); + if (do_sort) { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_sort_uniform_set, 1); + } + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->trail_bind_pose_uniform_set, 2); + RD::get_singleton()->compute_list_set_push_constant(compute_list, ©_push_constant, sizeof(ParticlesShader::CopyPushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, particles->amount, 1, 1); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, copy_push_constant.total_particles, 1, 1); RD::get_singleton()->compute_list_end(); } +void RendererStorageRD::_particles_update_buffers(Particles *particles) { + if (particles->amount > 0 && particles->particle_buffer.is_null()) { + int total_amount = particles->amount; + if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { + total_amount *= particles->trail_bind_poses.size(); + } + particles->particle_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticleData) * total_amount); + particles->particle_instance_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * 4 * (3 + 1 + 1) * total_amount); + //needs to clear it + + { + Vector<RD::Uniform> uniforms; + + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 1; + u.ids.push_back(particles->particle_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 2; + u.ids.push_back(particles->particle_instance_buffer); + uniforms.push_back(u); + } + + 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); + } + } +} void RendererStorageRD::update_particles() { while (particle_update_list) { //use transform feedback to process particles @@ -4657,6 +4822,8 @@ void RendererStorageRD::update_particles() { particles->update_list = nullptr; particles->dirty = false; + _particles_update_buffers(particles); + if (particles->restart_request) { particles->prev_ticks = 0; particles->phase = 0; @@ -4688,12 +4855,81 @@ void RendererStorageRD::update_particles() { } } +#ifndef _MSC_VER +#warning Should use display refresh rate for all this +#endif + + float screen_hz = 60; + + int fixed_fps = 0; + if (particles->fixed_fps > 0) { + fixed_fps = particles->fixed_fps; + } else if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { + fixed_fps = screen_hz; + } + { + //update trails + int history_size = 1; + int trail_steps = 1; + if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { + history_size = MAX(1, int(particles->trail_length * fixed_fps)); + trail_steps = particles->trail_bind_poses.size(); + } + + if (uint32_t(history_size) != particles->frame_history.size()) { + particles->frame_history.resize(history_size); + memset(particles->frame_history.ptr(), 0, sizeof(ParticlesFrameParams) * history_size); + } + + if (uint32_t(trail_steps) != particles->trail_params.size() || particles->frame_params_buffer.is_null()) { + particles->trail_params.resize(trail_steps); + if (particles->frame_params_buffer.is_valid()) { + RD::get_singleton()->free(particles->frame_params_buffer); + } + particles->frame_params_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticlesFrameParams) * trail_steps); + } + + if (particles->trail_bind_poses.size() > 1 && particles->trail_bind_pose_buffer.is_null()) { + particles->trail_bind_pose_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * 16 * particles->trail_bind_poses.size()); + particles->trail_bind_poses_dirty = true; + } + + if (particles->trail_bind_pose_uniform_set.is_null()) { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 0; + if (particles->trail_bind_pose_buffer.is_valid()) { + u.ids.push_back(particles->trail_bind_pose_buffer); + } else { + u.ids.push_back(default_rd_storage_buffer); + } + uniforms.push_back(u); + } + + particles->trail_bind_pose_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.copy_shader.version_get_shader(particles_shader.copy_shader_version, 0), 2); + } + + if (particles->trail_bind_pose_buffer.is_valid() && particles->trail_bind_poses_dirty) { + if (particles_shader.pose_update_buffer.size() < uint32_t(particles->trail_bind_poses.size()) * 16) { + particles_shader.pose_update_buffer.resize(particles->trail_bind_poses.size() * 16); + } + + for (int i = 0; i < particles->trail_bind_poses.size(); i++) { + store_transform(particles->trail_bind_poses[i], &particles_shader.pose_update_buffer[i * 16]); + } + + RD::get_singleton()->buffer_update(particles->trail_bind_pose_buffer, 0, particles->trail_bind_poses.size() * 16 * sizeof(float), particles_shader.pose_update_buffer.ptr()); + } + } + bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0; if (particles->clear && particles->pre_process_time > 0.0) { float frame_time; - if (particles->fixed_fps > 0) { - frame_time = 1.0 / particles->fixed_fps; + if (fixed_fps > 0) { + frame_time = 1.0 / fixed_fps; } else { frame_time = 1.0 / 30.0; } @@ -4706,14 +4942,14 @@ void RendererStorageRD::update_particles() { } } - if (particles->fixed_fps > 0) { + if (fixed_fps > 0) { float frame_time; float decr; if (zero_time_scale) { frame_time = 0.0; - decr = 1.0 / particles->fixed_fps; + decr = 1.0 / fixed_fps; } else { - frame_time = 1.0 / particles->fixed_fps; + frame_time = 1.0 / fixed_fps; decr = frame_time; } float delta = RendererCompositorRD::singleton->get_frame_delta_time(); @@ -4741,16 +4977,39 @@ void RendererStorageRD::update_particles() { //copy particles to instance buffer - if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH) { + 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; - copy_push_constant.total_particles = particles->amount; + + int total_amount = particles->amount; + if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { + total_amount *= particles->trail_bind_poses.size(); + } + + copy_push_constant.total_particles = total_amount; + copy_push_constant.frame_remainder = particles->interpolate ? particles->frame_remainder : 0.0; + copy_push_constant.align_mode = particles->transform_align; + copy_push_constant.align_up[0] = 0; + copy_push_constant.align_up[1] = 0; + copy_push_constant.align_up[2] = 0; + + if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { + copy_push_constant.trail_size = particles->trail_bind_poses.size(); + copy_push_constant.trail_total = particles->frame_history.size(); + copy_push_constant.frame_delta = 1.0 / fixed_fps; + } else { + copy_push_constant.trail_size = 1; + copy_push_constant.trail_total = 1; + copy_push_constant.frame_delta = 0.0; + } RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[ParticlesShader::COPY_MODE_FILL_INSTANCES]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->trail_bind_pose_uniform_set, 2); RD::get_singleton()->compute_list_set_push_constant(compute_list, ©_push_constant, sizeof(ParticlesShader::CopyPushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, particles->amount, 1, 1); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, total_amount, 1, 1); RD::get_singleton()->compute_list_end(); } @@ -5230,7 +5489,7 @@ void RendererStorageRD::skeleton_allocate_data(RID p_skeleton, int p_bones, bool if (skeleton->size) { skeleton->data.resize(skeleton->size * (skeleton->use_2d ? 8 : 12)); skeleton->buffer = RD::get_singleton()->storage_buffer_create(skeleton->data.size() * sizeof(float)); - zeromem(skeleton->data.ptrw(), skeleton->data.size() * sizeof(float)); + memset(skeleton->data.ptrw(), 0, skeleton->data.size() * sizeof(float)); _skeleton_make_dirty(skeleton); @@ -6872,7 +7131,7 @@ RID RendererStorageRD::render_target_get_sdf_texture(RID p_render_target) { Vector<uint8_t> pv; pv.resize(16 * 4); - zeromem(pv.ptrw(), 16 * 4); + memset(pv.ptrw(), 0, 16 * 4); Vector<Vector<uint8_t>> vpv; rt->sdf_buffer_read = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); @@ -7359,7 +7618,7 @@ void RendererStorageRD::_update_decal_atlas() { v_offsetsv.resize(base_size); int *v_offsets = v_offsetsv.ptrw(); - zeromem(v_offsets, sizeof(int) * base_size); + memset(v_offsets, 0, sizeof(int) * base_size); int max_height = 0; @@ -7923,7 +8182,6 @@ void RendererStorageRD::global_variable_set_override(const StringName &p_name, c _global_variable_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); } else { //texture - //texture for (Set<RID>::Element *E = gv.texture_materials.front(); E; E = E->next()) { Material *material = material_owner.getornull(E->get()); ERR_CONTINUE(!material); @@ -8116,7 +8374,7 @@ void RendererStorageRD::_update_global_variables() { if (total_regions / global_variables.buffer_dirty_region_count <= 4) { // 25% of regions dirty, just update all buffer RD::get_singleton()->buffer_update(global_variables.buffer, 0, sizeof(GlobalVariables::Value) * global_variables.buffer_size, global_variables.buffer_values); - zeromem(global_variables.buffer_dirty_regions, sizeof(bool) * total_regions); + memset(global_variables.buffer_dirty_regions, 0, sizeof(bool) * total_regions); } else { uint32_t region_byte_size = sizeof(GlobalVariables::Value) * GlobalVariables::BUFFER_DIRTY_REGION_SIZE; @@ -8325,9 +8583,10 @@ bool RendererStorageRD::free(RID p_rid) { light_owner.free(p_rid); } else if (particles_owner.owns(p_rid)) { + update_particles(); Particles *particles = particles_owner.getornull(p_rid); - _particles_free_data(particles); particles->dependency.deleted_notify(p_rid); + _particles_free_data(particles); particles_owner.free(p_rid); } else if (particles_collision_owner.owns(p_rid)) { ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_rid); @@ -8404,10 +8663,10 @@ RendererStorageRD::RendererStorageRD() { global_variables.buffer_size = GLOBAL_GET("rendering/limits/global_shader_variables/buffer_size"); global_variables.buffer_size = MAX(4096, global_variables.buffer_size); global_variables.buffer_values = memnew_arr(GlobalVariables::Value, global_variables.buffer_size); - zeromem(global_variables.buffer_values, sizeof(GlobalVariables::Value) * global_variables.buffer_size); + memset(global_variables.buffer_values, 0, sizeof(GlobalVariables::Value) * global_variables.buffer_size); global_variables.buffer_usage = memnew_arr(GlobalVariables::ValueUsage, global_variables.buffer_size); global_variables.buffer_dirty_regions = memnew_arr(bool, global_variables.buffer_size / GlobalVariables::BUFFER_DIRTY_REGION_SIZE); - zeromem(global_variables.buffer_dirty_regions, sizeof(bool) * global_variables.buffer_size / GlobalVariables::BUFFER_DIRTY_REGION_SIZE); + memset(global_variables.buffer_dirty_regions, 0, sizeof(bool) * global_variables.buffer_size / GlobalVariables::BUFFER_DIRTY_REGION_SIZE); global_variables.buffer = RD::get_singleton()->storage_buffer_create(sizeof(GlobalVariables::Value) * global_variables.buffer_size); material_update_list = nullptr; @@ -8864,14 +9123,14 @@ RendererStorageRD::RendererStorageRD() { actions.renames["COLOR"] = "PARTICLE.color"; actions.renames["VELOCITY"] = "PARTICLE.velocity"; //actions.renames["MASS"] = "mass"; ? - actions.renames["ACTIVE"] = "PARTICLE.is_active"; + actions.renames["ACTIVE"] = "particle_active"; actions.renames["RESTART"] = "restart"; actions.renames["CUSTOM"] = "PARTICLE.custom"; actions.renames["TRANSFORM"] = "PARTICLE.xform"; actions.renames["TIME"] = "FRAME.time"; actions.renames["LIFETIME"] = "params.lifetime"; actions.renames["DELTA"] = "local_delta"; - actions.renames["NUMBER"] = "particle"; + actions.renames["NUMBER"] = "particle_number"; actions.renames["INDEX"] = "index"; //actions.renames["GRAVITY"] = "current_gravity"; actions.renames["EMISSION_TRANSFORM"] = "FRAME.emission_transform"; diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h index 6405bb75b0..961bdfb178 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.h +++ b/servers/rendering/renderer_rd/renderer_storage_rd.h @@ -660,6 +660,11 @@ private: float time; float delta; + uint32_t frame; + uint32_t pad0; + uint32_t pad1; + uint32_t pad2; + uint32_t random_seed; uint32_t attractor_count; uint32_t collider_count; @@ -704,10 +709,16 @@ private: AABB custom_aabb = AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8)); bool use_local_coords = true; RID process_material; + uint32_t frame_counter = 0; + RS::ParticlesTransformAlign transform_align = RS::PARTICLES_TRANSFORM_ALIGN_DISABLED; RS::ParticlesDrawOrder draw_order = RS::PARTICLES_DRAW_ORDER_INDEX; Vector<RID> draw_passes; + Vector<Transform> trail_bind_poses; + bool trail_bind_poses_dirty = false; + RID trail_bind_pose_buffer; + RID trail_bind_pose_uniform_set; RID particle_buffer; RID particle_instance_buffer; @@ -739,7 +750,8 @@ private: float speed_scale = 1.0; - int fixed_fps = 0; + int fixed_fps = 30; + bool interpolate = true; bool fractional_delta = false; float frame_remainder = 0; float collision_base_size = 0.01; @@ -759,12 +771,19 @@ private: Dependency dependency; - ParticlesFrameParams frame_params; + float trail_length = 1.0; + bool trails_enabled = false; + LocalVector<ParticlesFrameParams> frame_history; + LocalVector<ParticlesFrameParams> trail_params; + + Particles() { + } }; void _particles_process(Particles *p_particles, float p_delta); void _particles_allocate_emission_buffer(Particles *particles); void _particles_free_data(Particles *particles); + void _particles_update_buffers(Particles *particles); struct ParticlesShader { struct PushConstant { @@ -776,7 +795,7 @@ private: uint32_t use_fractional_delta; uint32_t sub_emitter_mode; uint32_t can_emit; - uint32_t pad; + uint32_t trail_pass; }; ParticlesShaderRD shader; @@ -791,6 +810,14 @@ private: struct CopyPushConstant { float sort_direction[3]; uint32_t total_particles; + + uint32_t trail_size; + uint32_t trail_total; + float frame_delta; + float frame_remainder; + + float align_up[3]; + uint32_t align_mode; }; enum { @@ -804,6 +831,8 @@ private: RID copy_shader_version; RID copy_pipelines[COPY_MODE_MAX]; + LocalVector<float> pose_update_buffer; + } particles_shader; Particles *particle_update_list = nullptr; @@ -2076,10 +2105,17 @@ public: void particles_set_use_local_coordinates(RID p_particles, bool p_enable); void particles_set_process_material(RID p_particles, RID p_material); void particles_set_fixed_fps(RID p_particles, int p_fps); + void particles_set_interpolate(RID p_particles, bool p_enable); void particles_set_fractional_delta(RID p_particles, bool p_enable); void particles_set_collision_base_size(RID p_particles, float p_size); + void particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align); + + void particles_set_trails(RID p_particles, bool p_enable, float p_length); + void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform> &p_bind_poses); + void particles_restart(RID p_particles); void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags); + void particles_set_subemitter(RID p_particles, RID p_subemitter_particles); void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order); @@ -2097,15 +2133,21 @@ public: int particles_get_draw_passes(RID p_particles) const; RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const; - void particles_set_view_axis(RID p_particles, const Vector3 &p_axis); + void particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis); virtual bool particles_is_inactive(RID p_particles) const; - _FORCE_INLINE_ uint32_t particles_get_amount(RID p_particles) { + _FORCE_INLINE_ uint32_t particles_get_amount(RID p_particles, uint32_t &r_trail_divisor) { Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND_V(!particles, 0); - return particles->amount; + if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { + r_trail_divisor = particles->trail_bind_poses.size(); + } else { + r_trail_divisor = 1; + } + + return particles->amount * r_trail_divisor; } _FORCE_INLINE_ uint32_t particles_is_using_local_coords(RID p_particles) { @@ -2119,6 +2161,8 @@ public: Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND_V(!particles, RID()); if (particles->particles_transforms_buffer_uniform_set.is_null()) { + _particles_update_buffers(particles); + Vector<RD::Uniform> uniforms; { diff --git a/servers/rendering/renderer_rd/shaders/particles.glsl b/servers/rendering/renderer_rd/shaders/particles.glsl index 3712220dc4..beaff10793 100644 --- a/servers/rendering/renderer_rd/shaders/particles.glsl +++ b/servers/rendering/renderer_rd/shaders/particles.glsl @@ -76,6 +76,11 @@ struct FrameParams { float time; float delta; + uint frame; + uint pad0; + uint pad1; + uint pad2; + uint random_seed; uint attractor_count; uint collider_count; @@ -92,10 +97,16 @@ layout(set = 1, binding = 0, std430) restrict buffer FrameHistory { } frame_history; +#define PARTICLE_FLAG_ACTIVE uint(1) +#define PARTICLE_FLAG_STARTED uint(2) +#define PARTICLE_FLAG_TRAILED uint(4) +#define PARTICLE_FRAME_MASK uint(0xFFFF) +#define PARTICLE_FRAME_SHIFT uint(16) + struct ParticleData { mat4 xform; vec3 velocity; - bool is_active; + uint flags; vec4 color; vec4 custom; }; @@ -162,7 +173,7 @@ layout(push_constant, binding = 0, std430) uniform Params { bool use_fractional_delta; bool sub_emitter_mode; bool can_emit; - uint pad; + bool trail_pass; } params; @@ -201,6 +212,14 @@ bool emit_subparticle(mat4 p_xform, vec3 p_velocity, vec4 p_color, vec4 p_custom void main() { uint particle = gl_GlobalInvocationID.x; + if (params.trail_size > 1) { + if (params.trail_pass) { + particle += (particle / (params.trail_size - 1)) + 1; + } else { + particle *= params.trail_size; + } + } + if (particle >= params.total_particles * params.trail_size) { return; //discard } @@ -229,7 +248,7 @@ void main() { PARTICLE.color = vec4(1.0); PARTICLE.custom = vec4(0.0); PARTICLE.velocity = vec3(0.0); - PARTICLE.is_active = false; + PARTICLE.flags = 0; PARTICLE.xform = mat4( vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), @@ -237,6 +256,29 @@ void main() { vec4(0.0, 0.0, 0.0, 1.0)); } + //clear started flag if set + + if (params.trail_pass) { + //trail started + uint src_idx = index * params.trail_size; + if (bool(particles.data[src_idx].flags & PARTICLE_FLAG_STARTED)) { + //save start conditions for trails + PARTICLE.color = particles.data[src_idx].color; + PARTICLE.custom = particles.data[src_idx].custom; + PARTICLE.velocity = particles.data[src_idx].velocity; + PARTICLE.flags = PARTICLE_FLAG_TRAILED | ((frame_history.data[0].frame & PARTICLE_FRAME_MASK) << PARTICLE_FRAME_SHIFT); //mark it as trailed, save in which frame it will start + PARTICLE.xform = particles.data[src_idx].xform; + } + + if (bool(PARTICLE.flags & PARTICLE_FLAG_TRAILED) && ((PARTICLE.flags >> PARTICLE_FRAME_SHIFT) == (FRAME.frame & PARTICLE_FRAME_MASK))) { //check this is trailed and see if it should start now + // we just assume that this is the first frame of the particle, the rest is deterministic + PARTICLE.flags = PARTICLE_FLAG_ACTIVE | (particles.data[src_idx].flags & (PARTICLE_FRAME_MASK << PARTICLE_FRAME_SHIFT)); + return; //- this appears like it should be correct, but it seems not to be.. wonder why. + } + } else { + PARTICLE.flags &= ~PARTICLE_FLAG_STARTED; + } + bool collided = false; vec3 collision_normal = vec3(0.0); float collision_depth = 0.0; @@ -245,19 +287,17 @@ void main() { #if !defined(DISABLE_VELOCITY) - if (PARTICLE.is_active) { + if (bool(PARTICLE.flags & PARTICLE_FLAG_ACTIVE)) { PARTICLE.xform[3].xyz += PARTICLE.velocity * local_delta; } #endif - /* Process physics if active */ - - if (params.sub_emitter_mode) { - if (!PARTICLE.is_active) { + if (!params.trail_pass && params.sub_emitter_mode) { + if (!bool(PARTICLE.flags & PARTICLE_FLAG_ACTIVE)) { int src_index = atomicAdd(src_particles.particle_count, -1) - 1; if (src_index >= 0) { - PARTICLE.is_active = true; + PARTICLE.flags = (PARTICLE_FLAG_ACTIVE | PARTICLE_FLAG_STARTED | (FRAME.cycle << PARTICLE_FRAME_SHIFT)); restart = true; if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) { @@ -339,16 +379,12 @@ void main() { } } - uint current_cycle = FRAME.cycle; - - if (FRAME.system_phase < restart_phase) { - current_cycle -= uint(1); + if (params.trail_pass) { + restart = false; } - uint particle_number = current_cycle * uint(params.total_particles) + particle; - if (restart) { - PARTICLE.is_active = FRAME.emitting; + PARTICLE.flags = FRAME.emitting ? (PARTICLE_FLAG_ACTIVE | PARTICLE_FLAG_STARTED | (FRAME.cycle << PARTICLE_FRAME_SHIFT)) : 0; restart_position = true; restart_rotation_scale = true; restart_velocity = true; @@ -357,11 +393,15 @@ void main() { } } - if (restart && PARTICLE.is_active) { + bool particle_active = bool(PARTICLE.flags & PARTICLE_FLAG_ACTIVE); + + uint particle_number = (PARTICLE.flags >> PARTICLE_FRAME_SHIFT) * uint(params.total_particles) + index; + + if (restart && particle_active) { #CODE : START } - if (PARTICLE.is_active) { + if (particle_active) { for (uint i = 0; i < FRAME.attractor_count; i++) { vec3 dir; float amount; @@ -539,7 +579,12 @@ void main() { } } - if (PARTICLE.is_active) { + if (particle_active) { #CODE : PROCESS } + + PARTICLE.flags &= ~PARTICLE_FLAG_ACTIVE; + if (particle_active) { + PARTICLE.flags |= PARTICLE_FLAG_ACTIVE; + } } diff --git a/servers/rendering/renderer_rd/shaders/particles_copy.glsl b/servers/rendering/renderer_rd/shaders/particles_copy.glsl index 80adb49619..7804d66d1c 100644 --- a/servers/rendering/renderer_rd/shaders/particles_copy.glsl +++ b/servers/rendering/renderer_rd/shaders/particles_copy.glsl @@ -6,10 +6,14 @@ layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; +#define PARTICLE_FLAG_ACTIVE uint(1) +#define PARTICLE_FLAG_STARTED uint(2) +#define PARTICLE_FLAG_TRAILED uint(4) + struct ParticleData { mat4 xform; vec3 velocity; - bool is_active; + uint flags; vec4 color; vec4 custom; }; @@ -33,12 +37,30 @@ sort_buffer; #endif // USE_SORT_BUFFER +layout(set = 2, binding = 0, std430) restrict readonly buffer TrailBindPoses { + mat4 data[]; +} +trail_bind_poses; + layout(push_constant, binding = 0, std430) uniform Params { vec3 sort_direction; uint total_particles; + + uint trail_size; + uint trail_total; + float frame_delta; + float frame_remainder; + + vec3 align_up; + uint align_mode; } params; +#define TRANSFORM_ALIGN_DISABLED 0 +#define TRANSFORM_ALIGN_Z_BILLBOARD 1 +#define TRANSFORM_ALIGN_Y_TO_VELOCITY 2 +#define TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY 3 + void main() { #ifdef MODE_FILL_SORT_BUFFER @@ -47,7 +69,11 @@ void main() { return; //discard } - sort_buffer.data[particle].x = dot(params.sort_direction, particles.data[particle].xform[3].xyz); + uint src_particle = particle; + if (params.trail_size > 1) { + src_particle = src_particle * params.trail_size + params.trail_size / 2; //use trail center for sorting + } + sort_buffer.data[particle].x = dot(params.sort_direction, particles.data[src_particle].xform[3].xyz); sort_buffer.data[particle].y = float(particle); #endif @@ -61,13 +87,78 @@ void main() { } #ifdef USE_SORT_BUFFER - particle = uint(sort_buffer.data[particle].y); //use index from sort buffer + + if (params.trail_size > 1) { + particle = uint(sort_buffer.data[particle / params.trail_size].y) + (particle % params.trail_size); + } else { + particle = uint(sort_buffer.data[particle].y); //use index from sort buffer + } #endif mat4 txform; - if (particles.data[particle].is_active) { - txform = transpose(particles.data[particle].xform); + if (bool(particles.data[particle].flags & PARTICLE_FLAG_ACTIVE) || bool(particles.data[particle].flags & PARTICLE_FLAG_TRAILED)) { + txform = particles.data[particle].xform; + if (params.trail_size > 1) { + // since the steps dont fit precisely in the history frames, must do a tiny bit of + // interpolation to get them close to their intended location. + uint part_ofs = particle % params.trail_size; + float natural_ofs = fract((float(part_ofs) / float(params.trail_size)) * float(params.trail_total)) * params.frame_delta; + + txform[3].xyz -= particles.data[particle].velocity * natural_ofs; + } + + switch (params.align_mode) { + case TRANSFORM_ALIGN_DISABLED: { + } break; //nothing + case TRANSFORM_ALIGN_Z_BILLBOARD: { + mat3 local = mat3(normalize(cross(params.align_up, params.sort_direction)), params.align_up, params.sort_direction); + local = local * mat3(txform); + txform[0].xyz = local[0]; + txform[1].xyz = local[1]; + txform[2].xyz = local[2]; + + } break; + case TRANSFORM_ALIGN_Y_TO_VELOCITY: { + vec3 v = particles.data[particle].velocity; + float s = (length(txform[0]) + length(txform[1]) + length(txform[2])) / 3.0; + if (length(v) > 0.0) { + txform[1].xyz = normalize(v); + } else { + txform[1].xyz = normalize(txform[1].xyz); + } + + txform[0].xyz = normalize(cross(txform[1].xyz, txform[2].xyz)); + txform[2].xyz = vec3(0.0, 0.0, 1.0) * s; + txform[0].xyz *= s; + txform[1].xyz *= s; + } break; + case TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY: { + vec3 v = particles.data[particle].velocity; + vec3 sv = v - params.sort_direction * dot(params.sort_direction, v); //screen velocity + float s = (length(txform[0]) + length(txform[1]) + length(txform[2])) / 3.0; + + if (length(sv) == 0) { + sv = params.align_up; + } + + sv = normalize(sv); + + txform[0].xyz = normalize(cross(sv, params.sort_direction)) * s; + txform[1].xyz = sv * s; + txform[2].xyz = params.sort_direction * s; + + } break; + } + + txform[3].xyz += particles.data[particle].velocity * params.frame_remainder; + + if (params.trail_size > 1) { + uint part_ofs = particle % params.trail_size; + txform = txform * trail_bind_poses.data[part_ofs]; + } + + txform = transpose(txform); } else { txform = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); //zero scale, becomes invisible } diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl index fce17c47e8..f0dbe4eedf 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl @@ -48,11 +48,11 @@ layout(location = 8) in vec4 custom2_attrib; layout(location = 9) in vec4 custom3_attrib; #endif -#if defined(BONES_USED) +#if defined(BONES_USED) || defined(USE_PARTICLE_TRAILS) layout(location = 10) in uvec4 bone_attrib; #endif -#if defined(WEIGHTS_USED) +#if defined(WEIGHTS_USED) || defined(USE_PARTICLE_TRAILS) layout(location = 11) in vec4 weight_attrib; #endif @@ -125,10 +125,72 @@ void main() { if (is_multimesh) { //multimesh, instances are for it - uint offset = (instances.data[instance_index].flags >> INSTANCE_FLAGS_MULTIMESH_STRIDE_SHIFT) & INSTANCE_FLAGS_MULTIMESH_STRIDE_MASK; - offset *= gl_InstanceIndex; mat4 matrix; + +#ifdef USE_PARTICLE_TRAILS + uint trail_size = (instances.data[instance_index].flags >> INSTANCE_FLAGS_PARTICLE_TRAIL_SHIFT) & INSTANCE_FLAGS_PARTICLE_TRAIL_MASK; + uint stride = 3 + 1 + 1; //particles always uses this format + + uint offset = trail_size * stride * gl_InstanceIndex; + +#ifdef COLOR_USED + vec4 pcolor; +#endif + { + uint boffset = offset + bone_attrib.x * stride; + matrix = mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], transforms.data[boffset + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weight_attrib.x; +#ifdef COLOR_USED + pcolor = transforms.data[boffset + 3] * weight_attrib.x; +#endif + } + if (weight_attrib.y > 0.001) { + uint boffset = offset + bone_attrib.y * stride; + matrix += mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], transforms.data[boffset + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weight_attrib.y; +#ifdef COLOR_USED + pcolor += transforms.data[boffset + 3] * weight_attrib.y; +#endif + } + if (weight_attrib.z > 0.001) { + uint boffset = offset + bone_attrib.z * stride; + matrix += mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], transforms.data[boffset + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weight_attrib.z; +#ifdef COLOR_USED + pcolor += transforms.data[boffset + 3] * weight_attrib.z; +#endif + } + if (weight_attrib.w > 0.001) { + uint boffset = offset + bone_attrib.w * stride; + matrix += mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], transforms.data[boffset + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weight_attrib.w; +#ifdef COLOR_USED + pcolor += transforms.data[boffset + 3] * weight_attrib.w; +#endif + } + + instance_custom = transforms.data[offset + 4]; + +#ifdef COLOR_USED + color_interp *= pcolor; +#endif + +#else + uint stride = 0; + { + //TODO implement a small lookup table for the stride + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_FORMAT_2D)) { + stride += 2; + } else { + stride += 3; + } + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_COLOR)) { + stride += 1; + } + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA)) { + stride += 1; + } + } + + uint offset = stride * gl_InstanceIndex; + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_FORMAT_2D)) { matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); offset += 2; @@ -148,6 +210,7 @@ void main() { instance_custom = transforms.data[offset]; } +#endif //transpose matrix = transpose(matrix); world_matrix = world_matrix * matrix; @@ -165,32 +228,6 @@ void main() { vec3 binormal = normalize(cross(normal, tangent) * binormalf); #endif -#if 0 - if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_SKELETON)) { - //multimesh, instances are for it - - uvec2 bones_01 = uvec2(bone_attrib.x & 0xFFFF, bone_attrib.x >> 16) * 3; - uvec2 bones_23 = uvec2(bone_attrib.y & 0xFFFF, bone_attrib.y >> 16) * 3; - vec2 weights_01 = unpackUnorm2x16(bone_attrib.z); - vec2 weights_23 = unpackUnorm2x16(bone_attrib.w); - - mat4 m = mat4(transforms.data[bones_01.x], transforms.data[bones_01.x + 1], transforms.data[bones_01.x + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_01.x; - m += mat4(transforms.data[bones_01.y], transforms.data[bones_01.y + 1], transforms.data[bones_01.y + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_01.y; - m += mat4(transforms.data[bones_23.x], transforms.data[bones_23.x + 1], transforms.data[bones_23.x + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.x; - m += mat4(transforms.data[bones_23.y], transforms.data[bones_23.y + 1], transforms.data[bones_23.y + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.y; - - //reverse order because its transposed - vertex = (vec4(vertex, 1.0) * m).xyz; - normal = (vec4(normal, 0.0) * m).xyz; - -#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) - - tangent = (vec4(tangent, 0.0) * m).xyz; - binormal = (vec4(binormal, 0.0) * m).xyz; -#endif - } -#endif - #ifdef UV_USED uv_interp = uv_attrib; #endif diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl index 48e331714d..83423cdc24 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl @@ -61,12 +61,11 @@ layout(set = 0, binding = 2) uniform sampler shadow_sampler; #define INSTANCE_FLAGS_MULTIMESH_FORMAT_2D (1 << 13) #define INSTANCE_FLAGS_MULTIMESH_HAS_COLOR (1 << 14) #define INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA (1 << 15) -#define INSTANCE_FLAGS_MULTIMESH_STRIDE_SHIFT 16 +#define INSTANCE_FLAGS_PARTICLE_TRAIL_SHIFT 16 //3 bits of stride -#define INSTANCE_FLAGS_MULTIMESH_STRIDE_MASK 0x7 +#define INSTANCE_FLAGS_PARTICLE_TRAIL_MASK 0xFF -#define INSTANCE_FLAGS_SKELETON (1 << 19) -#define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 20) +#define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 24) layout(set = 0, binding = 3, std430) restrict readonly buffer OmniLights { LightData data[]; diff --git a/servers/rendering/renderer_scene.h b/servers/rendering/renderer_scene.h index 551d4f4240..db1e3d1377 100644 --- a/servers/rendering/renderer_scene.h +++ b/servers/rendering/renderer_scene.h @@ -49,6 +49,10 @@ public: virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable) = 0; virtual bool is_camera(RID p_camera) const = 0; + virtual RID occluder_allocate() = 0; + virtual void occluder_initialize(RID p_occluder) = 0; + virtual void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) = 0; + virtual RID scenario_allocate() = 0; virtual void scenario_initialize(RID p_rid) = 0; @@ -197,8 +201,8 @@ public: virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0; virtual void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas) = 0; - virtual void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas) = 0; - virtual void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas) = 0; + virtual void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas) = 0; + virtual void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas) = 0; virtual void update() = 0; virtual void render_probes() = 0; diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index c7caf71fcc..f34727fd0a 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -109,6 +109,20 @@ bool RendererSceneCull::is_camera(RID p_camera) const { return camera_owner.owns(p_camera); } +/* OCCLUDER API */ + +RID RendererSceneCull::occluder_allocate() { + return RendererSceneOcclusionCull::get_singleton()->occluder_allocate(); +} + +void RendererSceneCull::occluder_initialize(RID p_rid) { + RendererSceneOcclusionCull::get_singleton()->occluder_initialize(p_rid); +} + +void RendererSceneCull::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) { + RendererSceneOcclusionCull::get_singleton()->occluder_set_mesh(p_occluder, p_vertices, p_indices); +} + /* SCENARIO API */ void RendererSceneCull::_instance_pair(Instance *p_A, Instance *p_B) { @@ -310,6 +324,8 @@ void RendererSceneCull::scenario_initialize(RID p_rid) { scenario->instance_aabbs.set_page_pool(&instance_aabb_page_pool); scenario->instance_data.set_page_pool(&instance_data_page_pool); + RendererSceneOcclusionCull::get_singleton()->add_scenario(p_rid); + scenario_owner.initialize_rid(p_rid, scenario); } @@ -497,6 +513,11 @@ void RendererSceneCull::instance_set_base(RID p_instance, RID p_base) { scene_render->free(gi_probe->probe_instance); } break; + case RS::INSTANCE_OCCLUDER: { + if (scenario && instance->visible) { + RendererSceneOcclusionCull::get_singleton()->scenario_remove_instance(instance->scenario->self, p_instance); + } + } break; default: { } } @@ -514,6 +535,11 @@ void RendererSceneCull::instance_set_base(RID p_instance, RID p_base) { if (p_base.is_valid()) { instance->base_type = RSG::storage->get_base_type(p_base); + + if (instance->base_type == RS::INSTANCE_NONE && RendererSceneOcclusionCull::get_singleton()->is_occluder(p_base)) { + instance->base_type = RS::INSTANCE_OCCLUDER; + } + ERR_FAIL_COND(instance->base_type == RS::INSTANCE_NONE); switch (instance->base_type) { @@ -588,6 +614,11 @@ void RendererSceneCull::instance_set_base(RID p_instance, RID p_base) { gi_probe->probe_instance = scene_render->gi_probe_instance_create(p_base); } break; + case RS::INSTANCE_OCCLUDER: { + if (scenario) { + RendererSceneOcclusionCull::get_singleton()->scenario_set_instance(scenario->self, p_instance, p_base, instance->transform, instance->visible); + } + } break; default: { } } @@ -655,6 +686,11 @@ void RendererSceneCull::instance_set_scenario(RID p_instance, RID p_scenario) { gi_probe_update_list.remove(&gi_probe->update_element); } } break; + case RS::INSTANCE_OCCLUDER: { + if (instance->visible) { + RendererSceneOcclusionCull::get_singleton()->scenario_remove_instance(instance->scenario->self, p_instance); + } + } break; default: { } } @@ -684,6 +720,9 @@ void RendererSceneCull::instance_set_scenario(RID p_instance, RID p_scenario) { gi_probe_update_list.add(&gi_probe->update_element); } } break; + case RS::INSTANCE_OCCLUDER: { + RendererSceneOcclusionCull::get_singleton()->scenario_set_instance(scenario->self, p_instance, instance->base, instance->transform, instance->visible); + } break; default: { } } @@ -801,6 +840,12 @@ void RendererSceneCull::instance_set_visible(RID p_instance, bool p_visible) { InstanceParticlesCollisionData *collision = static_cast<InstanceParticlesCollisionData *>(instance->base_data); RSG::storage->particles_collision_instance_set_active(collision->instance, p_visible); } + + if (instance->base_type == RS::INSTANCE_OCCLUDER) { + if (instance->scenario) { + RendererSceneOcclusionCull::get_singleton()->scenario_set_instance(instance->scenario->self, p_instance, instance->base, instance->transform, p_visible); + } + } } inline bool is_geometry_instance(RenderingServer::InstanceType p_type) { @@ -998,6 +1043,18 @@ void RendererSceneCull::instance_geometry_set_flag(RID p_instance, RS::InstanceF } } break; + case RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING: { + instance->ignore_occlusion_culling = p_enabled; + + if (instance->scenario && instance->array_index >= 0) { + InstanceData &idata = instance->scenario->instance_data[instance->array_index]; + if (instance->ignore_occlusion_culling) { + idata.flags |= InstanceData::FLAG_IGNORE_OCCLUSION_CULLING; + } else { + idata.flags &= ~uint32_t(InstanceData::FLAG_IGNORE_OCCLUSION_CULLING); + } + } + } break; default: { } } @@ -1210,6 +1267,10 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { heightfield_particle_colliders_update_list.insert(p_instance); } RSG::storage->particles_collision_instance_set_transform(collision->instance, p_instance->transform); + } else if (p_instance->base_type == RS::INSTANCE_OCCLUDER) { + if (p_instance->scenario) { + RendererSceneOcclusionCull::get_singleton()->scenario_set_instance(p_instance->scenario->self, p_instance->self, p_instance->base, p_instance->transform, p_instance->visible); + } } if (p_instance->aabb.has_no_surface()) { @@ -1337,6 +1398,9 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { if (p_instance->mesh_instance.is_valid()) { idata.flags |= InstanceData::FLAG_USES_MESH_INSTANCE; } + if (p_instance->ignore_occlusion_culling) { + idata.flags |= InstanceData::FLAG_IGNORE_OCCLUSION_CULLING; + } p_instance->scenario->instance_data.push_back(idata); p_instance->scenario->instance_aabbs.push_back(InstanceBounds(p_instance->transformed_aabb)); @@ -1363,6 +1427,9 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { pair.pair_mask |= 1 << RS::INSTANCE_LIGHT; pair.pair_mask |= 1 << RS::INSTANCE_GI_PROBE; pair.pair_mask |= 1 << RS::INSTANCE_LIGHTMAP; + if (p_instance->base_type == RS::INSTANCE_PARTICLES) { + pair.pair_mask |= 1 << RS::INSTANCE_PARTICLES_COLLISION; + } pair.pair_mask |= geometry_instance_pair_mask; @@ -2119,7 +2186,7 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons return animated_material_found; } -void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas) { +void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas) { // render to mono camera #ifndef _3D_DISABLED @@ -2164,11 +2231,14 @@ void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_ RID environment = _render_get_environment(p_camera, p_scenario); - _render_scene(camera->transform, camera_matrix, ortho, camera->vaspect, p_render_buffers, environment, camera->effects, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), -1, p_screen_lod_threshold); + RENDER_TIMESTAMP("Update occlusion buffer") + RendererSceneOcclusionCull::get_singleton()->buffer_update(p_viewport, camera->transform, camera_matrix, ortho, RendererThreadPool::singleton->thread_work_pool); + + _render_scene(camera->transform, camera_matrix, ortho, camera->vaspect, p_render_buffers, environment, camera->effects, camera->visible_layers, p_scenario, p_viewport, p_shadow_atlas, RID(), -1, p_screen_lod_threshold); #endif } -void RendererSceneCull::render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas) { +void RendererSceneCull::render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas) { // render for AR/VR interface #if 0 Camera *camera = camera_owner.getornull(p_camera); @@ -2253,7 +2323,7 @@ void RendererSceneCull::render_camera(RID p_render_buffers, Ref<XRInterface> &p_ #endif }; -void RendererSceneCull::_frustum_cull_threaded(uint32_t p_thread, FrustumCullData *cull_data) { +void RendererSceneCull::_frustum_cull_threaded(uint32_t p_thread, CullData *cull_data) { uint32_t cull_total = cull_data->scenario->instance_data.size(); uint32_t total_threads = RendererThreadPool::singleton->thread_work_pool.get_thread_count(); uint32_t cull_from = p_thread * cull_total / total_threads; @@ -2262,7 +2332,7 @@ void RendererSceneCull::_frustum_cull_threaded(uint32_t p_thread, FrustumCullDat _frustum_cull(*cull_data, frustum_cull_result_threads[p_thread], cull_from, cull_to); } -void RendererSceneCull::_frustum_cull(FrustumCullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to) { +void RendererSceneCull::_frustum_cull(CullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to) { uint64_t frame_number = RSG::rasterizer->get_frame_number(); float lightmap_probe_update_speed = RSG::storage->lightmap_get_probe_capture_update_speed() * RSG::rasterizer->get_frame_delta_time(); @@ -2271,10 +2341,14 @@ void RendererSceneCull::_frustum_cull(FrustumCullData &cull_data, FrustumCullRes RID instance_pair_buffer[MAX_INSTANCE_PAIRS]; + Transform inv_cam_transform = cull_data.cam_transform.inverse(); + float z_near = cull_data.camera_matrix->get_z_near(); + for (uint64_t i = p_from; i < p_to; i++) { bool mesh_visible = false; - if (cull_data.scenario->instance_aabbs[i].in_frustum(cull_data.cull->frustum)) { + if (cull_data.scenario->instance_aabbs[i].in_frustum(cull_data.cull->frustum) && (cull_data.occlusion_buffer == nullptr || cull_data.scenario->instance_data[i].flags & InstanceData::FLAG_IGNORE_OCCLUSION_CULLING || + !cull_data.occlusion_buffer->is_occluded(cull_data.scenario->instance_aabbs[i].bounds, cull_data.cam_transform.origin, inv_cam_transform, *cull_data.camera_matrix, z_near))) { InstanceData &idata = cull_data.scenario->instance_data[i]; uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; @@ -2339,7 +2413,7 @@ void RendererSceneCull::_frustum_cull(FrustumCullData &cull_data, FrustumCullRes cull_data.cull->lock.lock(); RSG::storage->particles_request_process(idata.base_rid); cull_data.cull->lock.unlock(); - RSG::storage->particles_set_view_axis(idata.base_rid, -cull_data.cam_transform.basis.get_axis(2).normalized()); + RSG::storage->particles_set_view_axis(idata.base_rid, -cull_data.cam_transform.basis.get_axis(2).normalized(), cull_data.cam_transform.basis.get_axis(1).normalized()); //particles visible? request redraw RenderingServerDefault::redraw_request(); } @@ -2469,7 +2543,7 @@ void RendererSceneCull::_frustum_cull(FrustumCullData &cull_data, FrustumCullRes } } -void RendererSceneCull::_render_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows) { +void RendererSceneCull::_render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows) { // Note, in stereo rendering: // - p_cam_transform will be a transform in the middle of our two eyes // - p_cam_projection is a wider frustrum that encompasses both eyes @@ -2566,7 +2640,7 @@ void RendererSceneCull::_render_scene(const Transform p_cam_transform, const Cam uint64_t cull_from = 0; uint64_t cull_to = scenario->instance_data.size(); - FrustumCullData cull_data; + CullData cull_data; //prepare for eventual thread usage cull_data.cull = &cull; @@ -2575,6 +2649,8 @@ void RendererSceneCull::_render_scene(const Transform p_cam_transform, const Cam cull_data.cam_transform = p_cam_transform; cull_data.visible_layers = p_visible_layers; cull_data.render_reflection_probe = render_reflection_probe; + cull_data.occlusion_buffer = RendererSceneOcclusionCull::get_singleton()->buffer_get_ptr(p_viewport); + cull_data.camera_matrix = &p_cam_projection; //#define DEBUG_CULL_TIME #ifdef DEBUG_CULL_TIME uint64_t time_from = OS::get_singleton()->get_ticks_usec(); @@ -2781,8 +2857,13 @@ void RendererSceneCull::_render_scene(const Transform p_cam_transform, const Cam } /* PROCESS GEOMETRY AND DRAW SCENE */ + RID occluders_tex; + if (p_viewport.is_valid()) { + occluders_tex = RSG::viewport->viewport_get_occluder_debug_texture(p_viewport); + } + RENDER_TIMESTAMP("Render Scene "); - scene_render->render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_orthogonal, frustum_cull_result.geometry_instances, frustum_cull_result.light_instances, frustum_cull_result.reflections, frustum_cull_result.gi_probes, frustum_cull_result.decals, frustum_cull_result.lightmaps, p_environment, camera_effects, p_shadow_atlas, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data); + scene_render->render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_orthogonal, frustum_cull_result.geometry_instances, frustum_cull_result.light_instances, frustum_cull_result.reflections, frustum_cull_result.gi_probes, frustum_cull_result.decals, frustum_cull_result.lightmaps, p_environment, camera_effects, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data); for (uint32_t i = 0; i < max_shadows_used; i++) { render_shadow_data[i].instances.clear(); @@ -2829,7 +2910,7 @@ void RendererSceneCull::render_empty_scene(RID p_render_buffers, RID p_scenario, environment = scenario->fallback_environment; } RENDER_TIMESTAMP("Render Empty Scene "); - scene_render->render_scene(p_render_buffers, Transform(), CameraMatrix(), true, PagedArray<RendererSceneRender::GeometryInstance *>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), RID(), RID(), p_shadow_atlas, scenario->reflection_atlas, RID(), 0, 0, nullptr, 0, nullptr, 0, nullptr); + scene_render->render_scene(p_render_buffers, Transform(), CameraMatrix(), true, PagedArray<RendererSceneRender::GeometryInstance *>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), RID(), RID(), p_shadow_atlas, RID(), scenario->reflection_atlas, RID(), 0, 0, nullptr, 0, nullptr, 0, nullptr); #endif } @@ -2899,7 +2980,7 @@ bool RendererSceneCull::_render_reflection_probe_step(Instance *p_instance, int } RENDER_TIMESTAMP("Render Reflection Probe, Step " + itos(p_step)); - _render_scene(xform, cm, false, false, RID(), environment, RID(), RSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, p_step, lod_threshold, use_shadows); + _render_scene(xform, cm, false, false, RID(), environment, RID(), RSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, RID(), shadow_atlas, reflection_probe->instance, p_step, lod_threshold, use_shadows); } else { //do roughness postprocess step until it believes it's done @@ -3473,8 +3554,11 @@ bool RendererSceneCull::free(RID p_rid) { scene_render->free(scenario->reflection_probe_shadow_atlas); scene_render->free(scenario->reflection_atlas); scenario_owner.free(p_rid); + RendererSceneOcclusionCull::get_singleton()->remove_scenario(p_rid); memdelete(scenario); + } else if (RendererSceneOcclusionCull::get_singleton()->is_occluder(p_rid)) { + RendererSceneOcclusionCull::get_singleton()->free_occluder(p_rid); } else if (instance_owner.owns(p_rid)) { // delete the instance @@ -3543,6 +3627,8 @@ RendererSceneCull::RendererSceneCull() { indexer_update_iterations = GLOBAL_GET("rendering/limits/spatial_indexer/update_iterations_per_frame"); thread_cull_threshold = GLOBAL_GET("rendering/limits/spatial_indexer/threaded_cull_minimum_instances"); thread_cull_threshold = MAX(thread_cull_threshold, (uint32_t)RendererThreadPool::singleton->thread_work_pool.get_thread_count()); //make sure there is at least one thread per CPU + + dummy_occlusion_culling = memnew(RendererSceneOcclusionCull); } RendererSceneCull::~RendererSceneCull() { @@ -3561,4 +3647,8 @@ RendererSceneCull::~RendererSceneCull() { frustum_cull_result_threads[i].reset(); } frustum_cull_result_threads.clear(); + + if (dummy_occlusion_culling) { + memdelete(dummy_occlusion_culling); + } } diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index d7d59665ec..a61b04afc8 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -45,8 +45,10 @@ #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" #include "servers/rendering/renderer_scene.h" +#include "servers/rendering/renderer_scene_occlusion_cull.h" #include "servers/rendering/renderer_scene_render.h" #include "servers/xr/xr_interface.h" + class RendererSceneCull : public RendererScene { public: RendererSceneRender *scene_render; @@ -109,6 +111,14 @@ public: virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable); virtual bool is_camera(RID p_camera) const; + /* OCCLUDER API */ + + virtual RID occluder_allocate(); + virtual void occluder_initialize(RID p_occluder); + virtual void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices); + + RendererSceneOcclusionCull *dummy_occlusion_culling; + /* SCENARIO API */ struct Instance; @@ -248,6 +258,7 @@ public: FLAG_USES_BAKED_LIGHT = (1 << 16), FLAG_USES_MESH_INSTANCE = (1 << 17), FLAG_REFLECTION_PROBE_DIRTY = (1 << 18), + FLAG_IGNORE_OCCLUSION_CULLING = (1 << 19), }; uint32_t flags = 0; @@ -346,6 +357,8 @@ public: float lod_bias; + bool ignore_occlusion_culling; + Vector<RID> materials; RS::ShadowCastingSetting cast_shadows; @@ -430,6 +443,7 @@ public: singleton->_instance_queue_update(instance, false, true); } break; case RendererStorage::DEPENDENCY_CHANGED_MESH: + case RendererStorage::DEPENDENCY_CHANGED_PARTICLES: case RendererStorage::DEPENDENCY_CHANGED_MULTIMESH: case RendererStorage::DEPENDENCY_CHANGED_DECAL: case RendererStorage::DEPENDENCY_CHANGED_LIGHT: @@ -470,6 +484,7 @@ public: lightmap = nullptr; lightmap_cull_index = 0; lod_bias = 1.0; + ignore_occlusion_culling = false; scenario = nullptr; @@ -647,6 +662,7 @@ public: _FORCE_INLINE_ bool operator()(void *p_data) { Instance *p_instance = (Instance *)p_data; + if (instance != p_instance && instance->transformed_aabb.intersects(p_instance->transformed_aabb) && (pair_mask & (1 << p_instance->base_type))) { //test is more coarse in indexer p_instance->pair_check = pair_pass; @@ -921,24 +937,26 @@ public: Frustum frustum; } cull; - struct FrustumCullData { + struct CullData { Cull *cull; Scenario *scenario; RID shadow_atlas; Transform cam_transform; uint32_t visible_layers; Instance *render_reflection_probe; + const RendererSceneOcclusionCull::HZBuffer *occlusion_buffer; + const CameraMatrix *camera_matrix; }; - void _frustum_cull_threaded(uint32_t p_thread, FrustumCullData *cull_data); - void _frustum_cull(FrustumCullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to); + void _frustum_cull_threaded(uint32_t p_thread, CullData *cull_data); + void _frustum_cull(CullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to); bool _render_reflection_probe_step(Instance *p_instance, int p_step); - void _render_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows = true); + void _render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows = true); void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas); - void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas); - void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas); + void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas); + void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas); void update_dirty_instances(); void render_particle_colliders(); diff --git a/servers/rendering/renderer_scene_occlusion_cull.cpp b/servers/rendering/renderer_scene_occlusion_cull.cpp new file mode 100644 index 0000000000..c491ccbe7a --- /dev/null +++ b/servers/rendering/renderer_scene_occlusion_cull.cpp @@ -0,0 +1,192 @@ +/*************************************************************************/ +/* renderer_scene_occlusion_cull.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "renderer_scene_occlusion_cull.h" + +RendererSceneOcclusionCull *RendererSceneOcclusionCull::singleton = nullptr; + +const Vector3 RendererSceneOcclusionCull::HZBuffer::corners[8] = { + Vector3(0, 0, 0), + Vector3(0, 0, 1), + Vector3(0, 1, 0), + Vector3(0, 1, 1), + Vector3(1, 0, 0), + Vector3(1, 0, 1), + Vector3(1, 1, 0), + Vector3(1, 1, 1) +}; + +bool RendererSceneOcclusionCull::HZBuffer::is_empty() const { + return sizes.is_empty(); +} + +void RendererSceneOcclusionCull::HZBuffer::clear() { + if (sizes.is_empty()) { + return; // Already cleared + } + + data.clear(); + sizes.clear(); + mips.clear(); + + debug_data.clear(); + if (debug_image.is_valid()) { + debug_image.unref(); + } + RS::get_singleton()->free(debug_texture); +} + +void RendererSceneOcclusionCull::HZBuffer::resize(const Size2i &p_size) { + if (p_size == Size2i()) { + clear(); + return; + } + + if (!sizes.is_empty() && p_size == sizes[0]) { + return; // Size didn't change + } + + int mip_count = 0; + int data_size = 0; + int w = p_size.x; + int h = p_size.y; + + while (true) { + data_size += h * w; + + w = MAX(1, w >> 1); + h = MAX(1, h >> 1); + + mip_count++; + + if (w == 1U && h == 1U) { + data_size += 1U; + mip_count++; + break; + } + } + + data.resize(data_size); + mips.resize(mip_count); + sizes.resize(mip_count); + + w = p_size.x; + h = p_size.y; + float *ptr = data.ptr(); + + for (int i = 0; i < mip_count; i++) { + sizes[i] = Size2i(w, h); + mips[i] = ptr; + + ptr = &ptr[w * h]; + w = MAX(1, w >> 1); + h = MAX(1, h >> 1); + } + + for (int i = 0; i < data_size; i++) { + data[i] = FLT_MAX; + } + + debug_data.resize(sizes[0].x * sizes[0].y); + if (debug_texture.is_valid()) { + RS::get_singleton()->free(debug_texture); + debug_texture = RID(); + } +} + +void RendererSceneOcclusionCull::HZBuffer::update_mips() { + if (sizes.is_empty()) { + return; + } + + for (uint32_t mip = 1; mip < mips.size(); mip++) { + for (int y = 0; y < sizes[mip].y; y++) { + for (int x = 0; x < sizes[mip].x; x++) { + int prev_x = x * 2; + int prev_y = y * 2; + + int prev_w = sizes[mip - 1].width; + int prev_h = sizes[mip - 1].height; + + bool odd_w = (prev_w % 2) != 0; + bool odd_h = (prev_h % 2) != 0; + +#define CHECK_OFFSET(xx, yy) max_depth = MAX(max_depth, mips[mip - 1][MIN(prev_h - 1, prev_y + (yy)) * prev_w + MIN(prev_w - 1, prev_x + (xx))]) + + float max_depth = mips[mip - 1][prev_y * sizes[mip - 1].x + prev_x]; + CHECK_OFFSET(0, 1); + CHECK_OFFSET(1, 0); + CHECK_OFFSET(1, 1); + + if (odd_w) { + CHECK_OFFSET(2, 0); + CHECK_OFFSET(2, 1); + } + + if (odd_h) { + CHECK_OFFSET(0, 2); + CHECK_OFFSET(1, 2); + } + + if (odd_w && odd_h) { + CHECK_OFFSET(2, 2); + } + + mips[mip][y * sizes[mip].x + x] = max_depth; +#undef CHECK_OFFSET + } + } + } +} + +RID RendererSceneOcclusionCull::HZBuffer::get_debug_texture() { + if (sizes.is_empty() || sizes[0] == Size2i()) { + return RID(); + } + + if (debug_image.is_null()) { + debug_image.instance(); + } + + unsigned char *ptrw = debug_data.ptrw(); + for (int i = 0; i < debug_data.size(); i++) { + ptrw[i] = MIN(mips[0][i] / debug_tex_range, 1.0) * 255; + } + + debug_image->create(sizes[0].x, sizes[0].y, false, Image::FORMAT_L8, debug_data); + + if (debug_texture.is_null()) { + debug_texture = RS::get_singleton()->texture_2d_create(debug_image); + } else { + RenderingServer::get_singleton()->texture_2d_update_immediate(debug_texture, debug_image); + } + + return debug_texture; +} diff --git a/servers/rendering/renderer_scene_occlusion_cull.h b/servers/rendering/renderer_scene_occlusion_cull.h new file mode 100644 index 0000000000..390bbaa64b --- /dev/null +++ b/servers/rendering/renderer_scene_occlusion_cull.h @@ -0,0 +1,201 @@ +/*************************************************************************/ +/* renderer_scene_occlusion_cull.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef RENDERER_SCENE_OCCLUSION_CULL_H +#define RENDERER_SCENE_OCCLUSION_CULL_H + +#include "core/math/camera_matrix.h" +#include "core/templates/local_vector.h" +#include "servers/rendering_server.h" + +class RendererSceneOcclusionCull { +protected: + static RendererSceneOcclusionCull *singleton; + +public: + class HZBuffer { + protected: + static const Vector3 corners[8]; + + LocalVector<float> data; + LocalVector<Size2i> sizes; + LocalVector<float *> mips; + + RID debug_texture; + Ref<Image> debug_image; + PackedByteArray debug_data; + float debug_tex_range = 0.0f; + + public: + bool is_empty() const; + virtual void clear(); + virtual void resize(const Size2i &p_size); + + void update_mips(); + + _FORCE_INLINE_ bool is_occluded(const float p_bounds[6], const Vector3 &p_cam_position, const Transform &p_cam_inv_transform, const CameraMatrix &p_cam_projection, float p_near) const { + if (is_empty()) { + return false; + } + + Vector3 closest_point = Vector3(CLAMP(p_cam_position.x, p_bounds[0], p_bounds[3]), CLAMP(p_cam_position.y, p_bounds[1], p_bounds[4]), CLAMP(p_cam_position.z, p_bounds[2], p_bounds[5])); + + if (closest_point == p_cam_position) { + return false; + } + + Vector3 closest_point_view = p_cam_inv_transform.xform(closest_point); + if (closest_point_view.z > -p_near) { + return false; + } + + float min_depth; + if (p_cam_projection.is_orthogonal()) { + min_depth = (-closest_point_view.z) - p_near; + } else { + float r = -p_near / closest_point_view.z; + Vector3 closest_point_proj = Vector3(closest_point_view.x * r, closest_point_view.y * r, -p_near); + min_depth = closest_point_proj.distance_to(closest_point_view); + } + + Vector2 rect_min = Vector2(FLT_MAX, FLT_MAX); + Vector2 rect_max = Vector2(FLT_MIN, FLT_MIN); + + for (int j = 0; j < 8; j++) { + Vector3 c = RendererSceneOcclusionCull::HZBuffer::corners[j]; + Vector3 nc = Vector3(1, 1, 1) - c; + Vector3 corner = Vector3(p_bounds[0] * c.x + p_bounds[3] * nc.x, p_bounds[1] * c.y + p_bounds[4] * nc.y, p_bounds[2] * c.z + p_bounds[5] * nc.z); + Vector3 view = p_cam_inv_transform.xform(corner); + + Vector3 projected = p_cam_projection.xform(view); + Vector2 normalized = Vector2(projected.x * 0.5f + 0.5f, projected.y * 0.5f + 0.5f); + rect_min = rect_min.min(normalized); + rect_max = rect_max.max(normalized); + } + + rect_max = rect_max.min(Vector2(1, 1)); + rect_min = rect_min.max(Vector2(0, 0)); + + int mip_count = mips.size(); + + Vector2 screen_diagonal = (rect_max - rect_min) * sizes[0]; + float size = MAX(screen_diagonal.x, screen_diagonal.y); + float l = Math::ceil(Math::log2(size)); + int lod = CLAMP(l, 0, mip_count - 1); + + const int max_samples = 512; + int sample_count = 0; + bool visible = true; + + for (; lod >= 0; lod--) { + int w = sizes[lod].x; + int h = sizes[lod].y; + + int minx = CLAMP(rect_min.x * w - 1, 0, w - 1); + int maxx = CLAMP(rect_max.x * w + 1, 0, w - 1); + + int miny = CLAMP(rect_min.y * h - 1, 0, h - 1); + int maxy = CLAMP(rect_max.y * h + 1, 0, h - 1); + + sample_count += (maxx - minx + 1) * (maxy - miny + 1); + + if (sample_count > max_samples) { + return false; + } + + visible = false; + for (int y = miny; y <= maxy; y++) { + for (int x = minx; x <= maxx; x++) { + float depth = mips[lod][y * w + x]; + if (depth > min_depth) { + visible = true; + break; + } + } + if (visible) { + break; + } + } + + if (!visible) { + return true; + } + } + + return !visible; + } + + RID get_debug_texture(); + + virtual ~HZBuffer(){}; + }; + + static RendererSceneOcclusionCull *get_singleton() { return singleton; } + + void _print_warining() { + WARN_PRINT_ONCE("Occlusion culling is disabled at build time."); + } + + virtual bool is_occluder(RID p_rid) { return false; } + virtual RID occluder_allocate() { return RID(); } + virtual void occluder_initialize(RID p_occluder) {} + virtual void free_occluder(RID p_occluder) { _print_warining(); } + virtual void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) { _print_warining(); } + + virtual void add_scenario(RID p_scenario) {} + virtual void remove_scenario(RID p_scenario) {} + virtual void scenario_set_instance(RID p_scenario, RID p_instance, RID p_occluder, const Transform &p_xform, bool p_enabled) { _print_warining(); } + virtual void scenario_remove_instance(RID p_scenario, RID p_instance) { _print_warining(); } + + virtual void add_buffer(RID p_buffer) { _print_warining(); } + virtual void remove_buffer(RID p_buffer) { _print_warining(); } + virtual HZBuffer *buffer_get_ptr(RID p_buffer) { + return nullptr; + } + virtual void buffer_set_scenario(RID p_buffer, RID p_scenario) { _print_warining(); } + virtual void buffer_set_size(RID p_buffer, const Vector2i &p_size) { _print_warining(); } + virtual void buffer_update(RID p_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_pool) {} + virtual RID buffer_get_debug_texture(RID p_buffer) { + _print_warining(); + return RID(); + } + + virtual void set_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality) {} + + RendererSceneOcclusionCull() { + singleton = this; + }; + + virtual ~RendererSceneOcclusionCull() { + singleton = nullptr; + }; +}; + +#endif //RENDERER_SCENE_OCCLUSION_CULL_H diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h index 9ca9574f6f..3f28fac549 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -216,7 +216,7 @@ public: uint32_t positional_light_count; }; - virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr) = 0; + virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr) = 0; virtual void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; virtual void render_particle_collider_heightfield(RID p_collider, const Transform &p_transform, const PagedArray<GeometryInstance *> &p_instances) = 0; diff --git a/servers/rendering/renderer_storage.h b/servers/rendering/renderer_storage.h index 22cf6acb19..15d99c038e 100644 --- a/servers/rendering/renderer_storage.h +++ b/servers/rendering/renderer_storage.h @@ -43,6 +43,7 @@ public: DEPENDENCY_CHANGED_MESH, DEPENDENCY_CHANGED_MULTIMESH, DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES, + DEPENDENCY_CHANGED_PARTICLES, DEPENDENCY_CHANGED_DECAL, DEPENDENCY_CHANGED_SKELETON_DATA, DEPENDENCY_CHANGED_SKELETON_BONES, @@ -498,8 +499,15 @@ public: virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable) = 0; virtual void particles_set_process_material(RID p_particles, RID p_material) = 0; virtual void particles_set_fixed_fps(RID p_particles, int p_fps) = 0; + virtual void particles_set_interpolate(RID p_particles, bool p_enable) = 0; virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) = 0; virtual void particles_set_collision_base_size(RID p_particles, float p_size) = 0; + + virtual void particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) = 0; + + virtual void particles_set_trails(RID p_particles, bool p_enable, float p_length) = 0; + virtual void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform> &p_bind_poses) = 0; + virtual void particles_restart(RID p_particles) = 0; virtual void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) = 0; virtual void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) = 0; @@ -520,7 +528,7 @@ public: virtual int particles_get_draw_passes(RID p_particles) const = 0; virtual RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const = 0; - virtual void particles_set_view_axis(RID p_particles, const Vector3 &p_axis) = 0; + virtual void particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) = 0; virtual void particles_add_collision(RID p_particles, RID p_particles_collision_instance) = 0; virtual void particles_remove_collision(RID p_particles, RID p_particles_collision_instance) = 0; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index a5d5033c18..f7be6c6c60 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -79,11 +79,26 @@ void RendererViewport::_draw_3d(Viewport *p_viewport, XRInterface::Eyes p_eye) { xr_interface = XRServer::get_singleton()->get_primary_interface(); } + if (p_viewport->use_occlusion_culling) { + if (p_viewport->occlusion_buffer_dirty) { + float aspect = p_viewport->size.aspect(); + int max_size = occlusion_rays_per_thread * RendererThreadPool::singleton->thread_work_pool.get_thread_count(); + + int viewport_size = p_viewport->size.width * p_viewport->size.height; + max_size = CLAMP(max_size, viewport_size / (32 * 32), viewport_size / (2 * 2)); // At least one depth pixel for every 16x16 region. At most one depth pixel for every 2x2 region. + + float height = Math::sqrt(max_size / aspect); + Size2i new_size = Size2i(height * aspect, height); + RendererSceneOcclusionCull::get_singleton()->buffer_set_size(p_viewport->self, new_size); + p_viewport->occlusion_buffer_dirty = false; + } + } + float screen_lod_threshold = p_viewport->lod_threshold / float(p_viewport->size.width); if (p_viewport->use_xr && xr_interface.is_valid()) { - RSG::scene->render_camera(p_viewport->render_buffers, xr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas); + RSG::scene->render_camera(p_viewport->render_buffers, xr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas); } else { - RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas); + RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas); } RENDER_TIMESTAMP("<End Rendering 3D Scene"); } @@ -647,6 +662,8 @@ void RendererViewport::viewport_set_size(RID p_viewport, int p_width, int p_heig RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, viewport->use_debanding); } } + + viewport->occlusion_buffer_dirty = true; } void RendererViewport::viewport_set_active(RID p_viewport, bool p_active) { @@ -655,6 +672,7 @@ void RendererViewport::viewport_set_active(RID p_viewport, bool p_active) { if (p_active) { ERR_FAIL_COND(active_viewports.find(viewport) != -1); //already active + viewport->occlusion_buffer_dirty = true; active_viewports.push_back(viewport); } else { active_viewports.erase(viewport); @@ -739,6 +757,16 @@ RID RendererViewport::viewport_get_texture(RID p_viewport) const { return RSG::storage->render_target_get_texture(viewport->render_target); } +RID RendererViewport::viewport_get_occluder_debug_texture(RID p_viewport) const { + const Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND_V(!viewport, RID()); + + if (viewport->use_occlusion_culling && viewport->debug_draw == RenderingServer::VIEWPORT_DEBUG_DRAW_OCCLUDERS) { + return RendererSceneOcclusionCull::get_singleton()->buffer_get_debug_texture(p_viewport); + } + return RID(); +} + void RendererViewport::viewport_set_hide_scenario(RID p_viewport, bool p_hide) { Viewport *viewport = viewport_owner.getornull(p_viewport); ERR_FAIL_COND(!viewport); @@ -772,6 +800,9 @@ void RendererViewport::viewport_set_scenario(RID p_viewport, RID p_scenario) { ERR_FAIL_COND(!viewport); viewport->scenario = p_scenario; + if (viewport->use_occlusion_culling) { + RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, p_scenario); + } } void RendererViewport::viewport_attach_canvas(RID p_viewport, RID p_canvas) { @@ -888,6 +919,41 @@ void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_deb } } +void RendererViewport::viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling) { + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + if (viewport->use_occlusion_culling == p_use_occlusion_culling) { + return; + } + viewport->use_occlusion_culling = p_use_occlusion_culling; + + if (viewport->use_occlusion_culling) { + RendererSceneOcclusionCull::get_singleton()->add_buffer(p_viewport); + RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, viewport->scenario); + } else { + RendererSceneOcclusionCull::get_singleton()->remove_buffer(p_viewport); + } + + viewport->occlusion_buffer_dirty = true; +} + +void RendererViewport::viewport_set_occlusion_rays_per_thread(int p_rays_per_thread) { + if (occlusion_rays_per_thread == p_rays_per_thread) { + return; + } + + occlusion_rays_per_thread = p_rays_per_thread; + + for (int i = 0; i < active_viewports.size(); i++) { + active_viewports[i]->occlusion_buffer_dirty = true; + } +} + +void RendererViewport::viewport_set_occlusion_culling_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality) { + RendererSceneOcclusionCull::get_singleton()->set_build_quality(p_quality); +} + void RendererViewport::viewport_set_lod_threshold(RID p_viewport, float p_pixels) { Viewport *viewport = viewport_owner.getornull(p_viewport); ERR_FAIL_COND(!viewport); @@ -985,6 +1051,10 @@ bool RendererViewport::free(RID p_rid) { viewport_set_scenario(p_rid, RID()); active_viewports.erase(viewport); + if (viewport->use_occlusion_culling) { + RendererSceneOcclusionCull::get_singleton()->remove_buffer(p_rid); + } + viewport_owner.free(p_rid); memdelete(viewport); @@ -1026,4 +1096,5 @@ void RendererViewport::call_set_use_vsync(bool p_enable) { } RendererViewport::RendererViewport() { + occlusion_rays_per_thread = GLOBAL_GET("rendering/occlusion_culling/occlusion_rays_per_thread"); } diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index f5ed543e8d..5c372e8c9a 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -31,9 +31,9 @@ #ifndef VISUALSERVERVIEWPORT_H #define VISUALSERVERVIEWPORT_H +#include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" -#include "renderer_compositor.h" #include "servers/rendering_server.h" #include "servers/xr/xr_interface.h" @@ -61,6 +61,9 @@ public: RS::ViewportScreenSpaceAA screen_space_aa; bool use_debanding; + bool use_occlusion_culling; + bool occlusion_buffer_dirty; + DisplayServer::WindowID viewport_to_screen; Rect2 viewport_to_screen_rect; bool viewport_render_direct_to_screen; @@ -143,6 +146,8 @@ public: msaa = RS::VIEWPORT_MSAA_DISABLED; screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; use_debanding = false; + use_occlusion_culling = false; + occlusion_buffer_dirty = true; snap_2d_transforms_to_pixel = false; snap_2d_vertices_to_pixel = false; @@ -185,6 +190,10 @@ private: void _draw_3d(Viewport *p_viewport, XRInterface::Eyes p_eye); void _draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_eye = XRInterface::EYE_MONO); + int occlusion_rays_per_thread = 512; + + void _resize_occlusion_culling_buffer(const Size2i &p_size); + public: RID viewport_allocate(); void viewport_initialize(RID p_rid); @@ -204,6 +213,7 @@ public: void viewport_set_clear_mode(RID p_viewport, RS::ViewportClearMode p_clear_mode); RID viewport_get_texture(RID p_viewport) const; + RID viewport_get_occluder_debug_texture(RID p_viewport) const; void viewport_set_hide_scenario(RID p_viewport, bool p_hide); void viewport_set_hide_canvas(RID p_viewport, bool p_hide); @@ -225,7 +235,9 @@ public: void viewport_set_msaa(RID p_viewport, RS::ViewportMSAA p_msaa); void viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode); void viewport_set_use_debanding(RID p_viewport, bool p_use_debanding); - + void viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling); + void viewport_set_occlusion_rays_per_thread(int p_rays_per_thread); + void viewport_set_occlusion_culling_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality); void viewport_set_lod_threshold(RID p_viewport, float p_pixels); virtual int viewport_get_render_info(RID p_viewport, RS::ViewportRenderInfo p_info); diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 683a22fd9a..c76ae1bb34 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -491,14 +491,20 @@ public: FUNC2(particles_set_use_local_coordinates, RID, bool) FUNC2(particles_set_process_material, RID, RID) FUNC2(particles_set_fixed_fps, RID, int) + FUNC2(particles_set_interpolate, RID, bool) FUNC2(particles_set_fractional_delta, RID, bool) FUNC1R(bool, particles_is_inactive, RID) + FUNC3(particles_set_trails, RID, bool, float) + FUNC2(particles_set_trail_bind_poses, RID, const Vector<Transform> &) + FUNC1(particles_request_process, RID) FUNC1(particles_restart, RID) FUNC6(particles_emit, RID, const Transform &, const Vector3 &, const Color &, const Color &, uint32_t) FUNC2(particles_set_subemitter, RID, RID) FUNC2(particles_set_collision_base_size, RID, float) + FUNC2(particles_set_transform_align, RID, RS::ParticlesTransformAlign) + FUNC2(particles_set_draw_order, RID, RS::ParticlesDrawOrder) FUNC2(particles_set_draw_passes, RID, int) @@ -540,6 +546,10 @@ public: FUNC2(camera_set_camera_effects, RID, RID) FUNC2(camera_set_use_vertical_aspect, RID, bool) + /* OCCLUDER */ + FUNCRIDSPLIT(occluder) + FUNC3(occluder_set_mesh, RID, const PackedVector3Array &, const PackedInt32Array &); + #undef server_name #undef ServerName //from now on, calls forwarded to this singleton @@ -590,6 +600,9 @@ public: FUNC2(viewport_set_msaa, RID, ViewportMSAA) FUNC2(viewport_set_screen_space_aa, RID, ViewportScreenSpaceAA) FUNC2(viewport_set_use_debanding, RID, bool) + FUNC2(viewport_set_use_occlusion_culling, RID, bool) + FUNC1(viewport_set_occlusion_rays_per_thread, int) + FUNC1(viewport_set_occlusion_culling_build_quality, ViewportOcclusionCullingBuildQuality) FUNC2(viewport_set_lod_threshold, RID, float) FUNC2R(int, viewport_get_render_info, RID, ViewportRenderInfo) diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index f5228f9747..0d6d3f5e13 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -3168,6 +3168,36 @@ bool ShaderLanguage::_validate_varying_using(ShaderNode::Varying &p_varying, Str return true; } +bool ShaderLanguage::_check_node_constness(const Node *p_node) const { + switch (p_node->type) { + case Node::TYPE_OPERATOR: { + OperatorNode *op_node = (OperatorNode *)p_node; + for (int i = (1 ? op_node->op == OP_CALL : 0); i < op_node->arguments.size(); i++) { + if (!_check_node_constness(op_node->arguments[i])) { + return false; + } + } + } break; + case Node::TYPE_CONSTANT: + break; + case Node::TYPE_VARIABLE: { + VariableNode *varn = (VariableNode *)p_node; + if (!varn->is_const) { + return false; + } + } break; + case Node::TYPE_ARRAY: { + ArrayNode *arrn = (ArrayNode *)p_node; + if (!arrn->is_const) { + return false; + } + } break; + default: + return false; + } + return true; +} + bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message) { if (p_node->type == Node::TYPE_OPERATOR) { OperatorNode *op = static_cast<OperatorNode *>(p_node); @@ -3956,8 +3986,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons ERR_FAIL_COND_V(!expr, nullptr); /* OK now see what's NEXT to the operator.. */ - /* OK now see what's NEXT to the operator.. */ - /* OK now see what's NEXT to the operator.. */ while (true) { TkPos pos2 = _get_tkpos(); @@ -4735,7 +4763,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons ERR_FAIL_COND_V(next_op == -1, nullptr); // OK! create operator.. - // OK! create operator.. if (is_unary) { int expr_pos = next_op; while (expression[expr_pos].is_op) { @@ -5387,8 +5414,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun return ERR_PARSE_ERROR; } if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) { - _set_error("Expected constant expression after '='"); - return ERR_PARSE_ERROR; + OperatorNode *op = ((OperatorNode *)n); + for (int i = 1; i < op->arguments.size(); i++) { + if (!_check_node_constness(op->arguments[i])) { + _set_error("Expected constant expression for argument '" + itos(i - 1) + "' of function call after '='"); + return ERR_PARSE_ERROR; + } + } } decl.initializer = n; @@ -6967,8 +6999,13 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct return ERR_PARSE_ERROR; } if (expr->type == Node::TYPE_OPERATOR && ((OperatorNode *)expr)->op == OP_CALL) { - _set_error("Expected constant expression after '='"); - return ERR_PARSE_ERROR; + OperatorNode *op = ((OperatorNode *)expr); + for (int i = 1; i < op->arguments.size(); i++) { + if (!_check_node_constness(op->arguments[i])) { + _set_error("Expected constant expression for argument '" + itos(i - 1) + "' of function call after '='"); + return ERR_PARSE_ERROR; + } + } } constant.initializer = static_cast<ConstantNode *>(expr); diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 03327f9677..470f3d38d5 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -891,6 +891,7 @@ private: bool _propagate_function_call_sampler_builtin_reference(StringName p_name, int p_argument, const StringName &p_builtin); bool _validate_varying_assign(ShaderNode::Varying &p_varying, String *r_message); bool _validate_varying_using(ShaderNode::Varying &p_varying, String *r_message); + bool _check_node_constness(const Node *p_node) const; Node *_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info); Node *_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info, DataType p_type, const StringName &p_struct_name, int p_array_size); diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index 460fd5fc97..0bf68b9e0f 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -219,6 +219,7 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].modes.push_back("shadow_to_opacity"); shader_modes[RS::SHADER_SPATIAL].modes.push_back("vertex_lighting"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("particle_trails"); shader_modes[RS::SHADER_SPATIAL].modes.push_back("alpha_to_coverage"); shader_modes[RS::SHADER_SPATIAL].modes.push_back("alpha_to_coverage_and_one"); @@ -327,7 +328,7 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["LIFETIME"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["DELTA"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["NUMBER"] = constt(ShaderLanguage::TYPE_UINT); - shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_INT); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_UINT); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["EMISSION_TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_POSITION"] = constt(ShaderLanguage::TYPE_BOOL); |