diff options
Diffstat (limited to 'servers')
39 files changed, 777 insertions, 599 deletions
diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 6af4783fa5..82343e0402 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -38,6 +38,9 @@ DisplayServer *DisplayServer::singleton = nullptr; bool DisplayServer::hidpi_allowed = false; +bool DisplayServer::window_early_clear_override_enabled = false; +Color DisplayServer::window_early_clear_override_color = Color(0, 0, 0, 0); + DisplayServer::DisplayServerCreate DisplayServer::server_create_functions[DisplayServer::MAX_SERVERS] = { { "headless", &DisplayServerHeadless::create_func, &DisplayServerHeadless::get_rendering_drivers_func } }; @@ -315,6 +318,23 @@ void DisplayServer::tts_post_utterance_event(TTSUtteranceEvent p_event, int p_id } } +bool DisplayServer::_get_window_early_clear_override(Color &r_color) { + if (window_early_clear_override_enabled) { + r_color = window_early_clear_override_color; + return true; + } else if (RenderingServer::get_singleton()) { + r_color = RenderingServer::get_singleton()->get_default_clear_color(); + return true; + } else { + return false; + } +} + +void DisplayServer::set_early_window_clear_color_override(bool p_enabled, Color p_color) { + window_early_clear_override_enabled = p_enabled; + window_early_clear_override_color = p_color; +} + void DisplayServer::mouse_set_mode(MouseMode p_mode) { WARN_PRINT("Mouse is not supported by this display server."); } diff --git a/servers/display_server.h b/servers/display_server.h index 0e4c8aa172..f8ade60aca 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -217,6 +217,16 @@ public: virtual bool is_dark_mode() const { return false; }; virtual Color get_accent_color() const { return Color(0, 0, 0, 0); }; +private: + static bool window_early_clear_override_enabled; + static Color window_early_clear_override_color; + +protected: + static bool _get_window_early_clear_override(Color &r_color); + +public: + static void set_early_window_clear_color_override(bool p_enabled, Color p_color = Color(0, 0, 0, 0)); + enum MouseMode { MOUSE_MODE_VISIBLE, MOUSE_MODE_HIDDEN, diff --git a/servers/extensions/physics_server_2d_extension.cpp b/servers/extensions/physics_server_2d_extension.cpp index a0c082ee44..2dbb59a323 100644 --- a/servers/extensions/physics_server_2d_extension.cpp +++ b/servers/extensions/physics_server_2d_extension.cpp @@ -37,6 +37,8 @@ bool PhysicsDirectSpaceState2DExtension::is_body_excluded_from_query(const RID & thread_local const HashSet<RID> *PhysicsDirectSpaceState2DExtension::exclude = nullptr; void PhysicsDirectSpaceState2DExtension::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_body_excluded_from_query", "body"), &PhysicsDirectSpaceState2DExtension::is_body_excluded_from_query); + GDVIRTUAL_BIND(_intersect_ray, "from", "to", "collision_mask", "collide_with_bodies", "collide_with_areas", "hit_from_inside", "result"); GDVIRTUAL_BIND(_intersect_point, "position", "canvas_instance_id", "collision_mask", "collide_with_bodies", "collide_with_areas", "results", "max_results"); GDVIRTUAL_BIND(_intersect_shape, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "result", "max_results"); @@ -101,6 +103,7 @@ void PhysicsDirectBodyState2DExtension::_bind_methods() { GDVIRTUAL_BIND(_get_contact_collider_object, "contact_idx"); GDVIRTUAL_BIND(_get_contact_collider_shape, "contact_idx"); GDVIRTUAL_BIND(_get_contact_collider_velocity_at_position, "contact_idx"); + GDVIRTUAL_BIND(_get_contact_impulse, "contact_idx"); GDVIRTUAL_BIND(_get_step); GDVIRTUAL_BIND(_integrate_forces); @@ -202,6 +205,9 @@ void PhysicsServer2DExtension::_bind_methods() { /* BODY API */ + ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_body", "body"), &PhysicsServer2DExtension::body_test_motion_is_excluding_body); + ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_object", "object"), &PhysicsServer2DExtension::body_test_motion_is_excluding_object); + GDVIRTUAL_BIND(_body_create); GDVIRTUAL_BIND(_body_set_space, "body", "space"); diff --git a/servers/extensions/physics_server_2d_extension.h b/servers/extensions/physics_server_2d_extension.h index c4970f6398..0008653f66 100644 --- a/servers/extensions/physics_server_2d_extension.h +++ b/servers/extensions/physics_server_2d_extension.h @@ -100,6 +100,7 @@ public: EXBIND1RC(Object *, get_contact_collider_object, int) EXBIND1RC(int, get_contact_collider_shape, int) EXBIND1RC(Vector2, get_contact_collider_velocity_at_position, int) + EXBIND1RC(Vector2, get_contact_impulse, int) EXBIND0RC(real_t, get_step) EXBIND0(integrate_forces) diff --git a/servers/extensions/physics_server_3d_extension.cpp b/servers/extensions/physics_server_3d_extension.cpp index 788ece721b..0d8476fd68 100644 --- a/servers/extensions/physics_server_3d_extension.cpp +++ b/servers/extensions/physics_server_3d_extension.cpp @@ -37,6 +37,8 @@ bool PhysicsDirectSpaceState3DExtension::is_body_excluded_from_query(const RID & thread_local const HashSet<RID> *PhysicsDirectSpaceState3DExtension::exclude = nullptr; void PhysicsDirectSpaceState3DExtension::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_body_excluded_from_query", "body"), &PhysicsDirectSpaceState3DExtension::is_body_excluded_from_query); + GDVIRTUAL_BIND(_intersect_ray, "from", "to", "collision_mask", "collide_with_bodies", "collide_with_areas", "hit_from_inside", "hit_back_faces", "result"); GDVIRTUAL_BIND(_intersect_point, "position", "collision_mask", "collide_with_bodies", "collide_with_areas", "results", "max_results"); GDVIRTUAL_BIND(_intersect_shape, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "result_count", "max_results"); @@ -206,6 +208,9 @@ void PhysicsServer3DExtension::_bind_methods() { /* BODY API */ + ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_body", "body"), &PhysicsServer3DExtension::body_test_motion_is_excluding_body); + ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_object", "object"), &PhysicsServer3DExtension::body_test_motion_is_excluding_object); + GDVIRTUAL_BIND(_body_create); GDVIRTUAL_BIND(_body_set_space, "body", "space"); diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index 97c0ae0d54..253b28a623 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -123,6 +123,18 @@ void NavigationServer3D::_bind_methods() { ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::RID, "map"))); ADD_SIGNAL(MethodInfo("navigation_debug_changed")); + + ClassDB::bind_method(D_METHOD("get_process_info", "process_info"), &NavigationServer3D::get_process_info); + + BIND_ENUM_CONSTANT(INFO_ACTIVE_MAPS); + BIND_ENUM_CONSTANT(INFO_REGION_COUNT); + BIND_ENUM_CONSTANT(INFO_AGENT_COUNT); + BIND_ENUM_CONSTANT(INFO_LINK_COUNT); + BIND_ENUM_CONSTANT(INFO_POLYGON_COUNT); + BIND_ENUM_CONSTANT(INFO_EDGE_COUNT); + BIND_ENUM_CONSTANT(INFO_EDGE_MERGE_COUNT); + BIND_ENUM_CONSTANT(INFO_EDGE_CONNECTION_COUNT); + BIND_ENUM_CONSTANT(INFO_EDGE_FREE_COUNT); } NavigationServer3D *NavigationServer3D::get_singleton() { diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index afd7216a43..6c4bd2e151 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -260,6 +260,20 @@ public: NavigationServer3D(); virtual ~NavigationServer3D(); + enum ProcessInfo { + INFO_ACTIVE_MAPS, + INFO_REGION_COUNT, + INFO_AGENT_COUNT, + INFO_LINK_COUNT, + INFO_POLYGON_COUNT, + INFO_EDGE_COUNT, + INFO_EDGE_MERGE_COUNT, + INFO_EDGE_CONNECTION_COUNT, + INFO_EDGE_FREE_COUNT, + }; + + virtual int get_process_info(ProcessInfo p_info) const = 0; + #ifdef DEBUG_ENABLED private: bool debug_enabled = false; @@ -357,4 +371,6 @@ public: static NavigationServer3D *new_default_server(); }; +VARIANT_ENUM_CAST(NavigationServer3D::ProcessInfo); + #endif // NAVIGATION_SERVER_3D_H diff --git a/servers/physics_2d/godot_body_2d.h b/servers/physics_2d/godot_body_2d.h index 34da64dac9..71dc826604 100644 --- a/servers/physics_2d/godot_body_2d.h +++ b/servers/physics_2d/godot_body_2d.h @@ -132,6 +132,7 @@ class GodotBody2D : public GodotCollisionObject2D { ObjectID collider_instance_id; RID collider; Vector2 collider_velocity_at_pos; + Vector2 impulse; }; Vector<Contact> contacts; //no contacts by default @@ -190,7 +191,7 @@ public: _FORCE_INLINE_ int get_max_contacts_reported() const { return contacts.size(); } _FORCE_INLINE_ bool can_report_contacts() const { return !contacts.is_empty(); } - _FORCE_INLINE_ void add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos); + _FORCE_INLINE_ void add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos, const Vector2 &p_impulse); _FORCE_INLINE_ void add_exception(const RID &p_exception) { exceptions.insert(p_exception); } _FORCE_INLINE_ void remove_exception(const RID &p_exception) { exceptions.erase(p_exception); } @@ -340,7 +341,7 @@ public: //add contact inline -void GodotBody2D::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos) { +void GodotBody2D::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos, const Vector2 &p_impulse) { int c_max = contacts.size(); if (c_max == 0) { @@ -380,6 +381,7 @@ void GodotBody2D::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local c[idx].collider_instance_id = p_collider_instance_id; c[idx].collider = p_collider; c[idx].collider_velocity_at_pos = p_collider_velocity_at_pos; + c[idx].impulse = p_impulse; } #endif // GODOT_BODY_2D_H diff --git a/servers/physics_2d/godot_body_direct_state_2d.cpp b/servers/physics_2d/godot_body_direct_state_2d.cpp index 35092b631a..2fa933ce17 100644 --- a/servers/physics_2d/godot_body_direct_state_2d.cpp +++ b/servers/physics_2d/godot_body_direct_state_2d.cpp @@ -210,6 +210,11 @@ Vector2 GodotPhysicsDirectBodyState2D::get_contact_collider_velocity_at_position return body->contacts[p_contact_idx].collider_velocity_at_pos; } +Vector2 GodotPhysicsDirectBodyState2D::get_contact_impulse(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); + return body->contacts[p_contact_idx].impulse; +} + PhysicsDirectSpaceState2D *GodotPhysicsDirectBodyState2D::get_space_state() { return body->get_space()->get_direct_state(); } diff --git a/servers/physics_2d/godot_body_direct_state_2d.h b/servers/physics_2d/godot_body_direct_state_2d.h index c01ee62920..545d52ad23 100644 --- a/servers/physics_2d/godot_body_direct_state_2d.h +++ b/servers/physics_2d/godot_body_direct_state_2d.h @@ -92,8 +92,8 @@ public: virtual Vector2 get_contact_collider_position(int p_contact_idx) const override; virtual ObjectID get_contact_collider_id(int p_contact_idx) const override; virtual int get_contact_collider_shape(int p_contact_idx) const override; - virtual Vector2 get_contact_collider_velocity_at_position(int p_contact_idx) const override; + virtual Vector2 get_contact_impulse(int p_contact_idx) const override; virtual PhysicsDirectSpaceState2D *get_space_state() override; diff --git a/servers/physics_2d/godot_body_pair_2d.cpp b/servers/physics_2d/godot_body_pair_2d.cpp index 474367def0..40dbb4fcf4 100644 --- a/servers/physics_2d/godot_body_pair_2d.cpp +++ b/servers/physics_2d/godot_body_pair_2d.cpp @@ -434,21 +434,6 @@ bool GodotBodyPair2D::pre_solve(real_t p_step) { c.rA = global_A - A->get_center_of_mass(); c.rB = global_B - B->get_center_of_mass() - offset_B; - if (A->can_report_contacts()) { - Vector2 crB(-B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x); - A->add_contact(global_A + offset_A, -c.normal, depth, shape_A, global_B + offset_A, shape_B, B->get_instance_id(), B->get_self(), crB + B->get_linear_velocity()); - } - - if (B->can_report_contacts()) { - Vector2 crA(-A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x); - B->add_contact(global_B + offset_A, c.normal, depth, shape_B, global_A + offset_A, shape_A, A->get_instance_id(), A->get_self(), crA + A->get_linear_velocity()); - } - - if (report_contacts_only) { - collided = false; - continue; - } - // Precompute normal mass, tangent mass, and bias. real_t rnA = c.rA.dot(c.normal); real_t rnB = c.rB.dot(c.normal); @@ -466,11 +451,28 @@ bool GodotBodyPair2D::pre_solve(real_t p_step) { c.bias = -bias * inv_dt * MIN(0.0f, -depth + max_penetration); c.depth = depth; + Vector2 P = c.acc_normal_impulse * c.normal + c.acc_tangent_impulse * tangent; + + c.acc_impulse -= P; + + if (A->can_report_contacts()) { + Vector2 crB(-B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x); + A->add_contact(global_A + offset_A, -c.normal, depth, shape_A, global_B + offset_A, shape_B, B->get_instance_id(), B->get_self(), crB + B->get_linear_velocity(), c.acc_impulse); + } + + if (B->can_report_contacts()) { + Vector2 crA(-A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x); + B->add_contact(global_B + offset_A, c.normal, depth, shape_B, global_A + offset_A, shape_A, A->get_instance_id(), A->get_self(), crA + A->get_linear_velocity(), c.acc_impulse); + } + + if (report_contacts_only) { + collided = false; + continue; + } + #ifdef ACCUMULATE_IMPULSES { // Apply normal + friction impulse - Vector2 P = c.acc_normal_impulse * c.normal + c.acc_tangent_impulse * tangent; - if (collide_A) { A->apply_impulse(-P, c.rA + A->get_center_of_mass()); } @@ -581,6 +583,7 @@ void GodotBodyPair2D::solve(real_t p_step) { if (collide_B) { B->apply_impulse(j, c.rB + B->get_center_of_mass()); } + c.acc_impulse -= j; } } diff --git a/servers/physics_2d/godot_body_pair_2d.h b/servers/physics_2d/godot_body_pair_2d.h index 7e7a9839c1..4e9bfa6022 100644 --- a/servers/physics_2d/godot_body_pair_2d.h +++ b/servers/physics_2d/godot_body_pair_2d.h @@ -59,6 +59,7 @@ class GodotBodyPair2D : public GodotConstraint2D { Vector2 position; Vector2 normal; Vector2 local_A, local_B; + Vector2 acc_impulse; // accumulated impulse real_t acc_normal_impulse = 0.0; // accumulated normal impulse (Pn) real_t acc_tangent_impulse = 0.0; // accumulated tangent impulse (Pt) real_t acc_bias_impulse = 0.0; // accumulated normal impulse for position bias (Pnb) diff --git a/servers/physics_3d/godot_collision_solver_3d_sat.cpp b/servers/physics_3d/godot_collision_solver_3d_sat.cpp index 36c47a07b9..d13f4ee801 100644 --- a/servers/physics_3d/godot_collision_solver_3d_sat.cpp +++ b/servers/physics_3d/godot_collision_solver_3d_sat.cpp @@ -758,24 +758,72 @@ public: typedef void (*CollisionFunc)(const GodotShape3D *, const Transform3D &, const GodotShape3D *, const Transform3D &, _CollectorCallback *p_callback, real_t, real_t); +// Perform analytic sphere-sphere collision and report results to collector template <bool withMargin> -static void _collision_sphere_sphere(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { - const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a); - const GodotSphereShape3D *sphere_B = static_cast<const GodotSphereShape3D *>(p_b); +static void analytic_sphere_collision(const Vector3 &p_origin_a, real_t p_radius_a, const Vector3 &p_origin_b, real_t p_radius_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { + // Expand the spheres by the margins if enabled + if (withMargin) { + p_radius_a += p_margin_a; + p_radius_b += p_margin_b; + } - SeparatorAxisTest<GodotSphereShape3D, GodotSphereShape3D, withMargin> separator(sphere_A, p_transform_a, sphere_B, p_transform_b, p_collector, p_margin_a, p_margin_b); + // Get the vector from sphere B to A + Vector3 b_to_a = p_origin_a - p_origin_b; - // previous axis + // Get the length from B to A + real_t b_to_a_len = b_to_a.length(); - if (!separator.test_previous_axis()) { + // Calculate the sphere overlap, and bail if not overlapping + real_t overlap = p_radius_a + p_radius_b - b_to_a_len; + if (overlap < 0) return; - } - if (!separator.test_axis((p_transform_a.origin - p_transform_b.origin).normalized())) { + // Report collision + p_collector->collided = true; + + // Bail if there is no callback to receive the A and B collision points. + if (!p_collector->callback) { return; } - separator.generate_contacts(); + // Normalize the B to A vector + if (b_to_a_len < CMP_EPSILON) { + b_to_a = Vector3(0, 1, 0); // Spheres coincident, use arbitrary direction + } else { + b_to_a /= b_to_a_len; + } + + // Report collision points. The operations below are intended to minimize + // floating-point precision errors. This is done by calculating the first + // collision point from the smaller sphere, and then jumping across to + // the larger spheres collision point using the overlap distance. This + // jump is usually small even if the large sphere is massive, and so the + // second point will not suffer from precision errors. + if (p_radius_a < p_radius_b) { + Vector3 point_a = p_origin_a - b_to_a * p_radius_a; + Vector3 point_b = point_a + b_to_a * overlap; + p_collector->call(point_a, point_b); // Consider adding b_to_a vector + } else { + Vector3 point_b = p_origin_b + b_to_a * p_radius_b; + Vector3 point_a = point_b - b_to_a * overlap; + p_collector->call(point_a, point_b); // Consider adding b_to_a vector + } +} + +template <bool withMargin> +static void _collision_sphere_sphere(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { + const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a); + const GodotSphereShape3D *sphere_B = static_cast<const GodotSphereShape3D *>(p_b); + + // Perform an analytic sphere collision between the two spheres + analytic_sphere_collision<withMargin>( + p_transform_a.origin, + sphere_A->get_radius(), + p_transform_b.origin, + sphere_B->get_radius(), + p_collector, + p_margin_a, + p_margin_b); } template <bool withMargin> @@ -783,50 +831,36 @@ static void _collision_sphere_box(const GodotShape3D *p_a, const Transform3D &p_ const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a); const GodotBoxShape3D *box_B = static_cast<const GodotBoxShape3D *>(p_b); - SeparatorAxisTest<GodotSphereShape3D, GodotBoxShape3D, withMargin> separator(sphere_A, p_transform_a, box_B, p_transform_b, p_collector, p_margin_a, p_margin_b); + // Find the point on the box nearest to the center of the sphere. - if (!separator.test_previous_axis()) { - return; - } + Vector3 center = p_transform_b.xform_inv(p_transform_a.origin); + Vector3 extents = box_B->get_half_extents(); + Vector3 nearest(MIN(MAX(center.x, -extents.x), extents.x), + MIN(MAX(center.y, -extents.y), extents.y), + MIN(MAX(center.z, -extents.z), extents.z)); + nearest = p_transform_b.xform(nearest); - // test faces + // See if it is inside the sphere. - for (int i = 0; i < 3; i++) { - Vector3 axis = p_transform_b.basis.get_column(i).normalized(); - - if (!separator.test_axis(axis)) { - return; - } + Vector3 delta = nearest - p_transform_a.origin; + real_t length = delta.length(); + if (length > sphere_A->get_radius() + p_margin_a + p_margin_b) { + return; } - - // calculate closest point to sphere - - Vector3 cnormal = p_transform_b.xform_inv(p_transform_a.origin); - - Vector3 cpoint = p_transform_b.xform(Vector3( - - (cnormal.x < 0) ? -box_B->get_half_extents().x : box_B->get_half_extents().x, - (cnormal.y < 0) ? -box_B->get_half_extents().y : box_B->get_half_extents().y, - (cnormal.z < 0) ? -box_B->get_half_extents().z : box_B->get_half_extents().z)); - - // use point to test axis - Vector3 point_axis = (p_transform_a.origin - cpoint).normalized(); - - if (!separator.test_axis(point_axis)) { + p_collector->collided = true; + if (!p_collector->callback) { return; } - - // test edges - - for (int i = 0; i < 3; i++) { - Vector3 axis = point_axis.cross(p_transform_b.basis.get_column(i)).cross(p_transform_b.basis.get_column(i)).normalized(); - - if (!separator.test_axis(axis)) { - return; - } + Vector3 axis; + if (length == 0) { + // The box passes through the sphere center. Select an axis based on the box's center. + axis = (p_transform_b.origin - nearest).normalized(); + } else { + axis = delta / length; } - - separator.generate_contacts(); + Vector3 point_a = p_transform_a.origin + (sphere_A->get_radius() + p_margin_a) * axis; + Vector3 point_b = (withMargin ? nearest + p_margin_b * axis : nearest); + p_collector->call(point_a, point_b); } template <bool withMargin> @@ -834,41 +868,66 @@ static void _collision_sphere_capsule(const GodotShape3D *p_a, const Transform3D const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a); const GodotCapsuleShape3D *capsule_B = static_cast<const GodotCapsuleShape3D *>(p_b); - SeparatorAxisTest<GodotSphereShape3D, GodotCapsuleShape3D, withMargin> separator(sphere_A, p_transform_a, capsule_B, p_transform_b, p_collector, p_margin_a, p_margin_b); - - if (!separator.test_previous_axis()) { - return; - } - - //capsule sphere 1, sphere - - Vector3 capsule_axis = p_transform_b.basis.get_column(1) * (capsule_B->get_height() * 0.5 - capsule_B->get_radius()); - - Vector3 capsule_ball_1 = p_transform_b.origin + capsule_axis; + real_t capsule_B_radius = capsule_B->get_radius(); + + // Construct the capsule segment (ball-center to ball-center) + Vector3 capsule_segment[2]; + Vector3 capsule_axis = p_transform_b.basis.get_column(1) * (capsule_B->get_height() * 0.5 - capsule_B_radius); + capsule_segment[0] = p_transform_b.origin + capsule_axis; + capsule_segment[1] = p_transform_b.origin - capsule_axis; + + // Get the capsules closest segment-point to the sphere + Vector3 capsule_closest = Geometry3D::get_closest_point_to_segment(p_transform_a.origin, capsule_segment); + + // Perform an analytic sphere collision between the sphere and the sphere-collider in the capsule + analytic_sphere_collision<withMargin>( + p_transform_a.origin, + sphere_A->get_radius(), + capsule_closest, + capsule_B_radius, + p_collector, + p_margin_a, + p_margin_b); +} - if (!separator.test_axis((capsule_ball_1 - p_transform_a.origin).normalized())) { +template <bool withMargin> +static void analytic_sphere_cylinder_collision(real_t p_radius_a, real_t p_radius_b, real_t p_height_b, const Transform3D &p_transform_a, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { + // Find the point on the cylinder nearest to the center of the sphere. + + Vector3 center = p_transform_b.xform_inv(p_transform_a.origin); + Vector3 nearest = center; + real_t radius = p_radius_b; + real_t r = Math::sqrt(center.x * center.x + center.z * center.z); + if (r > radius) { + real_t scale = radius / r; + nearest.x *= scale; + nearest.z *= scale; + } + real_t half_height = p_height_b / 2; + nearest.y = MIN(MAX(center.y, -half_height), half_height); + nearest = p_transform_b.xform(nearest); + + // See if it is inside the sphere. + + Vector3 delta = nearest - p_transform_a.origin; + real_t length = delta.length(); + if (length > p_radius_a + p_margin_a + p_margin_b) { return; } - - //capsule sphere 2, sphere - - Vector3 capsule_ball_2 = p_transform_b.origin - capsule_axis; - - if (!separator.test_axis((capsule_ball_2 - p_transform_a.origin).normalized())) { + p_collector->collided = true; + if (!p_collector->callback) { return; } - - //capsule edge, sphere - - Vector3 b2a = p_transform_a.origin - p_transform_b.origin; - - Vector3 axis = b2a.cross(capsule_axis).cross(capsule_axis).normalized(); - - if (!separator.test_axis(axis)) { - return; + Vector3 axis; + if (length == 0) { + // The cylinder passes through the sphere center. Select an axis based on the cylinder's center. + axis = (p_transform_b.origin - nearest).normalized(); + } else { + axis = delta / length; } - - separator.generate_contacts(); + Vector3 point_a = p_transform_a.origin + (p_radius_a + p_margin_a) * axis; + Vector3 point_b = (withMargin ? nearest + p_margin_b * axis : nearest); + p_collector->call(point_a, point_b); } template <bool withMargin> @@ -876,58 +935,7 @@ static void _collision_sphere_cylinder(const GodotShape3D *p_a, const Transform3 const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a); const GodotCylinderShape3D *cylinder_B = static_cast<const GodotCylinderShape3D *>(p_b); - SeparatorAxisTest<GodotSphereShape3D, GodotCylinderShape3D, withMargin> separator(sphere_A, p_transform_a, cylinder_B, p_transform_b, p_collector, p_margin_a, p_margin_b); - - if (!separator.test_previous_axis()) { - return; - } - - // Cylinder B end caps. - Vector3 cylinder_B_axis = p_transform_b.basis.get_column(1).normalized(); - if (!separator.test_axis(cylinder_B_axis)) { - return; - } - - Vector3 cylinder_diff = p_transform_b.origin - p_transform_a.origin; - - // Cylinder B lateral surface. - if (!separator.test_axis(cylinder_B_axis.cross(cylinder_diff).cross(cylinder_B_axis).normalized())) { - return; - } - - // Closest point to cylinder caps. - const Vector3 &sphere_center = p_transform_a.origin; - Vector3 cyl_axis = p_transform_b.basis.get_column(1); - Vector3 cap_axis = p_transform_b.basis.get_column(0); - real_t height_scale = cyl_axis.length(); - real_t cap_dist = cylinder_B->get_height() * 0.5 * height_scale; - cyl_axis /= height_scale; - real_t radius_scale = cap_axis.length(); - real_t cap_radius = cylinder_B->get_radius() * radius_scale; - - for (int i = 0; i < 2; i++) { - Vector3 cap_dir = ((i == 0) ? cyl_axis : -cyl_axis); - Vector3 cap_pos = p_transform_b.origin + cap_dir * cap_dist; - - Vector3 closest_point; - - Vector3 diff = sphere_center - cap_pos; - Vector3 proj = diff - cap_dir.dot(diff) * cap_dir; - - real_t proj_len = proj.length(); - if (Math::is_zero_approx(proj_len)) { - // Point is equidistant to all circle points. - continue; - } - - closest_point = cap_pos + (cap_radius / proj_len) * proj; - - if (!separator.test_axis((closest_point - sphere_center).normalized())) { - return; - } - } - - separator.generate_contacts(); + analytic_sphere_cylinder_collision<withMargin>(sphere_A->get_radius(), cylinder_B->get_radius(), cylinder_B->get_height(), p_transform_a, p_transform_b, p_collector, p_margin_a, p_margin_b); } template <bool withMargin> @@ -1615,63 +1623,31 @@ static void _collision_capsule_capsule(const GodotShape3D *p_a, const Transform3 const GodotCapsuleShape3D *capsule_A = static_cast<const GodotCapsuleShape3D *>(p_a); const GodotCapsuleShape3D *capsule_B = static_cast<const GodotCapsuleShape3D *>(p_b); - SeparatorAxisTest<GodotCapsuleShape3D, GodotCapsuleShape3D, withMargin> separator(capsule_A, p_transform_a, capsule_B, p_transform_b, p_collector, p_margin_a, p_margin_b); - - if (!separator.test_previous_axis()) { - return; - } - - // some values - - Vector3 capsule_A_axis = p_transform_a.basis.get_column(1) * (capsule_A->get_height() * 0.5 - capsule_A->get_radius()); - Vector3 capsule_B_axis = p_transform_b.basis.get_column(1) * (capsule_B->get_height() * 0.5 - capsule_B->get_radius()); - - Vector3 capsule_A_ball_1 = p_transform_a.origin + capsule_A_axis; - Vector3 capsule_A_ball_2 = p_transform_a.origin - capsule_A_axis; - Vector3 capsule_B_ball_1 = p_transform_b.origin + capsule_B_axis; - Vector3 capsule_B_ball_2 = p_transform_b.origin - capsule_B_axis; - - //balls-balls - - if (!separator.test_axis((capsule_A_ball_1 - capsule_B_ball_1).normalized())) { - return; - } - if (!separator.test_axis((capsule_A_ball_1 - capsule_B_ball_2).normalized())) { - return; - } - - if (!separator.test_axis((capsule_A_ball_2 - capsule_B_ball_1).normalized())) { - return; - } - if (!separator.test_axis((capsule_A_ball_2 - capsule_B_ball_2).normalized())) { - return; - } - - // edges-balls - - if (!separator.test_axis((capsule_A_ball_1 - capsule_B_ball_1).cross(capsule_A_axis).cross(capsule_A_axis).normalized())) { - return; - } - - if (!separator.test_axis((capsule_A_ball_1 - capsule_B_ball_2).cross(capsule_A_axis).cross(capsule_A_axis).normalized())) { - return; - } - - if (!separator.test_axis((capsule_B_ball_1 - capsule_A_ball_1).cross(capsule_B_axis).cross(capsule_B_axis).normalized())) { - return; - } - - if (!separator.test_axis((capsule_B_ball_1 - capsule_A_ball_2).cross(capsule_B_axis).cross(capsule_B_axis).normalized())) { - return; - } - - // edges - - if (!separator.test_axis(capsule_A_axis.cross(capsule_B_axis).normalized())) { - return; - } - - separator.generate_contacts(); + real_t capsule_A_radius = capsule_A->get_radius(); + real_t capsule_B_radius = capsule_B->get_radius(); + + // Get the closest points between the capsule segments + Vector3 capsule_A_closest; + Vector3 capsule_B_closest; + Vector3 capsule_A_axis = p_transform_a.basis.get_column(1) * (capsule_A->get_height() * 0.5 - capsule_A_radius); + Vector3 capsule_B_axis = p_transform_b.basis.get_column(1) * (capsule_B->get_height() * 0.5 - capsule_B_radius); + Geometry3D::get_closest_points_between_segments( + p_transform_a.origin + capsule_A_axis, + p_transform_a.origin - capsule_A_axis, + p_transform_b.origin + capsule_B_axis, + p_transform_b.origin - capsule_B_axis, + capsule_A_closest, + capsule_B_closest); + + // Perform the analytic collision between the two closest capsule spheres + analytic_sphere_collision<withMargin>( + capsule_A_closest, + capsule_A_radius, + capsule_B_closest, + capsule_B_radius, + p_collector, + p_margin_a, + p_margin_b); } template <bool withMargin> @@ -1679,61 +1655,24 @@ static void _collision_capsule_cylinder(const GodotShape3D *p_a, const Transform const GodotCapsuleShape3D *capsule_A = static_cast<const GodotCapsuleShape3D *>(p_a); const GodotCylinderShape3D *cylinder_B = static_cast<const GodotCylinderShape3D *>(p_b); - SeparatorAxisTest<GodotCapsuleShape3D, GodotCylinderShape3D, withMargin> separator(capsule_A, p_transform_a, cylinder_B, p_transform_b, p_collector, p_margin_a, p_margin_b); - - if (!separator.test_previous_axis()) { - return; - } - - // Cylinder B end caps. - Vector3 cylinder_B_axis = p_transform_b.basis.get_column(1).normalized(); - if (!separator.test_axis(cylinder_B_axis)) { - return; - } - - // Cylinder edge against capsule balls. - - Vector3 capsule_A_axis = p_transform_a.basis.get_column(1); - - Vector3 capsule_A_ball_1 = p_transform_a.origin + capsule_A_axis * (capsule_A->get_height() * 0.5 - capsule_A->get_radius()); - Vector3 capsule_A_ball_2 = p_transform_a.origin - capsule_A_axis * (capsule_A->get_height() * 0.5 - capsule_A->get_radius()); - - if (!separator.test_axis((p_transform_b.origin - capsule_A_ball_1).cross(cylinder_B_axis).cross(cylinder_B_axis).normalized())) { - return; - } - - if (!separator.test_axis((p_transform_b.origin - capsule_A_ball_2).cross(cylinder_B_axis).cross(cylinder_B_axis).normalized())) { - return; - } - - // Cylinder edge against capsule edge. + // Find the closest points between the axes of the two objects. - Vector3 center_diff = p_transform_b.origin - p_transform_a.origin; - - if (!separator.test_axis(capsule_A_axis.cross(center_diff).cross(capsule_A_axis).normalized())) { - return; - } - - if (!separator.test_axis(cylinder_B_axis.cross(center_diff).cross(cylinder_B_axis).normalized())) { - return; - } - - real_t proj = capsule_A_axis.cross(cylinder_B_axis).cross(cylinder_B_axis).dot(capsule_A_axis); - if (Math::is_zero_approx(proj)) { - // Parallel capsule and cylinder axes, handle with specific axes only. - // Note: GJKEPA with no margin can lead to degenerate cases in this situation. - separator.generate_contacts(); - return; - } - - GodotCollisionSolver3D::CallbackResult callback = SeparatorAxisTest<GodotCapsuleShape3D, GodotCylinderShape3D, withMargin>::test_contact_points; - - // Fallback to generic algorithm to find the best separating axis. - if (!fallback_collision_solver(p_a, p_transform_a, p_b, p_transform_b, callback, &separator, false, p_margin_a, p_margin_b)) { - return; - } - - separator.generate_contacts(); + Vector3 capsule_A_closest; + Vector3 cylinder_B_closest; + Vector3 capsule_A_axis = p_transform_a.basis.get_column(1) * (capsule_A->get_height() * 0.5 - capsule_A->get_radius()); + Vector3 cylinder_B_axis = p_transform_b.basis.get_column(1) * (cylinder_B->get_height() * 0.5); + Geometry3D::get_closest_points_between_segments( + p_transform_a.origin + capsule_A_axis, + p_transform_a.origin - capsule_A_axis, + p_transform_b.origin + cylinder_B_axis, + p_transform_b.origin - cylinder_B_axis, + capsule_A_closest, + cylinder_B_closest); + + // Perform the collision test between the cylinder and the nearest sphere on the capsule axis. + + Transform3D sphere_transform(p_transform_a.basis, capsule_A_closest); + analytic_sphere_cylinder_collision<withMargin>(capsule_A->get_radius(), cylinder_B->get_radius(), cylinder_B->get_height(), sphere_transform, p_transform_b, p_collector, p_margin_a, p_margin_b); } template <bool withMargin> diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 4973b9970a..214de27b35 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -126,6 +126,7 @@ void PhysicsDirectBodyState2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_contact_collider_object", "contact_idx"), &PhysicsDirectBodyState2D::get_contact_collider_object); ClassDB::bind_method(D_METHOD("get_contact_collider_shape", "contact_idx"), &PhysicsDirectBodyState2D::get_contact_collider_shape); ClassDB::bind_method(D_METHOD("get_contact_collider_velocity_at_position", "contact_idx"), &PhysicsDirectBodyState2D::get_contact_collider_velocity_at_position); + ClassDB::bind_method(D_METHOD("get_contact_impulse", "contact_idx"), &PhysicsDirectBodyState2D::get_contact_impulse); ClassDB::bind_method(D_METHOD("get_step"), &PhysicsDirectBodyState2D::get_step); ClassDB::bind_method(D_METHOD("integrate_forces"), &PhysicsDirectBodyState2D::integrate_forces); ClassDB::bind_method(D_METHOD("get_space_state"), &PhysicsDirectBodyState2D::get_space_state); diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h index aac4d9d69e..836ab5bd76 100644 --- a/servers/physics_server_2d.h +++ b/servers/physics_server_2d.h @@ -99,6 +99,7 @@ public: virtual Object *get_contact_collider_object(int p_contact_idx) const; virtual int get_contact_collider_shape(int p_contact_idx) const = 0; virtual Vector2 get_contact_collider_velocity_at_position(int p_contact_idx) const = 0; + virtual Vector2 get_contact_impulse(int p_contact_idx) const = 0; virtual real_t get_step() const = 0; virtual void integrate_forces(); diff --git a/servers/rendering/renderer_rd/effects/luminance.cpp b/servers/rendering/renderer_rd/effects/luminance.cpp new file mode 100644 index 0000000000..7462282932 --- /dev/null +++ b/servers/rendering/renderer_rd/effects/luminance.cpp @@ -0,0 +1,255 @@ +/**************************************************************************/ +/* luminance.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "luminance.h" +#include "../framebuffer_cache_rd.h" +#include "../uniform_set_cache_rd.h" +#include "servers/rendering/renderer_rd/storage_rd/material_storage.h" + +using namespace RendererRD; + +Luminance::Luminance(bool p_prefer_raster_effects) { + prefer_raster_effects = p_prefer_raster_effects; + + if (prefer_raster_effects) { + Vector<String> luminance_reduce_modes; + luminance_reduce_modes.push_back("\n#define FIRST_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FIRST + luminance_reduce_modes.push_back("\n"); // LUMINANCE_REDUCE_FRAGMENT + luminance_reduce_modes.push_back("\n#define FINAL_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FINAL + + luminance_reduce_raster.shader.initialize(luminance_reduce_modes); + luminance_reduce_raster.shader_version = luminance_reduce_raster.shader.version_create(); + + for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) { + luminance_reduce_raster.pipelines[i].setup(luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); + } + } else { + // Initialize luminance_reduce + Vector<String> luminance_reduce_modes; + luminance_reduce_modes.push_back("\n#define READ_TEXTURE\n"); + luminance_reduce_modes.push_back("\n"); + luminance_reduce_modes.push_back("\n#define WRITE_LUMINANCE\n"); + + luminance_reduce.shader.initialize(luminance_reduce_modes); + luminance_reduce.shader_version = luminance_reduce.shader.version_create(); + + for (int i = 0; i < LUMINANCE_REDUCE_MAX; i++) { + luminance_reduce.pipelines[i] = RD::get_singleton()->compute_pipeline_create(luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, i)); + } + + for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) { + luminance_reduce_raster.pipelines[i].clear(); + } + } +} + +Luminance::~Luminance() { + if (prefer_raster_effects) { + luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version); + } else { + luminance_reduce.shader.version_free(luminance_reduce.shader_version); + } +} + +void Luminance::LuminanceBuffers::set_prefer_raster_effects(bool p_prefer_raster_effects) { + prefer_raster_effects = p_prefer_raster_effects; +} + +void Luminance::LuminanceBuffers::configure(RenderSceneBuffersRD *p_render_buffers) { + Size2i internal_size = p_render_buffers->get_internal_size(); + int w = internal_size.x; + int h = internal_size.y; + + while (true) { + w = MAX(w / 8, 1); + h = MAX(h / 8, 1); + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R32_SFLOAT; + tf.width = w; + tf.height = h; + + bool final = w == 1 && h == 1; + + if (prefer_raster_effects) { + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + } else { + tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; + if (final) { + tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT; + } + } + + RID texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + reduce.push_back(texture); + + if (final) { + current = RD::get_singleton()->texture_create(tf, RD::TextureView()); + break; + } + } +} + +void Luminance::LuminanceBuffers::free_data() { + for (int i = 0; i < reduce.size(); i++) { + RD::get_singleton()->free(reduce[i]); + } + reduce.clear(); + + if (current.is_valid()) { + RD::get_singleton()->free(current); + current = RID(); + } +} + +Ref<Luminance::LuminanceBuffers> Luminance::get_luminance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers) { + if (p_render_buffers->has_custom_data(RB_LUMINANCE_BUFFERS)) { + return p_render_buffers->get_custom_data(RB_LUMINANCE_BUFFERS); + } + + Ref<LuminanceBuffers> buffers; + buffers.instantiate(); + buffers->set_prefer_raster_effects(prefer_raster_effects); + buffers->configure(p_render_buffers.ptr()); + + p_render_buffers->set_custom_data(RB_LUMINANCE_BUFFERS, buffers); + + return buffers; +} + +RID Luminance::get_current_luminance_buffer(Ref<RenderSceneBuffersRD> p_render_buffers) { + if (p_render_buffers->has_custom_data(RB_LUMINANCE_BUFFERS)) { + Ref<LuminanceBuffers> buffers = p_render_buffers->get_custom_data(RB_LUMINANCE_BUFFERS); + return buffers->current; + } + + return RID(); +} + +void Luminance::luminance_reduction(RID p_source_texture, const Size2i p_source_size, Ref<LuminanceBuffers> p_luminance_buffers, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) { + UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); + ERR_FAIL_NULL(uniform_set_cache); + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + ERR_FAIL_NULL(material_storage); + + // setup our uniforms + RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + + if (prefer_raster_effects) { + LuminanceReduceRasterPushConstant push_constant; + memset(&push_constant, 0, sizeof(LuminanceReduceRasterPushConstant)); + + push_constant.max_luminance = p_max_luminance; + push_constant.min_luminance = p_min_luminance; + push_constant.exposure_adjust = p_adjust; + + for (int i = 0; i < p_luminance_buffers->reduce.size(); i++) { + push_constant.source_size[0] = i == 0 ? p_source_size.x : push_constant.dest_size[0]; + push_constant.source_size[1] = i == 0 ? p_source_size.y : push_constant.dest_size[1]; + push_constant.dest_size[0] = MAX(push_constant.source_size[0] / 8, 1); + push_constant.dest_size[1] = MAX(push_constant.source_size[1] / 8, 1); + + bool final = !p_set && (push_constant.dest_size[0] == 1) && (push_constant.dest_size[1] == 1); + LuminanceReduceRasterMode mode = final ? LUMINANCE_REDUCE_FRAGMENT_FINAL : (i == 0 ? LUMINANCE_REDUCE_FRAGMENT_FIRST : LUMINANCE_REDUCE_FRAGMENT); + RID shader = luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, mode); + + RID framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_luminance_buffers->reduce[i]); + + RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, i == 0 ? p_source_texture : p_luminance_buffers->reduce[i - 1] })); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, luminance_reduce_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0); + if (final) { + RD::Uniform u_current_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_luminance_buffers->current })); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_current_texture), 1); + } + RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); + + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(LuminanceReduceRasterPushConstant)); + + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); + } + } else { + LuminanceReducePushConstant push_constant; + memset(&push_constant, 0, sizeof(LuminanceReducePushConstant)); + + push_constant.source_size[0] = p_source_size.x; + push_constant.source_size[1] = p_source_size.y; + push_constant.max_luminance = p_max_luminance; + push_constant.min_luminance = p_min_luminance; + push_constant.exposure_adjust = p_adjust; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + for (int i = 0; i < p_luminance_buffers->reduce.size(); i++) { + RID shader; + + if (i == 0) { + shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE_READ); + RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_texture })); + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_READ]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0); + } else { + RD::get_singleton()->compute_list_add_barrier(compute_list); //needs barrier, wait until previous is done + + if (i == p_luminance_buffers->reduce.size() - 1 && !p_set) { + shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE_WRITE); + RD::Uniform u_current_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_luminance_buffers->current })); + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_WRITE]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_current_texture), 2); + } else { + shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE]); + } + + RD::Uniform u_source_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_luminance_buffers->reduce[i - 1]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0); + } + + RD::Uniform u_reduce_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_luminance_buffers->reduce[i]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_reduce_texture), 1); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(LuminanceReducePushConstant)); + + RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.source_size[0], push_constant.source_size[1], 1); + + push_constant.source_size[0] = MAX(push_constant.source_size[0] / 8, 1); + push_constant.source_size[1] = MAX(push_constant.source_size[1] / 8, 1); + } + + RD::get_singleton()->compute_list_end(); + } + + SWAP(p_luminance_buffers->current, p_luminance_buffers->reduce.write[p_luminance_buffers->reduce.size() - 1]); +} diff --git a/servers/rendering/renderer_rd/effects/luminance.h b/servers/rendering/renderer_rd/effects/luminance.h new file mode 100644 index 0000000000..0f343fceab --- /dev/null +++ b/servers/rendering/renderer_rd/effects/luminance.h @@ -0,0 +1,120 @@ +/**************************************************************************/ +/* luminance.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef LUMINANCE_RD_H +#define LUMINANCE_RD_H + +#include "servers/rendering/renderer_rd/pipeline_cache_rd.h" +#include "servers/rendering/renderer_rd/shaders/effects/luminance_reduce.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl.gen.h" +#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h" +#include "servers/rendering/renderer_scene_render.h" + +#include "servers/rendering_server.h" + +#define RB_LUMINANCE_BUFFERS SNAME("luminance_buffers") + +namespace RendererRD { + +class Luminance { +private: + bool prefer_raster_effects; + + enum LuminanceReduceMode { + LUMINANCE_REDUCE_READ, + LUMINANCE_REDUCE, + LUMINANCE_REDUCE_WRITE, + LUMINANCE_REDUCE_MAX + }; + + struct LuminanceReducePushConstant { + int32_t source_size[2]; + float max_luminance; + float min_luminance; + float exposure_adjust; + float pad[3]; + }; + + struct LuminanceReduce { + LuminanceReduceShaderRD shader; + RID shader_version; + RID pipelines[LUMINANCE_REDUCE_MAX]; + } luminance_reduce; + + enum LuminanceReduceRasterMode { + LUMINANCE_REDUCE_FRAGMENT_FIRST, + LUMINANCE_REDUCE_FRAGMENT, + LUMINANCE_REDUCE_FRAGMENT_FINAL, + LUMINANCE_REDUCE_FRAGMENT_MAX + }; + + struct LuminanceReduceRasterPushConstant { + int32_t source_size[2]; + int32_t dest_size[2]; + float exposure_adjust; + float min_luminance; + float max_luminance; + uint32_t pad1; + }; + + struct LuminanceReduceFragment { + LuminanceReduceRasterShaderRD shader; + RID shader_version; + PipelineCacheRD pipelines[LUMINANCE_REDUCE_FRAGMENT_MAX]; + } luminance_reduce_raster; + +public: + class LuminanceBuffers : public RenderBufferCustomDataRD { + GDCLASS(LuminanceBuffers, RenderBufferCustomDataRD); + + private: + bool prefer_raster_effects; + + public: + Vector<RID> reduce; + RID current; + + virtual void configure(RenderSceneBuffersRD *p_render_buffers) override; + virtual void free_data() override; + + void set_prefer_raster_effects(bool p_prefer_raster_effects); + }; + + Ref<LuminanceBuffers> get_luminance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers); + RID get_current_luminance_buffer(Ref<RenderSceneBuffersRD> p_render_buffers); + void luminance_reduction(RID p_source_texture, const Size2i p_source_size, Ref<LuminanceBuffers> p_luminance_buffers, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false); + + Luminance(bool p_prefer_raster_effects); + ~Luminance(); +}; + +} // namespace RendererRD + +#endif // LUMINANCE_RD_H diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp index 6d15d5c77b..b7a1396f9c 100644 --- a/servers/rendering/renderer_rd/effects_rd.cpp +++ b/servers/rendering/renderer_rd/effects_rd.cpp @@ -55,36 +55,13 @@ RID EffectsRD::_get_uniform_set_from_image(RID p_image) { u.append_id(p_image); uniforms.push_back(u); //any thing with the same configuration (one texture in binding 0 for set 0), is good - RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, 0), 1); + RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, roughness_limiter.shader.version_get_shader(roughness_limiter.shader_version, 0), 1); image_to_uniform_set_cache[p_image] = uniform_set; return uniform_set; } -RID EffectsRD::_get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps) { - if (texture_to_uniform_set_cache.has(p_texture)) { - RID uniform_set = texture_to_uniform_set_cache[p_texture]; - if (RD::get_singleton()->uniform_set_is_valid(uniform_set)) { - return uniform_set; - } - } - - Vector<RD::Uniform> uniforms; - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; - u.binding = 0; - u.append_id(p_use_mipmaps ? default_mipmap_sampler : default_sampler); - u.append_id(p_texture); - uniforms.push_back(u); - // anything with the same configuration (one texture in binding 0 for set 0), is good - RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, 0), 0); - - texture_to_uniform_set_cache[p_texture] = uniform_set; - - return uniform_set; -} - RID EffectsRD::_get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps) { if (texture_to_compute_uniform_set_cache.has(p_texture)) { RID uniform_set = texture_to_compute_uniform_set_cache[p_texture]; @@ -101,86 +78,13 @@ RID EffectsRD::_get_compute_uniform_set_from_texture(RID p_texture, bool p_use_m u.append_id(p_texture); uniforms.push_back(u); //any thing with the same configuration (one texture in binding 0 for set 0), is good - RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, 0), 0); + RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, roughness_limiter.shader.version_get_shader(roughness_limiter.shader_version, 0), 0); texture_to_compute_uniform_set_cache[p_texture] = uniform_set; return uniform_set; } -void EffectsRD::luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) { - ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of luminance reduction with the mobile renderer."); - - luminance_reduce.push_constant.source_size[0] = p_source_size.x; - luminance_reduce.push_constant.source_size[1] = p_source_size.y; - luminance_reduce.push_constant.max_luminance = p_max_luminance; - luminance_reduce.push_constant.min_luminance = p_min_luminance; - luminance_reduce.push_constant.exposure_adjust = p_adjust; - - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - - for (int i = 0; i < p_reduce.size(); i++) { - if (i == 0) { - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_READ]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_texture), 0); - } else { - RD::get_singleton()->compute_list_add_barrier(compute_list); //needs barrier, wait until previous is done - - if (i == p_reduce.size() - 1 && !p_set) { - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_WRITE]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_prev_luminance), 2); - } else { - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE]); - } - - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_reduce[i - 1]), 0); - } - - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_reduce[i]), 1); - - RD::get_singleton()->compute_list_set_push_constant(compute_list, &luminance_reduce.push_constant, sizeof(LuminanceReducePushConstant)); - - RD::get_singleton()->compute_list_dispatch_threads(compute_list, luminance_reduce.push_constant.source_size[0], luminance_reduce.push_constant.source_size[1], 1); - - luminance_reduce.push_constant.source_size[0] = MAX(luminance_reduce.push_constant.source_size[0] / 8, 1); - luminance_reduce.push_constant.source_size[1] = MAX(luminance_reduce.push_constant.source_size[1] / 8, 1); - } - - RD::get_singleton()->compute_list_end(); -} - -void EffectsRD::luminance_reduction_raster(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, Vector<RID> p_fb, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) { - ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use raster version of luminance reduction with the clustered renderer."); - ERR_FAIL_COND_MSG(p_reduce.size() != p_fb.size(), "Incorrect frame buffer account for luminance reduction."); - - luminance_reduce_raster.push_constant.max_luminance = p_max_luminance; - luminance_reduce_raster.push_constant.min_luminance = p_min_luminance; - luminance_reduce_raster.push_constant.exposure_adjust = p_adjust; - - for (int i = 0; i < p_reduce.size(); i++) { - luminance_reduce_raster.push_constant.source_size[0] = i == 0 ? p_source_size.x : luminance_reduce_raster.push_constant.dest_size[0]; - luminance_reduce_raster.push_constant.source_size[1] = i == 0 ? p_source_size.y : luminance_reduce_raster.push_constant.dest_size[1]; - luminance_reduce_raster.push_constant.dest_size[0] = MAX(luminance_reduce_raster.push_constant.source_size[0] / 8, 1); - luminance_reduce_raster.push_constant.dest_size[1] = MAX(luminance_reduce_raster.push_constant.source_size[1] / 8, 1); - - bool final = !p_set && (luminance_reduce_raster.push_constant.dest_size[0] == 1) && (luminance_reduce_raster.push_constant.dest_size[1] == 1); - LuminanceReduceRasterMode mode = final ? LUMINANCE_REDUCE_FRAGMENT_FINAL : (i == 0 ? LUMINANCE_REDUCE_FRAGMENT_FIRST : LUMINANCE_REDUCE_FRAGMENT); - - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, luminance_reduce_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_fb[i]))); - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(i == 0 ? p_source_texture : p_reduce[i - 1]), 0); - if (final) { - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_prev_luminance), 1); - } - RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); - - RD::get_singleton()->draw_list_set_push_constant(draw_list, &luminance_reduce_raster.push_constant, sizeof(LuminanceReduceRasterPushConstant)); - - RD::get_singleton()->draw_list_draw(draw_list, true); - RD::get_singleton()->draw_list_end(); - } -} - void EffectsRD::roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve) { roughness_limiter.push_constant.screen_size[0] = p_size.x; roughness_limiter.push_constant.screen_size[1] = p_size.y; @@ -270,39 +174,6 @@ void EffectsRD::sort_buffer(RID p_uniform_set, int p_size) { EffectsRD::EffectsRD(bool p_prefer_raster_effects) { prefer_raster_effects = p_prefer_raster_effects; - if (prefer_raster_effects) { - Vector<String> luminance_reduce_modes; - luminance_reduce_modes.push_back("\n#define FIRST_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FIRST - luminance_reduce_modes.push_back("\n"); // LUMINANCE_REDUCE_FRAGMENT - luminance_reduce_modes.push_back("\n#define FINAL_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FINAL - - luminance_reduce_raster.shader.initialize(luminance_reduce_modes); - memset(&luminance_reduce_raster.push_constant, 0, sizeof(LuminanceReduceRasterPushConstant)); - luminance_reduce_raster.shader_version = luminance_reduce_raster.shader.version_create(); - - for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) { - luminance_reduce_raster.pipelines[i].setup(luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); - } - } else { - // Initialize luminance_reduce - Vector<String> luminance_reduce_modes; - luminance_reduce_modes.push_back("\n#define READ_TEXTURE\n"); - luminance_reduce_modes.push_back("\n"); - luminance_reduce_modes.push_back("\n#define WRITE_LUMINANCE\n"); - - luminance_reduce.shader.initialize(luminance_reduce_modes); - - luminance_reduce.shader_version = luminance_reduce.shader.version_create(); - - for (int i = 0; i < LUMINANCE_REDUCE_MAX; i++) { - luminance_reduce.pipelines[i] = RD::get_singleton()->compute_pipeline_create(luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, i)); - } - - for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) { - luminance_reduce_raster.pipelines[i].clear(); - } - } - if (!prefer_raster_effects) { // Initialize roughness limiter Vector<String> shader_modes; @@ -368,11 +239,6 @@ EffectsRD::~EffectsRD() { RD::get_singleton()->free(default_mipmap_sampler); RD::get_singleton()->free(index_buffer); //array gets freed as dependency - if (prefer_raster_effects) { - luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version); - } else { - luminance_reduce.shader.version_free(luminance_reduce.shader_version); - } if (!prefer_raster_effects) { roughness_limiter.shader.version_free(roughness_limiter.shader_version); } diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h index bbe240b241..45198e5fc5 100644 --- a/servers/rendering/renderer_rd/effects_rd.h +++ b/servers/rendering/renderer_rd/effects_rd.h @@ -33,8 +33,6 @@ #include "core/math/projection.h" #include "servers/rendering/renderer_rd/pipeline_cache_rd.h" -#include "servers/rendering/renderer_rd/shaders/luminance_reduce.glsl.gen.h" -#include "servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/roughness_limiter.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/sort.glsl.gen.h" #include "servers/rendering/renderer_scene_render.h" @@ -45,51 +43,6 @@ class EffectsRD { private: bool prefer_raster_effects; - enum LuminanceReduceMode { - LUMINANCE_REDUCE_READ, - LUMINANCE_REDUCE, - LUMINANCE_REDUCE_WRITE, - LUMINANCE_REDUCE_MAX - }; - - struct LuminanceReducePushConstant { - int32_t source_size[2]; - float max_luminance; - float min_luminance; - float exposure_adjust; - float pad[3]; - }; - - struct LuminanceReduce { - LuminanceReducePushConstant push_constant; - LuminanceReduceShaderRD shader; - RID shader_version; - RID pipelines[LUMINANCE_REDUCE_MAX]; - } luminance_reduce; - - enum LuminanceReduceRasterMode { - LUMINANCE_REDUCE_FRAGMENT_FIRST, - LUMINANCE_REDUCE_FRAGMENT, - LUMINANCE_REDUCE_FRAGMENT_FINAL, - LUMINANCE_REDUCE_FRAGMENT_MAX - }; - - struct LuminanceReduceRasterPushConstant { - int32_t source_size[2]; - int32_t dest_size[2]; - float exposure_adjust; - float min_luminance; - float max_luminance; - uint32_t pad1; - }; - - struct LuminanceReduceFragment { - LuminanceReduceRasterPushConstant push_constant; - LuminanceReduceRasterShaderRD shader; - RID shader_version; - PipelineCacheRD pipelines[LUMINANCE_REDUCE_FRAGMENT_MAX]; - } luminance_reduce_raster; - struct RoughnessLimiterPushConstant { int32_t screen_size[2]; float curve; @@ -164,15 +117,11 @@ private: RBMap<TextureSamplerPair, RID> texture_sampler_to_compute_uniform_set_cache; RID _get_uniform_set_from_image(RID p_texture); - RID _get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false); RID _get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false); public: bool get_prefer_raster_effects(); - void luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false); - void luminance_reduction_raster(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, Vector<RID> p_fb, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false); - void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve); void sort_buffer(RID p_uniform_set, int p_size); 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 85b5ef5e09..bd34d83ea3 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -1668,39 +1668,15 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co using_voxelgi = true; } - if (p_render_data->environment.is_null() && using_voxelgi) { - depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI; - } else if (p_render_data->environment.is_valid() && (environment_get_ssr_enabled(p_render_data->environment) || environment_get_sdfgi_enabled(p_render_data->environment) || using_voxelgi)) { + if (p_render_data->environment.is_valid()) { if (environment_get_sdfgi_enabled(p_render_data->environment)) { - depth_pass_mode = using_voxelgi ? PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI : PASS_MODE_DEPTH_NORMAL_ROUGHNESS; // also voxelgi using_sdfgi = true; - } else { - depth_pass_mode = using_voxelgi ? PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI : PASS_MODE_DEPTH_NORMAL_ROUGHNESS; } if (environment_get_ssr_enabled(p_render_data->environment)) { using_separate_specular = true; using_ssr = true; color_pass_flags |= COLOR_PASS_FLAG_SEPARATE_SPECULAR; } - } else if (p_render_data->environment.is_valid() && (environment_get_ssao_enabled(p_render_data->environment) || using_ssil || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER)) { - depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS; - } - - switch (depth_pass_mode) { - case PASS_MODE_DEPTH: { - depth_framebuffer = rb_data->get_depth_fb(); - } break; - case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: { - depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS); - depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); - } break; - case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI: { - depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS_VOXELGI); - depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); - depth_pass_clear.push_back(Color(0, 0, 0, 0)); - } break; - default: { - }; } if (p_render_data->scene_data->view_count > 1) { @@ -1731,6 +1707,38 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_end_label(); + if (rb.is_valid()) { + if (using_voxelgi) { + depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI; + } else if (p_render_data->environment.is_valid()) { + if (environment_get_ssr_enabled(p_render_data->environment) || + environment_get_sdfgi_enabled(p_render_data->environment) || + environment_get_ssao_enabled(p_render_data->environment) || + using_ssil || + get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER || + scene_state.used_normal_texture) { + depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS; + } + } + + switch (depth_pass_mode) { + case PASS_MODE_DEPTH: { + depth_framebuffer = rb_data->get_depth_fb(); + } break; + case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: { + depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS); + depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); + } break; + case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI: { + depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS_VOXELGI); + depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); + depth_pass_clear.push_back(Color(0, 0, 0, 0)); + } break; + default: { + }; + } + } + bool using_sss = rb_data.is_valid() && scene_state.used_sss && ss_effects->sss_get_quality() != RS::SUB_SURFACE_SCATTERING_QUALITY_DISABLED; if (using_sss && !using_separate_specular) { @@ -1914,7 +1922,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, true); bool can_continue_color = !scene_state.used_screen_texture && !using_ssr && !using_sss; - bool can_continue_depth = !scene_state.used_depth_texture && !using_ssr && !using_sss; + bool can_continue_depth = !(scene_state.used_depth_texture || scene_state.used_normal_texture) && !using_ssr && !using_sss; { bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only || debug_voxelgis || debug_sdfgi_probes); @@ -3436,7 +3444,7 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); bool has_read_screen_alpha = p_material->shader_data->uses_screen_texture || p_material->shader_data->uses_depth_texture || p_material->shader_data->uses_normal_texture; - bool has_base_alpha = (p_material->shader_data->uses_alpha && !p_material->shader_data->uses_alpha_clip) || has_read_screen_alpha; + bool has_base_alpha = (p_material->shader_data->uses_alpha && (!p_material->shader_data->uses_alpha_clip || p_material->shader_data->uses_alpha_antialiasing)) || has_read_screen_alpha; bool has_blend_alpha = p_material->shader_data->uses_blend_alpha; bool has_alpha = has_base_alpha || has_blend_alpha; @@ -3465,7 +3473,7 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet if (has_alpha || has_read_screen_alpha || p_material->shader_data->depth_draw == SceneShaderForwardClustered::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardClustered::ShaderData::DEPTH_TEST_DISABLED) { //material is only meant for alpha pass flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA; - if (p_material->shader_data->uses_depth_prepass_alpha && !(p_material->shader_data->depth_draw == SceneShaderForwardClustered::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardClustered::ShaderData::DEPTH_TEST_DISABLED)) { + if ((p_material->shader_data->uses_depth_prepass_alpha || p_material->shader_data->uses_alpha_antialiasing) && !(p_material->shader_data->depth_draw == SceneShaderForwardClustered::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardClustered::ShaderData::DEPTH_TEST_DISABLED)) { flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH; flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW; } @@ -3481,7 +3489,7 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet SceneShaderForwardClustered::MaterialData *material_shadow = nullptr; void *surface_shadow = nullptr; - 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_position && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && p_material->shader_data->cull_mode == SceneShaderForwardClustered::ShaderData::CULL_BACK && !p_material->shader_data->uses_point_size) { + 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_position && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_alpha_antialiasing && p_material->shader_data->cull_mode == SceneShaderForwardClustered::ShaderData::CULL_BACK && !p_material->shader_data->uses_point_size) { flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_SHARED_SHADOW_MATERIAL; material_shadow = static_cast<SceneShaderForwardClustered::MaterialData *>(RendererRD::MaterialStorage::get_singleton()->material_get_data(scene_shader.default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D)); 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 9117320eab..7eabce2f79 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 @@ -60,6 +60,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { uses_point_size = false; uses_alpha = false; uses_alpha_clip = false; + uses_alpha_antialiasing = false; uses_blend_alpha = false; uses_depth_prepass_alpha = false; uses_discard = false; @@ -111,9 +112,9 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["ALPHA"] = &uses_alpha; actions.usage_flag_pointers["ALPHA_SCISSOR_THRESHOLD"] = &uses_alpha_clip; - // Use alpha clip pipeline for alpha hash/dither. - // This prevents sorting issues inherent to alpha blending and allows such materials to cast shadows. actions.usage_flag_pointers["ALPHA_HASH_SCALE"] = &uses_alpha_clip; + actions.usage_flag_pointers["ALPHA_ANTIALIASING_EDGE"] = &uses_alpha_antialiasing; + actions.usage_flag_pointers["ALPHA_TEXTURE_COORDINATE"] = &uses_alpha_antialiasing; actions.render_mode_flags["depth_prepass_alpha"] = &uses_depth_prepass_alpha; actions.usage_flag_pointers["SSS_STRENGTH"] = &uses_sss; @@ -121,7 +122,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["SCREEN_TEXTURE"] = &uses_screen_texture; actions.usage_flag_pointers["DEPTH_TEXTURE"] = &uses_depth_texture; - actions.usage_flag_pointers["NORMAL_TEXTURE"] = &uses_normal_texture; + actions.usage_flag_pointers["NORMAL_ROUGHNESS_TEXTURE"] = &uses_normal_texture; actions.usage_flag_pointers["DISCARD"] = &uses_discard; actions.usage_flag_pointers["TIME"] = &uses_time; actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness; @@ -309,14 +310,6 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { } RD::PipelineDepthStencilState depth_stencil = depth_stencil_state; - if (depth_pre_pass_enabled && casts_shadows() && !uses_depth_prepass_alpha) { - // We already have a depth from the depth pre-pass, there is no need to write it again. - // In addition we can use COMPARE_OP_EQUAL instead of COMPARE_OP_LESS_OR_EQUAL. - // This way we can use the early depth test to discard transparent fragments before the fragment shader even starts. - // This cannot be used with depth_prepass_alpha as it uses a different threshold during the depth-prepass and regular drawing. - depth_stencil.depth_compare_operator = RD::COMPARE_OP_EQUAL; - depth_stencil.enable_depth_write = false; - } RD::PipelineColorBlendState blend_state; RD::PipelineMultisampleState multisample_state; @@ -338,6 +331,14 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { } else { blend_state = blend_state_color_opaque; + if (depth_pre_pass_enabled) { + // We already have a depth from the depth pre-pass, there is no need to write it again. + // In addition we can use COMPARE_OP_EQUAL instead of COMPARE_OP_LESS_OR_EQUAL. + // This way we can use the early depth test to discard transparent fragments before the fragment shader even starts. + depth_stencil.depth_compare_operator = RD::COMPARE_OP_EQUAL; + depth_stencil.enable_depth_write = false; + } + if (l & PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR) { shader_flags |= SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR; } @@ -392,7 +393,7 @@ bool SceneShaderForwardClustered::ShaderData::is_animated() const { bool SceneShaderForwardClustered::ShaderData::casts_shadows() const { bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture; - bool has_base_alpha = (uses_alpha && !uses_alpha_clip) || has_read_screen_alpha; + bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha; bool has_alpha = has_base_alpha || uses_blend_alpha; return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED)); 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 1169ae784c..ffaf091b36 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 @@ -153,6 +153,7 @@ public: bool uses_alpha = false; bool uses_blend_alpha = false; bool uses_alpha_clip = false; + bool uses_alpha_antialiasing = false; bool uses_depth_prepass_alpha = false; bool uses_discard = false; bool uses_roughness = false; diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 72857cdea7..78d29e2a41 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -2349,9 +2349,9 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); bool has_read_screen_alpha = p_material->shader_data->uses_screen_texture || p_material->shader_data->uses_depth_texture || p_material->shader_data->uses_normal_texture; - bool has_base_alpha = ((p_material->shader_data->uses_alpha && !p_material->shader_data->uses_alpha_clip) || has_read_screen_alpha); + bool has_base_alpha = p_material->shader_data->uses_alpha && (!p_material->shader_data->uses_alpha_clip || p_material->shader_data->uses_alpha_antialiasing); bool has_blend_alpha = p_material->shader_data->uses_blend_alpha; - bool has_alpha = has_base_alpha || has_blend_alpha; + bool has_alpha = has_base_alpha || has_blend_alpha || has_read_screen_alpha; uint32_t flags = 0; @@ -2375,10 +2375,10 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_DOUBLE_SIDED_SHADOWS; } - if (has_alpha || has_read_screen_alpha || p_material->shader_data->depth_draw == SceneShaderForwardMobile::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardMobile::ShaderData::DEPTH_TEST_DISABLED) { + if (has_alpha || p_material->shader_data->depth_draw == SceneShaderForwardMobile::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardMobile::ShaderData::DEPTH_TEST_DISABLED) { //material is only meant for alpha pass flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA; - if (p_material->shader_data->uses_depth_prepass_alpha && !(p_material->shader_data->depth_draw == SceneShaderForwardMobile::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardMobile::ShaderData::DEPTH_TEST_DISABLED)) { + if ((p_material->shader_data->uses_depth_prepass_alpha || p_material->shader_data->uses_alpha_antialiasing) && !(p_material->shader_data->depth_draw == SceneShaderForwardMobile::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardMobile::ShaderData::DEPTH_TEST_DISABLED)) { flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH; flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW; } @@ -2394,7 +2394,7 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI SceneShaderForwardMobile::MaterialData *material_shadow = nullptr; void *surface_shadow = nullptr; - 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_prepass_alpha && !p_material->shader_data->uses_alpha_clip) { + 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_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_alpha_antialiasing) { flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_SHARED_SHADOW_MATERIAL; material_shadow = static_cast<SceneShaderForwardMobile::MaterialData *>(RendererRD::MaterialStorage::get_singleton()->material_get_data(scene_shader.default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D)); diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index 2e9a33a636..ee4c8001eb 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -62,6 +62,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { uses_point_size = false; uses_alpha = false; uses_alpha_clip = false; + uses_alpha_antialiasing = false; uses_blend_alpha = false; uses_depth_prepass_alpha = false; uses_discard = false; @@ -112,9 +113,9 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["ALPHA"] = &uses_alpha; actions.usage_flag_pointers["ALPHA_SCISSOR_THRESHOLD"] = &uses_alpha_clip; - // Use alpha clip pipeline for alpha hash/dither. - // This prevents sorting issues inherent to alpha blending and allows such materials to cast shadows. actions.usage_flag_pointers["ALPHA_HASH_SCALE"] = &uses_alpha_clip; + actions.usage_flag_pointers["ALPHA_ANTIALIASING_EDGE"] = &uses_alpha_antialiasing; + actions.usage_flag_pointers["ALPHA_TEXTURE_COORDINATE"] = &uses_alpha_antialiasing; actions.render_mode_flags["depth_prepass_alpha"] = &uses_depth_prepass_alpha; // actions.usage_flag_pointers["SSS_STRENGTH"] = &uses_sss; @@ -122,7 +123,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["SCREEN_TEXTURE"] = &uses_screen_texture; actions.usage_flag_pointers["DEPTH_TEXTURE"] = &uses_depth_texture; - actions.usage_flag_pointers["NORMAL_TEXTURE"] = &uses_normal_texture; + actions.usage_flag_pointers["NORMAL_ROUGHNESS_TEXTURE"] = &uses_normal_texture; actions.usage_flag_pointers["DISCARD"] = &uses_discard; actions.usage_flag_pointers["TIME"] = &uses_time; actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness; @@ -338,7 +339,7 @@ bool SceneShaderForwardMobile::ShaderData::is_animated() const { bool SceneShaderForwardMobile::ShaderData::casts_shadows() const { bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture; - bool has_base_alpha = (uses_alpha && !uses_alpha_clip) || has_read_screen_alpha; + bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha; bool has_alpha = has_base_alpha || uses_blend_alpha; return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED)); diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h index 99f252b9ca..1f92697ecc 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h @@ -114,6 +114,7 @@ public: bool uses_alpha = false; bool uses_blend_alpha = false; bool uses_alpha_clip = false; + bool uses_alpha_antialiasing = false; bool uses_depth_prepass_alpha = false; bool uses_discard = false; bool uses_roughness = false; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index d426c4fc2e..1d45db8eaf 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -249,57 +249,6 @@ Ref<RenderSceneBuffers> RendererSceneRenderRD::render_buffers_create() { return rb; } -void RendererSceneRenderRD::_allocate_luminance_textures(Ref<RenderSceneBuffersRD> rb) { - ERR_FAIL_COND(!rb->luminance.current.is_null()); - - Size2i internal_size = rb->get_internal_size(); - int w = internal_size.x; - int h = internal_size.y; - - while (true) { - w = MAX(w / 8, 1); - h = MAX(h / 8, 1); - - RD::TextureFormat tf; - tf.format = RD::DATA_FORMAT_R32_SFLOAT; - tf.width = w; - tf.height = h; - - bool final = w == 1 && h == 1; - - if (_render_buffers_can_be_storage()) { - tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; - if (final) { - tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT; - } - } else { - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; - } - - RID texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - rb->luminance.reduce.push_back(texture); - if (!_render_buffers_can_be_storage()) { - Vector<RID> fb; - fb.push_back(texture); - - rb->luminance.fb.push_back(RD::get_singleton()->framebuffer_create(fb)); - } - - if (final) { - rb->luminance.current = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - if (!_render_buffers_can_be_storage()) { - Vector<RID> fb; - fb.push_back(rb->luminance.current); - - rb->luminance.current_fb = RD::get_singleton()->framebuffer_create(fb); - } - break; - } - } -} - void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderDataRD *p_render_data) { Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; ERR_FAIL_COND(rb.is_null()); @@ -443,9 +392,9 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende RENDER_TIMESTAMP("Auto exposure"); RD::get_singleton()->draw_command_begin_label("Auto exposure"); - if (rb->luminance.current.is_null()) { - _allocate_luminance_textures(rb); - } + + Ref<RendererRD::Luminance::LuminanceBuffers> luminance_buffers = luminance->get_luminance_buffers(rb); + uint64_t auto_exposure_version = RSG::camera_attributes->camera_attributes_get_auto_exposure_version(p_render_data->camera_attributes); bool set_immediate = auto_exposure_version != rb->get_auto_exposure_version(); rb->set_auto_exposure_version(auto_exposure_version); @@ -453,16 +402,9 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende double step = RSG::camera_attributes->camera_attributes_get_auto_exposure_adjust_speed(p_render_data->camera_attributes) * time_step; float auto_exposure_min_sensitivity = RSG::camera_attributes->camera_attributes_get_auto_exposure_min_sensitivity(p_render_data->camera_attributes); float auto_exposure_max_sensitivity = RSG::camera_attributes->camera_attributes_get_auto_exposure_max_sensitivity(p_render_data->camera_attributes); - if (can_use_storage) { - RendererCompositorRD::singleton->get_effects()->luminance_reduction(internal_texture, internal_size, rb->luminance.reduce, rb->luminance.current, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate); - } else { - RendererCompositorRD::singleton->get_effects()->luminance_reduction_raster(internal_texture, internal_size, rb->luminance.reduce, rb->luminance.fb, rb->luminance.current, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate); - } + luminance->luminance_reduction(internal_texture, internal_size, luminance_buffers, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate); + // Swap final reduce with prev luminance. - SWAP(rb->luminance.current, rb->luminance.reduce.write[rb->luminance.reduce.size() - 1]); - if (!can_use_storage) { - SWAP(rb->luminance.current_fb, rb->luminance.fb.write[rb->luminance.fb.size() - 1]); - } auto_exposure_scale = RSG::camera_attributes->camera_attributes_get_auto_exposure_scale(p_render_data->camera_attributes); @@ -496,8 +438,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende if (i == 0) { RID luminance_texture; - if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && rb->luminance.current.is_valid()) { - luminance_texture = rb->luminance.current; + if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes)) { + luminance_texture = luminance->get_current_luminance_buffer(rb); // this will return and empty RID if we don't have an auto exposure buffer } RID source = rb->get_internal_texture(l); RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i); @@ -530,9 +472,9 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende RendererRD::ToneMapper::TonemapSettings tonemap; - if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && rb->luminance.current.is_valid()) { + tonemap.exposure_texture = luminance->get_current_luminance_buffer(rb); + if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && tonemap.exposure_texture.is_valid()) { tonemap.use_auto_exposure = true; - tonemap.exposure_texture = rb->luminance.current; tonemap.auto_exposure_scale = auto_exposure_scale; } else { tonemap.exposure_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE); @@ -746,10 +688,11 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD> } if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE) { - if (p_render_buffers->luminance.current.is_valid()) { + RID luminance_texture = luminance->get_current_luminance_buffer(p_render_buffers); + if (luminance_texture.is_valid()) { Size2i rtsize = texture_storage->render_target_get_size(render_target); - copy_effects->copy_to_fb_rect(p_render_buffers->luminance.current, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize / 8), false, true); + copy_effects->copy_to_fb_rect(luminance_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize / 8), false, true); } } @@ -1334,6 +1277,7 @@ void RendererSceneRenderRD::init() { bool can_use_vrs = is_vrs_supported(); bokeh_dof = memnew(RendererRD::BokehDOF(!can_use_storage)); copy_effects = memnew(RendererRD::CopyEffects(!can_use_storage)); + luminance = memnew(RendererRD::Luminance(!can_use_storage)); tone_mapper = memnew(RendererRD::ToneMapper); if (can_use_vrs) { vrs = memnew(RendererRD::VRS); @@ -1354,6 +1298,9 @@ RendererSceneRenderRD::~RendererSceneRenderRD() { if (copy_effects) { memdelete(copy_effects); } + if (luminance) { + memdelete(luminance); + } if (tone_mapper) { memdelete(tone_mapper); } diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 54f068c314..6fa2f7a570 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -38,6 +38,7 @@ #include "servers/rendering/renderer_rd/effects/bokeh_dof.h" #include "servers/rendering/renderer_rd/effects/copy_effects.h" #include "servers/rendering/renderer_rd/effects/fsr.h" +#include "servers/rendering/renderer_rd/effects/luminance.h" #include "servers/rendering/renderer_rd/effects/tone_mapper.h" #include "servers/rendering/renderer_rd/effects/vrs.h" #include "servers/rendering/renderer_rd/environment/fog.h" @@ -105,6 +106,7 @@ protected: RendererRD::ForwardIDStorage *forward_id_storage = nullptr; RendererRD::BokehDOF *bokeh_dof = nullptr; RendererRD::CopyEffects *copy_effects = nullptr; + RendererRD::Luminance *luminance = nullptr; RendererRD::ToneMapper *tone_mapper = nullptr; RendererRD::FSR *fsr = nullptr; RendererRD::VRS *vrs = nullptr; @@ -180,9 +182,6 @@ private: /* RENDER BUFFERS */ - // TODO move into effects/luminance.h/cpp - void _allocate_luminance_textures(Ref<RenderSceneBuffersRD> rb); - /* GI */ bool screen_space_roughness_limiter = false; float screen_space_roughness_limiter_amount = 0.25; diff --git a/servers/rendering/renderer_rd/shaders/luminance_reduce.glsl b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce.glsl index 0ee4cf6e31..0ee4cf6e31 100644 --- a/servers/rendering/renderer_rd/shaders/luminance_reduce.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce.glsl diff --git a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl index 29ebd74a90..29ebd74a90 100644 --- a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl diff --git a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster_inc.glsl index b8860f6518..b8860f6518 100644 --- a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster_inc.glsl diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index be53a7ae49..d32e6d717f 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -118,9 +118,15 @@ layout(location = 10) out flat uint instance_index_interp; // !BAS! This needs to become an input once we implement our fallback! #define ViewIndex 0 #endif // has_VK_KHR_multiview +vec3 normal_roughness_uv(vec2 uv) { + return vec3(uv, ViewIndex); +} #else // USE_MULTIVIEW // Set to zero, not supported in non stereo #define ViewIndex 0 +vec2 normal_roughness_uv(vec2 uv) { + return uv; +} #endif //USE_MULTIVIEW invariant gl_Position; @@ -544,9 +550,15 @@ layout(location = 10) in flat uint instance_index_interp; // !BAS! This needs to become an input once we implement our fallback! #define ViewIndex 0 #endif // has_VK_KHR_multiview +vec3 normal_roughness_uv(vec2 uv) { + return vec3(uv, ViewIndex); +} #else // USE_MULTIVIEW // Set to zero, not supported in non stereo #define ViewIndex 0 +vec2 normal_roughness_uv(vec2 uv) { + return uv; +} #endif //USE_MULTIVIEW //defines to keep compatibility with vertex diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl index 3a45ab0059..1f524313f2 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl @@ -268,6 +268,7 @@ layout(r32ui, set = 1, binding = 13) uniform restrict uimage3D geom_facing_grid; #define color_buffer shadow_atlas #define normal_roughness_buffer shadow_atlas +#define multiviewSampler sampler2D #else layout(set = 1, binding = 10) uniform texture2D depth_buffer; @@ -277,10 +278,12 @@ layout(set = 1, binding = 11) uniform texture2D color_buffer; layout(set = 1, binding = 12) uniform texture2DArray normal_roughness_buffer; layout(set = 1, binding = 14) uniform texture2DArray ambient_buffer; layout(set = 1, binding = 15) uniform texture2DArray reflection_buffer; +#define multiviewSampler sampler2DArray #else // USE_MULTIVIEW layout(set = 1, binding = 12) uniform texture2D normal_roughness_buffer; layout(set = 1, binding = 14) uniform texture2D ambient_buffer; layout(set = 1, binding = 15) uniform texture2D reflection_buffer; +#define multiviewSampler sampler2D #endif layout(set = 1, binding = 13) uniform texture2D ao_buffer; layout(set = 1, binding = 16) uniform texture2DArray sdfgi_lightprobe_texture; diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index a953bac433..d67a848a40 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -94,28 +94,6 @@ void RenderSceneBuffersRD::cleanup() { free_named_texture(E.value); } named_textures.clear(); - - // old stuff, to be re-evaluated... - - for (int i = 0; i < luminance.fb.size(); i++) { - RD::get_singleton()->free(luminance.fb[i]); - } - luminance.fb.clear(); - - for (int i = 0; i < luminance.reduce.size(); i++) { - RD::get_singleton()->free(luminance.reduce[i]); - } - luminance.reduce.clear(); - - if (luminance.current_fb.is_valid()) { - RD::get_singleton()->free(luminance.current_fb); - luminance.current_fb = RID(); - } - - if (luminance.current.is_valid()) { - RD::get_singleton()->free(luminance.current); - luminance.current = RID(); - } } void RenderSceneBuffersRD::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h index 1bd542500c..ff946f410f 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h @@ -213,15 +213,6 @@ public: // 2 full size, 2 half size WeightBuffers weight_buffers[4]; // Only used in raster - - struct Luminance { - Vector<RID> reduce; - RID current; - - // used only on mobile renderer - Vector<RID> fb; - RID current_fb; - } luminance; }; #endif // RENDER_SCENE_BUFFERS_RD_H diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 4c4b3d13f9..6017eff55e 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -291,6 +291,10 @@ void RenderingServerDefault::set_boot_image(const Ref<Image> &p_image, const Col RSG::rasterizer->set_boot_image(p_image, p_color, p_scale, p_use_filter); } +Color RenderingServerDefault::get_default_clear_color() { + return RSG::texture_storage->get_default_clear_color(); +} + void RenderingServerDefault::set_default_clear_color(const Color &p_color) { RSG::viewport->set_default_clear_color(p_color); } diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index f2fadd5e1f..d053c60b85 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -986,6 +986,7 @@ public: virtual double get_frame_setup_time_cpu() const override; virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) override; + virtual Color get_default_clear_color() override; virtual void set_default_clear_color(const Color &p_color) override; virtual bool has_feature(Features p_feature) const override; diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 57215d9d63..626da90168 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -1183,6 +1183,10 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene code += "("; + // if normal roughness texture is used, we will add logic to automatically switch between + // sampler2D and sampler2D array and vec2 UV and vec3 UV. + bool normal_roughness_texture_used = false; + for (int i = 1; i < onode->arguments.size(); i++) { if (i > 1) { code += ", "; @@ -1282,11 +1286,24 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene } } - code += ShaderLanguage::get_datatype_name(onode->arguments[i]->get_datatype()) + "(" + node_code + ", " + sampler_name + ")"; + String data_type_name = ""; + if (texture_uniform == "NORMAL_ROUGHNESS_TEXTURE") { + data_type_name = "multiviewSampler"; + normal_roughness_texture_used = true; + } else { + data_type_name = ShaderLanguage::get_datatype_name(onode->arguments[i]->get_datatype()); + } + + code += data_type_name + "(" + node_code + ", " + sampler_name + ")"; } else { code += node_code; } } else { + if (normal_roughness_texture_used && i == 2) { + // UV coordinate after using normal roughness texture. + node_code = "normal_roughness_uv(" + node_code + ".xy)"; + } + code += node_code; } } diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 658d683398..14d8d035e2 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2763,6 +2763,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_white_texture"), &RenderingServer::get_white_texture); ClassDB::bind_method(D_METHOD("set_boot_image", "image", "color", "scale", "use_filter"), &RenderingServer::set_boot_image, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_default_clear_color"), &RenderingServer::get_default_clear_color); ClassDB::bind_method(D_METHOD("set_default_clear_color", "color"), &RenderingServer::set_default_clear_color); ClassDB::bind_method(D_METHOD("has_feature", "feature"), &RenderingServer::has_feature); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 88144cfeeb..6efe28a847 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1560,6 +1560,7 @@ public: virtual void mesh_add_surface_from_planes(RID p_mesh, const Vector<Plane> &p_planes); virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) = 0; + virtual Color get_default_clear_color() = 0; virtual void set_default_clear_color(const Color &p_color) = 0; enum Features { |