Skip to content

Commit

Permalink
Added indirect drawing functionality to MultiMesh
Browse files Browse the repository at this point in the history
Update doc/classes/RenderingServer.xml

Co-Authored-By: Micky <[email protected]>
  • Loading branch information
Bonkahe and Mickeon committed Dec 14, 2024
1 parent dc5f1b7 commit 036006f
Show file tree
Hide file tree
Showing 17 changed files with 207 additions and 15 deletions.
25 changes: 25 additions & 0 deletions doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2518,6 +2518,7 @@
<param index="2" name="transform_format" type="int" enum="RenderingServer.MultimeshTransformFormat" />
<param index="3" name="color_format" type="bool" default="false" />
<param index="4" name="custom_data_format" type="bool" default="false" />
<param index="5" name="use_indirect" type="bool" default="false" />
<description>
</description>
</method>
Expand Down Expand Up @@ -2552,6 +2553,30 @@
Returns the [RenderingDevice] [RID] handle of the [MultiMesh], which can be used as any other buffer on the Rendering Device.
</description>
</method>
<method name="multimesh_get_command_buffer_rd_rid" qualifiers="const">
<return type="RID" />
<param index="0" name="multimesh" type="RID" />
<description>
Returns the [RenderingDevice] [RID] handle of the [MultiMesh] command buffer. This [RID] is only valid if [code]use_indirect[/code] is set to [code]true[/code] when allocating data through [method multimesh_allocate_data]. It can be used to directly modify the instance count via buffer.
The data structure is dependent on both how many surfaces the mesh contains and whether it is indexed or not, 5 * surface count if indexed, 4 * surface count if not.
Each of the values in the buffer correspond to these options:
[codeblock lang=text]
Indexed:
0 - indexCount;
1 - instanceCount;
2 - firstIndex;
3 - vertexOffset;
4 - firstInstance;
Non Indexed:
0 - vertexCount;
1 - instanceCount;
2 - firstVertex;
3 - firstInstance;
[/codeblock]

For more information see this documentation on draw indirect functionality in Vulkan: https://vkguide.dev/docs/gpudriven/draw_indirect/
</description>
</method>
<method name="multimesh_get_custom_aabb" qualifiers="const">
<return type="AABB" />
<param index="0" name="multimesh" type="RID" />
Expand Down
6 changes: 5 additions & 1 deletion drivers/gles3/storage/mesh_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1452,7 +1452,7 @@ void MeshStorage::_multimesh_free(RID p_rid) {
multimesh_owner.free(p_rid);
}

void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) {
void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data, bool p_use_indirect) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);

Expand Down Expand Up @@ -1975,6 +1975,10 @@ void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_
}
}

RID MeshStorage::_multimesh_get_command_buffer_rd_rid(RID p_multimesh) const {
ERR_FAIL_V_MSG(RID(), "GLES3 does not implement indirect multimeshes.");
}

RID MeshStorage::_multimesh_get_buffer_rd_rid(RID p_multimesh) const {
ERR_FAIL_V_MSG(RID(), "GLES3 does not contain a Rid for the multimesh buffer.");
}
Expand Down
3 changes: 2 additions & 1 deletion drivers/gles3/storage/mesh_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ class MeshStorage : public RendererMeshStorage {
virtual RID _multimesh_allocate() override;
virtual void _multimesh_initialize(RID p_rid) override;
virtual void _multimesh_free(RID p_rid) override;
virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override;
virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false, bool p_use_indirect = false) override;
virtual int _multimesh_get_instance_count(RID p_multimesh) const override;

virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) override;
Expand All @@ -517,6 +517,7 @@ class MeshStorage : public RendererMeshStorage {
virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override;
virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override;
virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
virtual RID _multimesh_get_command_buffer_rd_rid(RID p_multimesh) const override;
virtual RID _multimesh_get_buffer_rd_rid(RID p_multimesh) const override;
virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const override;

Expand Down
7 changes: 7 additions & 0 deletions misc/extension_api_validation/4.3-stable.expected
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ Validate extension JSON: Error: Field 'classes/Control/properties/offset_top': t
Property type changed to float to match the actual internal API and documentation.


GH-99455
--------
Validate extension JSON: Error: Field 'classes/RenderingServer/methods/multimesh_allocate_data/arguments': size changed value in new API, from 5 to 6.

Optional argument added to allow setting indirect draw mode on Multimesh. Compatibility method registered.


GH-100129
---------
Validate extension JSON: Error: Field 'classes/NavigationServer2D/methods/query_path': is_const changed value in new API, from true to false.
Expand Down
3 changes: 2 additions & 1 deletion servers/rendering/dummy/storage/mesh_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class MeshStorage : public RendererMeshStorage {
virtual void _multimesh_initialize(RID p_rid) override;
virtual void _multimesh_free(RID p_rid) override;

virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override {}
virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false, bool p_use_indirect = false) override {}
virtual int _multimesh_get_instance_count(RID p_multimesh) const override { return 0; }

virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) override {}
Expand All @@ -170,6 +170,7 @@ class MeshStorage : public RendererMeshStorage {
virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override { return Color(); }
virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override { return Color(); }
virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
virtual RID _multimesh_get_command_buffer_rd_rid(RID p_multimesh) const override { return RID(); }
virtual RID _multimesh_get_buffer_rd_rid(RID p_multimesh) const override { return RID(); }
virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const override;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,16 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p
instance_count /= surf->owner->trail_steps;
}

RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count);
if (bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT)) {
RID command_buffer = mesh_storage->_multimesh_get_command_buffer(surf->owner->data->base);
if (mesh_storage->_multimesh_get_is_indexed(surf->owner->data->base)) {
RD::get_singleton()->draw_list_draw_indirect(draw_list, index_array_rd.is_valid(), command_buffer, surf->surface_index * sizeof(uint32_t) * 5, 1, 0);
} else {
RD::get_singleton()->draw_list_draw_indirect(draw_list, index_array_rd.is_valid(), command_buffer, surf->surface_index * sizeof(uint32_t) * 4, 1, 0);
}
} else {
RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count);
}
}

i += element_info.repeat - 1; //skip equal elements
Expand Down Expand Up @@ -1059,6 +1068,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
} else {
surf->sort.lod_index = 0;
if (p_render_data->render_info) {
// This does not include primitives rendered via indirect draw calls.
uint32_t to_draw = mesh_storage->mesh_surface_get_vertices_drawn_count(surf->surface);
to_draw = _indices_to_primitives(surf->primitive, to_draw);
to_draw *= inst->instance_count;
Expand Down Expand Up @@ -4119,9 +4129,9 @@ void RenderForwardClustered::_geometry_instance_update(RenderGeometryInstance *p
ginstance->base_flags = 0;

bool store_transform = true;

if (ginstance->data->base_type == RS::INSTANCE_MULTIMESH) {
ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH;
//Set flag here defining that a multimesh is considered indirect.
if (mesh_storage->multimesh_get_transform_format(ginstance->data->base) == RS::MULTIMESH_TRANSFORM_2D) {
ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D;
}
Expand All @@ -4131,6 +4141,9 @@ void RenderForwardClustered::_geometry_instance_update(RenderGeometryInstance *p
if (mesh_storage->multimesh_uses_custom_data(ginstance->data->base)) {
ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA;
}
if (mesh_storage->multimesh_uses_indirect(ginstance->data->base)) {
ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT;
}

ginstance->transforms_uniform_set = mesh_storage->multimesh_get_3d_uniform_set(ginstance->data->base, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {

// When changing any of these enums, remember to change the corresponding enums in the shader files as well.
enum {
INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT = 1 << 2,
INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3,
INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4,
INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2366,7 +2366,16 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
instance_count /= surf->owner->trail_steps;
}

RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count);
if (bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT)) {
RID command_buffer = mesh_storage->_multimesh_get_command_buffer(surf->owner->data->base);
if (mesh_storage->_multimesh_get_is_indexed(surf->owner->data->base)) {
RD::get_singleton()->draw_list_draw_indirect(draw_list, index_array_rd.is_valid(), command_buffer, surf->surface_index * sizeof(uint32_t) * 5, 1, 0);
} else {
RD::get_singleton()->draw_list_draw_indirect(draw_list, index_array_rd.is_valid(), command_buffer, surf->surface_index * sizeof(uint32_t) * 4, 1, 0);
}
} else {
RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count);
}
}
}

Expand Down Expand Up @@ -2787,6 +2796,7 @@ void RenderForwardMobile::_geometry_instance_update(RenderGeometryInstance *p_ge

if (ginstance->data->base_type == RS::INSTANCE_MULTIMESH) {
ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH;

if (mesh_storage->multimesh_get_transform_format(ginstance->data->base) == RS::MULTIMESH_TRANSFORM_2D) {
ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D;
}
Expand All @@ -2796,6 +2806,9 @@ void RenderForwardMobile::_geometry_instance_update(RenderGeometryInstance *p_ge
if (mesh_storage->multimesh_uses_custom_data(ginstance->data->base)) {
ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA;
}
if (mesh_storage->multimesh_uses_indirect(ginstance->data->base)) {
ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT;
}

ginstance->transforms_uniform_set = mesh_storage->multimesh_get_3d_uniform_set(ginstance->data->base, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ class RenderForwardMobile : public RendererSceneRenderRD {

// When changing any of these enums, remember to change the corresponding enums in the shader files as well.
enum {
INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT = 1 << 2,
INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3,
INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4,
INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5,
Expand Down
98 changes: 97 additions & 1 deletion servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1419,7 +1419,7 @@ void MeshStorage::_multimesh_free(RID p_rid) {
multimesh_owner.free(p_rid);
}

void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) {
void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data, bool p_use_indirect) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);

Expand Down Expand Up @@ -1455,6 +1455,19 @@ void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS:
multimesh->stride_cache = multimesh->custom_data_offset_cache + (p_use_custom_data ? 4 : 0);
multimesh->buffer_set = false;

multimesh->indirect = p_use_indirect;
multimesh->command_buffer = RID();
//if (p_use_indirect) {
// Vector<uint8_t> newVector;
// {
// newVector.resize_zeroed(sizeof(uint8_t) * 5 * 4);
// {
// multimesh->command_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 5, newVector, RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT);
// multimesh->indirect_indexed = true;
// }
// }
//}

//print_line("allocate, elements: " + itos(p_instances) + " 2D: " + itos(p_transform_format == RS::MULTIMESH_TRANSFORM_2D) + " colors " + itos(multimesh->uses_colors) + " data " + itos(multimesh->uses_custom_data) + " stride " + itos(multimesh->stride_cache) + " total size " + itos(multimesh->stride_cache * multimesh->instances));
multimesh->data_cache = Vector<float>();
multimesh->aabb = AABB();
Expand Down Expand Up @@ -1513,6 +1526,18 @@ void MeshStorage::_multimesh_enable_motion_vectors(MultiMesh *multimesh) {
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH);
}

RID MeshStorage::_multimesh_get_command_buffer(RID p_multimesh) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, RID());
return multimesh->command_buffer;
}

bool MeshStorage::_multimesh_get_is_indexed(RID p_multimesh) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, false);
return multimesh->indirect_indexed;
}

void MeshStorage::_multimesh_get_motion_vectors_offsets(RID p_multimesh, uint32_t &r_current_offset, uint32_t &r_prev_offset) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
Expand Down Expand Up @@ -1543,6 +1568,51 @@ void MeshStorage::_multimesh_set_mesh(RID p_multimesh, RID p_mesh) {
}
multimesh->mesh = p_mesh;

if (multimesh->indirect) { // Necessitates altering the command buffer to account for the new vertex count, going to assume at this time one surface.

Mesh *mesh = mesh_owner.get_or_null(p_mesh);
ERR_FAIL_NULL(mesh);
if (mesh->surface_count > 0) {
if (multimesh->command_buffer.is_valid()) {
RD::get_singleton()->free(multimesh->command_buffer);
}

bool indexedMesh = false;
for (uint32_t i = 0; i < mesh->surface_count; i++) {
if (mesh->surfaces[i]->index_count > 0) {
indexedMesh = true;
break;
}
}

if (indexedMesh) {
Vector<uint8_t> newVector;
newVector.resize_zeroed(sizeof(uint32_t) * 5 * mesh->surface_count);

RID newBuffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 5 * mesh->surface_count, newVector, RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT);

multimesh->command_buffer = newBuffer;
multimesh->indirect_indexed = true;

for (uint32_t i = 0; i < mesh->surface_count; i++) {
RD::get_singleton()->buffer_update(multimesh->command_buffer, i * sizeof(uint32_t) * 5, sizeof(uint32_t), &mesh->surfaces[i]->index_count);
}
} else {
Vector<uint8_t> newVector;
newVector.resize_zeroed(sizeof(uint32_t) * 4 * mesh->surface_count);

RID newBuffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4 * mesh->surface_count, newVector, RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT);

multimesh->command_buffer = newBuffer;
multimesh->indirect_indexed = false;

for (uint32_t i = 0; i < mesh->surface_count; i++) {
RD::get_singleton()->buffer_update(multimesh->command_buffer, i * sizeof(uint32_t) * 4, sizeof(uint32_t), &mesh->surfaces[i]->vertex_count);
}
}
}
}

