2017-01-16 07:04:19 +00:00
/**************************************************************************/
/* editor_profiler.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-04 23:50:27 +00:00
2016-05-22 00:18:16 +00:00
# include "editor_profiler.h"
2017-01-16 07:04:19 +00:00
2018-09-11 16:13:45 +00:00
# include "core/os/os.h"
2020-03-08 11:21:08 +00:00
# include "editor/editor_settings.h"
2023-08-13 00:33:39 +00:00
# include "editor/editor_string_names.h"
2024-01-15 12:14:55 +00:00
# include "editor/themes/editor_scale.h"
2023-07-11 20:29:09 +00:00
# include "scene/resources/image_texture.h"
2016-05-22 00:18:16 +00:00
void EditorProfiler : : _make_metric_ptrs ( Metric & m ) {
for ( int i = 0 ; i < m . categories . size ( ) ; i + + ) {
2018-07-25 01:11:03 +00:00
m . category_ptrs [ m . categories [ i ] . signature ] = & m . categories . write [ i ] ;
2016-05-22 00:18:16 +00:00
for ( int j = 0 ; j < m . categories [ i ] . items . size ( ) ; j + + ) {
2018-07-25 01:11:03 +00:00
m . item_ptrs [ m . categories [ i ] . items [ j ] . signature ] = & m . categories . write [ i ] . items . write [ j ] ;
2016-05-22 00:18:16 +00:00
}
}
}
2020-04-17 16:50:05 +00:00
EditorProfiler : : Metric EditorProfiler : : _get_frame_metric ( int index ) {
return frame_metrics [ ( frame_metrics . size ( ) + last_metric - ( total_metrics - 1 ) + index ) % frame_metrics . size ( ) ] ;
}
2016-05-22 00:18:16 +00:00
void EditorProfiler : : add_frame_metric ( const Metric & p_metric , bool p_final ) {
+ + last_metric ;
2020-05-14 14:41:43 +00:00
if ( last_metric > = frame_metrics . size ( ) ) {
2016-05-22 00:18:16 +00:00
last_metric = 0 ;
2020-05-14 14:41:43 +00:00
}
2016-05-22 00:18:16 +00:00
2020-04-17 16:50:05 +00:00
total_metrics + + ;
if ( total_metrics > frame_metrics . size ( ) ) {
total_metrics = frame_metrics . size ( ) ;
}
2018-07-25 01:11:03 +00:00
frame_metrics . write [ last_metric ] = p_metric ;
_make_metric_ptrs ( frame_metrics . write [ last_metric ] ) ;
2016-05-22 00:18:16 +00:00
updating_frame = true ;
2020-04-17 16:50:05 +00:00
clear_button - > set_disabled ( false ) ;
cursor_metric_edit - > set_editable ( true ) ;
cursor_metric_edit - > set_max ( p_metric . frame_number ) ;
cursor_metric_edit - > set_min ( _get_frame_metric ( 0 ) . frame_number ) ;
2016-05-22 00:18:16 +00:00
if ( ! seeking ) {
2020-04-17 16:50:05 +00:00
cursor_metric_edit - > set_value ( p_metric . frame_number ) ;
2016-05-22 00:18:16 +00:00
}
2020-04-17 16:50:05 +00:00
2016-05-22 00:18:16 +00:00
updating_frame = false ;
2018-06-20 00:42:29 +00:00
if ( frame_delay - > is_stopped ( ) ) {
2016-05-22 00:18:16 +00:00
frame_delay - > set_wait_time ( p_final ? 0.1 : 1 ) ;
frame_delay - > start ( ) ;
}
2018-06-20 00:42:29 +00:00
if ( plot_delay - > is_stopped ( ) ) {
2016-05-22 00:18:16 +00:00
plot_delay - > set_wait_time ( 0.1 ) ;
plot_delay - > start ( ) ;
}
}
void EditorProfiler : : clear ( ) {
2022-10-18 14:43:37 +00:00
int metric_size = EDITOR_GET ( " debugger/profiler_frame_history_size " ) ;
2022-05-27 23:03:32 +00:00
metric_size = CLAMP ( metric_size , 60 , 10000 ) ;
2016-05-22 00:18:16 +00:00
frame_metrics . clear ( ) ;
frame_metrics . resize ( metric_size ) ;
2020-04-17 16:50:05 +00:00
total_metrics = 0 ;
2016-05-22 00:18:16 +00:00
last_metric = - 1 ;
variables - > clear ( ) ;
plot_sigs . clear ( ) ;
2017-09-30 14:19:07 +00:00
plot_sigs . insert ( " physics_frame_time " ) ;
2016-05-22 00:18:16 +00:00
plot_sigs . insert ( " category_frame_time " ) ;
2023-03-05 12:37:11 +00:00
display_internal_profiles - > set_visible ( EDITOR_GET ( " debugger/profile_native_calls " ) ) ;
2016-05-22 00:18:16 +00:00
updating_frame = true ;
cursor_metric_edit - > set_min ( 0 ) ;
2019-11-26 11:09:58 +00:00
cursor_metric_edit - > set_max ( 100 ) ; // Doesn't make much sense, but we can't have min == max. Doesn't hurt.
2017-01-04 04:16:14 +00:00
cursor_metric_edit - > set_value ( 0 ) ;
2020-04-17 16:50:05 +00:00
cursor_metric_edit - > set_editable ( false ) ;
2016-05-22 00:18:16 +00:00
updating_frame = false ;
hover_metric = - 1 ;
seeking = false ;
2022-09-06 15:15:00 +00:00
// Ensure button text (start, stop) is correct
2022-12-16 15:35:44 +00:00
_update_button_text ( ) ;
2022-09-06 15:15:00 +00:00
emit_signal ( SNAME ( " enable_profiling " ) , activate - > is_pressed ( ) ) ;
2016-05-22 00:18:16 +00:00
}
static String _get_percent_txt ( float p_value , float p_total ) {
2020-01-24 11:07:38 +00:00
if ( p_total = = 0 ) {
2016-05-22 00:18:16 +00:00
p_total = 0.00001 ;
2020-01-24 11:07:38 +00:00
}
2020-09-03 11:22:16 +00:00
return TS - > format_number ( String : : num ( ( p_value / p_total ) * 100 , 1 ) ) + TS - > percent_sign ( ) ;
2016-05-22 00:18:16 +00:00
}
2018-07-25 01:11:03 +00:00
String EditorProfiler : : _get_time_as_text ( const Metric & m , float p_time , int p_calls ) {
2020-01-24 11:07:38 +00:00
const int dmode = display_mode - > get_selected ( ) ;
2016-05-22 00:18:16 +00:00
if ( dmode = = DISPLAY_FRAME_TIME ) {
2020-09-03 11:22:16 +00:00
return TS - > format_number ( rtos ( p_time * 1000 ) . pad_decimals ( 2 ) ) + " " + RTR ( " ms " ) ;
2016-05-22 00:18:16 +00:00
} else if ( dmode = = DISPLAY_AVERAGE_TIME ) {
2020-01-24 11:07:38 +00:00
if ( p_calls = = 0 ) {
2020-09-03 11:22:16 +00:00
return TS - > format_number ( " 0.00 " ) + " " + RTR ( " ms " ) ;
2020-01-24 11:07:38 +00:00
} else {
2020-09-03 11:22:16 +00:00
return TS - > format_number ( rtos ( ( p_time / p_calls ) * 1000 ) . pad_decimals ( 2 ) ) + " " + RTR ( " ms " ) ;
2020-01-24 11:07:38 +00:00
}
2016-05-22 00:18:16 +00:00
} else if ( dmode = = DISPLAY_FRAME_PERCENT ) {
return _get_percent_txt ( p_time , m . frame_time ) ;
2017-09-30 14:19:07 +00:00
} else if ( dmode = = DISPLAY_PHYSICS_FRAME_PERCENT ) {
return _get_percent_txt ( p_time , m . physics_frame_time ) ;
2016-05-22 00:18:16 +00:00
}
return " err " ;
}
Color EditorProfiler : : _get_color_from_signature ( const StringName & p_signature ) const {
2023-08-13 00:33:39 +00:00
Color bc = get_theme_color ( SNAME ( " error_color " ) , EditorStringName ( Editor ) ) ;
2016-05-22 00:18:16 +00:00
double rot = ABS ( double ( p_signature . hash ( ) ) / double ( 0x7FFFFFFF ) ) ;
Color c ;
2017-08-08 02:55:24 +00:00
c . set_hsv ( rot , bc . get_s ( ) , bc . get_v ( ) ) ;
2023-08-13 00:33:39 +00:00
return c . lerp ( get_theme_color ( SNAME ( " base_color " ) , EditorStringName ( Editor ) ) , 0.07 ) ;
2016-05-22 00:18:16 +00:00
}
void EditorProfiler : : _item_edited ( ) {
2020-05-14 14:41:43 +00:00
if ( updating_frame ) {
2016-05-22 00:18:16 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-05-22 00:18:16 +00:00
TreeItem * item = variables - > get_edited ( ) ;
2020-05-14 14:41:43 +00:00
if ( ! item ) {
2016-05-22 00:18:16 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-05-22 00:18:16 +00:00
StringName signature = item - > get_metadata ( 0 ) ;
bool checked = item - > is_checked ( 0 ) ;
2020-05-14 14:41:43 +00:00
if ( checked ) {
2016-05-22 00:18:16 +00:00
plot_sigs . insert ( signature ) ;
2020-05-14 14:41:43 +00:00
} else {
2016-05-22 00:18:16 +00:00
plot_sigs . erase ( signature ) ;
2020-05-14 14:41:43 +00:00
}
2016-05-22 00:18:16 +00:00
if ( ! frame_delay - > is_processing ( ) ) {
frame_delay - > set_wait_time ( 0.1 ) ;
frame_delay - > start ( ) ;
}
2016-07-21 21:01:57 +00:00
_update_plot ( ) ;
2016-05-22 00:18:16 +00:00
}
void EditorProfiler : : _update_plot ( ) {
2020-01-30 02:05:48 +00:00
const int w = graph - > get_size ( ) . width ;
const int h = graph - > get_size ( ) . height ;
2018-08-28 09:49:12 +00:00
bool reset_texture = false ;
2020-01-30 02:05:48 +00:00
const int desired_len = w * h * 4 ;
2016-05-22 00:18:16 +00:00
if ( graph_image . size ( ) ! = desired_len ) {
reset_texture = true ;
graph_image . resize ( desired_len ) ;
}
2020-02-17 21:06:54 +00:00
uint8_t * wr = graph_image . ptrw ( ) ;
2023-08-13 00:33:39 +00:00
const Color background_color = get_theme_color ( SNAME ( " dark_color_2 " ) , EditorStringName ( Editor ) ) ;
2016-05-22 00:18:16 +00:00
2020-01-30 02:05:48 +00:00
// Clear the previous frame and set the background color.
2016-05-22 00:18:16 +00:00
for ( int i = 0 ; i < desired_len ; i + = 4 ) {
2020-01-30 02:05:48 +00:00
wr [ i + 0 ] = Math : : fast_ftoi ( background_color . r * 255 ) ;
wr [ i + 1 ] = Math : : fast_ftoi ( background_color . g * 255 ) ;
wr [ i + 2 ] = Math : : fast_ftoi ( background_color . b * 255 ) ;
2016-05-22 00:18:16 +00:00
wr [ i + 3 ] = 255 ;
}
//find highest value
2020-01-30 02:05:48 +00:00
const bool use_self = display_time - > get_selected ( ) = = DISPLAY_SELF_TIME ;
2016-05-22 00:18:16 +00:00
float highest = 0 ;
2020-04-17 16:50:05 +00:00
for ( int i = 0 ; i < total_metrics ; i + + ) {
const Metric & m = _get_frame_metric ( i ) ;
2016-05-22 00:18:16 +00:00
2022-05-18 23:43:40 +00:00
for ( const StringName & E : plot_sigs ) {
HashMap < StringName , Metric : : Category * > : : ConstIterator F = m . category_ptrs . find ( E ) ;
2016-05-22 00:18:16 +00:00
if ( F ) {
2022-05-13 13:04:37 +00:00
highest = MAX ( F - > value - > total_time , highest ) ;
2016-05-22 00:18:16 +00:00
}
2022-05-18 23:43:40 +00:00
HashMap < StringName , Metric : : Category : : Item * > : : ConstIterator G = m . item_ptrs . find ( E ) ;
2016-05-22 00:18:16 +00:00
if ( G ) {
if ( use_self ) {
2022-05-13 13:04:37 +00:00
highest = MAX ( G - > value - > self , highest ) ;
2016-05-22 00:18:16 +00:00
} else {
2022-05-13 13:04:37 +00:00
highest = MAX ( G - > value - > total , highest ) ;
2016-05-22 00:18:16 +00:00
}
}
}
}
if ( highest > 0 ) {
//means some data exists..
highest * = 1.2 ; //leave some upper room
graph_height = highest ;
Vector < int > columnv ;
columnv . resize ( h * 4 ) ;
2017-11-25 03:07:54 +00:00
int * column = columnv . ptrw ( ) ;
2016-05-22 00:18:16 +00:00
2022-05-13 13:04:37 +00:00
HashMap < StringName , int > prev_plots ;
2016-05-22 00:18:16 +00:00
2020-04-17 16:50:05 +00:00
for ( int i = 0 ; i < total_metrics * w / frame_metrics . size ( ) - 1 ; i + + ) {
2016-05-22 00:18:16 +00:00
for ( int j = 0 ; j < h * 4 ; j + + ) {
column [ j ] = 0 ;
}
int current = i * frame_metrics . size ( ) / w ;
2022-05-18 23:43:40 +00:00
for ( const StringName & E : plot_sigs ) {
2020-04-17 16:50:05 +00:00
const Metric & m = _get_frame_metric ( current ) ;
2016-05-22 00:18:16 +00:00
2020-04-17 16:50:05 +00:00
float value = 0 ;
2016-05-22 00:18:16 +00:00
2022-05-18 23:43:40 +00:00
HashMap < StringName , Metric : : Category * > : : ConstIterator F = m . category_ptrs . find ( E ) ;
2020-04-17 16:50:05 +00:00
if ( F ) {
2022-05-13 13:04:37 +00:00
value = F - > value - > total_time ;
2020-04-17 16:50:05 +00:00
}
2016-05-22 00:18:16 +00:00
2022-05-18 23:43:40 +00:00
HashMap < StringName , Metric : : Category : : Item * > : : ConstIterator G = m . item_ptrs . find ( E ) ;
2020-04-17 16:50:05 +00:00
if ( G ) {
if ( use_self ) {
2022-05-13 13:04:37 +00:00
value = G - > value - > self ;
2020-04-17 16:50:05 +00:00
} else {
2022-05-13 13:04:37 +00:00
value = G - > value - > total ;
2016-05-22 00:18:16 +00:00
}
}
2020-04-17 16:50:05 +00:00
int plot_pos = CLAMP ( int ( value * h / highest ) , 0 , h - 1 ) ;
2016-05-22 00:18:16 +00:00
int prev_plot = plot_pos ;
2022-05-18 23:43:40 +00:00
HashMap < StringName , int > : : Iterator H = prev_plots . find ( E ) ;
2016-05-22 00:18:16 +00:00
if ( H ) {
2022-05-13 13:04:37 +00:00
prev_plot = H - > value ;
H - > value = plot_pos ;
2016-05-22 00:18:16 +00:00
} else {
2022-05-18 23:43:40 +00:00
prev_plots [ E ] = plot_pos ;
2016-05-22 00:18:16 +00:00
}
plot_pos = h - plot_pos - 1 ;
prev_plot = h - prev_plot - 1 ;
if ( prev_plot > plot_pos ) {
SWAP ( prev_plot , plot_pos ) ;
}
2022-05-18 23:43:40 +00:00
Color col = _get_color_from_signature ( E ) ;
2016-05-22 00:18:16 +00:00
for ( int j = prev_plot ; j < = plot_pos ; j + + ) {
column [ j * 4 + 0 ] + = Math : : fast_ftoi ( CLAMP ( col . r * 255 , 0 , 255 ) ) ;
column [ j * 4 + 1 ] + = Math : : fast_ftoi ( CLAMP ( col . g * 255 , 0 , 255 ) ) ;
column [ j * 4 + 2 ] + = Math : : fast_ftoi ( CLAMP ( col . b * 255 , 0 , 255 ) ) ;
column [ j * 4 + 3 ] + = 1 ;
}
}
for ( int j = 0 ; j < h * 4 ; j + = 4 ) {
2020-01-30 02:05:48 +00:00
const int a = column [ j + 3 ] ;
2016-05-22 00:18:16 +00:00
if ( a > 0 ) {
column [ j + 0 ] / = a ;
column [ j + 1 ] / = a ;
column [ j + 2 ] / = a ;
}
2020-01-30 02:05:48 +00:00
const uint8_t red = uint8_t ( column [ j + 0 ] ) ;
const uint8_t green = uint8_t ( column [ j + 1 ] ) ;
const uint8_t blue = uint8_t ( column [ j + 2 ] ) ;
const bool is_filled = red > = 1 | | green > = 1 | | blue > = 1 ;
const int widx = ( ( j > > 2 ) * w + i ) * 4 ;
2016-05-22 00:18:16 +00:00
2020-01-30 02:05:48 +00:00
// If the pixel isn't filled by any profiler line, apply the background color instead.
wr [ widx + 0 ] = is_filled ? red : Math : : fast_ftoi ( background_color . r * 255 ) ;
wr [ widx + 1 ] = is_filled ? green : Math : : fast_ftoi ( background_color . g * 255 ) ;
wr [ widx + 2 ] = is_filled ? blue : Math : : fast_ftoi ( background_color . b * 255 ) ;
2016-05-22 00:18:16 +00:00
wr [ widx + 3 ] = 255 ;
}
}
}
2022-07-22 18:06:19 +00:00
Ref < Image > img = Image : : create_from_data ( w , h , false , Image : : FORMAT_RGBA8 , graph_image ) ;
2016-05-22 00:18:16 +00:00
if ( reset_texture ) {
if ( graph_texture . is_null ( ) ) {
2021-06-17 22:03:09 +00:00
graph_texture . instantiate ( ) ;
2016-05-22 00:18:16 +00:00
}
2022-05-03 23:49:20 +00:00
graph_texture - > set_image ( img ) ;
2016-05-22 00:18:16 +00:00
}
2021-07-01 02:17:47 +00:00
graph_texture - > update ( img ) ;
2016-05-22 00:18:16 +00:00
graph - > set_texture ( graph_texture ) ;
2022-08-13 21:21:24 +00:00
graph - > queue_redraw ( ) ;
2016-05-22 00:18:16 +00:00
}
void EditorProfiler : : _update_frame ( ) {
2020-04-17 16:50:05 +00:00
int cursor_metric = cursor_metric_edit - > get_value ( ) - _get_frame_metric ( 0 ) . frame_number ;
2016-05-22 00:18:16 +00:00
updating_frame = true ;
variables - > clear ( ) ;
TreeItem * root = variables - > create_item ( ) ;
2020-04-17 16:50:05 +00:00
const Metric & m = _get_frame_metric ( cursor_metric ) ;
2016-05-22 00:18:16 +00:00
int dtime = display_time - > get_selected ( ) ;
for ( int i = 0 ; i < m . categories . size ( ) ; i + + ) {
TreeItem * category = variables - > create_item ( root ) ;
category - > set_cell_mode ( 0 , TreeItem : : CELL_MODE_CHECK ) ;
category - > set_editable ( 0 , true ) ;
category - > set_metadata ( 0 , m . categories [ i ] . signature ) ;
category - > set_text ( 0 , String ( m . categories [ i ] . name ) ) ;
category - > set_text ( 1 , _get_time_as_text ( m , m . categories [ i ] . total_time , 1 ) ) ;
if ( plot_sigs . has ( m . categories [ i ] . signature ) ) {
category - > set_checked ( 0 , true ) ;
category - > set_custom_color ( 0 , _get_color_from_signature ( m . categories [ i ] . signature ) ) ;
}
2024-01-28 00:59:13 +00:00
for ( int j = 0 ; j < m . categories [ i ] . items . size ( ) ; j + + ) {
2018-07-25 01:11:03 +00:00
const Metric : : Category : : Item & it = m . categories [ i ] . items [ j ] ;
2016-05-22 00:18:16 +00:00
2023-03-05 12:37:11 +00:00
if ( it . internal = = it . total & & ! display_internal_profiles - > is_pressed ( ) & & m . categories [ i ] . name = = " Script Functions " ) {
continue ;
}
2016-05-22 00:18:16 +00:00
TreeItem * item = variables - > create_item ( category ) ;
item - > set_cell_mode ( 0 , TreeItem : : CELL_MODE_CHECK ) ;
item - > set_editable ( 0 , true ) ;
item - > set_text ( 0 , it . name ) ;
item - > set_metadata ( 0 , it . signature ) ;
item - > set_metadata ( 1 , it . script ) ;
item - > set_metadata ( 2 , it . line ) ;
2021-11-25 02:58:47 +00:00
item - > set_text_alignment ( 2 , HORIZONTAL_ALIGNMENT_RIGHT ) ;
2022-08-29 13:55:49 +00:00
item - > set_tooltip_text ( 0 , it . name + " \n " + it . script + " : " + itos ( it . line ) ) ;
2016-05-22 00:18:16 +00:00
float time = dtime = = DISPLAY_SELF_TIME ? it . self : it . total ;
2023-03-05 12:37:11 +00:00
if ( dtime = = DISPLAY_SELF_TIME & & ! display_internal_profiles - > is_pressed ( ) ) {
time + = it . internal ;
}
2016-05-22 00:18:16 +00:00
item - > set_text ( 1 , _get_time_as_text ( m , time , it . calls ) ) ;
item - > set_text ( 2 , itos ( it . calls ) ) ;
if ( plot_sigs . has ( it . signature ) ) {
item - > set_checked ( 0 , true ) ;
item - > set_custom_color ( 0 , _get_color_from_signature ( it . signature ) ) ;
}
}
}
updating_frame = false ;
}
2022-12-16 15:35:44 +00:00
void EditorProfiler : : _update_button_text ( ) {
2016-05-22 00:18:16 +00:00
if ( activate - > is_pressed ( ) ) {
2023-08-13 00:33:39 +00:00
activate - > set_icon ( get_editor_theme_icon ( SNAME ( " Stop " ) ) ) ;
2018-06-24 01:15:10 +00:00
activate - > set_text ( TTR ( " Stop " ) ) ;
2016-05-22 00:18:16 +00:00
} else {
2023-08-13 00:33:39 +00:00
activate - > set_icon ( get_editor_theme_icon ( SNAME ( " Play " ) ) ) ;
2018-06-24 01:15:10 +00:00
activate - > set_text ( TTR ( " Start " ) ) ;
2016-05-22 00:18:16 +00:00
}
2022-09-06 15:15:00 +00:00
}
void EditorProfiler : : _activate_pressed ( ) {
2022-12-16 15:35:44 +00:00
_update_button_text ( ) ;
2022-09-06 15:15:00 +00:00
if ( activate - > is_pressed ( ) ) {
_clear_pressed ( ) ;
}
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " enable_profiling " ) , activate - > is_pressed ( ) ) ;
2016-05-22 00:18:16 +00:00
}
2018-06-24 01:15:10 +00:00
void EditorProfiler : : _clear_pressed ( ) {
2020-04-17 16:50:05 +00:00
clear_button - > set_disabled ( true ) ;
2018-06-24 01:15:10 +00:00
clear ( ) ;
2018-08-28 09:49:12 +00:00
_update_plot ( ) ;
2018-06-24 01:15:10 +00:00
}
2023-03-05 12:37:11 +00:00
void EditorProfiler : : _internal_profiles_pressed ( ) {
_combo_changed ( 0 ) ;
}
2016-05-22 00:18:16 +00:00
void EditorProfiler : : _notification ( int p_what ) {
2022-02-15 23:52:32 +00:00
switch ( p_what ) {
2022-08-29 09:04:31 +00:00
case NOTIFICATION_ENTER_TREE :
2022-02-15 23:52:32 +00:00
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED :
2022-08-29 09:04:31 +00:00
case NOTIFICATION_THEME_CHANGED :
case NOTIFICATION_TRANSLATION_CHANGED : {
2023-08-13 00:33:39 +00:00
activate - > set_icon ( get_editor_theme_icon ( SNAME ( " Play " ) ) ) ;
clear_button - > set_icon ( get_editor_theme_icon ( SNAME ( " Clear " ) ) ) ;
2022-02-15 23:52:32 +00:00
} break ;
2016-05-22 00:18:16 +00:00
}
}
void EditorProfiler : : _graph_tex_draw ( ) {
2020-04-17 16:50:05 +00:00
if ( total_metrics = = 0 ) {
2016-05-22 00:18:16 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-05-22 00:18:16 +00:00
if ( seeking ) {
2020-04-17 16:50:05 +00:00
int frame = cursor_metric_edit - > get_value ( ) - _get_frame_metric ( 0 ) . frame_number ;
int cur_x = ( 2 * frame + 1 ) * graph - > get_size ( ) . x / ( 2 * frame_metrics . size ( ) ) + 1 ;
2016-05-22 00:18:16 +00:00
graph - > draw_line ( Vector2 ( cur_x , 0 ) , Vector2 ( cur_x , graph - > get_size ( ) . y ) , Color ( 1 , 1 , 1 , 0.8 ) ) ;
}
2020-04-17 16:50:05 +00:00
if ( hover_metric > - 1 & & hover_metric < total_metrics ) {
int cur_x = ( 2 * hover_metric + 1 ) * graph - > get_size ( ) . x / ( 2 * frame_metrics . size ( ) ) + 1 ;
2016-05-22 00:18:16 +00:00
graph - > draw_line ( Vector2 ( cur_x , 0 ) , Vector2 ( cur_x , graph - > get_size ( ) . y ) , Color ( 1 , 1 , 1 , 0.4 ) ) ;
}
}
void EditorProfiler : : _graph_tex_mouse_exit ( ) {
hover_metric = - 1 ;
2022-08-13 21:21:24 +00:00
graph - > queue_redraw ( ) ;
2016-05-22 00:18:16 +00:00
}
void EditorProfiler : : _cursor_metric_changed ( double ) {
2020-05-14 14:41:43 +00:00
if ( updating_frame ) {
2016-05-22 00:18:16 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-05-22 00:18:16 +00:00
2022-08-13 21:21:24 +00:00
graph - > queue_redraw ( ) ;
2016-05-22 00:18:16 +00:00
_update_frame ( ) ;
}
2017-05-20 15:38:03 +00:00
void EditorProfiler : : _graph_tex_input ( const Ref < InputEvent > & p_ev ) {
2020-05-14 14:41:43 +00:00
if ( last_metric < 0 ) {
2016-05-22 00:18:16 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-05-22 00:18:16 +00:00
2017-05-20 15:38:03 +00:00
Ref < InputEventMouse > me = p_ev ;
Ref < InputEventMouseButton > mb = p_ev ;
Ref < InputEventMouseMotion > mm = p_ev ;
2016-05-22 00:18:16 +00:00
if (
2021-08-13 21:31:57 +00:00
( mb . is_valid ( ) & & mb - > get_button_index ( ) = = MouseButton : : LEFT & & mb - > is_pressed ( ) ) | |
2017-05-20 15:38:03 +00:00
( mm . is_valid ( ) ) ) {
2020-04-17 16:50:05 +00:00
int x = me - > get_position ( ) . x - 1 ;
2016-05-22 00:18:16 +00:00
x = x * frame_metrics . size ( ) / graph - > get_size ( ) . width ;
2020-04-17 16:50:05 +00:00
hover_metric = x ;
2016-05-22 00:18:16 +00:00
if ( x < 0 ) {
x = 0 ;
}
if ( x > = frame_metrics . size ( ) ) {
x = frame_metrics . size ( ) - 1 ;
}
2023-01-07 23:55:54 +00:00
if ( mb . is_valid ( ) | | ( mm - > get_button_mask ( ) . has_flag ( MouseButtonMask : : LEFT ) ) ) {
2016-05-22 00:18:16 +00:00
updating_frame = true ;
2022-01-27 16:34:33 +00:00
if ( x < total_metrics ) {
2020-04-17 16:50:05 +00:00
cursor_metric_edit - > set_value ( _get_frame_metric ( x ) . frame_number ) ;
2022-01-27 16:34:33 +00:00
}
2016-05-22 00:18:16 +00:00
updating_frame = false ;
if ( activate - > is_pressed ( ) ) {
if ( ! seeking ) {
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " break_request " ) ) ;
2016-05-22 00:18:16 +00:00
}
}
seeking = true ;
if ( ! frame_delay - > is_processing ( ) ) {
frame_delay - > set_wait_time ( 0.1 ) ;
frame_delay - > start ( ) ;
}
}
2022-08-13 21:21:24 +00:00
graph - > queue_redraw ( ) ;
2016-05-22 00:18:16 +00:00
}
}
void EditorProfiler : : disable_seeking ( ) {
seeking = false ;
2022-08-13 21:21:24 +00:00
graph - > queue_redraw ( ) ;
2016-05-22 00:18:16 +00:00
}
void EditorProfiler : : _combo_changed ( int ) {
_update_frame ( ) ;
_update_plot ( ) ;
}
void EditorProfiler : : _bind_methods ( ) {
ADD_SIGNAL ( MethodInfo ( " enable_profiling " , PropertyInfo ( Variant : : BOOL , " enable " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " break_request " ) ) ;
}
2022-09-06 15:15:00 +00:00
void EditorProfiler : : set_enabled ( bool p_enable , bool p_clear ) {
2016-05-22 00:18:16 +00:00
activate - > set_disabled ( ! p_enable ) ;
2022-09-06 15:15:00 +00:00
if ( p_clear ) {
clear ( ) ;
}
2016-05-22 00:18:16 +00:00
}
2022-12-16 15:35:44 +00:00
void EditorProfiler : : set_pressed ( bool p_pressed ) {
activate - > set_pressed ( p_pressed ) ;
_update_button_text ( ) ;
}
2016-05-22 00:18:16 +00:00
bool EditorProfiler : : is_profiling ( ) {
return activate - > is_pressed ( ) ;
}
2020-03-17 06:33:00 +00:00
Vector < Vector < String > > EditorProfiler : : get_data_as_csv ( ) const {
Vector < Vector < String > > res ;
2019-02-18 00:25:26 +00:00
2020-12-15 12:04:21 +00:00
if ( frame_metrics . is_empty ( ) ) {
2019-02-18 00:25:26 +00:00
return res ;
}
2021-05-21 07:40:03 +00:00
// Different metrics may contain different number of categories.
2022-05-19 15:00:06 +00:00
HashSet < StringName > possible_signatures ;
2021-05-21 07:40:03 +00:00
for ( int i = 0 ; i < frame_metrics . size ( ) ; i + + ) {
const Metric & m = frame_metrics [ i ] ;
if ( ! m . valid ) {
continue ;
}
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , Metric : : Category * > & E : m . category_ptrs ) {
possible_signatures . insert ( E . key ) ;
2021-05-21 07:40:03 +00:00
}
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , Metric : : Category : : Item * > & E : m . item_ptrs ) {
possible_signatures . insert ( E . key ) ;
2019-02-18 00:25:26 +00:00
}
}
2021-05-21 07:40:03 +00:00
// Generate CSV header and cache indices.
2022-05-13 13:04:37 +00:00
HashMap < StringName , int > sig_map ;
2021-05-21 07:40:03 +00:00
Vector < String > signatures ;
signatures . resize ( possible_signatures . size ( ) ) ;
int sig_index = 0 ;
2022-05-18 23:43:40 +00:00
for ( const StringName & E : possible_signatures ) {
signatures . write [ sig_index ] = E ;
sig_map [ E ] = sig_index ;
2021-05-21 07:40:03 +00:00
sig_index + + ;
}
2019-02-18 00:25:26 +00:00
res . push_back ( signatures ) ;
// values
Vector < String > values ;
int index = last_metric ;
for ( int i = 0 ; i < frame_metrics . size ( ) ; i + + ) {
+ + index ;
if ( index > = frame_metrics . size ( ) ) {
index = 0 ;
}
2021-05-21 07:40:03 +00:00
const Metric & m = frame_metrics [ index ] ;
if ( ! m . valid ) {
2019-02-18 00:25:26 +00:00
continue ;
}
2021-05-21 07:40:03 +00:00
// Don't keep old values since there may be empty cells.
values . clear ( ) ;
values . resize ( possible_signatures . size ( ) ) ;
2019-02-18 00:25:26 +00:00
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , Metric : : Category * > & E : m . category_ptrs ) {
values . write [ sig_map [ E . key ] ] = String : : num_real ( E . value - > total_time ) ;
2021-05-21 07:40:03 +00:00
}
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , Metric : : Category : : Item * > & E : m . item_ptrs ) {
values . write [ sig_map [ E . key ] ] = String : : num_real ( E . value - > total ) ;
2019-02-18 00:25:26 +00:00
}
2021-05-21 07:40:03 +00:00
2019-02-18 00:25:26 +00:00
res . push_back ( values ) ;
}
return res ;
}
2016-05-22 00:18:16 +00:00
EditorProfiler : : EditorProfiler ( ) {
HBoxContainer * hb = memnew ( HBoxContainer ) ;
add_child ( hb ) ;
activate = memnew ( Button ) ;
activate - > set_toggle_mode ( true ) ;
2022-12-16 15:35:44 +00:00
activate - > set_disabled ( true ) ;
2018-06-24 01:15:10 +00:00
activate - > set_text ( TTR ( " Start " ) ) ;
2020-02-21 17:28:45 +00:00
activate - > connect ( " pressed " , callable_mp ( this , & EditorProfiler : : _activate_pressed ) ) ;
2016-05-22 00:18:16 +00:00
hb - > add_child ( activate ) ;
2018-06-24 01:15:10 +00:00
clear_button = memnew ( Button ) ;
clear_button - > set_text ( TTR ( " Clear " ) ) ;
2020-02-21 17:28:45 +00:00
clear_button - > connect ( " pressed " , callable_mp ( this , & EditorProfiler : : _clear_pressed ) ) ;
2020-04-17 16:50:05 +00:00
clear_button - > set_disabled ( true ) ;
2018-06-24 01:15:10 +00:00
hb - > add_child ( clear_button ) ;
2016-05-22 00:18:16 +00:00
hb - > add_child ( memnew ( Label ( TTR ( " Measure: " ) ) ) ) ;
display_mode = memnew ( OptionButton ) ;
2021-07-24 04:15:49 +00:00
display_mode - > add_item ( TTR ( " Frame Time (ms) " ) ) ;
display_mode - > add_item ( TTR ( " Average Time (ms) " ) ) ;
2016-05-22 00:18:16 +00:00
display_mode - > add_item ( TTR ( " Frame % " ) ) ;
2017-10-21 14:28:08 +00:00
display_mode - > add_item ( TTR ( " Physics Frame % " ) ) ;
2020-02-21 17:28:45 +00:00
display_mode - > connect ( " item_selected " , callable_mp ( this , & EditorProfiler : : _combo_changed ) ) ;
2016-05-22 00:18:16 +00:00
hb - > add_child ( display_mode ) ;
hb - > add_child ( memnew ( Label ( TTR ( " Time: " ) ) ) ) ;
display_time = memnew ( OptionButton ) ;
2023-02-13 03:27:12 +00:00
// TRANSLATORS: This is an option in the profiler to display the time spent in a function, including the time spent in other functions called by that function.
2016-05-22 00:18:16 +00:00
display_time - > add_item ( TTR ( " Inclusive " ) ) ;
2023-02-13 03:27:12 +00:00
// TRANSLATORS: This is an option in the profiler to display the time spent in a function, exincluding the time spent in other functions called by that function.
2016-05-22 00:18:16 +00:00
display_time - > add_item ( TTR ( " Self " ) ) ;
2022-08-25 10:42:17 +00:00
display_time - > set_tooltip_text ( TTR ( " Inclusive: Includes time from other functions called by this function. \n Use this to spot bottlenecks. \n \n Self: Only count the time spent in the function itself, not in other functions called by that function. \n Use this to find individual functions to optimize. " ) ) ;
2020-02-21 17:28:45 +00:00
display_time - > connect ( " item_selected " , callable_mp ( this , & EditorProfiler : : _combo_changed ) ) ;
2016-05-22 00:18:16 +00:00
hb - > add_child ( display_time ) ;
2023-03-05 12:37:11 +00:00
display_internal_profiles = memnew ( CheckButton ( TTR ( " Display internal functions " ) ) ) ;
display_internal_profiles - > set_pressed ( false ) ;
display_internal_profiles - > connect ( " pressed " , callable_mp ( this , & EditorProfiler : : _internal_profiles_pressed ) ) ;
hb - > add_child ( display_internal_profiles ) ;
2016-05-22 00:18:16 +00:00
hb - > add_spacer ( ) ;
2016-05-28 10:46:17 +00:00
hb - > add_child ( memnew ( Label ( TTR ( " Frame #: " ) ) ) ) ;
2016-05-22 00:18:16 +00:00
cursor_metric_edit = memnew ( SpinBox ) ;
cursor_metric_edit - > set_h_size_flags ( SIZE_FILL ) ;
2020-04-17 16:50:05 +00:00
cursor_metric_edit - > set_value ( 0 ) ;
cursor_metric_edit - > set_editable ( false ) ;
2016-05-22 00:18:16 +00:00
hb - > add_child ( cursor_metric_edit ) ;
2020-02-21 17:28:45 +00:00
cursor_metric_edit - > connect ( " value_changed " , callable_mp ( this , & EditorProfiler : : _cursor_metric_changed ) ) ;
2016-05-22 00:18:16 +00:00
2022-02-08 09:14:58 +00:00
hb - > add_theme_constant_override ( " separation " , 8 * EDSCALE ) ;
2016-05-22 00:18:16 +00:00
h_split = memnew ( HSplitContainer ) ;
add_child ( h_split ) ;
h_split - > set_v_size_flags ( SIZE_EXPAND_FILL ) ;
variables = memnew ( Tree ) ;
2020-01-24 11:07:38 +00:00
variables - > set_custom_minimum_size ( Size2 ( 320 , 0 ) * EDSCALE ) ;
2016-05-22 00:18:16 +00:00
variables - > set_hide_folding ( true ) ;
h_split - > add_child ( variables ) ;
variables - > set_hide_root ( true ) ;
variables - > set_columns ( 3 ) ;
variables - > set_column_titles_visible ( true ) ;
2017-12-02 09:58:58 +00:00
variables - > set_column_title ( 0 , TTR ( " Name " ) ) ;
2016-05-22 00:18:16 +00:00
variables - > set_column_expand ( 0 , true ) ;
2021-07-04 03:13:28 +00:00
variables - > set_column_clip_content ( 0 , true ) ;
2023-06-03 09:47:59 +00:00
variables - > set_column_custom_minimum_width ( 0 , 60 ) ;
2017-12-02 09:58:58 +00:00
variables - > set_column_title ( 1 , TTR ( " Time " ) ) ;
2016-05-22 00:18:16 +00:00
variables - > set_column_expand ( 1 , false ) ;
2021-07-04 03:13:28 +00:00
variables - > set_column_clip_content ( 1 , true ) ;
2023-06-03 09:47:59 +00:00
variables - > set_column_custom_minimum_width ( 1 , 75 * EDSCALE ) ;
2017-12-02 09:58:58 +00:00
variables - > set_column_title ( 2 , TTR ( " Calls " ) ) ;
2016-05-22 00:18:16 +00:00
variables - > set_column_expand ( 2 , false ) ;
2021-07-04 03:13:28 +00:00
variables - > set_column_clip_content ( 2 , true ) ;
2023-06-03 09:47:59 +00:00
variables - > set_column_custom_minimum_width ( 2 , 50 * EDSCALE ) ;
2020-02-21 17:28:45 +00:00
variables - > connect ( " item_edited " , callable_mp ( this , & EditorProfiler : : _item_edited ) ) ;
2017-03-05 15:44:50 +00:00
2017-01-12 21:27:27 +00:00
graph = memnew ( TextureRect ) ;
2022-02-25 00:19:24 +00:00
graph - > set_expand_mode ( TextureRect : : EXPAND_IGNORE_SIZE ) ;
2017-01-08 22:54:19 +00:00
graph - > set_mouse_filter ( MOUSE_FILTER_STOP ) ;
2020-02-21 17:28:45 +00:00
graph - > connect ( " draw " , callable_mp ( this , & EditorProfiler : : _graph_tex_draw ) ) ;
graph - > connect ( " gui_input " , callable_mp ( this , & EditorProfiler : : _graph_tex_input ) ) ;
graph - > connect ( " mouse_exited " , callable_mp ( this , & EditorProfiler : : _graph_tex_mouse_exit ) ) ;
2016-05-22 00:18:16 +00:00
h_split - > add_child ( graph ) ;
graph - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
2022-05-27 23:03:32 +00:00
int metric_size = CLAMP ( int ( EDITOR_GET ( " debugger/profiler_frame_history_size " ) ) , 60 , 10000 ) ;
2016-05-22 00:18:16 +00:00
frame_metrics . resize ( metric_size ) ;
frame_delay = memnew ( Timer ) ;
frame_delay - > set_wait_time ( 0.1 ) ;
frame_delay - > set_one_shot ( true ) ;
add_child ( frame_delay ) ;
2020-02-21 17:28:45 +00:00
frame_delay - > connect ( " timeout " , callable_mp ( this , & EditorProfiler : : _update_frame ) ) ;
2016-05-22 00:18:16 +00:00
plot_delay = memnew ( Timer ) ;
plot_delay - > set_wait_time ( 0.1 ) ;
plot_delay - > set_one_shot ( true ) ;
add_child ( plot_delay ) ;
2020-02-21 17:28:45 +00:00
plot_delay - > connect ( " timeout " , callable_mp ( this , & EditorProfiler : : _update_plot ) ) ;
2016-05-22 00:18:16 +00:00
2017-09-30 14:19:07 +00:00
plot_sigs . insert ( " physics_frame_time " ) ;
2016-05-22 00:18:16 +00:00
plot_sigs . insert ( " category_frame_time " ) ;
}