Add Meshes to the Video RAM Profiler
Apply suggestions from code review Co-Authored-By: Hugo Locurcio <hugo.locurcio@hugo.pro>
This commit is contained in:
parent
1696ab0cb6
commit
4497e2a0d3
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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")));
|
||||
@ -2184,11 +2195,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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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);
|
||||
|
@ -78,11 +78,14 @@ private:
|
||||
RS::PrimitiveType primitive = RS::PRIMITIVE_POINTS;
|
||||
uint64_t format = 0;
|
||||
|
||||
RID vertex_buffer;
|
||||
RID attribute_buffer;
|
||||
RID skin_buffer;
|
||||
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 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) {
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user