2017-01-16 08:04:19 +01:00
/*************************************************************************/
/* editor_profiler.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2017-01-16 08:04:19 +01:00
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2017-01-16 08:04:19 +01:00
/* */
/* 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-05 00:50:27 +01:00
2016-05-22 02:18:16 +02:00
# include "editor_profiler.h"
2017-01-16 08:04:19 +01:00
2018-09-11 18:13:45 +02:00
# include "core/os/os.h"
2020-03-08 12:21:08 +01:00
# include "editor/editor_scale.h"
# include "editor/editor_settings.h"
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
void EditorProfiler : : _make_metric_ptrs ( Metric & m ) {
for ( int i = 0 ; i < m . categories . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
m . category_ptrs [ m . categories [ i ] . signature ] = & m . categories . write [ i ] ;
2017-03-05 16:44:50 +01:00
for ( int j = 0 ; j < m . categories [ i ] . items . size ( ) ; j + + ) {
2018-07-25 03:11:03 +02:00
m . item_ptrs [ m . categories [ i ] . items [ j ] . signature ] = & m . categories . write [ i ] . items . write [ j ] ;
2016-05-22 02:18:16 +02:00
}
}
}
2020-04-17 18:50:05 +02:00
EditorProfiler : : Metric EditorProfiler : : _get_frame_metric ( int index ) {
return frame_metrics [ ( frame_metrics . size ( ) + last_metric - ( total_metrics - 1 ) + index ) % frame_metrics . size ( ) ] ;
}
2017-03-05 16:44:50 +01:00
void EditorProfiler : : add_frame_metric ( const Metric & p_metric , bool p_final ) {
2016-05-22 02:18:16 +02:00
+ + last_metric ;
2020-05-14 16:41:43 +02:00
if ( last_metric > = frame_metrics . size ( ) ) {
2017-03-05 16:44:50 +01:00
last_metric = 0 ;
2020-05-14 16:41:43 +02:00
}
2016-05-22 02:18:16 +02:00
2020-04-17 18:50:05 +02:00
total_metrics + + ;
if ( total_metrics > frame_metrics . size ( ) ) {
total_metrics = frame_metrics . size ( ) ;
}
2018-07-25 03:11:03 +02:00
frame_metrics . write [ last_metric ] = p_metric ;
_make_metric_ptrs ( frame_metrics . write [ last_metric ] ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
updating_frame = true ;
2020-04-17 18:50:05 +02: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 02:18:16 +02:00
if ( ! seeking ) {
2020-04-17 18:50:05 +02:00
cursor_metric_edit - > set_value ( p_metric . frame_number ) ;
2016-05-22 02:18:16 +02:00
}
2020-04-17 18:50:05 +02:00
2017-03-05 16:44:50 +01:00
updating_frame = false ;
2016-05-22 02:18:16 +02:00
2018-06-20 02:42:29 +02:00
if ( frame_delay - > is_stopped ( ) ) {
2017-03-05 16:44:50 +01:00
frame_delay - > set_wait_time ( p_final ? 0.1 : 1 ) ;
2016-05-22 02:18:16 +02:00
frame_delay - > start ( ) ;
}
2018-06-20 02:42:29 +02:00
if ( plot_delay - > is_stopped ( ) ) {
2016-05-22 02:18:16 +02:00
plot_delay - > set_wait_time ( 0.1 ) ;
plot_delay - > start ( ) ;
}
}
void EditorProfiler : : clear ( ) {
2017-03-05 16:44:50 +01:00
int metric_size = EditorSettings : : get_singleton ( ) - > get ( " debugger/profiler_frame_history_size " ) ;
metric_size = CLAMP ( metric_size , 60 , 1024 ) ;
2016-05-22 02:18:16 +02:00
frame_metrics . clear ( ) ;
frame_metrics . resize ( metric_size ) ;
2020-04-17 18:50:05 +02:00
total_metrics = 0 ;
2017-03-05 16:44:50 +01:00
last_metric = - 1 ;
2016-05-22 02:18:16 +02:00
variables - > clear ( ) ;
plot_sigs . clear ( ) ;
2017-09-30 16:19:07 +02:00
plot_sigs . insert ( " physics_frame_time " ) ;
2016-05-22 02:18:16 +02:00
plot_sigs . insert ( " category_frame_time " ) ;
2017-03-05 16:44:50 +01:00
updating_frame = true ;
2016-05-22 02:18:16 +02:00
cursor_metric_edit - > set_min ( 0 ) ;
2019-11-26 12:09:58 +01: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 05:16:14 +01:00
cursor_metric_edit - > set_value ( 0 ) ;
2020-04-17 18:50:05 +02:00
cursor_metric_edit - > set_editable ( false ) ;
2017-03-05 16:44:50 +01:00
updating_frame = false ;
hover_metric = - 1 ;
seeking = false ;
2016-05-22 02:18:16 +02:00
}
2017-03-05 16:44:50 +01:00
static String _get_percent_txt ( float p_value , float p_total ) {
2020-01-24 12:07:38 +01:00
if ( p_total = = 0 ) {
2017-03-05 16:44:50 +01:00
p_total = 0.00001 ;
2020-01-24 12:07:38 +01:00
}
2020-09-03 13:22:16 +02:00
return TS - > format_number ( String : : num ( ( p_value / p_total ) * 100 , 1 ) ) + TS - > percent_sign ( ) ;
2016-05-22 02:18:16 +02:00
}
2018-07-25 03:11:03 +02:00
String EditorProfiler : : _get_time_as_text ( const Metric & m , float p_time , int p_calls ) {
2020-01-24 12:07:38 +01:00
const int dmode = display_mode - > get_selected ( ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
if ( dmode = = DISPLAY_FRAME_TIME ) {
2020-09-03 13:22:16 +02:00
return TS - > format_number ( rtos ( p_time * 1000 ) . pad_decimals ( 2 ) ) + " " + RTR ( " ms " ) ;
2017-03-05 16:44:50 +01:00
} else if ( dmode = = DISPLAY_AVERAGE_TIME ) {
2020-01-24 12:07:38 +01:00
if ( p_calls = = 0 ) {
2020-09-03 13:22:16 +02:00
return TS - > format_number ( " 0.00 " ) + " " + RTR ( " ms " ) ;
2020-01-24 12:07:38 +01:00
} else {
2020-09-03 13:22:16 +02:00
return TS - > format_number ( rtos ( ( p_time / p_calls ) * 1000 ) . pad_decimals ( 2 ) ) + " " + RTR ( " ms " ) ;
2020-01-24 12:07:38 +01:00
}
2017-03-05 16:44:50 +01:00
} else if ( dmode = = DISPLAY_FRAME_PERCENT ) {
return _get_percent_txt ( p_time , m . frame_time ) ;
2017-09-30 16:19:07 +02:00
} else if ( dmode = = DISPLAY_PHYSICS_FRAME_PERCENT ) {
return _get_percent_txt ( p_time , m . physics_frame_time ) ;
2016-05-22 02:18:16 +02:00
}
return " err " ;
}
2017-03-05 16:44:50 +01:00
Color EditorProfiler : : _get_color_from_signature ( const StringName & p_signature ) const {
2021-07-17 23:22:52 +02:00
Color bc = get_theme_color ( SNAME ( " error_color " ) , SNAME ( " Editor " ) ) ;
2017-03-05 16:44:50 +01:00
double rot = ABS ( double ( p_signature . hash ( ) ) / double ( 0x7FFFFFFF ) ) ;
2016-05-22 02:18:16 +02:00
Color c ;
2017-08-08 04:55:24 +02:00
c . set_hsv ( rot , bc . get_s ( ) , bc . get_v ( ) ) ;
2021-07-17 23:22:52 +02:00
return c . lerp ( get_theme_color ( SNAME ( " base_color " ) , SNAME ( " Editor " ) ) , 0.07 ) ;
2016-05-22 02:18:16 +02:00
}
void EditorProfiler : : _item_edited ( ) {
2020-05-14 16:41:43 +02:00
if ( updating_frame ) {
2016-05-22 02:18:16 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
TreeItem * item = variables - > get_edited ( ) ;
2020-05-14 16:41:43 +02:00
if ( ! item ) {
2016-05-22 02:18:16 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
StringName signature = item - > get_metadata ( 0 ) ;
bool checked = item - > is_checked ( 0 ) ;
2016-05-22 02:18:16 +02:00
2020-05-14 16:41:43 +02:00
if ( checked ) {
2016-05-22 02:18:16 +02:00
plot_sigs . insert ( signature ) ;
2020-05-14 16:41:43 +02:00
} else {
2016-05-22 02:18:16 +02:00
plot_sigs . erase ( signature ) ;
2020-05-14 16:41:43 +02:00
}
2016-05-22 02:18:16 +02:00
if ( ! frame_delay - > is_processing ( ) ) {
frame_delay - > set_wait_time ( 0.1 ) ;
frame_delay - > start ( ) ;
}
2016-07-21 23:01:57 +02:00
_update_plot ( ) ;
2016-05-22 02:18:16 +02:00
}
void EditorProfiler : : _update_plot ( ) {
2020-01-30 03:05:48 +01:00
const int w = graph - > get_size ( ) . width ;
const int h = graph - > get_size ( ) . height ;
2018-08-28 11:49:12 +02:00
bool reset_texture = false ;
2020-01-30 03:05:48 +01:00
const int desired_len = w * h * 4 ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
if ( graph_image . size ( ) ! = desired_len ) {
reset_texture = true ;
2016-05-22 02:18:16 +02:00
graph_image . resize ( desired_len ) ;
}
2020-02-17 22:06:54 +01:00
uint8_t * wr = graph_image . ptrw ( ) ;
2021-07-17 23:22:52 +02:00
const Color background_color = get_theme_color ( SNAME ( " dark_color_2 " ) , SNAME ( " Editor " ) ) ;
2016-05-22 02:18:16 +02:00
2020-01-30 03:05:48 +01:00
// Clear the previous frame and set the background color.
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < desired_len ; i + = 4 ) {
2020-01-30 03:05:48 +01: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 ) ;
2017-03-05 16:44:50 +01:00
wr [ i + 3 ] = 255 ;
2016-05-22 02:18:16 +02:00
}
//find highest value
2020-01-30 03:05:48 +01:00
const bool use_self = display_time - > get_selected ( ) = = DISPLAY_SELF_TIME ;
2017-03-05 16:44:50 +01:00
float highest = 0 ;
2016-05-22 02:18:16 +02:00
2020-04-17 18:50:05 +02:00
for ( int i = 0 ; i < total_metrics ; i + + ) {
const Metric & m = _get_frame_metric ( i ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
for ( Set < StringName > : : Element * E = plot_sigs . front ( ) ; E ; E = E - > next ( ) ) {
2018-07-25 03:11:03 +02:00
const Map < StringName , Metric : : Category * > : : Element * F = m . category_ptrs . find ( E - > get ( ) ) ;
2016-05-22 02:18:16 +02:00
if ( F ) {
2017-03-05 16:44:50 +01:00
highest = MAX ( F - > get ( ) - > total_time , highest ) ;
2016-05-22 02:18:16 +02:00
}
2018-07-25 03:11:03 +02:00
const Map < StringName , Metric : : Category : : Item * > : : Element * G = m . item_ptrs . find ( E - > get ( ) ) ;
2016-05-22 02:18:16 +02:00
if ( G ) {
if ( use_self ) {
2017-03-05 16:44:50 +01:00
highest = MAX ( G - > get ( ) - > self , highest ) ;
2016-05-22 02:18:16 +02:00
} else {
2017-03-05 16:44:50 +01:00
highest = MAX ( G - > get ( ) - > total , highest ) ;
2016-05-22 02:18:16 +02:00
}
}
}
}
2017-03-05 16:44:50 +01:00
if ( highest > 0 ) {
2016-05-22 02:18:16 +02:00
//means some data exists..
2017-03-05 16:44:50 +01:00
highest * = 1.2 ; //leave some upper room
graph_height = highest ;
2016-05-22 02:18:16 +02:00
Vector < int > columnv ;
2017-03-05 16:44:50 +01:00
columnv . resize ( h * 4 ) ;
2016-05-22 02:18:16 +02:00
2017-11-25 04:07:54 +01:00
int * column = columnv . ptrw ( ) ;
2016-05-22 02:18:16 +02:00
2020-04-17 18:50:05 +02:00
Map < StringName , int > prev_plots ;
2016-05-22 02:18:16 +02:00
2020-04-17 18:50:05 +02:00
for ( int i = 0 ; i < total_metrics * w / frame_metrics . size ( ) - 1 ; i + + ) {
2017-03-05 16:44:50 +01:00
for ( int j = 0 ; j < h * 4 ; j + + ) {
column [ j ] = 0 ;
2016-05-22 02:18:16 +02:00
}
2017-03-05 16:44:50 +01:00
int current = i * frame_metrics . size ( ) / w ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
for ( Set < StringName > : : Element * E = plot_sigs . front ( ) ; E ; E = E - > next ( ) ) {
2020-04-17 18:50:05 +02:00
const Metric & m = _get_frame_metric ( current ) ;
2016-05-22 02:18:16 +02:00
2020-04-17 18:50:05 +02:00
float value = 0 ;
2016-05-22 02:18:16 +02:00
2020-04-17 18:50:05 +02:00
const Map < StringName , Metric : : Category * > : : Element * F = m . category_ptrs . find ( E - > get ( ) ) ;
if ( F ) {
value = F - > get ( ) - > total_time ;
}
2016-05-22 02:18:16 +02:00
2020-04-17 18:50:05 +02:00
const Map < StringName , Metric : : Category : : Item * > : : Element * G = m . item_ptrs . find ( E - > get ( ) ) ;
if ( G ) {
if ( use_self ) {
value = G - > get ( ) - > self ;
} else {
value = G - > get ( ) - > total ;
2016-05-22 02:18:16 +02:00
}
}
2020-04-17 18:50:05 +02:00
int plot_pos = CLAMP ( int ( value * h / highest ) , 0 , h - 1 ) ;
2017-03-05 16:44:50 +01:00
int prev_plot = plot_pos ;
2020-04-17 18:50:05 +02:00
Map < StringName , int > : : Element * H = prev_plots . find ( E - > get ( ) ) ;
2016-05-22 02:18:16 +02:00
if ( H ) {
2017-03-05 16:44:50 +01:00
prev_plot = H - > get ( ) ;
H - > get ( ) = plot_pos ;
2016-05-22 02:18:16 +02:00
} else {
2020-04-17 18:50:05 +02:00
prev_plots [ E - > get ( ) ] = plot_pos ;
2016-05-22 02:18:16 +02:00
}
2017-03-05 16:44:50 +01:00
plot_pos = h - plot_pos - 1 ;
prev_plot = h - prev_plot - 1 ;
2016-05-22 02:18:16 +02:00
if ( prev_plot > plot_pos ) {
2017-03-05 16:44:50 +01:00
SWAP ( prev_plot , plot_pos ) ;
2016-05-22 02:18:16 +02:00
}
Color col = _get_color_from_signature ( E - > get ( ) ) ;
2017-03-05 16:44:50 +01: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 ;
2016-05-22 02:18:16 +02:00
}
}
2017-03-05 16:44:50 +01:00
for ( int j = 0 ; j < h * 4 ; j + = 4 ) {
2020-01-30 03:05:48 +01:00
const int a = column [ j + 3 ] ;
2017-03-05 16:44:50 +01:00
if ( a > 0 ) {
column [ j + 0 ] / = a ;
column [ j + 1 ] / = a ;
column [ j + 2 ] / = a ;
2016-05-22 02:18:16 +02:00
}
2020-01-30 03:05:48 +01: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 02:18:16 +02:00
2020-01-30 03:05:48 +01: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 ) ;
2017-03-05 16:44:50 +01:00
wr [ widx + 3 ] = 255 ;
2016-05-22 02:18:16 +02:00
}
}
}
2017-05-17 12:36:47 +02:00
Ref < Image > img ;
2021-06-18 00:03:09 +02:00
img . instantiate ( ) ;
2020-05-14 11:00:19 +02:00
img - > create ( w , h , false , Image : : FORMAT_RGBA8 , graph_image ) ;
2016-05-22 02:18:16 +02:00
if ( reset_texture ) {
if ( graph_texture . is_null ( ) ) {
2021-06-18 00:03:09 +02:00
graph_texture . instantiate ( ) ;
2016-05-22 02:18:16 +02:00
}
2019-06-11 20:43:37 +02:00
graph_texture - > create_from_image ( img ) ;
2016-05-22 02:18:16 +02:00
}
2021-07-01 04:17:47 +02:00
graph_texture - > update ( img ) ;
2016-05-22 02:18:16 +02:00
graph - > set_texture ( graph_texture ) ;
graph - > update ( ) ;
}
void EditorProfiler : : _update_frame ( ) {
2020-04-17 18:50:05 +02:00
int cursor_metric = cursor_metric_edit - > get_value ( ) - _get_frame_metric ( 0 ) . frame_number ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
updating_frame = true ;
2016-05-22 02:18:16 +02:00
variables - > clear ( ) ;
2017-03-05 16:44:50 +01:00
TreeItem * root = variables - > create_item ( ) ;
2020-04-17 18:50:05 +02:00
const Metric & m = _get_frame_metric ( cursor_metric ) ;
2016-05-22 02:18:16 +02:00
int dtime = display_time - > get_selected ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < m . categories . size ( ) ; i + + ) {
2016-05-22 02:18:16 +02:00
TreeItem * category = variables - > create_item ( root ) ;
2017-03-05 16:44:50 +01:00
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 ) ) ;
2016-05-22 02:18:16 +02:00
if ( plot_sigs . has ( m . categories [ i ] . signature ) ) {
2017-03-05 16:44:50 +01:00
category - > set_checked ( 0 , true ) ;
category - > set_custom_color ( 0 , _get_color_from_signature ( m . categories [ i ] . signature ) ) ;
2016-05-22 02:18:16 +02:00
}
2019-01-26 19:54:04 +01:00
for ( int j = m . categories [ i ] . items . size ( ) - 1 ; j > = 0 ; j - - ) {
2018-07-25 03:11:03 +02:00
const Metric : : Category : : Item & it = m . categories [ i ] . items [ j ] ;
2016-05-22 02:18:16 +02:00
TreeItem * item = variables - > create_item ( category ) ;
2017-03-05 16:44:50 +01:00
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 03:58:47 +01:00
item - > set_text_alignment ( 2 , HORIZONTAL_ALIGNMENT_RIGHT ) ;
2021-07-19 01:40:05 +02:00
item - > set_tooltip ( 0 , it . name + " \n " + it . script + " : " + itos ( it . line ) ) ;
2016-05-22 02:18:16 +02:00
float time = dtime = = DISPLAY_SELF_TIME ? it . self : it . total ;
2017-03-05 16:44:50 +01:00
item - > set_text ( 1 , _get_time_as_text ( m , time , it . calls ) ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
item - > set_text ( 2 , itos ( it . calls ) ) ;
2016-05-22 02:18:16 +02:00
if ( plot_sigs . has ( it . signature ) ) {
2017-03-05 16:44:50 +01:00
item - > set_checked ( 0 , true ) ;
item - > set_custom_color ( 0 , _get_color_from_signature ( it . signature ) ) ;
2016-05-22 02:18:16 +02:00
}
}
}
2017-03-05 16:44:50 +01:00
updating_frame = false ;
2016-05-22 02:18:16 +02:00
}
void EditorProfiler : : _activate_pressed ( ) {
if ( activate - > is_pressed ( ) ) {
2021-07-17 23:22:52 +02:00
activate - > set_icon ( get_theme_icon ( SNAME ( " Stop " ) , SNAME ( " EditorIcons " ) ) ) ;
2018-06-24 03:15:10 +02:00
activate - > set_text ( TTR ( " Stop " ) ) ;
2020-04-17 18:50:05 +02:00
_clear_pressed ( ) ;
2016-05-22 02:18:16 +02:00
} else {
2021-07-17 23:22:52 +02:00
activate - > set_icon ( get_theme_icon ( SNAME ( " Play " ) , SNAME ( " EditorIcons " ) ) ) ;
2018-06-24 03:15:10 +02:00
activate - > set_text ( TTR ( " Start " ) ) ;
2016-05-22 02:18:16 +02:00
}
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " enable_profiling " ) , activate - > is_pressed ( ) ) ;
2016-05-22 02:18:16 +02:00
}
2018-06-24 03:15:10 +02:00
void EditorProfiler : : _clear_pressed ( ) {
2020-04-17 18:50:05 +02:00
clear_button - > set_disabled ( true ) ;
2018-06-24 03:15:10 +02:00
clear ( ) ;
2018-08-28 11:49:12 +02:00
_update_plot ( ) ;
2018-06-24 03:15:10 +02:00
}
2016-05-22 02:18:16 +02:00
void EditorProfiler : : _notification ( int p_what ) {
2022-02-16 00:52:32 +01:00
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE :
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED :
2022-03-15 16:05:12 +01:00
case NOTIFICATION_THEME_CHANGED :
2022-02-16 00:52:32 +01:00
case NOTIFICATION_TRANSLATION_CHANGED : {
activate - > set_icon ( get_theme_icon ( SNAME ( " Play " ) , SNAME ( " EditorIcons " ) ) ) ;
clear_button - > set_icon ( get_theme_icon ( SNAME ( " Clear " ) , SNAME ( " EditorIcons " ) ) ) ;
} break ;
2016-05-22 02:18:16 +02:00
}
}
void EditorProfiler : : _graph_tex_draw ( ) {
2020-04-17 18:50:05 +02:00
if ( total_metrics = = 0 ) {
2016-05-22 02:18:16 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2016-05-22 02:18:16 +02:00
if ( seeking ) {
2020-04-17 18:50:05 +02: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 ;
2017-03-05 16:44:50 +01:00
graph - > draw_line ( Vector2 ( cur_x , 0 ) , Vector2 ( cur_x , graph - > get_size ( ) . y ) , Color ( 1 , 1 , 1 , 0.8 ) ) ;
2016-05-22 02:18:16 +02:00
}
2020-04-17 18:50:05 +02: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 ;
2017-03-05 16:44:50 +01:00
graph - > draw_line ( Vector2 ( cur_x , 0 ) , Vector2 ( cur_x , graph - > get_size ( ) . y ) , Color ( 1 , 1 , 1 , 0.4 ) ) ;
2016-05-22 02:18:16 +02:00
}
}
void EditorProfiler : : _graph_tex_mouse_exit ( ) {
2017-03-05 16:44:50 +01:00
hover_metric = - 1 ;
2016-05-22 02:18:16 +02:00
graph - > update ( ) ;
}
void EditorProfiler : : _cursor_metric_changed ( double ) {
2020-05-14 16:41:43 +02:00
if ( updating_frame ) {
2016-05-22 02:18:16 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2016-05-22 02:18:16 +02:00
graph - > update ( ) ;
_update_frame ( ) ;
}
2017-05-20 17:38:03 +02:00
void EditorProfiler : : _graph_tex_input ( const Ref < InputEvent > & p_ev ) {
2020-05-14 16:41:43 +02:00
if ( last_metric < 0 ) {
2016-05-22 02:18:16 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2016-05-22 02:18:16 +02:00
2017-05-20 17:38:03 +02:00
Ref < InputEventMouse > me = p_ev ;
Ref < InputEventMouseButton > mb = p_ev ;
Ref < InputEventMouseMotion > mm = p_ev ;
2016-05-22 02:18:16 +02:00
if (
2021-08-13 23:31:57 +02:00
( mb . is_valid ( ) & & mb - > get_button_index ( ) = = MouseButton : : LEFT & & mb - > is_pressed ( ) ) | |
2017-05-20 17:38:03 +02:00
( mm . is_valid ( ) ) ) {
2020-04-17 18:50:05 +02:00
int x = me - > get_position ( ) . x - 1 ;
2017-03-05 16:44:50 +01:00
x = x * frame_metrics . size ( ) / graph - > get_size ( ) . width ;
2016-05-22 02:18:16 +02:00
2020-04-17 18:50:05 +02:00
hover_metric = x ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
if ( x < 0 ) {
x = 0 ;
2016-05-22 02:18:16 +02:00
}
2017-03-05 16:44:50 +01:00
if ( x > = frame_metrics . size ( ) ) {
x = frame_metrics . size ( ) - 1 ;
2016-05-22 02:18:16 +02:00
}
2021-08-13 23:31:57 +02:00
if ( mb . is_valid ( ) | | ( mm - > get_button_mask ( ) & MouseButton : : MASK_LEFT ) ! = MouseButton : : NONE ) {
2017-03-05 16:44:50 +01:00
updating_frame = true ;
2016-05-22 02:18:16 +02:00
2022-01-27 17:34:33 +01:00
if ( x < total_metrics ) {
2020-04-17 18:50:05 +02:00
cursor_metric_edit - > set_value ( _get_frame_metric ( x ) . frame_number ) ;
2022-01-27 17:34:33 +01:00
}
2017-03-05 16:44:50 +01:00
updating_frame = false ;
2016-05-22 02:18:16 +02:00
if ( activate - > is_pressed ( ) ) {
if ( ! seeking ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " break_request " ) ) ;
2016-05-22 02:18:16 +02:00
}
}
2017-03-05 16:44:50 +01:00
seeking = true ;
2016-05-22 02:18:16 +02:00
if ( ! frame_delay - > is_processing ( ) ) {
frame_delay - > set_wait_time ( 0.1 ) ;
frame_delay - > start ( ) ;
}
}
graph - > update ( ) ;
}
}
void EditorProfiler : : disable_seeking ( ) {
2017-03-05 16:44:50 +01:00
seeking = false ;
2016-05-22 02:18:16 +02:00
graph - > update ( ) ;
}
void EditorProfiler : : _combo_changed ( int ) {
_update_frame ( ) ;
_update_plot ( ) ;
}
void EditorProfiler : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ADD_SIGNAL ( MethodInfo ( " enable_profiling " , PropertyInfo ( Variant : : BOOL , " enable " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " break_request " ) ) ;
2016-05-22 02:18:16 +02:00
}
void EditorProfiler : : set_enabled ( bool p_enable ) {
activate - > set_disabled ( ! p_enable ) ;
}
bool EditorProfiler : : is_profiling ( ) {
return activate - > is_pressed ( ) ;
}
2020-03-17 07:33:00 +01:00
Vector < Vector < String > > EditorProfiler : : get_data_as_csv ( ) const {
Vector < Vector < String > > res ;
2019-02-18 01:25:26 +01:00
2020-12-15 13:04:21 +01:00
if ( frame_metrics . is_empty ( ) ) {
2019-02-18 01:25:26 +01:00
return res ;
}
2021-05-21 09:40:03 +02:00
// Different metrics may contain different number of categories.
Set < StringName > possible_signatures ;
for ( int i = 0 ; i < frame_metrics . size ( ) ; i + + ) {
const Metric & m = frame_metrics [ i ] ;
if ( ! m . valid ) {
continue ;
}
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , Metric : : Category * > & E : m . category_ptrs ) {
possible_signatures . insert ( E . key ) ;
2021-05-21 09:40:03 +02:00
}
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , Metric : : Category : : Item * > & E : m . item_ptrs ) {
possible_signatures . insert ( E . key ) ;
2019-02-18 01:25:26 +01:00
}
}
2021-05-21 09:40:03 +02:00
// Generate CSV header and cache indices.
Map < StringName , int > sig_map ;
Vector < String > signatures ;
signatures . resize ( possible_signatures . size ( ) ) ;
int sig_index = 0 ;
for ( const Set < StringName > : : Element * E = possible_signatures . front ( ) ; E ; E = E - > next ( ) ) {
signatures . write [ sig_index ] = E - > get ( ) ;
sig_map [ E - > get ( ) ] = sig_index ;
sig_index + + ;
}
2019-02-18 01:25:26 +01: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 09:40:03 +02:00
const Metric & m = frame_metrics [ index ] ;
if ( ! m . valid ) {
2019-02-18 01:25:26 +01:00
continue ;
}
2021-05-21 09:40:03 +02:00
// Don't keep old values since there may be empty cells.
values . clear ( ) ;
values . resize ( possible_signatures . size ( ) ) ;
2019-02-18 01:25:26 +01:00
2021-08-09 22:13:42 +02: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 09:40:03 +02:00
}
2021-08-09 22:13:42 +02: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 01:25:26 +01:00
}
2021-05-21 09:40:03 +02:00
2019-02-18 01:25:26 +01:00
res . push_back ( values ) ;
}
return res ;
}
2017-03-05 16:44:50 +01:00
EditorProfiler : : EditorProfiler ( ) {
HBoxContainer * hb = memnew ( HBoxContainer ) ;
2016-05-22 02:18:16 +02:00
add_child ( hb ) ;
2017-03-05 16:44:50 +01:00
activate = memnew ( Button ) ;
2016-05-22 02:18:16 +02:00
activate - > set_toggle_mode ( true ) ;
2018-06-24 03:15:10 +02:00
activate - > set_text ( TTR ( " Start " ) ) ;
2020-02-21 18:28:45 +01:00
activate - > connect ( " pressed " , callable_mp ( this , & EditorProfiler : : _activate_pressed ) ) ;
2016-05-22 02:18:16 +02:00
hb - > add_child ( activate ) ;
2018-06-24 03:15:10 +02:00
clear_button = memnew ( Button ) ;
clear_button - > set_text ( TTR ( " Clear " ) ) ;
2020-02-21 18:28:45 +01:00
clear_button - > connect ( " pressed " , callable_mp ( this , & EditorProfiler : : _clear_pressed ) ) ;
2020-04-17 18:50:05 +02:00
clear_button - > set_disabled ( true ) ;
2018-06-24 03:15:10 +02:00
hb - > add_child ( clear_button ) ;
2017-03-05 16:44:50 +01:00
hb - > add_child ( memnew ( Label ( TTR ( " Measure: " ) ) ) ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
display_mode = memnew ( OptionButton ) ;
2021-07-24 06:15:49 +02:00
display_mode - > add_item ( TTR ( " Frame Time (ms) " ) ) ;
display_mode - > add_item ( TTR ( " Average Time (ms) " ) ) ;
2016-05-22 02:18:16 +02:00
display_mode - > add_item ( TTR ( " Frame % " ) ) ;
2017-10-21 16:28:08 +02:00
display_mode - > add_item ( TTR ( " Physics Frame % " ) ) ;
2020-02-21 18:28:45 +01:00
display_mode - > connect ( " item_selected " , callable_mp ( this , & EditorProfiler : : _combo_changed ) ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
hb - > add_child ( display_mode ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
hb - > add_child ( memnew ( Label ( TTR ( " Time: " ) ) ) ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
display_time = memnew ( OptionButton ) ;
2016-05-22 02:18:16 +02:00
display_time - > add_item ( TTR ( " Inclusive " ) ) ;
display_time - > add_item ( TTR ( " Self " ) ) ;
2021-07-24 06:15:49 +02:00
display_time - > set_tooltip ( 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 18:28:45 +01:00
display_time - > connect ( " item_selected " , callable_mp ( this , & EditorProfiler : : _combo_changed ) ) ;
2016-05-22 02:18:16 +02:00
hb - > add_child ( display_time ) ;
hb - > add_spacer ( ) ;
2017-03-05 16:44:50 +01:00
hb - > add_child ( memnew ( Label ( TTR ( " Frame #: " ) ) ) ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
cursor_metric_edit = memnew ( SpinBox ) ;
2016-05-22 02:18:16 +02:00
cursor_metric_edit - > set_h_size_flags ( SIZE_FILL ) ;
2020-04-17 18:50:05 +02:00
cursor_metric_edit - > set_value ( 0 ) ;
cursor_metric_edit - > set_editable ( false ) ;
2016-05-22 02:18:16 +02:00
hb - > add_child ( cursor_metric_edit ) ;
2020-02-21 18:28:45 +01:00
cursor_metric_edit - > connect ( " value_changed " , callable_mp ( this , & EditorProfiler : : _cursor_metric_changed ) ) ;
2016-05-22 02:18:16 +02:00
2022-02-08 10:14:58 +01:00
hb - > add_theme_constant_override ( " separation " , 8 * EDSCALE ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
h_split = memnew ( HSplitContainer ) ;
2016-05-22 02:18:16 +02:00
add_child ( h_split ) ;
h_split - > set_v_size_flags ( SIZE_EXPAND_FILL ) ;
2017-03-05 16:44:50 +01:00
variables = memnew ( Tree ) ;
2020-01-24 12:07:38 +01:00
variables - > set_custom_minimum_size ( Size2 ( 320 , 0 ) * EDSCALE ) ;
2016-05-22 02:18:16 +02: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 10:58:58 +01:00
variables - > set_column_title ( 0 , TTR ( " Name " ) ) ;
2017-03-05 16:44:50 +01:00
variables - > set_column_expand ( 0 , true ) ;
2021-07-04 05:13:28 +02:00
variables - > set_column_clip_content ( 0 , true ) ;
variables - > set_column_expand_ratio ( 0 , 60 ) ;
2017-12-02 10:58:58 +01:00
variables - > set_column_title ( 1 , TTR ( " Time " ) ) ;
2017-03-05 16:44:50 +01:00
variables - > set_column_expand ( 1 , false ) ;
2021-07-04 05:13:28 +02:00
variables - > set_column_clip_content ( 1 , true ) ;
variables - > set_column_expand_ratio ( 1 , 100 ) ;
2017-12-02 10:58:58 +01:00
variables - > set_column_title ( 2 , TTR ( " Calls " ) ) ;
2017-03-05 16:44:50 +01:00
variables - > set_column_expand ( 2 , false ) ;
2021-07-04 05:13:28 +02:00
variables - > set_column_clip_content ( 2 , true ) ;
variables - > set_column_expand_ratio ( 2 , 60 ) ;
2020-02-21 18:28:45 +01:00
variables - > connect ( " item_edited " , callable_mp ( this , & EditorProfiler : : _item_edited ) ) ;
2017-03-05 16:44:50 +01:00
graph = memnew ( TextureRect ) ;
2021-11-30 01:21:45 +01:00
graph - > set_ignore_texture_size ( true ) ;
2017-01-08 23:54:19 +01:00
graph - > set_mouse_filter ( MOUSE_FILTER_STOP ) ;
2020-02-21 18:28:45 +01: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 02:18:16 +02:00
h_split - > add_child ( graph ) ;
graph - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
2022-03-06 21:39:19 +01:00
int metric_size = CLAMP ( int ( EDITOR_GET ( " debugger/profiler_frame_history_size " ) ) , 60 , 1024 ) ;
2016-05-22 02:18:16 +02:00
frame_metrics . resize ( metric_size ) ;
2020-04-17 18:50:05 +02:00
total_metrics = 0 ;
2017-03-05 16:44:50 +01:00
last_metric = - 1 ;
hover_metric = - 1 ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
EDITOR_DEF ( " debugger/profiler_frame_max_functions " , 64 ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
frame_delay = memnew ( Timer ) ;
2016-05-22 02:18:16 +02:00
frame_delay - > set_wait_time ( 0.1 ) ;
frame_delay - > set_one_shot ( true ) ;
add_child ( frame_delay ) ;
2020-02-21 18:28:45 +01:00
frame_delay - > connect ( " timeout " , callable_mp ( this , & EditorProfiler : : _update_frame ) ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
plot_delay = memnew ( Timer ) ;
2016-05-22 02:18:16 +02:00
plot_delay - > set_wait_time ( 0.1 ) ;
plot_delay - > set_one_shot ( true ) ;
add_child ( plot_delay ) ;
2020-02-21 18:28:45 +01:00
plot_delay - > connect ( " timeout " , callable_mp ( this , & EditorProfiler : : _update_plot ) ) ;
2016-05-22 02:18:16 +02:00
2017-09-30 16:19:07 +02:00
plot_sigs . insert ( " physics_frame_time " ) ;
2016-05-22 02:18:16 +02:00
plot_sigs . insert ( " category_frame_time " ) ;
2017-03-05 16:44:50 +01:00
seeking = false ;
graph_height = 1 ;
2016-05-22 02:18:16 +02:00
}