diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index bb9fbef542..1c3995368e 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -370,6 +370,9 @@ Flag used to mark that a mesh is using compressed attributes (vertices, normals, tangents, UVs). When this form of compression is enabled, vertex positions will be packed into an RGBA16UNORM attribute and scaled in the vertex shader. The normal and tangent will be packed into an RG16UNORM representing an axis, and a 16-bit float stored in the A-channel of the vertex. UVs will use 16-bit normalized floats instead of full 32-bit signed floats. When using this compression mode you must use either vertices, normals, and tangents or only vertices. You cannot use normals without tangents. Importers will automatically enable this compression if they can. + + Flag used to mark that the surface's vertex, attribute, skin, and index buffers must be created with the storage-buffer usage bit so they can be bound as storage buffers in compute shaders. This is required to write into them from a compute pipeline through the [RID]s returned by [method RenderingServer.mesh_surface_get_vertex_buffer_rd_rid] and the matching methods for the attribute, skin, and index buffers. Has no effect on the OpenGL backend, which does not expose [RenderingDevice] [RID]s. + Blend shapes are normalized. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 67a591e4c8..5509c05ab3 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -2573,6 +2573,16 @@ Returns a mesh's surface's buffer arrays. + + + + + + Returns the [RenderingDevice] [RID] handle of the attribute buffer of the given mesh surface (Color, UV, UV2, Custom0-3). The returned [RID] can be used like any other buffer on the [RenderingDevice], so the surface attributes can be read or written from a compute shader without a CPU round-trip. + To bind the buffer as a storage buffer in compute, the surface must have been created with the [constant ARRAY_FLAG_USE_STORAGE_BUFFER] flag set in its format. + Returns an invalid [RID] if the surface has no attribute buffer or if the rendering backend does not expose [RenderingDevice] [RID]s (such as the OpenGL backend). + + @@ -2630,6 +2640,16 @@ Returns the stride of the vertex positions for a mesh with given [param format]. Note importantly that vertex positions are stored consecutively and are not interleaved with the other attributes in the vertex buffer (normals and tangents). + + + + + + Returns the [RenderingDevice] [RID] handle of the index buffer of the given mesh surface. The returned [RID] can be used like any other buffer on the [RenderingDevice], so the indices can be read or written from a compute shader without a CPU round-trip. + To bind the buffer as a storage buffer in compute, the surface must have been created with the [constant ARRAY_FLAG_USE_STORAGE_BUFFER] flag set in its format. + Returns an invalid [RID] if the surface is not indexed or if the rendering backend does not expose [RenderingDevice] [RID]s (such as the OpenGL backend). + + @@ -2638,6 +2658,26 @@ Returns a mesh's surface's material. + + + + + + Returns the [RenderingDevice] [RID] handle of the skin buffer of the given mesh surface (Bones, Weights). The returned [RID] can be used like any other buffer on the [RenderingDevice], so the skin data can be read or written from a compute shader without a CPU round-trip. + This buffer is created with the storage-buffer usage bit whenever the surface has skinning data, so it can be bound as a storage buffer in compute even without [constant ARRAY_FLAG_USE_STORAGE_BUFFER]. + Returns an invalid [RID] if the surface has no skin buffer or if the rendering backend does not expose [RenderingDevice] [RID]s (such as the OpenGL backend). + + + + + + + + Returns the [RenderingDevice] [RID] handle of the vertex buffer of the given mesh surface (Vertex, Normal, Tangent). The returned [RID] can be used like any other buffer on the [RenderingDevice], so the surface vertices can be read or written from a compute shader without a CPU round-trip. This is the [Mesh] equivalent of [method multimesh_get_buffer_rd_rid] and is intended for GPU-driven mesh workflows (procedural meshes, ribbon trails, cloth, hair, grass) implemented from a [GDExtension]. + To bind the buffer as a storage buffer in compute, the surface must have been created with the [constant ARRAY_FLAG_USE_STORAGE_BUFFER] flag set in its format. + Returns an invalid [RID] if the surface has no vertex buffer or if the rendering backend does not expose [RenderingDevice] [RID]s (such as the OpenGL backend). + + @@ -4953,6 +4993,9 @@ Flag used to mark that a mesh is using compressed attributes (vertices, normals, tangents, UVs). When this form of compression is enabled, vertex positions will be packed into an RGBA16UNORM attribute and scaled in the vertex shader. The normal and tangent will be packed into an RG16UNORM representing an axis, and a 16-bit float stored in the A-channel of the vertex. UVs will use 16-bit normalized floats instead of full 32-bit signed floats. When using this compression mode you must use either vertices, normals, and tangents or only vertices. You cannot use normals without tangents. Importers will automatically enable this compression if they can. + + Flag used to mark that the surface's vertex, attribute, skin, and index buffers must be created with the storage-buffer usage bit so they can be bound as storage buffers in compute shaders. This is required to write into them from a compute pipeline through the [RID]s returned by [method RenderingServer.mesh_surface_get_vertex_buffer_rd_rid] and the matching methods for the attribute, skin, and index buffers. Has no effect on the OpenGL backend, which does not expose [RenderingDevice] [RID]s. + Flag used to mark the start of the bits used to store the mesh version. diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index d9ab1b7722..b828a03cec 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -321,6 +321,12 @@ public: virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const override; virtual RenderingServerTypes::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const override; + + virtual RID mesh_surface_get_vertex_buffer_rd_rid(RID p_mesh, int p_surface) const override { return RID(); } + virtual RID mesh_surface_get_attribute_buffer_rd_rid(RID p_mesh, int p_surface) const override { return RID(); } + virtual RID mesh_surface_get_skin_buffer_rd_rid(RID p_mesh, int p_surface) const override { return RID(); } + virtual RID mesh_surface_get_index_buffer_rd_rid(RID p_mesh, int p_surface) const override { return RID(); } + virtual int mesh_get_surface_count(RID p_mesh) const override; virtual void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) override; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 383c28fc45..e183b0dbdc 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -891,6 +891,7 @@ void Mesh::_bind_methods() { BIND_BITFIELD_FLAG(ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY); BIND_BITFIELD_FLAG(ARRAY_FLAG_COMPRESS_ATTRIBUTES); + BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_STORAGE_BUFFER); BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED); BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 4c28e5cf7b..4152cdba76 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -159,6 +159,7 @@ public: ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY = RSE::ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY, ARRAY_FLAG_COMPRESS_ATTRIBUTES = RSE::ARRAY_FLAG_COMPRESS_ATTRIBUTES, + ARRAY_FLAG_USE_STORAGE_BUFFER = RSE::ARRAY_FLAG_USE_STORAGE_BUFFER, ARRAY_FLAG_FORMAT_VERSION_BASE = RSE::ARRAY_FLAG_FORMAT_VERSION_BASE, ARRAY_FLAG_FORMAT_VERSION_SHIFT = RSE::ARRAY_FLAG_FORMAT_VERSION_SHIFT, diff --git a/servers/rendering/dummy/storage/mesh_storage.h b/servers/rendering/dummy/storage/mesh_storage.h index 09bbd1f11c..18ddf81c3d 100644 --- a/servers/rendering/dummy/storage/mesh_storage.h +++ b/servers/rendering/dummy/storage/mesh_storage.h @@ -133,6 +133,11 @@ public: return s; } + virtual RID mesh_surface_get_vertex_buffer_rd_rid(RID p_mesh, int p_surface) const override { return RID(); } + virtual RID mesh_surface_get_attribute_buffer_rd_rid(RID p_mesh, int p_surface) const override { return RID(); } + virtual RID mesh_surface_get_skin_buffer_rd_rid(RID p_mesh, int p_surface) const override { return RID(); } + virtual RID mesh_surface_get_index_buffer_rd_rid(RID p_mesh, int p_surface) const override { return RID(); } + virtual int mesh_get_surface_count(RID p_mesh) const override { DummyMesh *m = mesh_owner.get_or_null(p_mesh); ERR_FAIL_NULL_V(m, 0); diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index b5c69b44bf..cceeec612e 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -375,7 +375,9 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RenderingServerTypes::Surfa s->primitive = new_surface.primitive; const bool use_as_storage = (new_surface.skin_data.size() || mesh->blend_shape_count > 0); - const BitField as_storage_flag = use_as_storage ? RD::BUFFER_CREATION_AS_STORAGE_BIT : 0; + const bool requested_storage_buffer = (new_surface.format & RSE::ARRAY_FLAG_USE_STORAGE_BUFFER); + const BitField as_storage_flag = (use_as_storage || requested_storage_buffer) ? RD::BUFFER_CREATION_AS_STORAGE_BIT : 0; + const BitField requested_storage_flag = requested_storage_buffer ? RD::BUFFER_CREATION_AS_STORAGE_BIT : 0; if (new_surface.vertex_data.size()) { // If we have an uncompressed surface that contains normals, but not tangents, we need to differentiate the array @@ -398,7 +400,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RenderingServerTypes::Surfa } if (new_surface.attribute_data.size()) { - s->attribute_buffer = RD::get_singleton()->vertex_buffer_create(new_surface.attribute_data.size(), new_surface.attribute_data); + s->attribute_buffer = RD::get_singleton()->vertex_buffer_create(new_surface.attribute_data.size(), new_surface.attribute_data, requested_storage_flag); s->attribute_buffer_size = new_surface.attribute_data.size(); } if (new_surface.skin_data.size()) { @@ -415,7 +417,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RenderingServerTypes::Surfa if (new_surface.index_count) { bool is_index_16 = new_surface.vertex_count <= 65536 && new_surface.vertex_count > 0; - s->index_buffer = RD::get_singleton()->index_buffer_create(new_surface.index_count, is_index_16 ? RD::INDEX_BUFFER_FORMAT_UINT16 : RD::INDEX_BUFFER_FORMAT_UINT32, new_surface.index_data, false); + s->index_buffer = RD::get_singleton()->index_buffer_create(new_surface.index_count, is_index_16 ? RD::INDEX_BUFFER_FORMAT_UINT16 : RD::INDEX_BUFFER_FORMAT_UINT32, new_surface.index_data, false, requested_storage_flag); s->index_buffer_size = new_surface.index_data.size(); s->index_count = new_surface.index_count; s->index_array = RD::get_singleton()->index_array_create(s->index_buffer, 0, s->index_count); @@ -620,6 +622,34 @@ void RendererRD::MeshStorage::mesh_surface_update_index_region(RID p_mesh, int p RD::get_singleton()->buffer_update(mesh->surfaces[p_surface]->index_buffer, p_offset, data_size, r); } +RID MeshStorage::mesh_surface_get_vertex_buffer_rd_rid(RID p_mesh, int p_surface) const { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_NULL_V(mesh, RID()); + ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_surface, mesh->surface_count, RID()); + return mesh->surfaces[p_surface]->vertex_buffer; +} + +RID MeshStorage::mesh_surface_get_attribute_buffer_rd_rid(RID p_mesh, int p_surface) const { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_NULL_V(mesh, RID()); + ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_surface, mesh->surface_count, RID()); + return mesh->surfaces[p_surface]->attribute_buffer; +} + +RID MeshStorage::mesh_surface_get_skin_buffer_rd_rid(RID p_mesh, int p_surface) const { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_NULL_V(mesh, RID()); + ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_surface, mesh->surface_count, RID()); + return mesh->surfaces[p_surface]->skin_buffer; +} + +RID MeshStorage::mesh_surface_get_index_buffer_rd_rid(RID p_mesh, int p_surface) const { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_NULL_V(mesh, RID()); + ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_surface, mesh->surface_count, RID()); + return mesh->surfaces[p_surface]->index_buffer; +} + void MeshStorage::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_NULL(mesh); diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index b409f94f96..6516a7c71a 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -392,6 +392,11 @@ public: virtual RenderingServerTypes::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const override; + virtual RID mesh_surface_get_vertex_buffer_rd_rid(RID p_mesh, int p_surface) const override; + virtual RID mesh_surface_get_attribute_buffer_rd_rid(RID p_mesh, int p_surface) const override; + virtual RID mesh_surface_get_skin_buffer_rd_rid(RID p_mesh, int p_surface) const override; + virtual RID mesh_surface_get_index_buffer_rd_rid(RID p_mesh, int p_surface) const override; + virtual int mesh_get_surface_count(RID p_mesh) const override; virtual void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) override; diff --git a/servers/rendering/rendering_server.cpp b/servers/rendering/rendering_server.cpp index feed88b7b5..34b13d60c6 100644 --- a/servers/rendering/rendering_server.cpp +++ b/servers/rendering/rendering_server.cpp @@ -2386,6 +2386,11 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("mesh_surface_update_skin_region", "mesh", "surface", "offset", "data"), &RenderingServer::mesh_surface_update_skin_region); ClassDB::bind_method(D_METHOD("mesh_surface_update_index_region", "mesh", "surface", "offset", "data"), &RenderingServer::mesh_surface_update_index_region); + ClassDB::bind_method(D_METHOD("mesh_surface_get_vertex_buffer_rd_rid", "mesh", "surface"), &RenderingServer::mesh_surface_get_vertex_buffer_rd_rid); + ClassDB::bind_method(D_METHOD("mesh_surface_get_attribute_buffer_rd_rid", "mesh", "surface"), &RenderingServer::mesh_surface_get_attribute_buffer_rd_rid); + ClassDB::bind_method(D_METHOD("mesh_surface_get_skin_buffer_rd_rid", "mesh", "surface"), &RenderingServer::mesh_surface_get_skin_buffer_rd_rid); + ClassDB::bind_method(D_METHOD("mesh_surface_get_index_buffer_rd_rid", "mesh", "surface"), &RenderingServer::mesh_surface_get_index_buffer_rd_rid); + ClassDB::bind_method(D_METHOD("mesh_set_shadow_mesh", "mesh", "shadow_mesh"), &RenderingServer::mesh_set_shadow_mesh); BIND_ENUM_CONSTANT(RSE::ARRAY_VERTEX); @@ -2448,6 +2453,8 @@ void RenderingServer::_bind_methods() { BIND_BITFIELD_FLAG(RSE::ARRAY_FLAG_COMPRESS_ATTRIBUTES); + BIND_BITFIELD_FLAG(RSE::ARRAY_FLAG_USE_STORAGE_BUFFER); + BIND_BITFIELD_FLAG(RSE::ARRAY_FLAG_FORMAT_VERSION_BASE); BIND_BITFIELD_FLAG(RSE::ARRAY_FLAG_FORMAT_VERSION_SHIFT); BIND_BITFIELD_FLAG(RSE::ARRAY_FLAG_FORMAT_VERSION_1); diff --git a/servers/rendering/rendering_server.h b/servers/rendering/rendering_server.h index 4a5f852bcd..78f63933c9 100644 --- a/servers/rendering/rendering_server.h +++ b/servers/rendering/rendering_server.h @@ -241,6 +241,11 @@ public: virtual void mesh_surface_remove(RID p_mesh, int p_surface) = 0; virtual void mesh_clear(RID p_mesh) = 0; + virtual RID mesh_surface_get_vertex_buffer_rd_rid(RID p_mesh, int p_surface) const = 0; + virtual RID mesh_surface_get_attribute_buffer_rd_rid(RID p_mesh, int p_surface) const = 0; + virtual RID mesh_surface_get_skin_buffer_rd_rid(RID p_mesh, int p_surface) const = 0; + virtual RID mesh_surface_get_index_buffer_rd_rid(RID p_mesh, int p_surface) const = 0; + virtual void mesh_debug_usage(List *r_info) = 0; /* MULTIMESH API */ diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 9bc71818ed..e91b58e748 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -420,6 +420,11 @@ public: FUNC2(mesh_surface_remove, RID, int) FUNC1(mesh_clear, RID) + FUNC2RC(RID, mesh_surface_get_vertex_buffer_rd_rid, RID, int) + FUNC2RC(RID, mesh_surface_get_attribute_buffer_rd_rid, RID, int) + FUNC2RC(RID, mesh_surface_get_skin_buffer_rd_rid, RID, int) + FUNC2RC(RID, mesh_surface_get_index_buffer_rd_rid, RID, int) + FUNC1(mesh_debug_usage, List *) /* MULTIMESH API */ diff --git a/servers/rendering/rendering_server_enums.h b/servers/rendering/rendering_server_enums.h index 47d4dbf235..b8542ce48b 100644 --- a/servers/rendering/rendering_server_enums.h +++ b/servers/rendering/rendering_server_enums.h @@ -191,7 +191,13 @@ enum ArrayFormat : uint64_t { ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY = 1 << (ARRAY_COMPRESS_FLAGS_BASE + 3), ARRAY_FLAG_COMPRESS_ATTRIBUTES = 1 << (ARRAY_COMPRESS_FLAGS_BASE + 4), - // We leave enough room for up to 5 more compression flags. + + // When set, the surface's vertex, attribute, skin, and index buffers are created with the + // `BUFFER_CREATION_AS_STORAGE_BIT` usage bit so they can be bound as storage buffers in + // compute shaders. Required to write into them from a compute pipeline through the RIDs + // returned by `mesh_surface_get_vertex_buffer_rd_rid` and friends. + ARRAY_FLAG_USE_STORAGE_BUFFER = 1 << (ARRAY_COMPRESS_FLAGS_BASE + 5), + // We leave enough room for up to 4 more compression flags. ARRAY_FLAG_FORMAT_VERSION_BASE = ARRAY_COMPRESS_FLAGS_BASE + 10, ARRAY_FLAG_FORMAT_VERSION_SHIFT = ARRAY_FLAG_FORMAT_VERSION_BASE, diff --git a/servers/rendering/storage/mesh_storage.h b/servers/rendering/storage/mesh_storage.h index bbbaebd6d6..586ba7acda 100644 --- a/servers/rendering/storage/mesh_storage.h +++ b/servers/rendering/storage/mesh_storage.h @@ -67,6 +67,11 @@ public: virtual RenderingServerTypes::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const = 0; + virtual RID mesh_surface_get_vertex_buffer_rd_rid(RID p_mesh, int p_surface) const = 0; + virtual RID mesh_surface_get_attribute_buffer_rd_rid(RID p_mesh, int p_surface) const = 0; + virtual RID mesh_surface_get_skin_buffer_rd_rid(RID p_mesh, int p_surface) const = 0; + virtual RID mesh_surface_get_index_buffer_rd_rid(RID p_mesh, int p_surface) const = 0; + virtual int mesh_get_surface_count(RID p_mesh) const = 0; virtual void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) = 0;