Skip to content

Improve render sorting for alpha pass #106593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core/templates/cowdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ static_assert(std::is_trivially_destructible_v<std::atomic<uint64_t>>);
GODOT_GCC_WARNING_PUSH
GODOT_GCC_WARNING_IGNORE("-Wplacement-new") // Silence a false positive warning (see GH-52119).
GODOT_GCC_WARNING_IGNORE("-Wmaybe-uninitialized") // False positive raised when using constexpr.
GODOT_MSVC_WARNING_PUSH_AND_IGNORE(4724) // False positive potential mod by 0 (added in GH-106593)

template <typename T>
class CowData {
Expand Down Expand Up @@ -440,6 +441,7 @@ CowData<T>::CowData(std::initializer_list<T> p_init) {
}
}

GODOT_MSVC_WARNING_POP
GODOT_GCC_WARNING_POP

// Zero-constructing CowData initializes _ptr to nullptr (and thus empty).
Expand Down
1 change: 1 addition & 0 deletions doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1985,6 +1985,7 @@
<param index="0" name="instance" type="RID" />
<param index="1" name="sorting_offset" type="float" />
<param index="2" name="use_aabb_center" type="bool" />
<param index="3" name="sorting_stacked_order" type="int" default="0" />
<description>
Sets the sorting offset and switches between using the bounding box or instance origin for depth sorting.
</description>
Expand Down
5 changes: 5 additions & 0 deletions doc/classes/VisualInstance3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
</member>
<member name="sorting_offset" type="float" setter="set_sorting_offset" getter="get_sorting_offset" default="0.0">
The amount by which the depth of this [VisualInstance3D] will be adjusted when sorting by depth. Uses the same units as the engine (which are typically meters). Adjusting it to a higher value will make the [VisualInstance3D] reliably draw on top of other [VisualInstance3D]s that are otherwise positioned at the same spot. To ensure it always draws on top of other objects around it (not positioned at the same spot), set the value to be greater than the distance between this [VisualInstance3D] and the other nearby [VisualInstance3D]s.
[b]Note:[/b] This is only effective on instances with transparent (alpha-blended) materials. Opaque or alpha scissor/hash sorting is not affected by [member sorting_offset], so it cannot resolve Z-fighting issues.
</member>
<member name="sorting_stacked_order" type="int" setter="set_sorting_stacked_order" getter="get_sorting_stacked_order">
How this [VisualInstance3D] will be adjusted when sorting depth is the same as the other [VisualInstance3D]. Adjusting it to a higher value will make the [VisualInstance3D] reliably draw on top of other [VisualInstance3D]s that are otherwise positioned at the exact same depth (relative to the camera). This is different from [member sorting_offset], which affects sorting even for [VisualInstance3D]s that are not at the exact same depth.
[b]Note:[/b] This is only effective on instances with transparent (alpha-blended) materials. Opaque or alpha scissor/hash sorting is not affected by [member sorting_stacked_order], so it cannot resolve Z-fighting issues.
</member>
<member name="sorting_use_aabb_center" type="bool" setter="set_sorting_use_aabb_center" getter="is_sorting_use_aabb_center">
If [code]true[/code], the object is sorted based on the [AABB] center. The object will be sorted based on the global position otherwise.
Expand Down
29 changes: 22 additions & 7 deletions drivers/gles3/rasterizer_scene_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ void RasterizerSceneGLES3::_geometry_instance_dependency_deleted(const RID &p_de
static_cast<GeometryInstanceGLES3 *>(p_tracker->userdata)->data->dirty_dependencies = true;
}

