Path: blob/master/servers/debugger/servers_debugger.cpp
11351 views
/**************************************************************************/1/* servers_debugger.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "servers_debugger.h"3132#include "core/config/project_settings.h"33#include "core/debugger/engine_debugger.h"34#include "core/debugger/engine_profiler.h"35#include "core/io/resource_loader.h"36#include "core/object/script_language.h"37#include "servers/display/display_server.h"3839#define CHECK_SIZE(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() < (uint32_t)(expected), false, String("Malformed ") + what + " message from script debugger, message too short. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size()))40#define CHECK_END(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() > (uint32_t)expected, false, String("Malformed ") + what + " message from script debugger, message too long. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size()))4142Array ServersDebugger::ResourceUsage::serialize() {43infos.sort();4445Array arr = { infos.size() * 4 };46for (const ResourceInfo &E : infos) {47arr.push_back(E.path);48arr.push_back(E.format);49arr.push_back(E.type);50arr.push_back(E.vram);51}52return arr;53}5455bool ServersDebugger::ResourceUsage::deserialize(const Array &p_arr) {56CHECK_SIZE(p_arr, 1, "ResourceUsage");57uint32_t size = p_arr[0];58ERR_FAIL_COND_V(size % 4, false);59CHECK_SIZE(p_arr, 1 + size, "ResourceUsage");60uint32_t idx = 1;61while (idx < 1 + size) {62ResourceInfo info;63info.path = p_arr[idx];64info.format = p_arr[idx + 1];65info.type = p_arr[idx + 2];66info.vram = p_arr[idx + 3];67infos.push_back(info);68idx += 4;69}70CHECK_END(p_arr, idx, "ResourceUsage");71return true;72}7374Array ServersDebugger::ScriptFunctionSignature::serialize() {75return Array{ name, id };76}7778bool ServersDebugger::ScriptFunctionSignature::deserialize(const Array &p_arr) {79CHECK_SIZE(p_arr, 2, "ScriptFunctionSignature");80name = p_arr[0];81id = p_arr[1];82CHECK_END(p_arr, 2, "ScriptFunctionSignature");83return true;84}8586Array ServersDebugger::ServersProfilerFrame::serialize() {87Array arr = { frame_number, frame_time, process_time, physics_time, physics_frame_time, script_time };8889arr.push_back(servers.size());90for (const ServerInfo &s : servers) {91arr.push_back(s.name);92arr.push_back(s.functions.size() * 2);93for (const ServerFunctionInfo &f : s.functions) {94arr.push_back(f.name);95arr.push_back(f.time);96}97}9899arr.push_back(script_functions.size() * 5);100for (int i = 0; i < script_functions.size(); i++) {101arr.push_back(script_functions[i].sig_id);102arr.push_back(script_functions[i].call_count);103arr.push_back(script_functions[i].self_time);104arr.push_back(script_functions[i].total_time);105arr.push_back(script_functions[i].internal_time);106}107return arr;108}109110bool ServersDebugger::ServersProfilerFrame::deserialize(const Array &p_arr) {111CHECK_SIZE(p_arr, 7, "ServersProfilerFrame");112frame_number = p_arr[0];113frame_time = p_arr[1];114process_time = p_arr[2];115physics_time = p_arr[3];116physics_frame_time = p_arr[4];117script_time = p_arr[5];118int servers_size = p_arr[6];119int idx = 7;120while (servers_size) {121CHECK_SIZE(p_arr, idx + 2, "ServersProfilerFrame");122servers_size--;123ServerInfo si;124si.name = p_arr[idx];125int sub_data_size = p_arr[idx + 1];126idx += 2;127CHECK_SIZE(p_arr, idx + sub_data_size, "ServersProfilerFrame");128for (int j = 0; j < sub_data_size / 2; j++) {129ServerFunctionInfo sf;130sf.name = p_arr[idx];131sf.time = p_arr[idx + 1];132idx += 2;133si.functions.push_back(sf);134}135servers.push_back(si);136}137CHECK_SIZE(p_arr, idx + 1, "ServersProfilerFrame");138int func_size = p_arr[idx];139idx += 1;140CHECK_SIZE(p_arr, idx + func_size, "ServersProfilerFrame");141for (int i = 0; i < func_size / 5; i++) {142ScriptFunctionInfo fi;143fi.sig_id = p_arr[idx];144fi.call_count = p_arr[idx + 1];145fi.self_time = p_arr[idx + 2];146fi.total_time = p_arr[idx + 3];147fi.internal_time = p_arr[idx + 4];148script_functions.push_back(fi);149idx += 5;150}151CHECK_END(p_arr, idx, "ServersProfilerFrame");152return true;153}154155Array ServersDebugger::VisualProfilerFrame::serialize() {156Array arr = { frame_number, areas.size() * 3 };157for (int i = 0; i < areas.size(); i++) {158arr.push_back(areas[i].name);159arr.push_back(areas[i].cpu_msec);160arr.push_back(areas[i].gpu_msec);161}162return arr;163}164165bool ServersDebugger::VisualProfilerFrame::deserialize(const Array &p_arr) {166CHECK_SIZE(p_arr, 2, "VisualProfilerFrame");167frame_number = p_arr[0];168int size = p_arr[1];169CHECK_SIZE(p_arr, size, "VisualProfilerFrame");170int idx = 2;171areas.resize(size / 3);172RS::FrameProfileArea *w = areas.ptrw();173for (int i = 0; i < size / 3; i++) {174w[i].name = p_arr[idx];175w[i].cpu_msec = p_arr[idx + 1];176w[i].gpu_msec = p_arr[idx + 2];177idx += 3;178}179CHECK_END(p_arr, idx, "VisualProfilerFrame");180return true;181}182class ServersDebugger::ScriptsProfiler : public EngineProfiler {183typedef ServersDebugger::ScriptFunctionSignature FunctionSignature;184typedef ServersDebugger::ScriptFunctionInfo FunctionInfo;185struct ProfileInfoSort {186bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {187return A->total_time > B->total_time;188}189};190Vector<ScriptLanguage::ProfilingInfo> info;191Vector<ScriptLanguage::ProfilingInfo *> ptrs;192HashMap<StringName, int> sig_map;193int max_frame_functions = 16;194195public:196void toggle(bool p_enable, const Array &p_opts) {197if (p_enable) {198sig_map.clear();199for (int i = 0; i < ScriptServer::get_language_count(); i++) {200ScriptServer::get_language(i)->profiling_start();201if (p_opts.size() == 2 && p_opts[1].get_type() == Variant::BOOL) {202ScriptServer::get_language(i)->profiling_set_save_native_calls(p_opts[1]);203}204}205if (p_opts.size() > 0 && p_opts[0].get_type() == Variant::INT) {206max_frame_functions = MAX(0, int(p_opts[0]));207}208} else {209for (int i = 0; i < ScriptServer::get_language_count(); i++) {210ScriptServer::get_language(i)->profiling_stop();211}212}213}214215void write_frame_data(Vector<FunctionInfo> &r_funcs, uint64_t &r_total, bool p_accumulated) {216int ofs = 0;217for (int i = 0; i < ScriptServer::get_language_count(); i++) {218if (p_accumulated) {219ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&info.write[ofs], info.size() - ofs);220} else {221ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&info.write[ofs], info.size() - ofs);222}223}224225for (int i = 0; i < ofs; i++) {226ptrs.write[i] = &info.write[i];227}228229SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;230sa.sort(ptrs.ptrw(), ofs);231232int to_send = MIN(ofs, max_frame_functions);233234// Check signatures first, and compute total time.235r_total = 0;236for (int i = 0; i < to_send; i++) {237if (!sig_map.has(ptrs[i]->signature)) {238int idx = sig_map.size();239FunctionSignature sig;240sig.name = ptrs[i]->signature;241sig.id = idx;242EngineDebugger::get_singleton()->send_message("servers:function_signature", sig.serialize());243sig_map[ptrs[i]->signature] = idx;244}245r_total += ptrs[i]->self_time;246}247248// Send frame, script time, functions information then249r_funcs.resize(to_send);250251FunctionInfo *w = r_funcs.ptrw();252for (int i = 0; i < to_send; i++) {253if (sig_map.has(ptrs[i]->signature)) {254w[i].sig_id = sig_map[ptrs[i]->signature];255}256w[i].call_count = ptrs[i]->call_count;257w[i].total_time = ptrs[i]->total_time / 1000000.0;258w[i].self_time = ptrs[i]->self_time / 1000000.0;259w[i].internal_time = ptrs[i]->internal_time / 1000000.0;260}261}262263ScriptsProfiler() {264info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));265ptrs.resize(info.size());266}267};268269class ServersDebugger::ServersProfiler : public EngineProfiler {270bool skip_profile_frame = false;271typedef ServersDebugger::ServerInfo ServerInfo;272typedef ServersDebugger::ServerFunctionInfo ServerFunctionInfo;273274HashMap<StringName, ServerInfo> server_data;275ScriptsProfiler scripts_profiler;276277double frame_time = 0;278double process_time = 0;279double physics_time = 0;280double physics_frame_time = 0;281282void _send_frame_data(bool p_final) {283ServersDebugger::ServersProfilerFrame frame;284frame.frame_number = Engine::get_singleton()->get_process_frames();285frame.frame_time = frame_time;286frame.process_time = process_time;287frame.physics_time = physics_time;288frame.physics_frame_time = physics_frame_time;289HashMap<StringName, ServerInfo>::Iterator E = server_data.begin();290while (E) {291if (!p_final) {292frame.servers.push_back(E->value);293}294E->value.functions.clear();295++E;296}297uint64_t time = 0;298scripts_profiler.write_frame_data(frame.script_functions, time, p_final);299frame.script_time = USEC_TO_SEC(time);300if (skip_profile_frame) {301skip_profile_frame = false;302return;303}304if (p_final) {305EngineDebugger::get_singleton()->send_message("servers:profile_total", frame.serialize());306} else {307EngineDebugger::get_singleton()->send_message("servers:profile_frame", frame.serialize());308}309}310311public:312void toggle(bool p_enable, const Array &p_opts) {313skip_profile_frame = false;314if (p_enable) {315server_data.clear(); // Clear old profiling data.316} else {317_send_frame_data(true); // Send final frame.318}319scripts_profiler.toggle(p_enable, p_opts);320}321322void add(const Array &p_data) {323String name = p_data[0];324if (!server_data.has(name)) {325ServerInfo info;326info.name = name;327server_data[name] = info;328}329ServerInfo &srv = server_data[name];330331for (int idx = 1; idx < p_data.size() - 1; idx += 2) {332ServerFunctionInfo fi;333fi.name = p_data[idx];334fi.time = p_data[idx + 1];335srv.functions.push_back(fi);336}337}338339void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {340frame_time = p_frame_time;341process_time = p_process_time;342physics_time = p_physics_time;343physics_frame_time = p_physics_frame_time;344_send_frame_data(false);345}346347void skip_frame() {348skip_profile_frame = true;349}350};351352class ServersDebugger::VisualProfiler : public EngineProfiler {353typedef ServersDebugger::ServerInfo ServerInfo;354typedef ServersDebugger::ServerFunctionInfo ServerFunctionInfo;355356HashMap<StringName, ServerInfo> server_data;357358public:359void toggle(bool p_enable, const Array &p_opts) {360RS::get_singleton()->set_frame_profiling_enabled(p_enable);361362// Send hardware information from the remote device so that it's accurate for remote debugging.363Array hardware_info = {364OS::get_singleton()->get_processor_name(),365RenderingServer::get_singleton()->get_video_adapter_name()366};367EngineDebugger::get_singleton()->send_message("visual:hardware_info", hardware_info);368}369370void add(const Array &p_data) {}371372void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {373Vector<RS::FrameProfileArea> profile_areas = RS::get_singleton()->get_frame_profile();374ServersDebugger::VisualProfilerFrame frame;375if (!profile_areas.size()) {376return;377}378379frame.frame_number = RS::get_singleton()->get_frame_profile_frame();380frame.areas.append_array(profile_areas);381EngineDebugger::get_singleton()->send_message("visual:profile_frame", frame.serialize());382}383};384385ServersDebugger *ServersDebugger::singleton = nullptr;386387void ServersDebugger::initialize() {388if (EngineDebugger::is_active()) {389memnew(ServersDebugger);390}391}392393void ServersDebugger::deinitialize() {394if (singleton) {395memdelete(singleton);396}397}398399Error ServersDebugger::_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {400ERR_FAIL_NULL_V(singleton, ERR_BUG);401r_captured = true;402if (p_cmd == "memory") {403singleton->_send_resource_usage();404} else if (p_cmd == "draw") { // Forced redraw.405// For camera override to stay live when the game is paused from the editor.406double delta = 0.0;407if (singleton->last_draw_time) {408delta = (OS::get_singleton()->get_ticks_usec() - singleton->last_draw_time) / 1000000.0;409}410singleton->last_draw_time = OS::get_singleton()->get_ticks_usec();411RenderingServer::get_singleton()->sync();412if (RenderingServer::get_singleton()->has_changed()) {413RenderingServer::get_singleton()->draw(true, delta);414}415EngineDebugger::get_singleton()->send_message("servers:drawn", Array());416} else if (p_cmd == "foreground") {417singleton->last_draw_time = 0.0;418DisplayServer::get_singleton()->window_move_to_foreground();419singleton->servers_profiler->skip_frame();420} else {421r_captured = false;422}423return OK;424}425426void ServersDebugger::_send_resource_usage() {427ServersDebugger::ResourceUsage usage;428429List<RS::TextureInfo> tinfo;430RS::get_singleton()->texture_debug_usage(&tinfo);431432for (const RS::TextureInfo &E : tinfo) {433ServersDebugger::ResourceInfo info;434info.path = E.path;435info.vram = E.bytes;436info.id = E.texture;437438switch (E.type) {439case RS::TextureType::TEXTURE_TYPE_2D:440info.type = "Texture2D";441break;442case RS::TextureType::TEXTURE_TYPE_3D:443info.type = "Texture3D";444break;445case RS::TextureType::TEXTURE_TYPE_LAYERED:446info.type = "TextureLayered";447break;448}449450String possible_type = _get_resource_type_from_path(E.path);451if (!possible_type.is_empty()) {452info.type = possible_type;453}454455if (E.depth == 0) {456info.format = itos(E.width) + "x" + itos(E.height) + " " + Image::get_format_name(E.format);457} else {458info.format = itos(E.width) + "x" + itos(E.height) + "x" + itos(E.depth) + " " + Image::get_format_name(E.format);459}460usage.infos.push_back(info);461}462463List<RS::MeshInfo> mesh_info;464RS::get_singleton()->mesh_debug_usage(&mesh_info);465466for (const RS::MeshInfo &E : mesh_info) {467ServersDebugger::ResourceInfo info;468info.path = E.path;469// We use 64-bit integers to avoid overflow, if for whatever reason, the sum is bigger than 4GB.470uint64_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;471// But can info.vram even hold that, and why is it an int instead of an uint?472info.vram = vram;473474// Even though these empty meshes can be indicative of issues somewhere else475// for UX reasons, we don't want to show them.476if (vram == 0 && E.path.is_empty()) {477continue;478}479480info.id = E.mesh;481info.type = "Mesh";482String possible_type = _get_resource_type_from_path(E.path);483if (!possible_type.is_empty()) {484info.type = possible_type;485}486487info.format = itos(E.vertex_count) + " Vertices";488usage.infos.push_back(info);489}490491EngineDebugger::get_singleton()->send_message("servers:memory_usage", usage.serialize());492}493494// Done on a best-effort basis.495String ServersDebugger::_get_resource_type_from_path(const String &p_path) {496if (p_path.is_empty()) {497return "";498}499500if (!ResourceLoader::exists(p_path)) {501return "";502}503504if (ResourceCache::has(p_path)) {505Ref<Resource> resource = ResourceCache::get_ref(p_path);506return resource->get_class();507} else {508// This doesn't work all the time for embedded resources.509String resource_type = ResourceLoader::get_resource_type(p_path);510if (resource_type != "") {511return resource_type;512}513}514515return "";516}517518ServersDebugger::ServersDebugger() {519singleton = this;520521// Generic servers profiler (audio/physics/...)522servers_profiler.instantiate();523servers_profiler->bind("servers");524525// Visual Profiler (cpu/gpu times)526visual_profiler.instantiate();527visual_profiler->bind("visual");528529EngineDebugger::Capture servers_cap(nullptr, &_capture);530EngineDebugger::register_message_capture("servers", servers_cap);531}532533ServersDebugger::~ServersDebugger() {534EngineDebugger::unregister_message_capture("servers");535singleton = nullptr;536}537538539