if (multimesh->instances == 0) {
return;
}
Expand Down Expand Up @@ -1998,6 +2068,12 @@ void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_
}
}

RID MeshStorage::_multimesh_get_command_buffer_rd_rid(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, RID());
return multimesh->command_buffer;
}

RID MeshStorage::_multimesh_get_buffer_rd_rid(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, RID());
Expand Down Expand Up @@ -2045,6 +2121,26 @@ void MeshStorage::_multimesh_set_visible_instances(RID p_multimesh, int p_visibl

multimesh->visible_instances = p_visible;

if (multimesh->indirect) { //we have to rebuild/update the command buffer if it's indirect.
Mesh *mesh = mesh_owner.get_or_null(multimesh->mesh);
if (mesh != nullptr) {
if (mesh->surface_count > 0) {
uint32_t index_count = 0;
uint32_t vertex_count = 0;
for (uint32_t i = 0; i < mesh->surface_count; i++) {
index_count += mesh->surfaces[i]->index_count;
vertex_count += mesh->surfaces[i]->vertex_count;
}

if (multimesh->indirect_indexed) {
RD::get_singleton()->buffer_update(multimesh->command_buffer, 0, sizeof(uint32_t), &index_count);
} else {
RD::get_singleton()->buffer_update(multimesh->command_buffer, 0, sizeof(uint32_t), &vertex_count);
}
}
}
}

multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES);
}

Expand Down
Loading

0 comments on commit 036006f

Please sign in to comment.