Path: blob/master/editor/debugger/editor_visual_profiler.cpp
9902 views
/**************************************************************************/1/* editor_visual_profiler.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 "editor_visual_profiler.h"3132#include "core/io/image.h"33#include "editor/editor_string_names.h"34#include "editor/run/editor_run_bar.h"35#include "editor/settings/editor_settings.h"36#include "editor/themes/editor_scale.h"37#include "scene/gui/flow_container.h"38#include "scene/resources/image_texture.h"3940void EditorVisualProfiler::set_hardware_info(const String &p_cpu_name, const String &p_gpu_name) {41cpu_name = p_cpu_name;42gpu_name = p_gpu_name;43queue_redraw();44}4546void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) {47++last_metric;48if (last_metric >= frame_metrics.size()) {49last_metric = 0;50}5152frame_metrics.write[last_metric] = p_metric;5354List<String> stack;55for (int i = 0; i < frame_metrics[last_metric].areas.size(); i++) {56String name = frame_metrics[last_metric].areas[i].name;57frame_metrics.write[last_metric].areas.write[i].color_cache = _get_color_from_signature(name);58String full_name;5960if (name[0] == '<') {61stack.pop_back();62}6364if (stack.size()) {65full_name = stack.back()->get() + name;66} else {67full_name = name;68}6970if (name[0] == '>') {71stack.push_back(full_name + "/");72}7374frame_metrics.write[last_metric].areas.write[i].fullpath_cache = full_name;75}7677updating_frame = true;78clear_button->set_disabled(false);79cursor_metric_edit->set_max(frame_metrics[last_metric].frame_number);80cursor_metric_edit->set_min(MAX(int64_t(frame_metrics[last_metric].frame_number) - frame_metrics.size(), 0));8182if (!seeking) {83cursor_metric_edit->set_value(frame_metrics[last_metric].frame_number);84if (hover_metric != -1) {85hover_metric++;86if (hover_metric >= frame_metrics.size()) {87hover_metric = 0;88}89}90}91updating_frame = false;9293if (frame_delay->is_stopped()) {94frame_delay->set_wait_time(0.1);95frame_delay->start();96}9798if (plot_delay->is_stopped()) {99plot_delay->set_wait_time(0.1);100plot_delay->start();101}102}103104void EditorVisualProfiler::clear() {105int metric_size = EDITOR_GET("debugger/profiler_frame_history_size");106metric_size = CLAMP(metric_size, 60, 10000);107frame_metrics.clear();108frame_metrics.resize(metric_size);109last_metric = -1;110variables->clear();111//activate->set_pressed(false);112113graph_limit = 1000.0f / CLAMP(int(EDITOR_GET("debugger/profiler_target_fps")), 1, 1000);114115updating_frame = true;116cursor_metric_edit->set_min(0);117cursor_metric_edit->set_max(0);118cursor_metric_edit->set_value(0);119updating_frame = false;120hover_metric = -1;121seeking = false;122}123124String EditorVisualProfiler::_get_time_as_text(float p_time) {125int dmode = display_mode->get_selected();126127if (dmode == DISPLAY_FRAME_TIME) {128return TS->format_number(String::num(p_time, 2)) + " " + TTR("ms");129} else if (dmode == DISPLAY_FRAME_PERCENT) {130return TS->format_number(String::num(p_time * 100 / graph_limit, 2)) + " " + TS->percent_sign();131}132133return "err";134}135136Color EditorVisualProfiler::_get_color_from_signature(const StringName &p_signature) const {137Color bc = get_theme_color(SNAME("error_color"), EditorStringName(Editor));138double rot = Math::abs(double(p_signature.hash()) / double(0x7FFFFFFF));139Color c;140c.set_hsv(rot, bc.get_s(), bc.get_v());141return c.lerp(get_theme_color(SNAME("base_color"), EditorStringName(Editor)), 0.07);142}143144void EditorVisualProfiler::_item_selected() {145if (updating_frame) {146return;147}148149TreeItem *item = variables->get_selected();150if (!item) {151return;152}153selected_area = item->get_metadata(0);154_update_plot();155}156157void EditorVisualProfiler::_update_plot() {158const int w = graph->get_size().width + 1; // `+1` is to prevent from crashing when visual profiler is auto started.159const int h = graph->get_size().height + 1;160161bool reset_texture = false;162163const int desired_len = w * h * 4;164165if (graph_image.size() != desired_len) {166reset_texture = true;167graph_image.resize(desired_len);168}169170uint8_t *wr = graph_image.ptrw();171const Color background_color = get_theme_color("dark_color_2", EditorStringName(Editor));172173// Clear the previous frame and set the background color.174for (int i = 0; i < desired_len; i += 4) {175wr[i + 0] = Math::fast_ftoi(background_color.r * 255);176wr[i + 1] = Math::fast_ftoi(background_color.g * 255);177wr[i + 2] = Math::fast_ftoi(background_color.b * 255);178wr[i + 3] = 255;179}180181//find highest value182183float highest_cpu = 0;184float highest_gpu = 0;185186for (int i = 0; i < frame_metrics.size(); i++) {187const Metric &m = frame_metrics[i];188if (!m.valid) {189continue;190}191192if (m.areas.size()) {193highest_cpu = MAX(highest_cpu, m.areas[m.areas.size() - 1].cpu_time);194highest_gpu = MAX(highest_gpu, m.areas[m.areas.size() - 1].gpu_time);195}196}197198if (highest_cpu > 0 || highest_gpu > 0) {199if (frame_relative->is_pressed()) {200highest_cpu = MAX(graph_limit, highest_cpu);201highest_gpu = MAX(graph_limit, highest_gpu);202}203204if (linked->is_pressed()) {205float highest = MAX(highest_cpu, highest_gpu);206highest_cpu = highest_gpu = highest;207}208209//means some data exists..210highest_cpu *= 1.2; //leave some upper room211highest_gpu *= 1.2; //leave some upper room212graph_height_cpu = highest_cpu;213graph_height_gpu = highest_gpu;214215Vector<Color> columnv_cpu;216columnv_cpu.resize(h);217Color *column_cpu = columnv_cpu.ptrw();218219Vector<Color> columnv_gpu;220columnv_gpu.resize(h);221Color *column_gpu = columnv_gpu.ptrw();222223int half_w = w / 2;224for (int i = 0; i < half_w; i++) {225for (int j = 0; j < h; j++) {226column_cpu[j] = Color(0, 0, 0, 0);227column_gpu[j] = Color(0, 0, 0, 0);228}229230int current = i * frame_metrics.size() / half_w;231int next = (i + 1) * frame_metrics.size() / half_w;232if (next > frame_metrics.size()) {233next = frame_metrics.size();234}235if (next == current) {236next = current + 1; //just because for loop must work237}238239for (int j = current; j < next; j++) {240//wrap241int idx = last_metric + 1 + j;242while (idx >= frame_metrics.size()) {243idx -= frame_metrics.size();244}245246int area_count = frame_metrics[idx].areas.size();247const Metric::Area *areas = frame_metrics[idx].areas.ptr();248int prev_cpu = 0;249int prev_gpu = 0;250for (int k = 1; k < area_count; k++) {251int ofs_cpu = int(areas[k].cpu_time * h / highest_cpu);252ofs_cpu = CLAMP(ofs_cpu, 0, h - 1);253Color color = selected_area == areas[k - 1].fullpath_cache ? Color(1, 1, 1, 1) : areas[k - 1].color_cache;254255for (int l = prev_cpu; l < ofs_cpu; l++) {256column_cpu[h - l - 1] += color;257}258prev_cpu = ofs_cpu;259260int ofs_gpu = int(areas[k].gpu_time * h / highest_gpu);261ofs_gpu = CLAMP(ofs_gpu, 0, h - 1);262for (int l = prev_gpu; l < ofs_gpu; l++) {263column_gpu[h - l - 1] += color;264}265266prev_gpu = ofs_gpu;267}268}269270//plot CPU271for (int j = 0; j < h; j++) {272uint8_t r, g, b;273274if (column_cpu[j].a == 0) {275r = Math::fast_ftoi(background_color.r * 255);276g = Math::fast_ftoi(background_color.g * 255);277b = Math::fast_ftoi(background_color.b * 255);278} else {279r = CLAMP((column_cpu[j].r / column_cpu[j].a) * 255.0, 0, 255);280g = CLAMP((column_cpu[j].g / column_cpu[j].a) * 255.0, 0, 255);281b = CLAMP((column_cpu[j].b / column_cpu[j].a) * 255.0, 0, 255);282}283284int widx = (j * w + i) * 4;285wr[widx + 0] = r;286wr[widx + 1] = g;287wr[widx + 2] = b;288wr[widx + 3] = 255;289}290//plot GPU291for (int j = 0; j < h; j++) {292uint8_t r, g, b;293294if (column_gpu[j].a == 0) {295r = Math::fast_ftoi(background_color.r * 255);296g = Math::fast_ftoi(background_color.g * 255);297b = Math::fast_ftoi(background_color.b * 255);298} else {299r = CLAMP((column_gpu[j].r / column_gpu[j].a) * 255.0, 0, 255);300g = CLAMP((column_gpu[j].g / column_gpu[j].a) * 255.0, 0, 255);301b = CLAMP((column_gpu[j].b / column_gpu[j].a) * 255.0, 0, 255);302}303304int widx = (j * w + w / 2 + i) * 4;305wr[widx + 0] = r;306wr[widx + 1] = g;307wr[widx + 2] = b;308wr[widx + 3] = 255;309}310}311}312313Ref<Image> img = Image::create_from_data(w, h, false, Image::FORMAT_RGBA8, graph_image);314315if (reset_texture) {316if (graph_texture.is_null()) {317graph_texture.instantiate();318}319graph_texture->set_image(img);320}321322graph_texture->update(img);323324graph->set_texture(graph_texture);325graph->queue_redraw();326}327328void EditorVisualProfiler::_update_frame(bool p_focus_selected) {329int cursor_metric = _get_cursor_index();330331Ref<Texture> track_icon = get_editor_theme_icon(SNAME("TrackColor"));332333ERR_FAIL_INDEX(cursor_metric, frame_metrics.size());334335updating_frame = true;336variables->clear();337338TreeItem *root = variables->create_item();339const Metric &m = frame_metrics[cursor_metric];340341List<TreeItem *> stack;342List<TreeItem *> categories;343344TreeItem *ensure_selected = nullptr;345346for (int i = 1; i < m.areas.size() - 1; i++) {347TreeItem *parent = stack.size() ? stack.back()->get() : root;348349String name = m.areas[i].name;350351float cpu_time = m.areas[i].cpu_time;352float gpu_time = m.areas[i].gpu_time;353if (i < m.areas.size() - 1) {354cpu_time = m.areas[i + 1].cpu_time - cpu_time;355gpu_time = m.areas[i + 1].gpu_time - gpu_time;356}357358if (name.begins_with(">")) {359TreeItem *category = variables->create_item(parent);360361stack.push_back(category);362categories.push_back(category);363364name = name.substr(1);365366category->set_text(0, name);367category->set_metadata(1, cpu_time);368category->set_metadata(2, gpu_time);369continue;370}371372if (name.begins_with("<")) {373stack.pop_back();374continue;375}376TreeItem *category = variables->create_item(parent);377378for (TreeItem *E : stack) {379float total_cpu = E->get_metadata(1);380float total_gpu = E->get_metadata(2);381total_cpu += cpu_time;382total_gpu += gpu_time;383E->set_metadata(1, total_cpu);384E->set_metadata(2, total_gpu);385}386387category->set_icon(0, track_icon);388category->set_icon_modulate(0, m.areas[i].color_cache);389category->set_selectable(0, true);390category->set_metadata(0, m.areas[i].fullpath_cache);391category->set_text(0, m.areas[i].name);392category->set_text(1, _get_time_as_text(cpu_time));393category->set_metadata(1, m.areas[i].cpu_time);394category->set_text(2, _get_time_as_text(gpu_time));395category->set_metadata(2, m.areas[i].gpu_time);396397if (selected_area == m.areas[i].fullpath_cache) {398category->select(0);399if (p_focus_selected) {400ensure_selected = category;401}402}403}404405for (TreeItem *E : categories) {406float total_cpu = E->get_metadata(1);407float total_gpu = E->get_metadata(2);408E->set_text(1, _get_time_as_text(total_cpu));409E->set_text(2, _get_time_as_text(total_gpu));410}411412if (ensure_selected) {413variables->ensure_cursor_is_visible();414}415updating_frame = false;416}417418void EditorVisualProfiler::_activate_pressed() {419if (activate->is_pressed()) {420activate->set_button_icon(get_editor_theme_icon(SNAME("Stop")));421activate->set_text(TTR("Stop"));422_clear_pressed(); //always clear on start423clear_button->set_disabled(false);424} else {425activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));426activate->set_text(TTR("Start"));427}428emit_signal(SNAME("enable_profiling"), activate->is_pressed());429}430431void EditorVisualProfiler::_clear_pressed() {432clear_button->set_disabled(true);433clear();434_update_plot();435}436437void EditorVisualProfiler::_autostart_toggled(bool p_toggled_on) {438EditorSettings::get_singleton()->set_project_metadata("debug_options", "autostart_visual_profiler", p_toggled_on);439EditorRunBar::get_singleton()->update_profiler_autostart_indicator();440}441442void EditorVisualProfiler::_notification(int p_what) {443switch (p_what) {444case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:445case NOTIFICATION_THEME_CHANGED:446case NOTIFICATION_TRANSLATION_CHANGED: {447activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));448clear_button->set_button_icon(get_editor_theme_icon(SNAME("Clear")));449} break;450}451}452453void EditorVisualProfiler::_graph_tex_draw() {454if (last_metric < 0) {455return;456}457458Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));459int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));460const Color color = get_theme_color(SceneStringName(font_color), EditorStringName(Editor));461462if (seeking) {463int max_frames = frame_metrics.size();464int frame = cursor_metric_edit->get_value() - (frame_metrics[last_metric].frame_number - max_frames + 1);465if (frame < 0) {466frame = 0;467}468469int half_width = graph->get_size().x / 2;470int cur_x = frame * half_width / max_frames;471472graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), color * Color(1, 1, 1));473graph->draw_line(Vector2(cur_x + half_width, 0), Vector2(cur_x + half_width, graph->get_size().y), color * Color(1, 1, 1));474}475476if (graph_height_cpu > 0) {477int frame_y = graph->get_size().y - graph_limit * graph->get_size().y / graph_height_cpu - 1;478479int half_width = graph->get_size().x / 2;480481graph->draw_line(Vector2(0, frame_y), Vector2(half_width, frame_y), color * Color(1, 1, 1, 0.5));482483const String limit_str = String::num(graph_limit, 2) + " ms";484graph->draw_string(font, Vector2(half_width - font->get_string_size(limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x - 2, frame_y - 2), limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75));485}486487if (graph_height_gpu > 0) {488int frame_y = graph->get_size().y - graph_limit * graph->get_size().y / graph_height_gpu - 1;489490int half_width = graph->get_size().x / 2;491492graph->draw_line(Vector2(half_width, frame_y), Vector2(graph->get_size().x, frame_y), color * Color(1, 1, 1, 0.5));493494const String limit_str = String::num(graph_limit, 2) + " ms";495graph->draw_string(font, Vector2(half_width * 2 - font->get_string_size(limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x - 2, frame_y - 2), limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75));496}497498graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x, font->get_ascent(font_size) + 2), "CPU: " + cpu_name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75));499graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x + graph->get_size().width / 2, font->get_ascent(font_size) + 2), "GPU: " + gpu_name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75));500}501502void EditorVisualProfiler::_graph_tex_mouse_exit() {503hover_metric = -1;504graph->queue_redraw();505}506507void EditorVisualProfiler::_cursor_metric_changed(double) {508if (updating_frame) {509return;510}511512graph->queue_redraw();513_update_frame();514}515516void EditorVisualProfiler::_graph_tex_input(const Ref<InputEvent> &p_ev) {517if (last_metric < 0) {518return;519}520521Ref<InputEventMouse> me = p_ev;522Ref<InputEventMouseButton> mb = p_ev;523Ref<InputEventMouseMotion> mm = p_ev;524525if (526(mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) ||527(mm.is_valid())) {528int half_w = graph->get_size().width / 2;529int x = me->get_position().x;530if (x > half_w) {531x -= half_w;532}533x = x * frame_metrics.size() / half_w;534535bool show_hover = x >= 0 && x < frame_metrics.size();536537if (x < 0) {538x = 0;539}540541if (x >= frame_metrics.size()) {542x = frame_metrics.size() - 1;543}544545int metric = frame_metrics.size() - x - 1;546metric = last_metric - metric;547while (metric < 0) {548metric += frame_metrics.size();549}550551if (show_hover) {552hover_metric = metric;553554} else {555hover_metric = -1;556}557558if (mb.is_valid() || mm->get_button_mask().has_flag(MouseButtonMask::LEFT)) {559//cursor_metric=x;560updating_frame = true;561562//metric may be invalid, so look for closest metric that is valid, this makes snap feel better563bool valid = false;564for (int i = 0; i < frame_metrics.size(); i++) {565if (frame_metrics[metric].valid) {566valid = true;567break;568}569570metric++;571if (metric >= frame_metrics.size()) {572metric = 0;573}574}575576if (!valid) {577return;578}579580cursor_metric_edit->set_value(frame_metrics[metric].frame_number);581582updating_frame = false;583584if (activate->is_pressed()) {585if (!seeking) {586// Break request is not required, just stop profiling587}588}589590seeking = true;591592if (!frame_delay->is_processing()) {593frame_delay->set_wait_time(0.1);594frame_delay->start();595}596597bool touched_cpu = me->get_position().x < graph->get_size().width * 0.5;598599const Metric::Area *areas = frame_metrics[metric].areas.ptr();600int area_count = frame_metrics[metric].areas.size();601float posy = (1.0 - (me->get_position().y / graph->get_size().height)) * (touched_cpu ? graph_height_cpu : graph_height_gpu);602int last_valid = -1;603bool found = false;604for (int i = 0; i < area_count - 1; i++) {605if (areas[i].name[0] != '<' && areas[i].name[0] != '>') {606last_valid = i;607}608float h = touched_cpu ? areas[i + 1].cpu_time : areas[i + 1].gpu_time;609610if (h > posy) {611found = true;612break;613}614}615616StringName area_found;617if (found && last_valid != -1) {618area_found = areas[last_valid].fullpath_cache;619}620621if (area_found != selected_area) {622selected_area = area_found;623_update_frame(true);624_update_plot();625}626}627628graph->queue_redraw();629}630}631632int EditorVisualProfiler::_get_cursor_index() const {633if (last_metric < 0) {634return 0;635}636if (!frame_metrics[last_metric].valid) {637return 0;638}639640int diff = (frame_metrics[last_metric].frame_number - cursor_metric_edit->get_value());641642int idx = last_metric - diff;643while (idx < 0) {644idx += frame_metrics.size();645}646647return idx;648}649650void EditorVisualProfiler::disable_seeking() {651seeking = false;652graph->queue_redraw();653}654655void EditorVisualProfiler::_combo_changed(int) {656_update_frame();657_update_plot();658}659660void EditorVisualProfiler::_bind_methods() {661ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable")));662}663664void EditorVisualProfiler::_update_button_text() {665if (activate->is_pressed()) {666activate->set_button_icon(get_editor_theme_icon(SNAME("Stop")));667activate->set_text(TTR("Stop"));668} else {669activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));670activate->set_text(TTR("Start"));671}672}673674void EditorVisualProfiler::set_enabled(bool p_enable) {675activate->set_disabled(!p_enable);676}677678void EditorVisualProfiler::set_profiling(bool p_profiling) {679activate->set_pressed(p_profiling);680_update_button_text();681emit_signal(SNAME("enable_profiling"), activate->is_pressed());682}683684bool EditorVisualProfiler::is_profiling() {685return activate->is_pressed();686}687688Vector<Vector<String>> EditorVisualProfiler::get_data_as_csv() const {689Vector<Vector<String>> res;690#if 0691if (frame_metrics.is_empty()) {692return res;693}694695// signatures696Vector<String> signatures;697const Vector<EditorFrameProfiler::Metric::Category> &categories = frame_metrics[0].categories;698699for (int j = 0; j < categories.size(); j++) {700const EditorFrameProfiler::Metric::Category &c = categories[j];701signatures.push_back(c.signature);702703for (int k = 0; k < c.items.size(); k++) {704signatures.push_back(c.items[k].signature);705}706}707res.push_back(signatures);708709// values710Vector<String> values;711values.resize(signatures.size());712713int index = last_metric;714715for (int i = 0; i < frame_metrics.size(); i++) {716++index;717718if (index >= frame_metrics.size()) {719index = 0;720}721722if (!frame_metrics[index].valid) {723continue;724}725int it = 0;726const Vector<EditorFrameProfiler::Metric::Category> &frame_cat = frame_metrics[index].categories;727728for (int j = 0; j < frame_cat.size(); j++) {729const EditorFrameProfiler::Metric::Category &c = frame_cat[j];730values.write[it++] = String::num_real(c.total_time);731732for (int k = 0; k < c.items.size(); k++) {733values.write[it++] = String::num_real(c.items[k].total);734}735}736res.push_back(values);737}738#endif739return res;740}741742EditorVisualProfiler::EditorVisualProfiler() {743HBoxContainer *hb = memnew(HBoxContainer);744hb->add_theme_constant_override(SNAME("separation"), 8 * EDSCALE);745add_child(hb);746747FlowContainer *container = memnew(FlowContainer);748container->set_h_size_flags(SIZE_EXPAND_FILL);749container->add_theme_constant_override(SNAME("h_separation"), 8 * EDSCALE);750container->add_theme_constant_override(SNAME("v_separation"), 2 * EDSCALE);751hb->add_child(container);752753activate = memnew(Button);754activate->set_toggle_mode(true);755activate->set_disabled(true);756activate->set_text(TTR("Start"));757activate->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_activate_pressed));758container->add_child(activate);759760clear_button = memnew(Button);761clear_button->set_text(TTR("Clear"));762clear_button->set_disabled(true);763clear_button->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_clear_pressed));764container->add_child(clear_button);765766CheckBox *autostart_checkbox = memnew(CheckBox);767autostart_checkbox->set_text(TTR("Autostart"));768autostart_checkbox->set_pressed(EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false));769autostart_checkbox->connect(SceneStringName(toggled), callable_mp(this, &EditorVisualProfiler::_autostart_toggled));770container->add_child(autostart_checkbox);771772HBoxContainer *hb_measure = memnew(HBoxContainer);773hb_measure->add_theme_constant_override(SNAME("separation"), 2 * EDSCALE);774container->add_child(hb_measure);775776hb_measure->add_child(memnew(Label(TTR("Measure:"))));777778display_mode = memnew(OptionButton);779display_mode->set_accessibility_name(TTRC("Measure:"));780display_mode->add_item(TTR("Frame Time (ms)"));781display_mode->add_item(TTR("Frame %"));782display_mode->connect(SceneStringName(item_selected), callable_mp(this, &EditorVisualProfiler::_combo_changed));783784hb_measure->add_child(display_mode);785786frame_relative = memnew(CheckBox(TTR("Fit to Frame")));787frame_relative->set_pressed(true);788container->add_child(frame_relative);789frame_relative->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_update_plot));790linked = memnew(CheckBox(TTR("Linked")));791linked->set_pressed(true);792container->add_child(linked);793linked->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_update_plot));794795HBoxContainer *hb_frame = memnew(HBoxContainer);796hb_frame->add_theme_constant_override(SNAME("separation"), 2 * EDSCALE);797hb_frame->set_v_size_flags(SIZE_SHRINK_BEGIN);798hb->add_child(hb_frame);799800hb_frame->add_child(memnew(Label(TTR("Frame #:"))));801802cursor_metric_edit = memnew(SpinBox);803cursor_metric_edit->set_accessibility_name(TTRC("Frame #:"));804cursor_metric_edit->set_h_size_flags(SIZE_FILL);805hb_frame->add_child(cursor_metric_edit);806cursor_metric_edit->connect(SceneStringName(value_changed), callable_mp(this, &EditorVisualProfiler::_cursor_metric_changed));807808h_split = memnew(HSplitContainer);809add_child(h_split);810h_split->set_v_size_flags(SIZE_EXPAND_FILL);811812variables = memnew(Tree);813variables->set_custom_minimum_size(Size2(300, 0) * EDSCALE);814variables->set_hide_folding(true);815h_split->add_child(variables);816variables->set_hide_root(true);817variables->set_columns(3);818variables->set_column_titles_visible(true);819variables->set_column_title(0, TTR("Name"));820variables->set_column_expand(0, true);821variables->set_column_clip_content(0, true);822variables->set_column_custom_minimum_width(0, 60);823variables->set_column_title(1, TTR("CPU"));824variables->set_column_expand(1, false);825variables->set_column_clip_content(1, true);826variables->set_column_custom_minimum_width(1, 75 * EDSCALE);827variables->set_column_title(2, TTR("GPU"));828variables->set_column_expand(2, false);829variables->set_column_clip_content(2, true);830variables->set_column_custom_minimum_width(2, 75 * EDSCALE);831variables->set_theme_type_variation("TreeSecondary");832variables->connect("cell_selected", callable_mp(this, &EditorVisualProfiler::_item_selected));833834graph = memnew(TextureRect);835graph->set_custom_minimum_size(Size2(250 * EDSCALE, 0));836graph->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);837graph->set_mouse_filter(MOUSE_FILTER_STOP);838graph->connect(SceneStringName(draw), callable_mp(this, &EditorVisualProfiler::_graph_tex_draw));839graph->connect(SceneStringName(gui_input), callable_mp(this, &EditorVisualProfiler::_graph_tex_input));840graph->connect(SceneStringName(mouse_exited), callable_mp(this, &EditorVisualProfiler::_graph_tex_mouse_exit));841842h_split->add_child(graph);843graph->set_h_size_flags(SIZE_EXPAND_FILL);844845int metric_size = CLAMP(int(EDITOR_GET("debugger/profiler_frame_history_size")), 60, 10000);846frame_metrics.resize(metric_size);847848graph_limit = 1000.0f / CLAMP(int(EDITOR_GET("debugger/profiler_target_fps")), 1, 1000);849850frame_delay = memnew(Timer);851frame_delay->set_wait_time(0.1);852frame_delay->set_one_shot(true);853add_child(frame_delay);854frame_delay->connect("timeout", callable_mp(this, &EditorVisualProfiler::_update_frame).bind(false));855856plot_delay = memnew(Timer);857plot_delay->set_wait_time(0.1);858plot_delay->set_one_shot(true);859add_child(plot_delay);860plot_delay->connect("timeout", callable_mp(this, &EditorVisualProfiler::_update_plot));861}862863864