void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh) {
void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, uint32_t p_material_id, uint8_t p_material_depth, uint32_t p_shader_id, RID p_mesh) {
GLES3::MeshStorage *mesh_storage = GLES3::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;
Expand Down Expand Up @@ -281,14 +281,19 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry

sdcache->sort.sort_key1 = 0;
sdcache->sort.sort_key2 = 0;
sdcache->transparent_sort.sort_key = 0;

sdcache->sort.surface_index = p_surface;
sdcache->transparent_sort.surface_index = sdcache->sort.surface_index = p_surface;
sdcache->transparent_sort.material_id = p_material_id;
sdcache->sort.material_id_low = p_material_id & 0x0000FFFF;
sdcache->sort.material_id_hi = p_material_id >> 16;
sdcache->sort.shader_id = p_shader_id;
sdcache->sort.geometry_id = p_mesh.get_local_index();
sdcache->transparent_sort.shader_id = sdcache->sort.shader_id = p_shader_id;
sdcache->transparent_sort.geometry_id = sdcache->sort.geometry_id = p_mesh.get_local_index();
sdcache->sort.priority = p_material->priority;

sdcache->transparent_sort.material_depth = p_material_depth;
sdcache->transparent_sort.stacked_order = static_cast<uint16_t>(ginstance->sorting_stacked_order) ^ (1 << 15); //Inverts sign bit

GLES3::Mesh::Surface *s = reinterpret_cast<GLES3::Mesh::Surface *>(sdcache->surface);
if (p_material->shader_data->uses_tangent && !(s->format & RS::ARRAY_FORMAT_TANGENT)) {
String shader_path = p_material->shader_data->path.is_empty() ? "" : "(" + p_material->shader_data->path + ")";
Expand All @@ -301,9 +306,11 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material_chain(Ge
GLES3::SceneMaterialData *material_data = p_material_data;
GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();

_geometry_instance_add_surface_with_material(ginstance, p_surface, material_data, p_mat_src.get_local_index(), material_storage->material_get_shader_id(p_mat_src), p_mesh);
uint8_t material_depth = 0;
_geometry_instance_add_surface_with_material(ginstance, p_surface, material_data, p_mat_src.get_local_index(), material_depth, material_storage->material_get_shader_id(p_mat_src), p_mesh);

while (material_data->next_pass.is_valid()) {
material_depth++;
RID next_pass = material_data->next_pass;
material_data = static_cast<GLES3::SceneMaterialData *>(material_storage->material_get_data(next_pass, RS::SHADER_SPATIAL));
if (!material_data || !material_data->shader_data->valid) {
Expand All @@ -312,7 +319,7 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material_chain(Ge
if (ginstance->data->dirty_dependencies) {
material_storage->material_update_dependency(next_pass, &ginstance->data->dependency_tracker);
}
_geometry_instance_add_surface_with_material(ginstance, p_surface, material_data, next_pass.get_local_index(), material_storage->material_get_shader_id(next_pass), p_mesh);
_geometry_instance_add_surface_with_material(ginstance, p_surface, material_data, next_pass.get_local_index(), material_depth, material_storage->material_get_shader_id(next_pass), p_mesh);
}
}

Expand Down Expand Up @@ -2382,7 +2389,15 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_

_fill_render_list(RENDER_LIST_OPAQUE, &render_data, PASS_MODE_COLOR);
render_list[RENDER_LIST_OPAQUE].sort_by_key();
render_list[RENDER_LIST_ALPHA].sort_by_reverse_depth_and_priority();

// sorts in the order of:
// 1. material priority
// 2. object depth
// 3. stacked sorting offset
// 4. surface index
// 5. material depth used for next-pass sorting
// 6. object ids used for constant buffer sorting
render_list[RENDER_LIST_ALPHA].sort_by_standard_scene_sort();

bool draw_sky = false;
bool draw_sky_fog_only = false;
Expand Down
43 changes: 37 additions & 6 deletions drivers/gles3/rasterizer_scene_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,22 @@ class RasterizerSceneGLES3 : public RendererSceneRender {
};
} sort;

union {
struct {
uint64_t sort_key;
};
struct {
//Ids dont need to be accurate just used to prevent inconsistency between frames
uint64_t shader_id : 10;
uint64_t material_id : 11;
uint64_t geometry_id : 11;

uint64_t material_depth : 8;
uint64_t surface_index : 8;
uint64_t stacked_order : 16;
};
} transparent_sort;

RS::PrimitiveType primitive = RS::PRIMITIVE_MAX;
uint32_t flags = 0;
uint32_t surface_index = 0;
Expand Down Expand Up @@ -373,7 +389,7 @@ class RasterizerSceneGLES3 : public RendererSceneRender {
PagedAllocator<GeometryInstanceGLES3> geometry_instance_alloc;
PagedAllocator<GeometryInstanceSurface> geometry_instance_surface_alloc;

void _geometry_instance_add_surface_with_material(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh);
void _geometry_instance_add_surface_with_material(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, uint32_t p_material_id, uint8_t p_material_depth, uint32_t p_shader_id, RID p_mesh);
void _geometry_instance_add_surface_with_material_chain(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, RID p_mat_src, RID p_mesh);
void _geometry_instance_add_surface(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, RID p_material, RID p_mesh);
void _geometry_instance_update(RenderGeometryInstance *p_geometry_instance);
Expand Down Expand Up @@ -620,15 +636,30 @@ class RasterizerSceneGLES3 : public RendererSceneRender {
sorter.sort(elements.ptr(), elements.size());
}

struct SortByReverseDepthAndPriority {
struct StandardSceneSort {
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const {
return (A->sort.priority == B->sort.priority) ? (A->owner->depth > B->owner->depth) : (A->sort.priority < B->sort.priority);
if (A->sort.priority == B->sort.priority) {
if (Math::is_equal_approx(A->owner->depth, B->owner->depth)) { //could maybe just be strict equality
return A->transparent_sort.sort_key < B->transparent_sort.sort_key;
}
return (A->owner->depth > B->owner->depth);
}
return A->sort.priority < B->sort.priority;
}
};

void sort_by_reverse_depth_and_priority() { //used for alpha

SortArray<GeometryInstanceSurface *, SortByReverseDepthAndPriority> sorter;
void sort_by_standard_scene_sort() {
// used for standard scene sorting also known as the alpha sort
// this is used for sorting transparent objects
//
// sorts in the order of:
// 1. material priority
// 2. object depth
// 3. stacked sorting offset
// 4. surface index
// 5. material depth used for next-pass sorting
// 6. object ids used for constant buffer sorting
SortArray<GeometryInstanceSurface *, StandardSceneSort> sorter;
sorter.sort(elements.ptr(), elements.size());
}

Expand Down
11 changes: 8 additions & 3 deletions misc/extension_api_validation/4.4-stable.expected
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ Validate extension JSON: Error: Field 'classes/EditorUndoRedoManager/methods/cre

New argument added. Compatibility method registered.


GH-98750
--------
Validate extension JSON: Error: Field 'builtin_classes/Color/constants/ALICE_BLUE': value changed value in new API, from "Color(0.941176, 0.972549, 1, 1)" to "Color(0.9411765, 0.972549, 1, 1)".
Expand Down Expand Up @@ -260,10 +259,16 @@ Validate extension JSON: API was removed: classes/Node/methods/get_rpc_config

Change Node `get_rpc_config` to `get_node_rpc_config`. Compatibility method registered.


GH-106300
---------
Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/RichTextLabel/methods/push_strikethrough': arguments
Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/RichTextLabel/methods/push_underline': arguments

Optional "color" argument added. Compatibility methods registered.
Optional "color" argument added. Compatibility methods registered.=======

GH-106593
--------

Validate extension JSON: Error: Field 'classes/RenderingServer/methods/instance_set_pivot_data/arguments': size changed value in new API, from 3 to 4.

Added a optional parameter with default value and Compatibility method was registered.
20 changes: 16 additions & 4 deletions scene/3d/visual_instance_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,24 +149,33 @@ bool VisualInstance3D::get_layer_mask_value(int p_layer_number) const {

void VisualInstance3D::set_sorting_offset(float p_offset) {
sorting_offset = p_offset;
RenderingServer::get_singleton()->instance_set_pivot_data(instance, sorting_offset, sorting_use_aabb_center);
RenderingServer::get_singleton()->instance_set_pivot_data(instance, sorting_offset, sorting_use_aabb_center, sorting_stacked_order);
}

float VisualInstance3D::get_sorting_offset() const {
return sorting_offset;
}

void VisualInstance3D::set_sorting_stacked_order(int16_t p_order) {
sorting_stacked_order = p_order;
RenderingServer::get_singleton()->instance_set_pivot_data(instance, sorting_offset, sorting_use_aabb_center, sorting_stacked_order);
}

int16_t VisualInstance3D::get_sorting_stacked_order() const {
return sorting_stacked_order;
}

void VisualInstance3D::set_sorting_use_aabb_center(bool p_enabled) {
sorting_use_aabb_center = p_enabled;
RenderingServer::get_singleton()->instance_set_pivot_data(instance, sorting_offset, sorting_use_aabb_center);
RenderingServer::get_singleton()->instance_set_pivot_data(instance, sorting_offset, sorting_use_aabb_center, sorting_stacked_order);
}

bool VisualInstance3D::is_sorting_use_aabb_center() const {
return sorting_use_aabb_center;
}

void VisualInstance3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "sorting_offset" || p_property.name == "sorting_use_aabb_center") {
if (p_property.name == "sorting_offset" || p_property.name == "sorting_use_aabb_center" || p_property.name == "sorting_stacked_order") {
p_property.usage = PROPERTY_USAGE_NONE;
}
}
Expand All @@ -181,6 +190,8 @@ void VisualInstance3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_layer_mask_value", "layer_number"), &VisualInstance3D::get_layer_mask_value);
ClassDB::bind_method(D_METHOD("set_sorting_offset", "offset"), &VisualInstance3D::set_sorting_offset);
ClassDB::bind_method(D_METHOD("get_sorting_offset"), &VisualInstance3D::get_sorting_offset);
ClassDB::bind_method(D_METHOD("set_sorting_stacked_order", "offset"), &VisualInstance3D::set_sorting_stacked_order);
ClassDB::bind_method(D_METHOD("get_sorting_stacked_order"), &VisualInstance3D::get_sorting_stacked_order);
ClassDB::bind_method(D_METHOD("set_sorting_use_aabb_center", "enabled"), &VisualInstance3D::set_sorting_use_aabb_center);
ClassDB::bind_method(D_METHOD("is_sorting_use_aabb_center"), &VisualInstance3D::is_sorting_use_aabb_center);

Expand All @@ -190,6 +201,7 @@ void VisualInstance3D::_bind_methods() {
ADD_GROUP("Sorting", "sorting_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sorting_offset"), "set_sorting_offset", "get_sorting_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sorting_use_aabb_center"), "set_sorting_use_aabb_center", "is_sorting_use_aabb_center");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sorting_stacked_order"), "set_sorting_stacked_order", "get_sorting_stacked_order");
}

void VisualInstance3D::set_base(const RID &p_base) {
Expand Down Expand Up @@ -537,7 +549,7 @@ PackedStringArray GeometryInstance3D::get_configuration_warnings() const {
}

void GeometryInstance3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "sorting_offset" || p_property.name == "sorting_use_aabb_center") {
if (p_property.name == "sorting_offset" || p_property.name == "sorting_use_aabb_center" || p_property.name == "sorting_stacked_order") {
p_property.usage = PROPERTY_USAGE_DEFAULT;
}
}
Expand Down
4 changes: 4 additions & 0 deletions scene/3d/visual_instance_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class VisualInstance3D : public Node3D {
uint32_t layers = 1;
float sorting_offset = 0.0;
bool sorting_use_aabb_center = true;
int16_t sorting_stacked_order = 0;

protected:
void _update_visibility();
Expand Down Expand Up @@ -75,6 +76,9 @@ class VisualInstance3D : public Node3D {
void set_sorting_offset(float p_offset);
float get_sorting_offset() const;

void set_sorting_stacked_order(int16_t p_order);
int16_t get_sorting_stacked_order() const;

void set_sorting_use_aabb_center(bool p_enabled);
bool is_sorting_use_aabb_center() const;

Expand Down
2 changes: 1 addition & 1 deletion servers/rendering/dummy/rasterizer_scene_dummy.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class RasterizerSceneDummy : public RendererSceneRender {
virtual void set_mesh_instance(RID p_mesh_instance) override {}
virtual void set_transform(const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabb) override {}
virtual void reset_motion_vectors() override {}
virtual void set_pivot_data(float p_sorting_offset, bool p_use_aabb_center) override {}
virtual void set_pivot_data(float p_sorting_offset, bool p_use_aabb_center, int16_t p_sorting_stacked_order = 0) override {}
virtual void set_lod_bias(float p_lod_bias) override {}
virtual void set_layer_mask(uint32_t p_layer_mask) override {}
virtual void set_fade_range(bool p_enable_near, float p_near_begin, float p_near_end, bool p_enable_far, float p_far_begin, float p_far_end) override {}
Expand Down
6 changes: 5 additions & 1 deletion servers/rendering/renderer_geometry_instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,13 @@ void RenderGeometryInstanceBase::set_transform(const Transform3D &p_transform, c
lod_model_scale = max_scale;
}

void RenderGeometryInstanceBase::set_pivot_data(float p_sorting_offset, bool p_use_aabb_center) {
void RenderGeometryInstanceBase::set_pivot_data(float p_sorting_offset, bool p_use_aabb_center, int16_t p_sorting_stacked_order) {
sorting_offset = p_sorting_offset;
use_aabb_center = p_use_aabb_center;
if (sorting_stacked_order != p_sorting_stacked_order) {
sorting_stacked_order = p_sorting_stacked_order;
_mark_dirty();
}
}

void RenderGeometryInstanceBase::set_lod_bias(float p_lod_bias) {
Expand Down
6 changes: 4 additions & 2 deletions servers/rendering/renderer_geometry_instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class RenderGeometryInstance {
virtual void set_surface_materials(const Vector<RID> &p_materials) = 0;
virtual void set_mesh_instance(RID p_mesh_instance) = 0;
virtual void set_transform(const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabb) = 0;
virtual void set_pivot_data(float p_sorting_offset, bool p_use_aabb_center) = 0;
virtual void set_pivot_data(float p_sorting_offset, bool p_use_aabb_center, int16_t p_sorting_stacked_order = 0) = 0;
virtual void set_lod_bias(float p_lod_bias) = 0;
virtual void set_layer_mask(uint32_t p_layer_mask) = 0;
virtual void set_fade_range(bool p_enable_near, float p_near_begin, float p_near_end, bool p_enable_far, float p_far_begin, float p_far_end) = 0;
Expand Down Expand Up @@ -94,6 +94,8 @@ class RenderGeometryInstanceBase : public RenderGeometryInstance {
float lod_bias = 0.0;
float sorting_offset = 0.0;
bool use_aabb_center = true;
//used for with overlapping depth sort
int16_t sorting_stacked_order = 0;

uint32_t layer_mask = 1;

Expand Down Expand Up @@ -136,7 +138,7 @@ class RenderGeometryInstanceBase : public RenderGeometryInstance {
virtual void set_surface_materials(const Vector<RID> &p_materials) override;
virtual void set_mesh_instance(RID p_mesh_instance) override;
virtual void set_transform(const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabb) override;
virtual void set_pivot_data(float p_sorting_offset, bool p_use_aabb_center) override;
virtual void set_pivot_data(float p_sorting_offset, bool p_use_aabb_center, int16_t p_sorting_stacked_order = 0) override;
virtual void set_lod_bias(float p_lod_bias) override;
virtual void set_layer_mask(uint32_t p_layer_mask) override;
virtual void set_fade_range(bool p_enable_near, float p_near_begin, float p_near_end, bool p_enable_far, float p_far_begin, float p_far_end) override;
Expand Down
Loading