Просмотр исходного кода

Merge pull request #103238 from Ryan-000/mesh_support_vram_profiler

Add Meshes to the Video RAM Profiler
Thaddeus Crews 3 месяцев назад
Родитель
Сommit
33065d8323

+ 1 - 0
drivers/gles3/storage/mesh_storage.h

@@ -319,6 +319,7 @@ public:
 
 	virtual void mesh_clear(RID p_mesh) override;
 	virtual void mesh_surface_remove(RID p_mesh, int p_surface) override;
+	virtual void mesh_debug_usage(List<RS::MeshInfo> *r_info) override {}
 
 	_FORCE_INLINE_ const RID *mesh_get_surface_count_and_materials(RID p_mesh, uint32_t &r_surface_count) {
 		Mesh *mesh = mesh_owner.get_or_null(p_mesh);

+ 1 - 0
drivers/gles3/storage/texture_storage.cpp

@@ -1481,6 +1481,7 @@ void TextureStorage::texture_debug_usage(List<RS::TextureInfo> *r_info) {
 		tinfo.width = t->alloc_width;
 		tinfo.height = t->alloc_height;
 		tinfo.bytes = t->total_data_size;
+		tinfo.type = static_cast<RenderingServer::TextureType>(t->type);
 
 		switch (t->type) {
 			case Texture::TYPE_3D:

+ 37 - 5
editor/debugger/script_editor_debugger.cpp

@@ -451,9 +451,19 @@ void ScriptEditorDebugger::_msg_servers_memory_usage(uint64_t p_thread_id, const
 		it->set_text(3, String::humanize_size(bytes));
 		total += bytes;
 
-		if (has_theme_icon(type, EditorStringName(EditorIcons))) {
-			it->set_icon(0, get_editor_theme_icon(type));
+		// If it does not have a theme icon, just go up the inheritance tree until we find one.
+		if (!has_theme_icon(type, EditorStringName(EditorIcons))) {
+			StringName base_type = type;
+			while (base_type != "Resource" || base_type != "") {
+				base_type = ClassDB::get_parent_class(base_type);
+				if (has_theme_icon(base_type, EditorStringName(EditorIcons))) {
+					type = base_type;
+					break;
+				}
+			}
 		}
+
+		it->set_icon(0, get_editor_theme_icon(type));
 	}
 
 	vmem_total->set_tooltip_text(TTR("Bytes:") + " " + itos(total));
@@ -1004,6 +1014,7 @@ void ScriptEditorDebugger::_notification(int p_what) {
 			next->set_button_icon(get_editor_theme_icon(SNAME("DebugNext")));
 			dobreak->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
 			docontinue->set_button_icon(get_editor_theme_icon(SNAME("DebugContinue")));
+			vmem_notice_icon->set_texture(get_editor_theme_icon(SNAME("NodeInfo")));
 			vmem_refresh->set_button_icon(get_editor_theme_icon(SNAME("Reload")));
 			vmem_export->set_button_icon(get_editor_theme_icon(SNAME("Save")));
 			search->set_right_icon(get_editor_theme_icon(SNAME("Search")));
@@ -2185,11 +2196,32 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
 	{ //vmem inspect
 		VBoxContainer *vmem_vb = memnew(VBoxContainer);
 		HBoxContainer *vmem_hb = memnew(HBoxContainer);
-		Label *vmlb = memnew(Label(TTR("List of Video Memory Usage by Resource:") + " "));
-		vmlb->set_theme_type_variation("HeaderSmall");
 
-		vmlb->set_h_size_flags(SIZE_EXPAND_FILL);
+		Label *vmlb = memnew(Label(TTRC("List of Video Memory Usage by Resource:")));
+		vmlb->set_theme_type_variation("HeaderSmall");
 		vmem_hb->add_child(vmlb);
+
+		{ // Add notice icon.
+			vmem_notice_icon = memnew(TextureRect);
+			vmem_notice_icon->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
+			vmem_notice_icon->set_h_size_flags(SIZE_SHRINK_CENTER);
+			vmem_notice_icon->set_visible(true);
+			vmem_notice_icon->set_tooltip_text(TTR(R"(Notice:
+This tool only reports memory allocations tracked by the engine.
+Therefore, total VRAM usage is inaccurate compared to what the Monitors tab or external tools can report.
+Instead, use the monitors tab to obtain more precise VRAM usage.
+
+- Buffer Memory (e.g. GPUParticles) is not tracked.
+- Meshes are not tracked in the Compatibility renderer.)"));
+			vmem_hb->add_child(vmem_notice_icon);
+		}
+
+		{ // Add some space to move the rest of the controls to the right.
+			Control *space = memnew(Control);
+			space->set_h_size_flags(SIZE_EXPAND_FILL);
+			vmem_hb->add_child(space);
+		}
+
 		vmem_hb->add_child(memnew(Label(TTR("Total:") + " ")));
 		vmem_total = memnew(LineEdit);
 		vmem_total->set_editable(false);

+ 1 - 0
editor/debugger/script_editor_debugger.h

@@ -137,6 +137,7 @@ private:
 	Button *vmem_refresh = nullptr;
 	Button *vmem_export = nullptr;
 	LineEdit *vmem_total = nullptr;
+	TextureRect *vmem_notice_icon = nullptr;
 
 	Tree *stack_dump = nullptr;
 	LineEdit *search = nullptr;

+ 71 - 1
servers/debugger/servers_debugger.cpp

@@ -33,6 +33,7 @@
 #include "core/config/project_settings.h"
 #include "core/debugger/engine_debugger.h"
 #include "core/debugger/engine_profiler.h"
+#include "core/io/resource_loader.h"
 #include "core/object/script_language.h"
 #include "servers/display_server.h"
 
@@ -435,7 +436,24 @@ void ServersDebugger::_send_resource_usage() {
 		info.path = E.path;
 		info.vram = E.bytes;
 		info.id = E.texture;
-		info.type = "Texture";
+
+		switch (E.type) {
+			case RS::TextureType::TEXTURE_TYPE_2D:
+				info.type = "Texture2D";
+				break;
+			case RS::TextureType::TEXTURE_TYPE_3D:
+				info.type = "Texture3D";
+				break;
+			case RS::TextureType::TEXTURE_TYPE_LAYERED:
+				info.type = "TextureLayered";
+				break;
+		}
+
+		String possible_type = _get_resource_type_from_path(E.path);
+		if (!possible_type.is_empty()) {
+			info.type = possible_type;
+		}
+
 		if (E.depth == 0) {
 			info.format = itos(E.width) + "x" + itos(E.height) + " " + Image::get_format_name(E.format);
 		} else {
@@ -444,9 +462,61 @@ void ServersDebugger::_send_resource_usage() {
 		usage.infos.push_back(info);
 	}
 
+	List<RS::MeshInfo> mesh_info;
+	RS::get_singleton()->mesh_debug_usage(&mesh_info);
+
+	for (const RS::MeshInfo &E : mesh_info) {
+		ServersDebugger::ResourceInfo info;
+		info.path = E.path;
+		// We use 64-bit integers to avoid overflow, if for whatever reason, the sum is bigger than 4GB.
+		uint64_t vram = E.vertex_buffer_size + E.attribute_buffer_size + E.skin_buffer_size + E.index_buffer_size + E.blend_shape_buffer_size + E.lod_index_buffers_size;
+		// But can info.vram even hold that, and why is it an int instead of an uint?
+		info.vram = vram;
+
+		// Even though these empty meshes can be indicative of issues somewhere else
+		// for UX reasons, we don't want to show them.
+		if (vram == 0 && E.path.is_empty()) {
+			continue;
+		}
+
+		info.id = E.mesh;
+		info.type = "Mesh";
+		String possible_type = _get_resource_type_from_path(E.path);
+		if (!possible_type.is_empty()) {
+			info.type = possible_type;
+		}
+
+		info.format = itos(E.vertex_count) + " Vertices";
+		usage.infos.push_back(info);
+	}
+
 	EngineDebugger::get_singleton()->send_message("servers:memory_usage", usage.serialize());
 }
 
+// Done on a best-effort basis.
+String ServersDebugger::_get_resource_type_from_path(const String &p_path) {
+	if (p_path.is_empty()) {
+		return "";
+	}
+
+	if (!ResourceLoader::exists(p_path)) {
+		return "";
+	}
+
+	if (ResourceCache::has(p_path)) {
+		Ref<Resource> resource = ResourceCache::get_ref(p_path);
+		return resource->get_class();
+	} else {
+		// This doesn't work all the time for embedded resources.
+		String resource_type = ResourceLoader::get_resource_type(p_path);
+		if (resource_type != "") {
+			return resource_type;
+		}
+	}
+
+	return "";
+}
+
 ServersDebugger::ServersDebugger() {
 	singleton = this;
 

+ 1 - 0
servers/debugger/servers_debugger.h

@@ -117,6 +117,7 @@ private:
 	static Error _capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured);
 
 	void _send_resource_usage();
+	String _get_resource_type_from_path(const String &p_path);
 
 	ServersDebugger();
 

+ 1 - 0
servers/rendering/dummy/storage/mesh_storage.h

@@ -132,6 +132,7 @@ public:
 
 	virtual void mesh_surface_remove(RID p_mesh, int p_surface) override;
 	virtual void mesh_clear(RID p_mesh) override;
+	virtual void mesh_debug_usage(List<RS::MeshInfo> *r_info) override {}
 
 	/* MESH INSTANCE */
 

+ 33 - 0
servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp

@@ -395,6 +395,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
 
 	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_size = new_surface.attribute_data.size();
 	}
 	if (new_surface.skin_data.size()) {
 		s->skin_buffer = RD::get_singleton()->vertex_buffer_create(new_surface.skin_data.size(), new_surface.skin_data, as_storage_flag);
@@ -411,6 +412,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
 		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_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);
 		if (new_surface.lods.size()) {
@@ -420,6 +422,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
 			for (int i = 0; i < new_surface.lods.size(); i++) {
 				uint32_t indices = new_surface.lods[i].index_data.size() / (is_index_16 ? 2 : 4);
 				s->lods[i].index_buffer = RD::get_singleton()->index_buffer_create(indices, is_index_16 ? RD::INDEX_BUFFER_FORMAT_UINT16 : RD::INDEX_BUFFER_FORMAT_UINT32, new_surface.lods[i].index_data);
+				s->lods[i].index_buffer_size = new_surface.lods[i].index_data.size();
 				s->lods[i].index_array = RD::get_singleton()->index_array_create(s->lods[i].index_buffer, 0, indices);
 				s->lods[i].edge_length = new_surface.lods[i].edge_length;
 				s->lods[i].index_count = indices;
@@ -437,6 +440,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
 
 	if (mesh->blend_shape_count > 0) {
 		s->blend_shape_buffer = RD::get_singleton()->storage_buffer_create(new_surface.blend_shape_data.size(), new_surface.blend_shape_data);
+		s->blend_shape_buffer_size = new_surface.blend_shape_data.size();
 	}
 
 	if (use_as_storage) {
@@ -917,6 +921,35 @@ void MeshStorage::mesh_surface_remove(RID p_mesh, int p_surface) {
 	}
 }
 
+void MeshStorage::mesh_debug_usage(List<RS::MeshInfo> *r_info) {
+	for (const RID &mesh_rid : mesh_owner.get_owned_list()) {
+		Mesh *mesh = mesh_owner.get_or_null(mesh_rid);
+		if (!mesh) {
+			continue;
+		}
+		RS::MeshInfo mesh_info;
+		mesh_info.mesh = mesh_rid;
+		mesh_info.path = mesh->path;
+
+		for (uint32_t surface_index = 0; surface_index < mesh->surface_count; surface_index++) {
+			MeshStorage::Mesh::Surface *surface = mesh->surfaces[surface_index];
+
+			mesh_info.vertex_buffer_size += surface->vertex_buffer_size;
+			mesh_info.attribute_buffer_size += surface->attribute_buffer_size;
+			mesh_info.skin_buffer_size += surface->skin_buffer_size;
+			mesh_info.index_buffer_size += surface->index_buffer_size;
+			mesh_info.blend_shape_buffer_size += surface->blend_shape_buffer_size;
+			mesh_info.vertex_count += surface->vertex_count;
+
+			for (uint32_t lod_index = 0; lod_index < surface->lod_count; lod_index++) {
+				mesh_info.lod_index_buffers_size += surface->lods[lod_index].index_buffer_size;
+			}
+		}
+
+		r_info->push_back(mesh_info);
+	}
+}
+
 bool MeshStorage::mesh_needs_instance(RID p_mesh, bool p_has_skeleton) {
 	Mesh *mesh = mesh_owner.get_or_null(p_mesh);
 	ERR_FAIL_NULL_V(mesh, false);

+ 10 - 2
servers/rendering/renderer_rd/storage_rd/mesh_storage.h

@@ -78,11 +78,14 @@ private:
 			RS::PrimitiveType primitive = RS::PRIMITIVE_POINTS;
 			uint64_t format = 0;
 
+			uint32_t vertex_count = 0;
 			RID vertex_buffer;
+			uint32_t vertex_buffer_size = 0;
+
 			RID attribute_buffer;
+			uint32_t attribute_buffer_size = 0;
+
 			RID skin_buffer;
-			uint32_t vertex_count = 0;
-			uint32_t vertex_buffer_size = 0;
 			uint32_t skin_buffer_size = 0;
 
 			// A different pipeline needs to be allocated
@@ -106,6 +109,7 @@ private:
 			uint32_t version_count = 0;
 
 			RID index_buffer;
+			uint32_t index_buffer_size = 0;
 			RID index_array;
 			uint32_t index_count = 0;
 
@@ -113,6 +117,7 @@ private:
 				float edge_length = 0.0;
 				uint32_t index_count = 0;
 				RID index_buffer;
+				uint32_t index_buffer_size = 0;
 				RID index_array;
 			};
 
@@ -130,6 +135,7 @@ private:
 			Vector4 uv_scale;
 
 			RID blend_shape_buffer;
+			uint32_t blend_shape_buffer_size = 0;
 
 			RID material;
 
@@ -397,6 +403,8 @@ public:
 	virtual void mesh_clear(RID p_mesh) override;
 	virtual void mesh_surface_remove(RID p_mesh, int p_surface) override;
 
+	virtual void mesh_debug_usage(List<RS::MeshInfo> *r_info) override;
+
 	virtual bool mesh_needs_instance(RID p_mesh, bool p_has_skeleton) override;
 
 	_FORCE_INLINE_ const RID *mesh_get_surface_count_and_materials(RID p_mesh, uint32_t &r_surface_count) {

+ 1 - 0
servers/rendering/renderer_rd/storage_rd/texture_storage.cpp

@@ -1651,6 +1651,7 @@ void TextureStorage::texture_debug_usage(List<RS::TextureInfo> *r_info) {
 		tinfo.width = t->width;
 		tinfo.height = t->height;
 		tinfo.bytes = Image::get_image_data_size(t->width, t->height, t->format, t->mipmaps > 1);
+		tinfo.type = static_cast<RenderingServer::TextureType>(t->type);
 
 		switch (t->type) {
 			case TextureType::TYPE_3D:

+ 2 - 0
servers/rendering/rendering_server_default.h

@@ -377,6 +377,8 @@ public:
 	FUNC2(mesh_surface_remove, RID, int)
 	FUNC1(mesh_clear, RID)
 
+	FUNC1(mesh_debug_usage, List<MeshInfo> *)
+
 	/* MULTIMESH API */
 
 	FUNCRIDSPLIT(multimesh)

+ 2 - 0
servers/rendering/storage/mesh_storage.h

@@ -76,6 +76,8 @@ public:
 	virtual void mesh_surface_remove(RID p_mesh, int p_surface) = 0;
 	virtual void mesh_clear(RID p_mesh) = 0;
 
+	virtual void mesh_debug_usage(List<RS::MeshInfo> *r_info) = 0;
+
 	virtual bool mesh_needs_instance(RID p_mesh, bool p_has_skeleton) = 0;
 
 	/* MESH INSTANCE */

+ 15 - 0
servers/rendering_server.h

@@ -183,6 +183,7 @@ public:
 		Image::Format format;
 		int64_t bytes;
 		String path;
+		TextureType type;
 	};
 
 	virtual void texture_debug_usage(List<TextureInfo> *r_info) = 0;
@@ -442,6 +443,20 @@ public:
 	virtual void mesh_surface_remove(RID p_mesh, int p_surface) = 0;
 	virtual void mesh_clear(RID p_mesh) = 0;
 
+	struct MeshInfo {
+		RID mesh;
+		String path;
+		uint32_t vertex_buffer_size = 0;
+		uint32_t attribute_buffer_size = 0;
+		uint32_t skin_buffer_size = 0;
+		uint32_t index_buffer_size = 0;
+		uint32_t blend_shape_buffer_size = 0;
+		uint32_t lod_index_buffers_size = 0;
+		uint64_t vertex_count = 0;
+	};
+
+	virtual void mesh_debug_usage(List<MeshInfo> *r_info) = 0;
+
 	/* MULTIMESH API */
 
 	virtual RID multimesh_create() = 0;