Compare commits
56 Commits
lineart-ob
...
profiler-e
Author | SHA1 | Date | |
---|---|---|---|
1895205be3 | |||
619b015a46 | |||
ab200c6edd | |||
9099b5b93f | |||
d79d4096bc | |||
cc682ef292 | |||
76ae2aba80 | |||
c8a94d6edf | |||
6c499c6ea5 | |||
50afb13ee0 | |||
23d034618d | |||
0848521699 | |||
dd8b19ebb4 | |||
fa918199c0 | |||
fd19b0123c | |||
b9d8f34e35 | |||
ca13fa8146 | |||
e6258f09d5 | |||
5490d79dd5 | |||
41f06beac9 | |||
3a6e4d76f9 | |||
3a3bebc3eb | |||
f318ce578b | |||
f9e22fd1ca | |||
db7519d9c9 | |||
b02635ae1b | |||
ef2bec0db2 | |||
6cbeb7de8b | |||
0523aab3fe | |||
ddb21b01be | |||
6951661dcc | |||
7663d40356 | |||
e13a3896d1 | |||
3bffd13742 | |||
7b8adb7d89 | |||
f559440014 | |||
3734e0f7f9 | |||
4f6280f6b4 | |||
feaeffa2bf | |||
e4fb564c54 | |||
d715a2ae09 | |||
3b3c82042e | |||
9ad17f6005 | |||
258038ca84 | |||
2025a21499 | |||
fff231daf0 | |||
d8ed26c760 | |||
e6cc94dabc | |||
33dd648605 | |||
aa9d8a2032 | |||
dfd3b8f26f | |||
05e6466ed0 | |||
7f15534e4c | |||
e36beb7f9e | |||
deead45b6e | |||
6420f81e9d |
@@ -1042,6 +1042,38 @@ const bTheme U_theme_default = {
|
||||
.edited_object = RGBA(0x00806266),
|
||||
.row_alternate = RGBA(0xffffff07),
|
||||
},
|
||||
.space_profiler = {
|
||||
.back = RGBA(0x28282800),
|
||||
.title = RGBA(0xffffffff),
|
||||
.text = RGBA(0xc3c3c3ff),
|
||||
.text_hi = RGBA(0xffffffff),
|
||||
.header = RGBA(0x454545ff),
|
||||
.header_text = RGBA(0xeeeeeeff),
|
||||
.header_text_hi = RGBA(0xffffffff),
|
||||
.tab_active = RGBA(0x4b4b4bff),
|
||||
.tab_inactive = RGBA(0x2b2b2bff),
|
||||
.tab_back = RGBA(0x232323ff),
|
||||
.tab_outline = RGBA(0x232323ff),
|
||||
.button = RGBA(0x424242ff),
|
||||
.button_title = RGBA(0xffffffff),
|
||||
.button_text = RGBA(0xe5e5e5ff),
|
||||
.button_text_hi = RGBA(0xffffffff),
|
||||
.panelcolors = {
|
||||
.header = RGBA(0x424242cc),
|
||||
.back = RGBA(0x333333b3),
|
||||
.sub_back = RGBA(0x0000003e),
|
||||
},
|
||||
.active = RGBA(0x3b5689ff),
|
||||
.vertex_size = 3,
|
||||
.outline_width = 1,
|
||||
.facedot_size = 4,
|
||||
.match = RGBA(0x337f334c),
|
||||
.selected_highlight = RGBA(0x223a5bff),
|
||||
.selected_object = RGBA(0xe96a00ff),
|
||||
.active_object = RGBA(0xffaf29ff),
|
||||
.edited_object = RGBA(0x00806266),
|
||||
.row_alternate = RGBA(0xffffff07),
|
||||
},
|
||||
.tarm = {
|
||||
{
|
||||
.solid = RGBA(0x9a0000ff),
|
||||
|
@@ -1352,6 +1352,42 @@
|
||||
</space>
|
||||
</ThemeSpreadsheet>
|
||||
</spreadsheet>
|
||||
<profiler>
|
||||
<ThemeProfiler
|
||||
row_alternate="#ffffff0f"
|
||||
>
|
||||
<space>
|
||||
<ThemeSpaceGeneric
|
||||
back="#999999"
|
||||
title="#000000"
|
||||
text="#000000"
|
||||
text_hi="#ffffff"
|
||||
header="#adadadff"
|
||||
header_text="#000000"
|
||||
header_text_hi="#ffffff"
|
||||
button="#999999e6"
|
||||
button_title="#1a1a1a"
|
||||
button_text="#000000"
|
||||
button_text_hi="#ffffff"
|
||||
navigation_bar="#00000000"
|
||||
execution_buts="#999999e6"
|
||||
tab_active="#6697e6"
|
||||
tab_inactive="#cccccc"
|
||||
tab_back="#999999ff"
|
||||
tab_outline="#999999"
|
||||
>
|
||||
<panelcolors>
|
||||
<ThemePanelColors
|
||||
header="#42424200"
|
||||
back="#00000028"
|
||||
sub_back="#00000024"
|
||||
>
|
||||
</ThemePanelColors>
|
||||
</panelcolors>
|
||||
</ThemeSpaceGeneric>
|
||||
</space>
|
||||
</ThemeProfiler>
|
||||
</profiler>
|
||||
<bone_color_sets>
|
||||
<ThemeBoneColorSet
|
||||
normal="#9a0000"
|
||||
|
@@ -42,6 +42,7 @@ _modules = [
|
||||
"object_quick_effects",
|
||||
"object_randomize_transform",
|
||||
"presets",
|
||||
"profiler",
|
||||
"rigidbody",
|
||||
"screen_play_rendered_anim",
|
||||
"sequencer",
|
||||
|
70
release/scripts/startup/bl_operators/profiler.py
Normal file
70
release/scripts/startup/bl_operators/profiler.py
Normal file
@@ -0,0 +1,70 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
class PROFILE_OT_enable(bpy.types.Operator):
|
||||
bl_idname = "profile.enable"
|
||||
bl_label = "Enable Profiling"
|
||||
bl_options = {'REGISTER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.area.type == 'PROFILER'
|
||||
|
||||
def execute(self, context):
|
||||
sprofiler = context.space_data
|
||||
sprofiler.profile_enable()
|
||||
return {'FINISHED'}
|
||||
|
||||
class PROFILE_OT_disable(bpy.types.Operator):
|
||||
bl_idname = "profile.disable"
|
||||
bl_label = "Disable Profiling"
|
||||
bl_options = {'REGISTER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.area.type == 'PROFILER'
|
||||
|
||||
def execute(self, context):
|
||||
sprofiler = context.space_data
|
||||
sprofiler.profile_disable()
|
||||
return {'FINISHED'}
|
||||
|
||||
class PROFILE_OT_clear(bpy.types.Operator):
|
||||
bl_idname = "profile.clear"
|
||||
bl_label = "Clear Profile Data"
|
||||
bl_options = {'REGISTER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.area.type == 'PROFILER'
|
||||
|
||||
def execute(self, context):
|
||||
sprofiler = context.space_data
|
||||
sprofiler.profile_clear()
|
||||
return {'FINISHED'}
|
||||
|
||||
classes = (
|
||||
PROFILE_OT_enable,
|
||||
PROFILE_OT_disable,
|
||||
PROFILE_OT_clear,
|
||||
)
|
@@ -85,6 +85,7 @@ _modules = [
|
||||
"space_nla",
|
||||
"space_node",
|
||||
"space_outliner",
|
||||
"space_profiler",
|
||||
"space_properties",
|
||||
"space_sequencer",
|
||||
"space_spreadsheet",
|
||||
|
45
release/scripts/startup/bl_ui/space_profiler.py
Normal file
45
release/scripts/startup/bl_ui/space_profiler.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
import bpy
|
||||
|
||||
class PROFILER_HT_header(bpy.types.Header):
|
||||
bl_space_type = 'PROFILER'
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
sprofiler = context.space_data
|
||||
|
||||
layout.template_header()
|
||||
|
||||
if sprofiler.profile_is_enabled:
|
||||
layout.operator("profile.disable")
|
||||
else:
|
||||
layout.operator("profile.enable")
|
||||
|
||||
layout.operator("profile.clear")
|
||||
|
||||
|
||||
classes = (
|
||||
PROFILER_HT_header,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # Only for live edit.
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
@@ -198,6 +198,7 @@ struct SpaceUserPref *CTX_wm_space_userpref(const bContext *C);
|
||||
struct SpaceClip *CTX_wm_space_clip(const bContext *C);
|
||||
struct SpaceTopBar *CTX_wm_space_topbar(const bContext *C);
|
||||
struct SpaceSpreadsheet *CTX_wm_space_spreadsheet(const bContext *C);
|
||||
struct SpaceProfiler *CTX_wm_space_profiler(const bContext *C);
|
||||
|
||||
void CTX_wm_manager_set(bContext *C, struct wmWindowManager *wm);
|
||||
void CTX_wm_window_set(bContext *C, struct wmWindow *win);
|
||||
|
@@ -41,6 +41,7 @@
|
||||
#include "BLI_float2.hh"
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_profile.hh"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_vector.hh"
|
||||
@@ -951,6 +952,7 @@ static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md,
|
||||
GeometryOwnershipType::Editable);
|
||||
|
||||
/* Let the modifier change the geometry set. */
|
||||
BLI_PROFILE_SCOPE(md->name);
|
||||
mti->modifyGeometrySet(md, &mectx, &geometry_set);
|
||||
|
||||
/* Release the mesh from the geometry set again. */
|
||||
@@ -1700,6 +1702,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
|
||||
BKE_mesh_vert_coords_apply(mesh_final, deformed_verts);
|
||||
}
|
||||
|
||||
BLI_PROFILE_SCOPE(md->name);
|
||||
if (mti->deformVertsEM) {
|
||||
BKE_modifier_deform_vertsEM(
|
||||
md, &mectx, em_input, mesh_final, deformed_verts, num_deformed_verts);
|
||||
@@ -2069,6 +2072,7 @@ void makeDerivedMesh(struct Depsgraph *depsgraph,
|
||||
BMEditMesh *em,
|
||||
const CustomData_MeshMasks *dataMask)
|
||||
{
|
||||
BLI_PROFILE_SCOPE((std::string(ob->id.name) + " Modifiers").c_str());
|
||||
bool need_mapping;
|
||||
CustomData_MeshMasks cddata_masks = *dataMask;
|
||||
object_get_datamask(depsgraph, ob, &cddata_masks, &need_mapping);
|
||||
|
@@ -938,6 +938,15 @@ struct SpaceSpreadsheet *CTX_wm_space_spreadsheet(const bContext *C)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct SpaceProfiler *CTX_wm_space_profiler(const bContext *C)
|
||||
{
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
if (area && area->spacetype == SPACE_PROFILER) {
|
||||
return area->spacedata.first;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CTX_wm_manager_set(bContext *C, wmWindowManager *wm)
|
||||
{
|
||||
C->wm.manager = wm;
|
||||
|
@@ -51,6 +51,7 @@
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_profile.h"
|
||||
#include "BLI_session_uuid.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.h"
|
||||
@@ -1052,7 +1053,11 @@ struct Mesh *BKE_modifier_modify_mesh(ModifierData *md,
|
||||
if (mti->dependsOnNormals && mti->dependsOnNormals(md)) {
|
||||
modwrap_dependsOnNormals(me);
|
||||
}
|
||||
return mti->modifyMesh(md, ctx, me);
|
||||
ProfileTask profile_task;
|
||||
BLI_profile_task_begin_named(&profile_task, md->name);
|
||||
Mesh *new_mesh = mti->modifyMesh(md, ctx, me);
|
||||
BLI_profile_task_end(&profile_task);
|
||||
return new_mesh;
|
||||
}
|
||||
|
||||
void BKE_modifier_deform_verts(ModifierData *md,
|
||||
@@ -1067,7 +1072,10 @@ void BKE_modifier_deform_verts(ModifierData *md,
|
||||
if (me && mti->dependsOnNormals && mti->dependsOnNormals(md)) {
|
||||
modwrap_dependsOnNormals(me);
|
||||
}
|
||||
ProfileTask profile_task;
|
||||
BLI_profile_task_begin_named(&profile_task, md->name);
|
||||
mti->deformVerts(md, ctx, me, vertexCos, numVerts);
|
||||
BLI_profile_task_end(&profile_task);
|
||||
}
|
||||
|
||||
void BKE_modifier_deform_vertsEM(ModifierData *md,
|
||||
|
@@ -1384,6 +1384,9 @@ static void write_area(BlendWriter *writer, ScrArea *area)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sl->spacetype == SPACE_PROFILER) {
|
||||
BLO_write_struct(writer, SpaceProfiler, sl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1761,6 +1764,10 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sl->spacetype == SPACE_PROFILER) {
|
||||
SpaceProfiler *sprofiler = (SpaceProfiler *)sl;
|
||||
sprofiler->runtime = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_listbase_clear(&area->actionzones);
|
||||
|
@@ -33,8 +33,8 @@ namespace blender {
|
||||
template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopyable, NonMovable {
|
||||
private:
|
||||
Allocator allocator_;
|
||||
Vector<void *> owned_buffers_;
|
||||
Vector<Span<char>> unused_borrowed_buffers_;
|
||||
Vector<void *, 4, Allocator> owned_buffers_;
|
||||
Vector<Span<char>, 1, Allocator> unused_borrowed_buffers_;
|
||||
|
||||
uintptr_t current_begin_;
|
||||
uintptr_t current_end_;
|
||||
@@ -89,9 +89,29 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
|
||||
this->allocate_new_buffer(size + alignment, alignment);
|
||||
return this->allocate(size, alignment);
|
||||
}
|
||||
return this->allocator_large_buffer(size, alignment);
|
||||
return this->allocate_large_buffer(size, alignment);
|
||||
};
|
||||
|
||||
void *allocate_unaligned(const int64_t size)
|
||||
{
|
||||
BLI_assert(size >= 0);
|
||||
|
||||
const uint64_t potential_allocation_begin = current_begin_;
|
||||
const uint64_t potential_allocation_end = potential_allocation_begin + size;
|
||||
if (potential_allocation_end <= current_end_) {
|
||||
#ifdef DEBUG
|
||||
debug_allocated_amount_ += size;
|
||||
#endif
|
||||
current_begin_ = potential_allocation_end;
|
||||
return reinterpret_cast<void *>(potential_allocation_begin);
|
||||
}
|
||||
if (size <= large_buffer_threshold) {
|
||||
this->allocate_new_buffer(size, 1);
|
||||
return this->allocate_unaligned(size);
|
||||
}
|
||||
return this->allocate_large_buffer(size, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a memory buffer that can hold an instance of T.
|
||||
*
|
||||
@@ -162,10 +182,12 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
|
||||
*/
|
||||
StringRefNull copy_string(StringRef str)
|
||||
{
|
||||
const int64_t alloc_size = str.size() + 1;
|
||||
char *buffer = static_cast<char *>(this->allocate(alloc_size, 1));
|
||||
str.copy(buffer, alloc_size);
|
||||
return StringRefNull(static_cast<const char *>(buffer));
|
||||
const int64_t str_size = str.size();
|
||||
const int64_t alloc_size = str_size + 1;
|
||||
char *buffer = static_cast<char *>(this->allocate_unaligned(alloc_size));
|
||||
memcpy(buffer, str.data(), str_size);
|
||||
buffer[str_size] = '\0';
|
||||
return StringRefNull(buffer, str_size);
|
||||
}
|
||||
|
||||
MutableSpan<void *> allocate_elements_and_pointer_array(int64_t element_amount,
|
||||
@@ -244,7 +266,7 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
|
||||
current_end_ = current_begin_ + size_in_bytes;
|
||||
}
|
||||
|
||||
void *allocator_large_buffer(const int64_t size, const int64_t alignment)
|
||||
void *allocate_large_buffer(const int64_t size, const int64_t alignment)
|
||||
{
|
||||
void *buffer = allocator_.allocate(size, alignment, __func__);
|
||||
owned_buffers_.append(buffer);
|
||||
|
92
source/blender/blenlib/BLI_profile.h
Normal file
92
source/blender/blenlib/BLI_profile.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern bool bli_profiling_is_enabled;
|
||||
|
||||
typedef struct ProfileTask {
|
||||
uint64_t id;
|
||||
} ProfileTask;
|
||||
|
||||
#define BLI_PROFILE_DUMMY_ID (~0)
|
||||
|
||||
BLI_INLINE bool BLI_profile_is_enabled(void)
|
||||
{
|
||||
return bli_profiling_is_enabled;
|
||||
}
|
||||
|
||||
void _bli_profile_task_begin_named(ProfileTask *task, const char *name);
|
||||
void _bli_profile_task_begin_named_subtask(ProfileTask *task,
|
||||
const char *name,
|
||||
const ProfileTask *parent_task);
|
||||
void _bli_profile_task_begin_range(ProfileTask *task,
|
||||
const ProfileTask *parent_task,
|
||||
int64_t start,
|
||||
int64_t one_after_last);
|
||||
void _bli_profile_task_end(ProfileTask *task);
|
||||
|
||||
BLI_INLINE void BLI_profile_task_begin_named(ProfileTask *task, const char *name)
|
||||
{
|
||||
if (bli_profiling_is_enabled) {
|
||||
_bli_profile_task_begin_named(task, name);
|
||||
}
|
||||
else {
|
||||
task->id = BLI_PROFILE_DUMMY_ID;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE void BLI_profile_task_begin_named_subtask(ProfileTask *task,
|
||||
const char *name,
|
||||
const ProfileTask *parent_task)
|
||||
{
|
||||
if (bli_profiling_is_enabled) {
|
||||
_bli_profile_task_begin_named_subtask(task, name, parent_task);
|
||||
}
|
||||
else {
|
||||
task->id = BLI_PROFILE_DUMMY_ID;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE void BLI_profile_task_begin_range(ProfileTask *task,
|
||||
const ProfileTask *parent_task,
|
||||
const int64_t start,
|
||||
const int64_t one_after_last)
|
||||
{
|
||||
if (bli_profiling_is_enabled) {
|
||||
_bli_profile_task_begin_range(task, parent_task, start, one_after_last);
|
||||
}
|
||||
else {
|
||||
task->id = BLI_PROFILE_DUMMY_ID;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE void BLI_profile_task_end(ProfileTask *task)
|
||||
{
|
||||
if (task->id != BLI_PROFILE_DUMMY_ID) {
|
||||
_bli_profile_task_end(task);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
49
source/blender/blenlib/BLI_profile.hh
Normal file
49
source/blender/blenlib/BLI_profile.hh
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_profile.h"
|
||||
|
||||
namespace blender::profile {
|
||||
|
||||
class ProfileTaskCPP {
|
||||
private:
|
||||
ProfileTask task_;
|
||||
|
||||
public:
|
||||
ProfileTaskCPP(const char *name)
|
||||
{
|
||||
BLI_profile_task_begin_named(&task_, name);
|
||||
}
|
||||
|
||||
ProfileTaskCPP(const char *name, const ProfileTask *parent_task)
|
||||
{
|
||||
BLI_profile_task_begin_named_subtask(&task_, name, parent_task);
|
||||
}
|
||||
|
||||
~ProfileTaskCPP()
|
||||
{
|
||||
BLI_profile_task_end(&task_);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::profile
|
||||
|
||||
#define BLI_PROFILE_SCOPE(name) blender::profile::ProfileTaskCPP profile_task((name))
|
||||
|
||||
#define BLI_PROFILE_SCOPE_SUBTASK(name, parent_task_ptr) \
|
||||
blender::profile::ProfileTaskCPP profile_task((name), (parent_task_ptr))
|
68
source/blender/blenlib/BLI_profile_manage.hh
Normal file
68
source/blender/blenlib/BLI_profile_manage.hh
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender::profile {
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using Duration = Clock::duration;
|
||||
using TimePoint = Clock::time_point;
|
||||
using Nanoseconds = std::chrono::nanoseconds;
|
||||
|
||||
struct ProfileTaskBegin {
|
||||
TimePoint time;
|
||||
uint64_t id;
|
||||
uint64_t parent_id;
|
||||
uint64_t thread_id;
|
||||
};
|
||||
|
||||
struct ProfileTaskBeginNamed : public ProfileTaskBegin {
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct ProfileTaskBeginRange : public ProfileTaskBegin {
|
||||
int64_t start;
|
||||
int64_t one_after_last;
|
||||
};
|
||||
|
||||
struct ProfileTaskEnd {
|
||||
TimePoint time;
|
||||
uint64_t begin_id;
|
||||
};
|
||||
|
||||
struct RecordedProfile {
|
||||
RawVector<ProfileTaskBeginNamed> task_begins_named;
|
||||
RawVector<ProfileTaskBeginRange> task_begins_range;
|
||||
RawVector<ProfileTaskEnd> task_ends;
|
||||
};
|
||||
|
||||
class ProfileListener {
|
||||
public:
|
||||
ProfileListener();
|
||||
virtual ~ProfileListener();
|
||||
|
||||
virtual void handle(const RecordedProfile &profile) = 0;
|
||||
|
||||
static void flush_to_all();
|
||||
};
|
||||
|
||||
} // namespace blender::profile
|
222
source/blender/blenlib/BLI_single_producer_chunk_consumer.hh
Normal file
222
source/blender/blenlib/BLI_single_producer_chunk_consumer.hh
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A `SingleProducerChunkConsumerQueue<T>` is designed to handle the case when
|
||||
* - A single producer thread wants to append elements to the queue very efficiently.
|
||||
* - A single consumer thread wants to consume large chunks from the queue at a time.
|
||||
* - The producer and consumer might run on different threads.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#include "BLI_allocator.hh"
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
template<typename T, typename Allocator = GuardedAllocator, typename UserData = void>
|
||||
class SingleProducerChunkConsumerQueue {
|
||||
private:
|
||||
struct Chunk {
|
||||
/**
|
||||
* Points to the next chunk that contains the elements added after the elements in this chunk.
|
||||
* This is only modified during the append-operation. When it is not null, it means that the
|
||||
* append-operation will not look at this chunk anymore.
|
||||
*/
|
||||
std::atomic<Chunk *> next = nullptr;
|
||||
|
||||
/**
|
||||
* Number of elements that have been committed to the chunk and won't be modified anymore.
|
||||
* This is modified during the append-operation and is only increasing.
|
||||
*/
|
||||
std::atomic<int64_t> committed_size = 0;
|
||||
|
||||
/**
|
||||
* Number of elements that have been consumed already from this chunk.
|
||||
* This is only accessed by the consume-operation.
|
||||
*/
|
||||
int64_t consumed_size = 0;
|
||||
|
||||
/**
|
||||
* Begin and end of the entire chunk buffer. Those are only set during construction and don't
|
||||
* change anymore afterwards.
|
||||
*/
|
||||
T *begin = nullptr;
|
||||
T *capacity_end = nullptr;
|
||||
|
||||
/**
|
||||
* This is modified by the append-operation and not accessed by the consume-operation.
|
||||
*/
|
||||
T *end = nullptr;
|
||||
|
||||
using RealUserData = std::conditional_t<std::is_void_v<UserData>, char, UserData>;
|
||||
RealUserData user_data;
|
||||
};
|
||||
|
||||
struct SharedChunkView {
|
||||
};
|
||||
|
||||
static constexpr inline int64_t ChunkCapacity = 1000;
|
||||
|
||||
Allocator allocator_;
|
||||
|
||||
/* Is only modified in constructor and during consume. */
|
||||
Chunk *begin_;
|
||||
|
||||
/* Is only accessed when appending. */
|
||||
Chunk *current_;
|
||||
|
||||
public:
|
||||
SingleProducerChunkConsumerQueue()
|
||||
{
|
||||
/* Create the first chunk in the constructor, so that the append-operation does not have to
|
||||
* handle this case. */
|
||||
begin_ = this->new_chunk();
|
||||
current_ = begin_;
|
||||
}
|
||||
|
||||
~SingleProducerChunkConsumerQueue()
|
||||
{
|
||||
Chunk *chunk = begin_;
|
||||
while (chunk) {
|
||||
Chunk *next_chunk = chunk->next;
|
||||
this->delete_chunk(chunk);
|
||||
chunk = next_chunk;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start appending a new element.
|
||||
* This constructs the new element with the given parameters.
|
||||
* This must be used in conjunction with #commit_append.
|
||||
*/
|
||||
template<typename... Args> T *prepare_append(Args &&... args)
|
||||
{
|
||||
if (current_->end == current_->capacity_end) {
|
||||
/* Create a new chunk when the current one is full. */
|
||||
Chunk *new_chunk = this->new_chunk();
|
||||
/* This tells the consume-operation that the append-operation does not look at this chunk
|
||||
* anymore. */
|
||||
current_->next.store(new_chunk, std::memory_order_release);
|
||||
current_ = new_chunk;
|
||||
}
|
||||
/* Return a pointer to the next element. */
|
||||
return new (current_->end) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
UserData *user_data_for_current_append()
|
||||
{
|
||||
return ¤t_->user_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the queue that the element has been constructed and is ready to be committed.
|
||||
* Once it is committed, the next consumer can read it.
|
||||
*/
|
||||
void commit_append()
|
||||
{
|
||||
current_->end++;
|
||||
/* Compute the committed size like that instead of doing an increment to avoid having a
|
||||
* read-modify-write operation on an atomic variable which could be more expensive than just
|
||||
* writing to it. */
|
||||
const int64_t new_committed_size = current_->end - current_->begin;
|
||||
current_->committed_size.store(new_committed_size, std::memory_order_release);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get access to all newly committed elements in this queue.
|
||||
* Only a single thread is allowed to entire this function.
|
||||
* However, another thread is allowed to perform an append-operation at the same time.
|
||||
* The spans passed to `consume_fn` are valid until `free_consumed` has been called.
|
||||
*/
|
||||
void consume(const FunctionRef<void(Span<T>)> consume_fn)
|
||||
{
|
||||
Chunk *chunk = begin_;
|
||||
while (chunk) {
|
||||
const int64_t already_consumed_size = chunk->consumed_size;
|
||||
const int64_t committed_chunk_size = chunk->committed_size.load(std::memory_order_acquire);
|
||||
const int64_t newly_consumed_size = committed_chunk_size - already_consumed_size;
|
||||
|
||||
const Span<T> committed_data{chunk->begin + already_consumed_size, newly_consumed_size};
|
||||
consume_fn(committed_data);
|
||||
chunk->consumed_size = committed_chunk_size;
|
||||
|
||||
/* Only try to consume the next chunk, if all elements from this chunk have been consumed. */
|
||||
if (committed_chunk_size == ChunkCapacity) {
|
||||
chunk = chunk->next.load(std::memory_order_acquire);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free chunks that have been consumed already and won't be accessed anymore.
|
||||
* Calling this will invalidate the spans provided by #consume.
|
||||
*/
|
||||
void free_consumed()
|
||||
{
|
||||
Chunk *chunk = begin_;
|
||||
while (chunk) {
|
||||
const int64_t consumed_size = chunk->consumed_size;
|
||||
/* Check if the entire capacity of the chunk has been consumed. */
|
||||
if (consumed_size == ChunkCapacity) {
|
||||
/* Check if the append-operation might still access this chunk. */
|
||||
Chunk *next_chunk = chunk->next.load(std::memory_order_acquire);
|
||||
if (next_chunk != nullptr) {
|
||||
begin_ = next_chunk;
|
||||
this->delete_chunk(chunk);
|
||||
}
|
||||
chunk = next_chunk;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Chunk *new_chunk()
|
||||
{
|
||||
/* We could also combine both allocations in a single one. */
|
||||
void *chunk_buffer = allocator_.allocate(sizeof(Chunk), alignof(Chunk), __func__);
|
||||
Chunk *chunk = new (chunk_buffer) Chunk();
|
||||
chunk->begin = (T *)allocator_.allocate(
|
||||
sizeof(T) * (size_t)ChunkCapacity, alignof(T), __func__);
|
||||
chunk->end = chunk->begin;
|
||||
chunk->capacity_end = chunk->begin + ChunkCapacity;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void delete_chunk(Chunk *chunk)
|
||||
{
|
||||
destruct_n(chunk->begin, chunk->committed_size);
|
||||
allocator_.deallocate(chunk->begin);
|
||||
chunk->~Chunk();
|
||||
allocator_.deallocate(chunk);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
@@ -287,6 +287,14 @@ class Stack {
|
||||
return *(top_ - 1);
|
||||
}
|
||||
|
||||
const T &peek_default(const T &default_value) const
|
||||
{
|
||||
if (size_ > 0) {
|
||||
return this->peek();
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple elements to the stack. The values are pushed in the order they are in the array.
|
||||
* This method is more efficient than pushing multiple elements individually and might cause less
|
||||
|
@@ -112,6 +112,7 @@ set(SRC
|
||||
intern/path_util.c
|
||||
intern/polyfill_2d.c
|
||||
intern/polyfill_2d_beautify.c
|
||||
intern/profile.cc
|
||||
intern/quadric.c
|
||||
intern/rand.cc
|
||||
intern/rct.c
|
||||
@@ -255,6 +256,9 @@ set(SRC
|
||||
BLI_polyfill_2d.h
|
||||
BLI_polyfill_2d_beautify.h
|
||||
BLI_probing_strategies.hh
|
||||
BLI_profile.h
|
||||
BLI_profile.hh
|
||||
BLI_profile_manage.hh
|
||||
BLI_quadric.h
|
||||
BLI_rand.h
|
||||
BLI_rand.hh
|
||||
@@ -265,6 +269,7 @@ set(SRC
|
||||
BLI_set.hh
|
||||
BLI_set_slots.hh
|
||||
BLI_simd.h
|
||||
BLI_single_producer_chunk_consumer.hh
|
||||
BLI_smallhash.h
|
||||
BLI_sort.h
|
||||
BLI_sort_utils.h
|
||||
|
241
source/blender/blenlib/intern/profile.cc
Normal file
241
source/blender/blenlib/intern/profile.cc
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_linear_allocator.hh"
|
||||
#include "BLI_profile.hh"
|
||||
#include "BLI_profile_manage.hh"
|
||||
#include "BLI_single_producer_chunk_consumer.hh"
|
||||
#include "BLI_stack.hh"
|
||||
|
||||
using namespace blender;
|
||||
using namespace blender::profile;
|
||||
|
||||
static uint64_t get_unique_session_id_range(const uint64_t size)
|
||||
{
|
||||
static std::atomic<uint64_t> id = 1;
|
||||
const uint64_t range_start = id.fetch_add(size, std::memory_order_relaxed);
|
||||
return range_start;
|
||||
}
|
||||
|
||||
struct ThreadLocalProfileData;
|
||||
|
||||
struct ProfileRegistry {
|
||||
static inline std::mutex threadlocals_mutex;
|
||||
RawVector<ThreadLocalProfileData *> threadlocals;
|
||||
|
||||
static inline std::mutex listeners_mutex;
|
||||
RawVector<ProfileListener *> listeners;
|
||||
};
|
||||
|
||||
/**
|
||||
* All threads that record profile data register themselves here.
|
||||
* It is a shared pointer, because the individual threadlocal variables have to own the registry as
|
||||
* well. Otherwise there are problems at shutdown when this static variable is destructed before
|
||||
* all other threads unregistered themselves.
|
||||
*/
|
||||
static std::shared_ptr<ProfileRegistry> registry;
|
||||
|
||||
static std::shared_ptr<ProfileRegistry> &ensure_registry()
|
||||
{
|
||||
static std::mutex mutex;
|
||||
std::lock_guard lock{mutex};
|
||||
if (!registry) {
|
||||
registry = std::make_shared<ProfileRegistry>();
|
||||
}
|
||||
return registry;
|
||||
}
|
||||
|
||||
template<typename T, typename UserData = void>
|
||||
using ProfileDataQueue = SingleProducerChunkConsumerQueue<T, RawAllocator, UserData>;
|
||||
|
||||
struct ThreadLocalProfileData {
|
||||
ThreadLocalProfileData()
|
||||
{
|
||||
std::lock_guard lock{ProfileRegistry::threadlocals_mutex};
|
||||
used_registry = ensure_registry();
|
||||
registry->threadlocals.append(this);
|
||||
thread_id = get_unique_session_id_range(1);
|
||||
}
|
||||
|
||||
~ThreadLocalProfileData()
|
||||
{
|
||||
std::lock_guard lock{ProfileRegistry::threadlocals_mutex};
|
||||
used_registry->threadlocals.remove_first_occurrence_and_reorder(this);
|
||||
}
|
||||
|
||||
uint64_t thread_id;
|
||||
ProfileDataQueue<ProfileTaskBeginNamed, LinearAllocator<RawAllocator>> queue_begins_named;
|
||||
ProfileDataQueue<ProfileTaskBeginRange> queue_begins_range;
|
||||
ProfileDataQueue<ProfileTaskEnd> queue_ends;
|
||||
RawStack<uint64_t> id_stack;
|
||||
|
||||
uint64_t get_next_unique_id()
|
||||
{
|
||||
if (unique_id_current_ == unique_id_end_) {
|
||||
constexpr uint64_t size = 100'000;
|
||||
unique_id_current_ = get_unique_session_id_range(size);
|
||||
unique_id_end_ = unique_id_current_ + size;
|
||||
}
|
||||
const uint64_t id = unique_id_current_;
|
||||
unique_id_current_++;
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Take ownership to make sure that the registry won't be destructed too early. */
|
||||
std::shared_ptr<ProfileRegistry> used_registry;
|
||||
|
||||
private:
|
||||
uint64_t unique_id_current_ = 0;
|
||||
uint64_t unique_id_end_ = 0;
|
||||
};
|
||||
|
||||
static thread_local ThreadLocalProfileData threadlocal_profile_data;
|
||||
bool bli_profiling_is_enabled = false;
|
||||
|
||||
namespace blender::profile {
|
||||
|
||||
static void start_profiling()
|
||||
{
|
||||
bli_profiling_is_enabled = true;
|
||||
}
|
||||
|
||||
static void stop_profiling()
|
||||
{
|
||||
bli_profiling_is_enabled = false;
|
||||
}
|
||||
|
||||
ProfileListener::ProfileListener()
|
||||
{
|
||||
std::lock_guard lock{ProfileRegistry::listeners_mutex};
|
||||
ensure_registry();
|
||||
registry->listeners.append(this);
|
||||
if (registry->listeners.size() == 1) {
|
||||
start_profiling();
|
||||
}
|
||||
}
|
||||
|
||||
ProfileListener::~ProfileListener()
|
||||
{
|
||||
std::lock_guard lock{ProfileRegistry::listeners_mutex};
|
||||
ensure_registry();
|
||||
registry->listeners.remove_first_occurrence_and_reorder(this);
|
||||
if (registry->listeners.is_empty()) {
|
||||
stop_profiling();
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileListener::flush_to_all()
|
||||
{
|
||||
/* Todo: How to handle short lived threads? */
|
||||
std::scoped_lock lock{ProfileRegistry::threadlocals_mutex, ProfileRegistry::listeners_mutex};
|
||||
if (!registry) {
|
||||
return;
|
||||
}
|
||||
RecordedProfile recorded_profile;
|
||||
for (ThreadLocalProfileData *data : registry->threadlocals) {
|
||||
data->queue_begins_named.consume([&](Span<ProfileTaskBeginNamed> data) {
|
||||
recorded_profile.task_begins_named.extend(data);
|
||||
});
|
||||
data->queue_begins_range.consume([&](Span<ProfileTaskBeginRange> data) {
|
||||
recorded_profile.task_begins_range.extend(data);
|
||||
});
|
||||
data->queue_ends.consume(
|
||||
[&](Span<ProfileTaskEnd> data) { recorded_profile.task_ends.extend(data); });
|
||||
}
|
||||
for (ProfileListener *listener : registry->listeners) {
|
||||
listener->handle(recorded_profile);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::profile
|
||||
|
||||
static void profile_task_begin_named(ProfileTask *task, const char *name, uint64_t parent_id)
|
||||
{
|
||||
ThreadLocalProfileData &local_data = threadlocal_profile_data;
|
||||
|
||||
const uint64_t id = local_data.get_next_unique_id();
|
||||
local_data.id_stack.push(id);
|
||||
task->id = id;
|
||||
|
||||
ProfileTaskBeginNamed *task_begin = local_data.queue_begins_named.prepare_append();
|
||||
LinearAllocator<RawAllocator> *allocator =
|
||||
local_data.queue_begins_named.user_data_for_current_append();
|
||||
StringRefNull name_copy = allocator->copy_string(name);
|
||||
|
||||
task_begin->id = id;
|
||||
task_begin->name = name_copy.c_str();
|
||||
task_begin->parent_id = parent_id;
|
||||
task_begin->thread_id = local_data.thread_id;
|
||||
task_begin->time = Clock::now();
|
||||
|
||||
local_data.queue_begins_named.commit_append();
|
||||
}
|
||||
|
||||
void _bli_profile_task_begin_named(ProfileTask *task, const char *name)
|
||||
{
|
||||
ThreadLocalProfileData &local_data = threadlocal_profile_data;
|
||||
const uint64_t parent_id = local_data.id_stack.peek_default(0);
|
||||
profile_task_begin_named(task, name, parent_id);
|
||||
}
|
||||
|
||||
void _bli_profile_task_begin_named_subtask(ProfileTask *task,
|
||||
const char *name,
|
||||
const ProfileTask *parent_task)
|
||||
{
|
||||
profile_task_begin_named(task, name, parent_task->id);
|
||||
}
|
||||
|
||||
void _bli_profile_task_begin_range(ProfileTask *task,
|
||||
const ProfileTask *parent_task,
|
||||
int64_t start,
|
||||
int64_t one_after_last)
|
||||
{
|
||||
ThreadLocalProfileData &local_data = threadlocal_profile_data;
|
||||
|
||||
const uint64_t id = local_data.get_next_unique_id();
|
||||
local_data.id_stack.push(id);
|
||||
task->id = id;
|
||||
|
||||
ProfileTaskBeginRange *task_begin = local_data.queue_begins_range.prepare_append();
|
||||
task_begin->id = id;
|
||||
task_begin->parent_id = parent_task->id;
|
||||
task_begin->thread_id = local_data.thread_id;
|
||||
task_begin->start = start;
|
||||
task_begin->one_after_last = one_after_last;
|
||||
task_begin->time = Clock::now();
|
||||
|
||||
local_data.queue_begins_range.commit_append();
|
||||
}
|
||||
|
||||
void _bli_profile_task_end(ProfileTask *task)
|
||||
{
|
||||
TimePoint time = Clock::now();
|
||||
|
||||
ThreadLocalProfileData &local_data = threadlocal_profile_data;
|
||||
|
||||
BLI_assert(local_data.id_stack.peek() == task->id);
|
||||
local_data.id_stack.pop();
|
||||
|
||||
ProfileTaskEnd *task_end = local_data.queue_ends.prepare_append();
|
||||
task_end->begin_id = task->id;
|
||||
task_end->time = time;
|
||||
|
||||
local_data.queue_ends.commit_append();
|
||||
}
|
@@ -288,6 +288,8 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
|
||||
*/
|
||||
{
|
||||
/* Keep this block, even when empty. */
|
||||
|
||||
btheme->space_profiler = btheme->space_outliner;
|
||||
}
|
||||
|
||||
#undef FROM_DEFAULT_V4_UCHAR
|
||||
|
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_gsqueue.h"
|
||||
#include "BLI_profile.hh"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
@@ -95,10 +96,13 @@ struct DepsgraphEvalState {
|
||||
bool do_stats;
|
||||
EvaluationStage stage;
|
||||
bool need_single_thread_pass;
|
||||
ProfileTask profile_task;
|
||||
};
|
||||
|
||||
void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_node)
|
||||
{
|
||||
BLI_PROFILE_SCOPE_SUBTASK("Depsgraph Node", &state->profile_task);
|
||||
|
||||
::Depsgraph *depsgraph = reinterpret_cast<::Depsgraph *>(state->graph);
|
||||
|
||||
/* Sanity checks. */
|
||||
@@ -379,6 +383,9 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
|
||||
state.graph = graph;
|
||||
state.do_stats = graph->debug.do_time_debug();
|
||||
state.need_single_thread_pass = false;
|
||||
|
||||
BLI_profile_task_begin_named(&state.profile_task, __func__);
|
||||
|
||||
/* Prepare all nodes for evaluation. */
|
||||
initialize_execution(&state, graph);
|
||||
|
||||
@@ -413,6 +420,8 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
|
||||
graph->is_evaluating = false;
|
||||
|
||||
graph->debug.end_graph_evaluation();
|
||||
|
||||
BLI_profile_task_end(&state.profile_task);
|
||||
}
|
||||
|
||||
} // namespace blender::deg
|
||||
|
@@ -51,6 +51,7 @@ if(WITH_BLENDER)
|
||||
add_subdirectory(space_nla)
|
||||
add_subdirectory(space_node)
|
||||
add_subdirectory(space_outliner)
|
||||
add_subdirectory(space_profiler)
|
||||
add_subdirectory(space_script)
|
||||
add_subdirectory(space_sequencer)
|
||||
add_subdirectory(space_spreadsheet)
|
||||
|
36
source/blender/editors/include/ED_profiler.h
Normal file
36
source/blender/editors/include/ED_profiler.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup editors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct SpaceProfiler;
|
||||
|
||||
void ED_profiler_profile_enable(struct SpaceProfiler *sprofiler);
|
||||
void ED_profiler_profile_disable(struct SpaceProfiler *sprofiler);
|
||||
bool ED_profiler_profile_is_enabled(struct SpaceProfiler *sprofiler);
|
||||
void ED_profiler_profile_clear(struct SpaceProfiler *sprofiler);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -56,6 +56,7 @@ void ED_spacetype_clip(void);
|
||||
void ED_spacetype_statusbar(void);
|
||||
void ED_spacetype_topbar(void);
|
||||
void ED_spacetype_spreadsheet(void);
|
||||
void ED_spacetype_profiler(void);
|
||||
|
||||
/* calls for instancing and freeing spacetype static data
|
||||
* called in WM_init_exit */
|
||||
|
@@ -651,6 +651,7 @@ static struct MenuSearch_Data *menu_items_from_ui_create(
|
||||
SPACE_MENU_NOP(SPACE_STATUSBAR);
|
||||
SPACE_MENU_NOP(SPACE_TOPBAR);
|
||||
SPACE_MENU_NOP(SPACE_SPREADSHEET);
|
||||
SPACE_MENU_NOP(SPACE_PROFILER);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < idname_array_len; i++) {
|
||||
|
@@ -163,6 +163,9 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid)
|
||||
case SPACE_SPREADSHEET:
|
||||
ts = &btheme->space_spreadsheet;
|
||||
break;
|
||||
case SPACE_PROFILER:
|
||||
ts = &btheme->space_profiler;
|
||||
break;
|
||||
default:
|
||||
ts = &btheme->space_view3d;
|
||||
break;
|
||||
|
@@ -48,6 +48,7 @@ set(LIB
|
||||
bf_editor_space_nla
|
||||
bf_editor_space_node
|
||||
bf_editor_space_outliner
|
||||
bf_editor_space_profiler
|
||||
bf_editor_space_script
|
||||
bf_editor_space_sequencer
|
||||
bf_editor_space_spreadsheet
|
||||
|
@@ -96,6 +96,7 @@ void ED_spacetypes_init(void)
|
||||
ED_spacetype_statusbar();
|
||||
ED_spacetype_topbar();
|
||||
ED_spacetype_spreadsheet();
|
||||
ED_spacetype_profiler();
|
||||
|
||||
/* Register operator types for screen and all spaces. */
|
||||
ED_operatortypes_userpref();
|
||||
|
45
source/blender/editors/space_profiler/CMakeLists.txt
Normal file
45
source/blender/editors/space_profiler/CMakeLists.txt
Normal file
@@ -0,0 +1,45 @@
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
set(INC
|
||||
../include
|
||||
../../blenkernel
|
||||
../../blenlib
|
||||
../../gpu
|
||||
../../makesdna
|
||||
../../makesrna
|
||||
../../windowmanager
|
||||
../../../../intern/glew-mx
|
||||
../../../../intern/guardedalloc
|
||||
)
|
||||
|
||||
set(SRC
|
||||
profiler_draw.cc
|
||||
profiler_layout.cc
|
||||
profiler_profile.cc
|
||||
profiler_runtime.cc
|
||||
space_profiler.cc
|
||||
|
||||
profiler_draw.hh
|
||||
profiler_layout.hh
|
||||
profiler_runtime.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
)
|
||||
|
||||
blender_add_lib(bf_editor_space_profiler "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
339
source/blender/editors/space_profiler/profiler_draw.cc
Normal file
339
source/blender/editors/space_profiler/profiler_draw.cc
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "GPU_immediate.h"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_profile.hh"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "profiler_draw.hh"
|
||||
#include "profiler_layout.hh"
|
||||
#include "profiler_runtime.hh"
|
||||
|
||||
namespace blender::ed::profiler {
|
||||
|
||||
using profile::Duration;
|
||||
|
||||
class ProfilerDrawer {
|
||||
private:
|
||||
const bContext *C;
|
||||
ARegion *region_;
|
||||
SpaceProfiler *sprofiler_;
|
||||
SpaceProfiler_Runtime *runtime_;
|
||||
ProfilerLayout *profiler_layout_;
|
||||
|
||||
public:
|
||||
ProfilerDrawer(const bContext *C, ARegion *region) : C(C), region_(region)
|
||||
{
|
||||
sprofiler_ = CTX_wm_space_profiler(C);
|
||||
runtime_ = sprofiler_->runtime;
|
||||
|
||||
if (!runtime_->profiler_layout) {
|
||||
runtime_->profiler_layout = std::make_unique<ProfilerLayout>();
|
||||
}
|
||||
if (runtime_->profile_listener) {
|
||||
profile::ProfileListener::flush_to_all();
|
||||
}
|
||||
profiler_layout_ = runtime_->profiler_layout.get();
|
||||
}
|
||||
|
||||
void draw()
|
||||
{
|
||||
UI_ThemeClearColor(TH_BACK);
|
||||
|
||||
Vector<ProfileNode *> nodes_to_draw;
|
||||
this->find_all_nodes_to_draw(nodes_to_draw);
|
||||
this->draw_nodes(nodes_to_draw);
|
||||
|
||||
this->update_view2d_bounds();
|
||||
}
|
||||
|
||||
void update_view2d_bounds()
|
||||
{
|
||||
const float duration_ms = duration_to_ms(profiler_layout_->end_time() -
|
||||
profiler_layout_->begin_time());
|
||||
|
||||
float min_bottom_y = 0.0f;
|
||||
if (!profiler_layout_->root_nodes().is_empty()) {
|
||||
for (ProfileNode *node : profiler_layout_->root_nodes().last()) {
|
||||
min_bottom_y = std::min(min_bottom_y, node->bottom_y);
|
||||
}
|
||||
}
|
||||
|
||||
/* Giving a bit more space on the right side is convenient. */
|
||||
const int width = std::max(duration_ms * 1.1f, 5000.0f);
|
||||
const int height = -min_bottom_y * UI_UNIT_Y + UI_UNIT_Y;
|
||||
|
||||
UI_view2d_totRect_set(®ion_->v2d, width, height);
|
||||
|
||||
UI_view2d_scrollers_draw(®ion_->v2d, nullptr);
|
||||
}
|
||||
|
||||
void find_all_nodes_to_draw(Vector<ProfileNode *> &r_nodes)
|
||||
{
|
||||
BLI_PROFILE_SCOPE(__func__);
|
||||
for (Span<ProfileNode *> nodes : profiler_layout_->root_nodes()) {
|
||||
this->find_nodes_to_draw(nodes, r_nodes);
|
||||
}
|
||||
}
|
||||
|
||||
void find_nodes_to_draw(Span<ProfileNode *> nodes, Vector<ProfileNode *> &r_nodes)
|
||||
{
|
||||
if (nodes.is_empty()) {
|
||||
return;
|
||||
}
|
||||
const float top_y = this->node_y_to_region_y(nodes[0]->top_y);
|
||||
if (top_y < 0) {
|
||||
return;
|
||||
}
|
||||
float node_bottom_y = nodes[0]->bottom_y;
|
||||
for (ProfileNode *node : nodes) {
|
||||
node_bottom_y = std::min(node_bottom_y, node->bottom_y);
|
||||
}
|
||||
const float bottom_y = this->node_y_to_region_y(node_bottom_y);
|
||||
if (bottom_y > region_->winy) {
|
||||
return;
|
||||
}
|
||||
|
||||
const TimePoint left_time = this->x_to_time(0);
|
||||
const TimePoint right_time = this->x_to_time(region_->winx);
|
||||
|
||||
auto end_is_smaller = [](const ProfileNode *node, const TimePoint time) {
|
||||
return node->end_time() < time;
|
||||
};
|
||||
auto begin_is_larger = [](const TimePoint time, const ProfileNode *node) {
|
||||
return node->begin_time() > time;
|
||||
};
|
||||
|
||||
const int start_index = std::lower_bound(
|
||||
nodes.begin(), nodes.end(), left_time, end_is_smaller) -
|
||||
nodes.begin();
|
||||
const int end_index = std::upper_bound(
|
||||
nodes.begin(), nodes.end(), right_time, begin_is_larger) -
|
||||
nodes.begin();
|
||||
|
||||
nodes = nodes.slice(start_index, end_index - start_index);
|
||||
if (nodes.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int size_before = r_nodes.size();
|
||||
r_nodes.append(nodes[0]);
|
||||
for (ProfileNode *node : nodes.drop_front(1)) {
|
||||
const float begin_x = this->time_to_x(node->begin_time());
|
||||
const float end_x = this->time_to_x(node->end_time());
|
||||
|
||||
ProfileNode *prev_node = r_nodes.last();
|
||||
const float prev_begin_x = this->time_to_x(prev_node->begin_time());
|
||||
const float prev_end_x = this->time_to_x(prev_node->end_time());
|
||||
|
||||
if (std::ceil(end_x) > std::ceil(prev_end_x)) {
|
||||
/* Node reaches into next pixel. */
|
||||
r_nodes.append(node);
|
||||
}
|
||||
else if (std::floor(begin_x) > std::floor(prev_begin_x)) {
|
||||
/* Previous node reaches into previous pixel. */
|
||||
r_nodes.append(node);
|
||||
}
|
||||
else {
|
||||
/* Both nodes are in the same pixel. */
|
||||
if (node->bottom_y < prev_node->bottom_y) {
|
||||
/* Replace previously added node because this when has a larger depth. */
|
||||
r_nodes.last() = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
const int tot_added = r_nodes.size() - size_before;
|
||||
|
||||
Vector<ProfileNode *> added_nodes = r_nodes.as_span().take_back(tot_added);
|
||||
|
||||
for (ProfileNode *node : added_nodes) {
|
||||
this->find_nodes_to_draw(node->direct_children(), r_nodes);
|
||||
for (Span<ProfileNode *> children : node->parallel_children()) {
|
||||
this->find_nodes_to_draw(children, r_nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_nodes(Span<ProfileNode *> nodes)
|
||||
{
|
||||
BLI_PROFILE_SCOPE(__func__);
|
||||
uiBlock *ui_block = UI_block_begin(C, region_, __func__, UI_EMBOSS_NONE);
|
||||
for (ProfileNode *node : nodes) {
|
||||
this->draw_node(*node, ui_block);
|
||||
}
|
||||
UI_block_end(C, ui_block);
|
||||
UI_block_draw(C, ui_block);
|
||||
}
|
||||
|
||||
void draw_node(ProfileNode &node, uiBlock *ui_block)
|
||||
{
|
||||
const float left_x = this->time_to_x(node.begin_time());
|
||||
const float real_right_x = this->time_to_x(node.end_time());
|
||||
const float right_x = std::max(left_x + 1, real_right_x);
|
||||
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
||||
|
||||
const Color4f color = this->get_node_color(node);
|
||||
immUniformColor4fv(color);
|
||||
|
||||
const float top_y = this->node_y_to_region_y(node.top_y);
|
||||
immRecti(pos, left_x, top_y, right_x, top_y - UI_UNIT_Y);
|
||||
|
||||
immUnbindProgram();
|
||||
|
||||
if (right_x - left_x > 1.0f) {
|
||||
this->draw_node_label(node, ui_block, left_x, right_x);
|
||||
}
|
||||
}
|
||||
|
||||
struct NodeTooltipArg {
|
||||
ProfileNode *node;
|
||||
};
|
||||
|
||||
void draw_node_label(ProfileNode &node, uiBlock *ui_block, const int left_x, const int right_x)
|
||||
{
|
||||
const int x = std::max(0, left_x);
|
||||
const int width = std::max(1, std::min<int>(right_x, region_->winx) - x);
|
||||
const float top_y = this->node_y_to_region_y(node.top_y);
|
||||
|
||||
uiBut *but = uiDefIconTextBut(ui_block,
|
||||
UI_BTYPE_BUT,
|
||||
0,
|
||||
ICON_NONE,
|
||||
node.name().c_str(),
|
||||
x,
|
||||
top_y - UI_UNIT_Y,
|
||||
width,
|
||||
UI_UNIT_Y,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
nullptr);
|
||||
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
||||
UI_but_drawflag_disable(but, UI_BUT_TEXT_RIGHT);
|
||||
|
||||
UI_but_func_tooltip_set(but,
|
||||
node_tooltip_fn,
|
||||
new (MEM_mallocN(sizeof(NodeTooltipArg), __func__))
|
||||
NodeTooltipArg{&node});
|
||||
UI_but_func_set(but, node_click_fn, &node, profiler_layout_);
|
||||
}
|
||||
|
||||
static char *node_tooltip_fn(bContext *UNUSED(C), void *argN, const char *UNUSED(tip))
|
||||
{
|
||||
NodeTooltipArg &arg = *(NodeTooltipArg *)argN;
|
||||
ProfileNode &node = *arg.node;
|
||||
const Duration duration = node.duration();
|
||||
std::stringstream ss;
|
||||
ss << std::setprecision(2) << std::fixed;
|
||||
for (const ProfileNode *parent = node.parent(); parent != nullptr; parent = parent->parent()) {
|
||||
const Duration parent_duration = parent->duration();
|
||||
const float percentage = (duration.count() / (float)parent_duration.count()) * 100.0f;
|
||||
ss << percentage << "% of " << parent->name() << "\n";
|
||||
}
|
||||
ss << "\n";
|
||||
ss << "Duration: " << duration_to_ms(duration) << " ms";
|
||||
return BLI_strdup(ss.str().c_str());
|
||||
}
|
||||
|
||||
static void node_click_fn(bContext *C, void *arg1, void *arg2)
|
||||
{
|
||||
ProfileNode &node = *(ProfileNode *)arg1;
|
||||
ProfilerLayout &profiler_layout = *(ProfilerLayout *)arg2;
|
||||
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
|
||||
const TimePoint begin_time = profiler_layout.begin_time();
|
||||
const float left_ms = duration_to_ms(node.begin_time() - begin_time);
|
||||
const float right_ms = duration_to_ms(node.end_time() - begin_time);
|
||||
const float duration_ms = right_ms - left_ms;
|
||||
const float padding = duration_ms * 0.05f;
|
||||
|
||||
rctf new_view;
|
||||
BLI_rctf_init(&new_view, left_ms - padding, right_ms + padding, -1, 0);
|
||||
|
||||
UI_view2d_smooth_view(C, region, &new_view, U.smooth_viewtx);
|
||||
}
|
||||
|
||||
float time_to_x(const TimePoint time) const
|
||||
{
|
||||
const TimePoint begin_time = profiler_layout_->begin_time();
|
||||
const Duration time_since_begin = time - begin_time;
|
||||
const float ms_since_begin = duration_to_ms(time_since_begin);
|
||||
return UI_view2d_view_to_region_x(®ion_->v2d, ms_since_begin);
|
||||
}
|
||||
|
||||
TimePoint x_to_time(const float x) const
|
||||
{
|
||||
const float ms_since_begin = UI_view2d_region_to_view_x(®ion_->v2d, x);
|
||||
const TimePoint begin_time = profiler_layout_->begin_time();
|
||||
return begin_time + ms_to_duration(ms_since_begin);
|
||||
}
|
||||
|
||||
float node_y_to_region_y(const float y) const
|
||||
{
|
||||
return region_->winy + y * UI_UNIT_Y - region_->v2d.cur.ymax;
|
||||
}
|
||||
|
||||
Color4f get_node_color(ProfileNode &node)
|
||||
{
|
||||
const uint64_t value = POINTER_AS_UINT(&node);
|
||||
const float variation = BLI_hash_int_2d_to_float(value, value >> 32);
|
||||
float r, g, b;
|
||||
hsv_to_rgb(variation * 0.2f, 0.5f, 0.5f, &r, &g, &b);
|
||||
return {r, g, b, 1.0f};
|
||||
}
|
||||
|
||||
static float duration_to_ms(const Duration duration)
|
||||
{
|
||||
return duration.count() / 1000000.0f;
|
||||
}
|
||||
|
||||
static Duration ms_to_duration(const float ms)
|
||||
{
|
||||
return std::chrono::nanoseconds((int64_t)(ms * 1'000'000.0f));
|
||||
}
|
||||
};
|
||||
|
||||
void draw_profiler(const bContext *C, ARegion *region)
|
||||
{
|
||||
BLI_PROFILE_SCOPE(__func__);
|
||||
ProfilerDrawer drawer{C, region};
|
||||
drawer.draw();
|
||||
}
|
||||
|
||||
} // namespace blender::ed::profiler
|
26
source/blender/editors/space_profiler/profiler_draw.hh
Normal file
26
source/blender/editors/space_profiler/profiler_draw.hh
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct bContext;
|
||||
struct ARegion;
|
||||
|
||||
namespace blender::ed::profiler {
|
||||
|
||||
void draw_profiler(const struct bContext *C, struct ARegion *region);
|
||||
|
||||
}
|
302
source/blender/editors/space_profiler/profiler_layout.cc
Normal file
302
source/blender/editors/space_profiler/profiler_layout.cc
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "profiler_layout.hh"
|
||||
|
||||
#include "BLI_profile.hh"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
namespace blender::ed::profiler {
|
||||
|
||||
using profile::ProfileTaskBegin;
|
||||
using profile::ProfileTaskBeginNamed;
|
||||
using profile::ProfileTaskBeginRange;
|
||||
using profile::ProfileTaskEnd;
|
||||
|
||||
static constexpr float node_height = 1.0f;
|
||||
static constexpr float parallel_padding = 0.1f;
|
||||
|
||||
bool ProfileNode::time_overlap(const ProfileNode &a, const ProfileNode &b)
|
||||
{
|
||||
const bool begin_of_a_is_in_b = (a.begin_time_ > b.begin_time_ && a.begin_time_ < b.end_time_);
|
||||
const bool begin_of_b_is_in_a = (b.begin_time_ > a.begin_time_ && b.begin_time_ < a.end_time_);
|
||||
return begin_of_a_is_in_b || begin_of_b_is_in_a;
|
||||
}
|
||||
|
||||
template<typename UseNodeF>
|
||||
static bool try_pack_into_vector(Vector<ProfileNode *> &sorted_nodes_vec,
|
||||
MutableSpan<ProfileNode *> sorted_nodes_to_pack,
|
||||
const UseNodeF &use_node_fn)
|
||||
{
|
||||
bool packed_everything = true;
|
||||
|
||||
MutableSpan<ProfileNode *> remaining_existing = sorted_nodes_vec;
|
||||
MutableSpan<ProfileNode *> remaining_new = sorted_nodes_to_pack;
|
||||
Vector<ProfileNode *> new_vec;
|
||||
while (!remaining_new.is_empty()) {
|
||||
ProfileNode *new_child = remaining_new[0];
|
||||
if (new_child == nullptr) {
|
||||
/* Child has been inserted already. */
|
||||
remaining_new = remaining_new.drop_front(1);
|
||||
continue;
|
||||
}
|
||||
if (!use_node_fn(*new_child)) {
|
||||
remaining_new = remaining_new.drop_front(1);
|
||||
continue;
|
||||
}
|
||||
while (true) {
|
||||
if (!new_vec.is_empty()) {
|
||||
ProfileNode *existing_child = new_vec.last();
|
||||
if (ProfileNode::time_overlap(*existing_child, *new_child)) {
|
||||
/* Node collides with previously added node. */
|
||||
remaining_new = remaining_new.drop_front(1);
|
||||
packed_everything = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (remaining_existing.is_empty()) {
|
||||
/* There are no remaining existing nodes the new child can collide with. */
|
||||
new_vec.append(new_child);
|
||||
remaining_new[0] = nullptr;
|
||||
remaining_new = remaining_new.drop_front(1);
|
||||
break;
|
||||
}
|
||||
ProfileNode *existing_child = remaining_existing[0];
|
||||
if (existing_child->end_time() <= new_child->begin_time()) {
|
||||
/* Existing child is completely before the new one. */
|
||||
new_vec.append(existing_child);
|
||||
remaining_existing = remaining_existing.drop_front(1);
|
||||
continue;
|
||||
}
|
||||
if (existing_child->begin_time() < new_child->end_time()) {
|
||||
/* Existing child collides with the new child. */
|
||||
new_vec.append(existing_child);
|
||||
remaining_existing = remaining_existing.drop_front(1);
|
||||
remaining_new = remaining_new.drop_front(1);
|
||||
packed_everything = false;
|
||||
break;
|
||||
}
|
||||
if (new_child->end_time() <= existing_child->begin_time()) {
|
||||
/* New child can be added safely. */
|
||||
new_vec.append(new_child);
|
||||
remaining_new[0] = nullptr;
|
||||
remaining_new = remaining_new.drop_front(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
new_vec.extend(remaining_existing);
|
||||
sorted_nodes_vec = std::move(new_vec);
|
||||
return packed_everything;
|
||||
}
|
||||
|
||||
static void pack_into_vectors(Vector<Vector<ProfileNode *>> &sorted_node_vectors,
|
||||
MutableSpan<ProfileNode *> sorted_nodes_to_pack)
|
||||
{
|
||||
if (sorted_nodes_to_pack.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int iteration = 0;
|
||||
while (true) {
|
||||
if (iteration == sorted_node_vectors.size()) {
|
||||
sorted_node_vectors.append({});
|
||||
}
|
||||
Vector<ProfileNode *> &children_vec = sorted_node_vectors[iteration];
|
||||
iteration++;
|
||||
const bool packed_all_nodes = try_pack_into_vector(
|
||||
children_vec, sorted_nodes_to_pack, [](ProfileNode &UNUSED(node)) { return true; });
|
||||
if (packed_all_nodes) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sort_nodes_by_begin_time(MutableSpan<ProfileNode *> nodes)
|
||||
{
|
||||
std::sort(nodes.begin(), nodes.end(), [](ProfileNode *a, ProfileNode *b) {
|
||||
return a->begin_time() < b->begin_time();
|
||||
});
|
||||
}
|
||||
|
||||
void ProfileNode::pack_added_children()
|
||||
{
|
||||
sort_nodes_by_begin_time(children_to_pack_);
|
||||
|
||||
/* Assume already packed children are sorted by begin time. */
|
||||
try_pack_into_vector(
|
||||
direct_children_, children_to_pack_, [thread_id = this->thread_id_](ProfileNode &node) {
|
||||
return node.thread_id() == thread_id;
|
||||
});
|
||||
|
||||
pack_into_vectors(parallel_children_, children_to_pack_);
|
||||
children_to_pack_.clear();
|
||||
}
|
||||
|
||||
void ProfilerLayout::add(const RecordedProfile &recorded_profile)
|
||||
{
|
||||
BLI_PROFILE_SCOPE("Add to ProfilerLayout");
|
||||
|
||||
/* Create new nodes for segments and add them to the id map. */
|
||||
auto init_node = [](const ProfileTaskBegin &task_begin, ProfileNode &node) {
|
||||
node.begin_time_ = task_begin.time;
|
||||
node.end_time_ = TimePoint{}; /* The end time is not known yet. */
|
||||
node.id_ = task_begin.id;
|
||||
node.parent_id_ = task_begin.parent_id;
|
||||
node.thread_id_ = task_begin.thread_id;
|
||||
};
|
||||
|
||||
for (const ProfileTaskBeginNamed &task_begin : recorded_profile.task_begins_named) {
|
||||
ProfileNode &node = *allocator_.construct<ProfileNode>().release();
|
||||
init_node(task_begin, node);
|
||||
node.name_ = task_begin.name;
|
||||
nodes_by_id_.add_new(task_begin.id, &node);
|
||||
}
|
||||
for (const ProfileTaskBeginRange &task_begin : recorded_profile.task_begins_range) {
|
||||
ProfileNode &node = *allocator_.construct<ProfileNode>().release();
|
||||
init_node(task_begin, node);
|
||||
node.name_ = "[" + std::to_string(task_begin.start) + ", " +
|
||||
std::to_string(task_begin.one_after_last) + ")";
|
||||
nodes_by_id_.add_new(task_begin.id, &node);
|
||||
}
|
||||
|
||||
for (const ProfileTaskEnd &task_end : recorded_profile.task_ends) {
|
||||
ProfileNode *node = nodes_by_id_.lookup_default(task_end.begin_id, nullptr);
|
||||
if (node != nullptr) {
|
||||
BLI_assert(node->end_time_ == TimePoint{});
|
||||
node->end_time_ = task_end.time;
|
||||
}
|
||||
}
|
||||
|
||||
Set<ProfileNode *> nodes_with_new_children;
|
||||
Vector<ProfileNode *> root_nodes_to_pack;
|
||||
|
||||
/* Create parent/child relation ships for new nodes. */
|
||||
auto create_relations = [&](const ProfileTaskBegin &task_begin) {
|
||||
ProfileNode *node = nodes_by_id_.lookup(task_begin.id);
|
||||
ProfileNode *parent_node = nodes_by_id_.lookup_default(task_begin.parent_id, nullptr);
|
||||
node->parent_ = parent_node;
|
||||
if (parent_node == nullptr) {
|
||||
if (root_nodes_.is_empty()) {
|
||||
begin_time_ = node->begin_time_;
|
||||
end_time_ = node->end_time_;
|
||||
}
|
||||
else {
|
||||
begin_time_ = std::min(begin_time_, node->begin_time_);
|
||||
end_time_ = std::max(end_time_, node->end_time_);
|
||||
}
|
||||
root_nodes_to_pack.append(node);
|
||||
}
|
||||
else {
|
||||
parent_node->children_to_pack_.append(node);
|
||||
nodes_with_new_children.add(parent_node);
|
||||
}
|
||||
};
|
||||
|
||||
for (const ProfileTaskBeginNamed &task_begin : recorded_profile.task_begins_named) {
|
||||
create_relations(task_begin);
|
||||
}
|
||||
for (const ProfileTaskBeginRange &task_begin : recorded_profile.task_begins_range) {
|
||||
create_relations(task_begin);
|
||||
}
|
||||
|
||||
/* Check if a previous root node is not a root anymore. */
|
||||
for (Vector<ProfileNode *> &nodes : root_nodes_) {
|
||||
Vector<ProfileNode *> nodes_that_are_not_root_anymore;
|
||||
for (ProfileNode *node : nodes) {
|
||||
ProfileNode *new_parent = nodes_by_id_.lookup_default(node->parent_id_, nullptr);
|
||||
if (new_parent != nullptr) {
|
||||
node->parent_ = new_parent;
|
||||
new_parent->children_to_pack_.append(node);
|
||||
nodes_with_new_children.add(new_parent);
|
||||
nodes_that_are_not_root_anymore.append(node);
|
||||
}
|
||||
}
|
||||
for (ProfileNode *node : nodes_that_are_not_root_anymore) {
|
||||
nodes.remove_first_occurrence_and_reorder(node);
|
||||
}
|
||||
if (!nodes_that_are_not_root_anymore.is_empty()) {
|
||||
sort_nodes_by_begin_time(nodes);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pack newly added children. */
|
||||
for (ProfileNode *node : nodes_with_new_children) {
|
||||
node->pack_added_children();
|
||||
}
|
||||
|
||||
pack_into_vectors(root_nodes_, root_nodes_to_pack);
|
||||
|
||||
this->update_y_positions();
|
||||
}
|
||||
|
||||
void ProfilerLayout::update_y_positions()
|
||||
{
|
||||
BLI_PROFILE_SCOPE(__func__);
|
||||
|
||||
float top_y = 0.0f;
|
||||
for (Span<ProfileNode *> nodes : root_nodes_) {
|
||||
top_y = this->update_y_positions_of_nodes(nodes, top_y);
|
||||
top_y -= parallel_padding;
|
||||
}
|
||||
}
|
||||
|
||||
float ProfilerLayout::update_y_positions_of_nodes(Span<ProfileNode *> nodes, float top_y)
|
||||
{
|
||||
float bottom_y = top_y;
|
||||
for (ProfileNode *node : nodes) {
|
||||
node->top_y = top_y;
|
||||
this->update_y_position_of_node(*node);
|
||||
bottom_y = std::min(bottom_y, node->bottom_y);
|
||||
}
|
||||
return bottom_y;
|
||||
}
|
||||
|
||||
void ProfilerLayout::update_y_position_of_node(ProfileNode &node)
|
||||
{
|
||||
node.bottom_y = node.top_y - node_height;
|
||||
node.bottom_y = this->update_y_positions_of_nodes(node.direct_children(), node.bottom_y);
|
||||
for (Span<ProfileNode *> children : node.parallel_children()) {
|
||||
if (!children.is_empty()) {
|
||||
node.bottom_y -= parallel_padding;
|
||||
node.bottom_y = this->update_y_positions_of_nodes(children, node.bottom_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileNode::destruct_recursively()
|
||||
{
|
||||
for (ProfileNode *node : direct_children_) {
|
||||
node->destruct_recursively();
|
||||
}
|
||||
for (Span<ProfileNode *> nodes : parallel_children_) {
|
||||
for (ProfileNode *node : nodes) {
|
||||
node->destruct_recursively();
|
||||
}
|
||||
}
|
||||
this->~ProfileNode();
|
||||
}
|
||||
|
||||
ProfilerLayout::~ProfilerLayout()
|
||||
{
|
||||
for (Span<ProfileNode *> nodes : root_nodes_) {
|
||||
for (ProfileNode *node : nodes) {
|
||||
node->destruct_recursively();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::ed::profiler
|
146
source/blender/editors/space_profiler/profiler_layout.hh
Normal file
146
source/blender/editors/space_profiler/profiler_layout.hh
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_linear_allocator.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_profile_manage.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
namespace blender::ed::profiler {
|
||||
|
||||
using profile::Duration;
|
||||
using profile::RecordedProfile;
|
||||
using profile::TimePoint;
|
||||
|
||||
class ProfilerLayout;
|
||||
|
||||
class ProfileNode {
|
||||
private:
|
||||
std::string name_;
|
||||
TimePoint begin_time_;
|
||||
TimePoint end_time_;
|
||||
ProfileNode *parent_ = nullptr;
|
||||
uint64_t id_;
|
||||
uint64_t parent_id_;
|
||||
uint64_t thread_id_;
|
||||
/* The nodes in these vectors are ordered by the begin time. Nodes in a single vector should not
|
||||
* overlap. */
|
||||
Vector<ProfileNode *> direct_children_;
|
||||
Vector<Vector<ProfileNode *>> parallel_children_;
|
||||
|
||||
/* These nodes still have to be inserted into the vectors above. They are not sorted. */
|
||||
Vector<ProfileNode *> children_to_pack_;
|
||||
|
||||
friend ProfilerLayout;
|
||||
|
||||
public:
|
||||
/* In ui units. */
|
||||
float top_y = 0.0f;
|
||||
float bottom_y = 0.0f;
|
||||
|
||||
public:
|
||||
StringRefNull name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
TimePoint begin_time() const
|
||||
{
|
||||
return begin_time_;
|
||||
}
|
||||
|
||||
TimePoint end_time() const
|
||||
{
|
||||
return end_time_;
|
||||
}
|
||||
|
||||
Duration duration() const
|
||||
{
|
||||
return end_time_ - begin_time_;
|
||||
}
|
||||
|
||||
ProfileNode *parent()
|
||||
{
|
||||
return parent_;
|
||||
}
|
||||
|
||||
const ProfileNode *parent() const
|
||||
{
|
||||
return parent_;
|
||||
}
|
||||
|
||||
uint64_t thread_id() const
|
||||
{
|
||||
return thread_id_;
|
||||
}
|
||||
|
||||
Span<ProfileNode *> direct_children()
|
||||
{
|
||||
return direct_children_;
|
||||
}
|
||||
|
||||
Span<Vector<ProfileNode *>> parallel_children() const
|
||||
{
|
||||
return parallel_children_;
|
||||
}
|
||||
|
||||
static bool time_overlap(const ProfileNode &a, const ProfileNode &b);
|
||||
|
||||
private:
|
||||
void pack_added_children();
|
||||
|
||||
void destruct_recursively();
|
||||
};
|
||||
|
||||
class ProfilerLayout {
|
||||
private:
|
||||
LinearAllocator<> allocator_;
|
||||
|
||||
Map<uint64_t, ProfileNode *> nodes_by_id_;
|
||||
Vector<Vector<ProfileNode *>> root_nodes_;
|
||||
|
||||
TimePoint begin_time_;
|
||||
TimePoint end_time_;
|
||||
|
||||
public:
|
||||
~ProfilerLayout();
|
||||
|
||||
void add(const RecordedProfile &recorded);
|
||||
|
||||
Span<Vector<ProfileNode *>> root_nodes() const
|
||||
{
|
||||
return root_nodes_;
|
||||
}
|
||||
|
||||
TimePoint begin_time() const
|
||||
{
|
||||
return begin_time_;
|
||||
}
|
||||
|
||||
TimePoint end_time() const
|
||||
{
|
||||
return end_time_;
|
||||
}
|
||||
|
||||
private:
|
||||
void update_y_positions();
|
||||
float update_y_positions_of_nodes(Span<ProfileNode *> nodes, float top_y);
|
||||
void update_y_position_of_node(ProfileNode &node);
|
||||
};
|
||||
|
||||
} // namespace blender::ed::profiler
|
61
source/blender/editors/space_profiler/profiler_profile.cc
Normal file
61
source/blender/editors/space_profiler/profiler_profile.cc
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ED_profiler.h"
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "profiler_runtime.hh"
|
||||
|
||||
void ED_profiler_profile_enable(SpaceProfiler *sprofiler)
|
||||
{
|
||||
if (ED_profiler_profile_is_enabled(sprofiler)) {
|
||||
return;
|
||||
}
|
||||
SpaceProfiler_Runtime &runtime = *sprofiler->runtime;
|
||||
runtime.profile_listener = std::make_unique<blender::ed::profiler::SpaceProfilerListener>(
|
||||
runtime);
|
||||
WM_main_add_notifier(NC_SPACE | ND_SPACE_PROFILER, nullptr);
|
||||
}
|
||||
|
||||
void ED_profiler_profile_disable(SpaceProfiler *sprofiler)
|
||||
{
|
||||
if (!ED_profiler_profile_is_enabled(sprofiler)) {
|
||||
return;
|
||||
}
|
||||
SpaceProfiler_Runtime &runtime = *sprofiler->runtime;
|
||||
runtime.profile_listener.reset();
|
||||
}
|
||||
|
||||
bool ED_profiler_profile_is_enabled(SpaceProfiler *sprofiler)
|
||||
{
|
||||
if (sprofiler->runtime == nullptr) {
|
||||
return false;
|
||||
}
|
||||
SpaceProfiler_Runtime &runtime = *sprofiler->runtime;
|
||||
WM_main_add_notifier(NC_SPACE | ND_SPACE_PROFILER, nullptr);
|
||||
return (bool)runtime.profile_listener;
|
||||
}
|
||||
|
||||
void ED_profiler_profile_clear(SpaceProfiler *sprofiler)
|
||||
{
|
||||
SpaceProfiler_Runtime &runtime = *sprofiler->runtime;
|
||||
runtime.profiler_layout.reset();
|
||||
WM_main_add_notifier(NC_SPACE | ND_SPACE_PROFILER, nullptr);
|
||||
}
|
34
source/blender/editors/space_profiler/profiler_runtime.cc
Normal file
34
source/blender/editors/space_profiler/profiler_runtime.cc
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "profiler_runtime.hh"
|
||||
|
||||
namespace blender::ed::profiler {
|
||||
|
||||
SpaceProfilerListener::SpaceProfilerListener(SpaceProfiler_Runtime &runtime) : runtime_(&runtime)
|
||||
{
|
||||
}
|
||||
|
||||
void SpaceProfilerListener::handle(const RecordedProfile &profile)
|
||||
{
|
||||
if (!runtime_->profiler_layout) {
|
||||
runtime_->profiler_layout = std::make_unique<ProfilerLayout>();
|
||||
}
|
||||
ProfilerLayout &profiler_layout = *runtime_->profiler_layout;
|
||||
profiler_layout.add(profile);
|
||||
}
|
||||
|
||||
} // namespace blender::ed::profiler
|
47
source/blender/editors/space_profiler/profiler_runtime.hh
Normal file
47
source/blender/editors/space_profiler/profiler_runtime.hh
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "profiler_layout.hh"
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
namespace blender::ed::profiler {
|
||||
|
||||
class SpaceProfilerListener : public profile::ProfileListener {
|
||||
private:
|
||||
SpaceProfiler_Runtime *runtime_;
|
||||
|
||||
public:
|
||||
SpaceProfilerListener(SpaceProfiler_Runtime &runtime);
|
||||
|
||||
void handle(const RecordedProfile &profile) final;
|
||||
};
|
||||
|
||||
} // namespace blender::ed::profiler
|
||||
|
||||
struct SpaceProfiler_Runtime {
|
||||
std::unique_ptr<blender::ed::profiler::ProfilerLayout> profiler_layout;
|
||||
std::unique_ptr<blender::ed::profiler::SpaceProfilerListener> profile_listener;
|
||||
|
||||
SpaceProfiler_Runtime() = default;
|
||||
SpaceProfiler_Runtime(const SpaceProfiler_Runtime &UNUSED(other))
|
||||
{
|
||||
}
|
||||
};
|
196
source/blender/editors/space_profiler/space_profiler.cc
Normal file
196
source/blender/editors/space_profiler/space_profiler.cc
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "ED_screen.h"
|
||||
#include "ED_space_api.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "UI_resources.h"
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "profiler_draw.hh"
|
||||
#include "profiler_runtime.hh"
|
||||
|
||||
static SpaceLink *profiler_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene))
|
||||
{
|
||||
SpaceProfiler *sprofiler = (SpaceProfiler *)MEM_callocN(sizeof(SpaceProfiler), "profiler space");
|
||||
sprofiler->spacetype = SPACE_PROFILER;
|
||||
|
||||
{
|
||||
/* header */
|
||||
ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "profiler header");
|
||||
BLI_addtail(&sprofiler->regionbase, region);
|
||||
region->regiontype = RGN_TYPE_HEADER;
|
||||
region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
|
||||
}
|
||||
|
||||
{
|
||||
/* main window */
|
||||
ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "profiler main region");
|
||||
BLI_addtail(&sprofiler->regionbase, region);
|
||||
region->regiontype = RGN_TYPE_WINDOW;
|
||||
|
||||
View2D *v2d = ®ion->v2d;
|
||||
v2d->scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM;
|
||||
v2d->keepzoom = V2D_LOCKZOOM_Y;
|
||||
v2d->keeptot = V2D_KEEPTOT_BOUNDS;
|
||||
v2d->keepofs = V2D_KEEPOFS_Y;
|
||||
v2d->align = V2D_ALIGN_NO_NEG_X | V2D_ALIGN_NO_POS_Y;
|
||||
v2d->min[0] = 0.0001f;
|
||||
v2d->min[1] = 1.0f;
|
||||
v2d->max[0] = 100000.0f;
|
||||
v2d->max[1] = 5000.0f;
|
||||
BLI_rctf_init(&v2d->tot, 0, 5000, -1000, 0);
|
||||
v2d->cur = v2d->tot;
|
||||
}
|
||||
|
||||
return (SpaceLink *)sprofiler;
|
||||
}
|
||||
|
||||
static void profiler_free(SpaceLink *sl)
|
||||
{
|
||||
SpaceProfiler *sprofiler = (SpaceProfiler *)sl;
|
||||
delete sprofiler->runtime;
|
||||
}
|
||||
|
||||
static void profiler_init(wmWindowManager *UNUSED(wm), ScrArea *area)
|
||||
{
|
||||
SpaceProfiler *sprofiler = (SpaceProfiler *)area->spacedata.first;
|
||||
if (sprofiler->runtime == nullptr) {
|
||||
sprofiler->runtime = new SpaceProfiler_Runtime();
|
||||
}
|
||||
}
|
||||
|
||||
static SpaceLink *profiler_duplicate(SpaceLink *sl)
|
||||
{
|
||||
SpaceProfiler *sprofiler_old = (SpaceProfiler *)sl;
|
||||
SpaceProfiler *sprofiler_new = (SpaceProfiler *)MEM_dupallocN(sl);
|
||||
sprofiler_new->runtime = new SpaceProfiler_Runtime(*sprofiler_old->runtime);
|
||||
return (SpaceLink *)sprofiler_new;
|
||||
}
|
||||
|
||||
static void profiler_keymap(wmKeyConfig *UNUSED(keyconf))
|
||||
{
|
||||
}
|
||||
|
||||
static void profiler_main_region_init(wmWindowManager *UNUSED(wm), ARegion *region)
|
||||
{
|
||||
UI_view2d_region_reinit(®ion->v2d, V2D_COMMONVIEW_CUSTOM, region->winx, region->winy);
|
||||
}
|
||||
|
||||
static void profiler_main_region_draw(const bContext *C, ARegion *region)
|
||||
{
|
||||
blender::ed::profiler::draw_profiler(C, region);
|
||||
}
|
||||
|
||||
static void profiler_main_region_listener(const wmRegionListenerParams *params)
|
||||
{
|
||||
ARegion *region = params->region;
|
||||
wmNotifier *wmn = params->notifier;
|
||||
|
||||
switch (wmn->category) {
|
||||
case NC_SPACE: {
|
||||
if (wmn->data == ND_SPACE_PROFILER) {
|
||||
ED_region_tag_redraw(region);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NC_PROFILE: {
|
||||
ED_region_tag_redraw(region);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void profiler_header_region_init(wmWindowManager *UNUSED(wm), ARegion *region)
|
||||
{
|
||||
ED_region_header_init(region);
|
||||
}
|
||||
|
||||
static void profiler_header_region_draw(const bContext *C, ARegion *region)
|
||||
{
|
||||
ED_region_header(C, region);
|
||||
}
|
||||
|
||||
static void profiler_header_region_free(ARegion *UNUSED(region))
|
||||
{
|
||||
}
|
||||
|
||||
static void profiler_header_region_listener(const wmRegionListenerParams *UNUSED(params))
|
||||
{
|
||||
}
|
||||
|
||||
static void profiler_operatortypes()
|
||||
{
|
||||
}
|
||||
|
||||
void ED_spacetype_profiler(void)
|
||||
{
|
||||
SpaceType *st = (SpaceType *)MEM_callocN(sizeof(SpaceType), "spacetype profiler");
|
||||
ARegionType *art;
|
||||
|
||||
st->spaceid = SPACE_PROFILER;
|
||||
strncpy(st->name, "Profiler", BKE_ST_MAXNAME);
|
||||
|
||||
st->create = profiler_create;
|
||||
st->free = profiler_free;
|
||||
st->init = profiler_init;
|
||||
st->duplicate = profiler_duplicate;
|
||||
st->operatortypes = profiler_operatortypes;
|
||||
st->keymap = profiler_keymap;
|
||||
|
||||
/* regions: main window */
|
||||
art = (ARegionType *)MEM_callocN(sizeof(ARegionType), "spacetype profiler region");
|
||||
art->regionid = RGN_TYPE_WINDOW;
|
||||
art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D;
|
||||
|
||||
art->init = profiler_main_region_init;
|
||||
art->draw = profiler_main_region_draw;
|
||||
art->listener = profiler_main_region_listener;
|
||||
BLI_addhead(&st->regiontypes, art);
|
||||
|
||||
/* regions: header */
|
||||
art = (ARegionType *)MEM_callocN(sizeof(ARegionType), "spacetype profiler header region");
|
||||
art->regionid = RGN_TYPE_HEADER;
|
||||
art->prefsizey = HEADERY;
|
||||
art->keymapflag = 0;
|
||||
art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_HEADER;
|
||||
|
||||
art->init = profiler_header_region_init;
|
||||
art->draw = profiler_header_region_draw;
|
||||
art->free = profiler_header_region_free;
|
||||
art->listener = profiler_header_region_listener;
|
||||
BLI_addhead(&st->regiontypes, art);
|
||||
|
||||
BKE_spacetype_register(st);
|
||||
}
|
@@ -77,6 +77,7 @@ set(SRC
|
||||
../include/ED_paint.h
|
||||
../include/ED_particle.h
|
||||
../include/ED_physics.h
|
||||
../include/ED_profiler.h
|
||||
../include/ED_render.h
|
||||
../include/ED_scene.h
|
||||
../include/ED_screen.h
|
||||
|
@@ -1959,6 +1959,26 @@ typedef enum eSpaceSpreadsheet_ContextType {
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Profiler
|
||||
* \{ */
|
||||
|
||||
typedef struct SpaceProfiler_Runtime SpaceProfiler_Runtime;
|
||||
|
||||
typedef struct SpaceProfiler {
|
||||
SpaceLink *next, *prev;
|
||||
/** Storage of regions for inactive spaces. */
|
||||
ListBase regionbase;
|
||||
char spacetype;
|
||||
char link_flag;
|
||||
char _pad0[6];
|
||||
/* End 'SpaceLink' header. */
|
||||
|
||||
SpaceProfiler_Runtime *runtime;
|
||||
} SpaceProfiler;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Space Defines (eSpace_Type)
|
||||
* \{ */
|
||||
@@ -1996,9 +2016,10 @@ typedef enum eSpace_Type {
|
||||
SPACE_CLIP = 20,
|
||||
SPACE_TOPBAR = 21,
|
||||
SPACE_STATUSBAR = 22,
|
||||
SPACE_SPREADSHEET = 23
|
||||
SPACE_SPREADSHEET = 23,
|
||||
SPACE_PROFILER = 24,
|
||||
|
||||
#define SPACE_TYPE_LAST SPACE_SPREADSHEET
|
||||
#define SPACE_TYPE_LAST SPACE_PROFILER
|
||||
} eSpace_Type;
|
||||
|
||||
/* use for function args */
|
||||
|
@@ -493,6 +493,7 @@ typedef struct bTheme {
|
||||
ThemeSpace space_topbar;
|
||||
ThemeSpace space_statusbar;
|
||||
ThemeSpace space_spreadsheet;
|
||||
ThemeSpace space_profiler;
|
||||
|
||||
/* 20 sets of bone colors for this theme */
|
||||
ThemeWireColor tarm[20];
|
||||
@@ -508,7 +509,7 @@ typedef struct bTheme {
|
||||
#define UI_THEMESPACE_START(btheme) \
|
||||
(CHECK_TYPE_INLINE(btheme, bTheme *), &((btheme)->space_properties))
|
||||
#define UI_THEMESPACE_END(btheme) \
|
||||
(CHECK_TYPE_INLINE(btheme, bTheme *), (&((btheme)->space_spreadsheet) + 1))
|
||||
(CHECK_TYPE_INLINE(btheme, bTheme *), (&((btheme)->space_profiler) + 1))
|
||||
|
||||
typedef struct bAddon {
|
||||
struct bAddon *next, *prev;
|
||||
|
@@ -602,6 +602,7 @@ extern StructRNA RNA_SpaceNLA;
|
||||
extern StructRNA RNA_SpaceNodeEditor;
|
||||
extern StructRNA RNA_SpaceOutliner;
|
||||
extern StructRNA RNA_SpacePreferences;
|
||||
extern StructRNA RNA_SpaceProfiler;
|
||||
extern StructRNA RNA_SpaceProperties;
|
||||
extern StructRNA RNA_SpaceSequenceEditor;
|
||||
extern StructRNA RNA_SpaceSpreadsheet;
|
||||
|
@@ -34,6 +34,7 @@
|
||||
#include "BKE_node.h"
|
||||
#include "BKE_studiolight.h"
|
||||
|
||||
#include "ED_profiler.h"
|
||||
#include "ED_spreadsheet.h"
|
||||
#include "ED_text.h"
|
||||
|
||||
@@ -124,6 +125,7 @@ const EnumPropertyItem rna_enum_space_type_items[] = {
|
||||
"Interactive programmatic console for "
|
||||
"advanced editing and script development"},
|
||||
{SPACE_INFO, "INFO", ICON_INFO, "Info", "Log of operations, warnings and error messages"},
|
||||
{SPACE_PROFILER, "PROFILER", ICON_MOD_TIME, "Profiler", "Performance profiler"},
|
||||
/* Special case: Top-bar and Status-bar aren't supposed to be a regular editor for the user. */
|
||||
{SPACE_TOPBAR,
|
||||
"TOPBAR",
|
||||
@@ -585,6 +587,8 @@ static StructRNA *rna_Space_refine(struct PointerRNA *ptr)
|
||||
return &RNA_SpaceClipEditor;
|
||||
case SPACE_SPREADSHEET:
|
||||
return &RNA_SpaceSpreadsheet;
|
||||
case SPACE_PROFILER:
|
||||
return &RNA_SpaceProfiler;
|
||||
|
||||
/* Currently no type info. */
|
||||
case SPACE_SCRIPT:
|
||||
@@ -3168,6 +3172,12 @@ static void rna_spreadsheet_set_geometry_node_context(SpaceSpreadsheet *sspreads
|
||||
WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
|
||||
}
|
||||
|
||||
static bool rna_SpaceProfiler_profile_is_enabled_get(PointerRNA *ptr)
|
||||
{
|
||||
SpaceProfiler *sprofiler = ptr->data;
|
||||
return ED_profiler_profile_is_enabled(sprofiler);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static const EnumPropertyItem dt_uv_items[] = {
|
||||
@@ -7582,6 +7592,31 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
}
|
||||
|
||||
static void rna_def_space_profiler(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
FunctionRNA *func;
|
||||
|
||||
srna = RNA_def_struct(brna, "SpaceProfiler", "Space");
|
||||
RNA_def_struct_ui_text(srna, "Space Profiler", "Profiler space data");
|
||||
|
||||
func = RNA_def_function(srna, "profile_enable", "ED_profiler_profile_enable");
|
||||
RNA_def_function_ui_description(func, "Enable profile recording in this editor");
|
||||
|
||||
func = RNA_def_function(srna, "profile_disable", "ED_profiler_profile_disable");
|
||||
RNA_def_function_ui_description(func, "Disable profile recording in this editor");
|
||||
|
||||
prop = RNA_def_property(srna, "profile_is_enabled", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_funcs(prop, "rna_SpaceProfiler_profile_is_enabled_get", NULL);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Profiling is Enabled", "True if this editor is currently recording");
|
||||
|
||||
func = RNA_def_function(srna, "profile_clear", "ED_profiler_profile_clear");
|
||||
RNA_def_function_ui_description(func, "Reset recorded profile in this editor");
|
||||
}
|
||||
|
||||
void RNA_def_space(BlenderRNA *brna)
|
||||
{
|
||||
rna_def_space(brna);
|
||||
@@ -7608,6 +7643,7 @@ void RNA_def_space(BlenderRNA *brna)
|
||||
rna_def_space_node(brna);
|
||||
rna_def_space_clip(brna);
|
||||
rna_def_space_spreadsheet(brna);
|
||||
rna_def_space_profiler(brna);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -3867,6 +3867,18 @@ static void rna_def_userdef_theme_space_spreadsheet(BlenderRNA *brna)
|
||||
rna_def_userdef_theme_spaces_main(srna);
|
||||
}
|
||||
|
||||
static void rna_def_userdef_theme_space_profiler(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
||||
srna = RNA_def_struct(brna, "ThemeProfiler", NULL);
|
||||
RNA_def_struct_sdna(srna, "ThemeSpace");
|
||||
RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
|
||||
RNA_def_struct_ui_text(srna, "Theme Profiler", "Theme settings for the Profiler");
|
||||
|
||||
rna_def_userdef_theme_spaces_main(srna);
|
||||
}
|
||||
|
||||
static void rna_def_userdef_themes(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
@@ -3894,6 +3906,7 @@ static void rna_def_userdef_themes(BlenderRNA *brna)
|
||||
{21, "TOPBAR", ICON_TOPBAR, "Top Bar", ""},
|
||||
{22, "STATUSBAR", ICON_STATUSBAR, "Status Bar", ""},
|
||||
{23, "SPREADSHEET", ICON_SPREADSHEET, "Spreadsheet"},
|
||||
{24, "PROFILER", ICON_TIME, "Profiler"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
@@ -4028,6 +4041,12 @@ static void rna_def_userdef_themes(BlenderRNA *brna)
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "space_spreadsheet");
|
||||
RNA_def_property_struct_type(prop, "ThemeSpreadsheet");
|
||||
RNA_def_property_ui_text(prop, "Spreadsheet", "");
|
||||
|
||||
prop = RNA_def_property(srna, "profiler", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_NEVER_NULL);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "space_profiler");
|
||||
RNA_def_property_struct_type(prop, "ThemeProfiler");
|
||||
RNA_def_property_ui_text(prop, "Profiler", "");
|
||||
/* end space types */
|
||||
|
||||
prop = RNA_def_property(srna, "bone_color_sets", PROP_COLLECTION, PROP_NONE);
|
||||
@@ -4282,6 +4301,7 @@ static void rna_def_userdef_dothemes(BlenderRNA *brna)
|
||||
rna_def_userdef_theme_space_topbar(brna);
|
||||
rna_def_userdef_theme_space_statusbar(brna);
|
||||
rna_def_userdef_theme_space_spreadsheet(brna);
|
||||
rna_def_userdef_theme_space_profiler(brna);
|
||||
rna_def_userdef_theme_colorset(brna);
|
||||
rna_def_userdef_theme_collection_color(brna);
|
||||
rna_def_userdef_themes(brna);
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include "BLI_float3.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_multi_value_map.hh"
|
||||
#include "BLI_profile.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_profile.hh"
|
||||
#include "BLI_rand.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
@@ -123,6 +124,9 @@ static void randomize_attribute(MutableSpan<T> span,
|
||||
const uint32_t seed,
|
||||
const GeometryNodeAttributeRandomizeMode operation)
|
||||
{
|
||||
ProfileTask profile_task;
|
||||
BLI_profile_task_begin_named(&profile_task, __func__);
|
||||
|
||||
/* The operations could be templated too, but it doesn't make the code much shorter. */
|
||||
switch (operation) {
|
||||
case GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE:
|
||||
@@ -161,6 +165,8 @@ static void randomize_attribute(MutableSpan<T> span,
|
||||
BLI_assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
BLI_profile_task_end(&profile_task);
|
||||
}
|
||||
|
||||
static void randomize_attribute_bool(MutableSpan<bool> span,
|
||||
|
@@ -307,6 +307,7 @@ typedef struct wmNotifier {
|
||||
#define NC_LIGHTPROBE (25 << 24)
|
||||
/* Changes to asset data in the current .blend. */
|
||||
#define NC_ASSET (26 << 24)
|
||||
#define NC_PROFILE (27 << 24)
|
||||
|
||||
/* data type, 256 entries is enough, it can overlap */
|
||||
#define NOTE_DATA 0x00FF0000
|
||||
@@ -437,6 +438,7 @@ typedef struct wmNotifier {
|
||||
#define ND_SPACE_CLIP (20 << 16)
|
||||
#define ND_SPACE_FILE_PREVIEW (21 << 16)
|
||||
#define ND_SPACE_SPREADSHEET (22 << 16)
|
||||
#define ND_SPACE_PROFILER (23 << 16)
|
||||
|
||||
/* subtype, 256 entries too */
|
||||
#define NOTE_SUBTYPE 0x0000FF00
|
||||
|
@@ -44,6 +44,7 @@
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_dynstr.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_profile.h"
|
||||
#include "BLI_timer.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
@@ -434,12 +435,18 @@ void wm_event_do_notifiers(bContext *C)
|
||||
{
|
||||
/* Run the timer before assigning 'wm' in the unlikely case a timer loads a file, see T80028. */
|
||||
wm_event_execute_timers(C);
|
||||
if (BLI_profile_is_enabled()) {
|
||||
WM_main_add_notifier(NC_PROFILE, NULL);
|
||||
}
|
||||
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
if (wm == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileTask profile_task;
|
||||
BLI_profile_task_begin_named(&profile_task, __func__);
|
||||
|
||||
/* Disable? - Keep for now since its used for window level notifiers. */
|
||||
#if 1
|
||||
/* Cache & catch WM level notifiers, such as frame change, scene/screen set. */
|
||||
@@ -630,6 +637,8 @@ void wm_event_do_notifiers(bContext *C)
|
||||
|
||||
/* Autorun warning */
|
||||
wm_test_autorun_warning(C);
|
||||
|
||||
BLI_profile_task_end(&profile_task);
|
||||
}
|
||||
|
||||
static int wm_event_always_pass(const wmEvent *event)
|
||||
|
Reference in New Issue
Block a user