213 Commits

Author SHA1 Message Date
c1f6bcb83f some linux work from summer
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-10-12 18:06:23 +03:00
eca4f6d9bc Merge remote-tracking branch 'origin/1.11' into work
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-07-05 14:19:52 +03:00
802074fca3 Update package requirements for Wayland 2025-07-05 12:55:25 +03:00
0bb13e1d91 Add overloads for physics queries with user-provided buffers 2025-07-05 12:36:11 +03:00
db47531a6d Support marshalling Arrays as dynamic arrays in scripting API 2025-07-05 12:36:11 +03:00
Wojtek Figat
48c6339ebb Fix memory leak on material instances when updating layout of Text Render 2025-07-04 12:21:25 +02:00
Wojtek Figat
2dd34b288c Merge remote-tracking branch 'origin/master' into 1.11 2025-07-04 12:05:19 +02:00
51feaa0730 Release mouse relative mode when focus changes during mouse tracking
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-07-03 23:21:42 +03:00
8e69aa8bd6 Fix error while closing Editor
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-07-03 22:57:07 +03:00
90b2fded48 Merge remote-tracking branch 'origin/master' into sdl_platform
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
# Conflicts:
#	Source/Editor/Windows/SceneTreeWindow.cs
2025-07-03 22:05:29 +03:00
9125ffeb9e Update third-party label in About dialog 2025-07-03 22:02:37 +03:00
28980e5fbf Cleanup 2025-07-03 22:02:36 +03:00
aab0d772a4 Implement client-side window decorations for Editor windows 2025-07-03 22:02:36 +03:00
Wojtek Figat
bf345f13ce Fix reflection probes capture seams on cube face edges due to volumetric fog
#3252
2025-07-03 13:54:22 +02:00
Wojtek Figat
a138c6b062 Optimize environment probes filtering shader 2025-07-03 11:45:12 +02:00
Wojtek Figat
33e58c12cb Optimize ProbesRenderer to use time-slicing for cubemap faces rendering and filtering 2025-07-03 11:43:56 +02:00
Wojtek Figat
094a6562b8 Refactor ProbesRenderer 2025-07-03 10:18:51 +02:00
Wojtek Figat
448eb48c23 Fix fog to draw Fog Cutoff Distance via a plane, not sphere test
Add support for negative Fog Cutoff Distance on fog to draw it in front of the camera Far Plane, no matter the setup.
Fix hot-reloading Fog shader in Editor.
2025-06-29 20:02:24 +02:00
Wojtek Figat
78d519cb9a Fix ConcurrentSystemLocker to have exclusive lock as an option 2025-06-29 19:16:41 +02:00
Wojtek Figat
43d11264f8 Fix asset references to use separate lightweight locking instead of full asset mutex 2025-06-29 19:16:23 +02:00
Wojtek Figat
f126a83b79 Fix graphical issues when batching materials that use Forward Shading for instancing 2025-06-29 13:52:29 +02:00
Wojtek Figat
bdd7bae459 Add new Custom Lit shading model for custom lighting in materials (eg. Cel Shading) 2025-06-29 13:51:59 +02:00
Wojtek Figat
3dc7546dd4 Fix crash when constant buffer is unused by shader but still exists 2025-06-27 19:06:25 +02:00
Wojtek Figat
185151b025 Minor fixes 2025-06-27 18:52:25 +02:00
Wojtek Figat
8cdec15fa6 Fix GlobalSignDistanceFieldCustomBuffer to be thread-safe (scene rendering events are not guarded via mutex anymore) 2025-06-27 15:41:48 +02:00
Wojtek Figat
1b40775d62 Fix deadloop in HtmlParser when parsing text with incorrect tags 2025-06-27 11:56:09 +02:00
Wojtek Figat
45e82d21f4 Fix ConcurrentSystemLocker to guard for a single writer at once 2025-06-26 19:51:06 +02:00
Wojtek Figat
5c37584eca Minor adjustment for alignment of perf-critical variables in rendering 2025-06-26 19:50:42 +02:00
Wojtek Figat
674fda7375 Add resizing to Custom Code nodes in Materials 2025-06-26 19:50:04 +02:00
6296e1a9eb Fix linker errors 2025-06-26 15:32:38 +03:00
1586bb0702 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-26 15:31:59 +03:00
Wojtek Figat
d1c43ec1fe Merge remote-tracking branch 'origin/master' into 1.11 2025-06-25 10:48:11 +02:00
edeaf6af09 Fix compilation without SDL 2025-06-24 22:57:49 +03:00
951edd95db Cleanup 2025-06-24 16:47:45 +03:00
9951211596 Fix mono glib.h wrapper compilation error 2025-06-24 16:47:20 +03:00
6d337464f7 Support XDG Desktop Portal as color picker provider
Wayland and XWayland fallback implementation uses XDP in
order to query picked color from desktop.
2025-06-24 16:41:32 +03:00
d0b552d74a Use logo as an icon in Linux windows 2025-06-23 18:58:02 +03:00
83512822b1 Support clipboard actions in Content Browser and Scene tree on Wayland
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-06-23 17:50:02 +03:00
ccb78103ec Support clipboard actions in Content Browser and Scene tree on X11 2025-06-23 17:07:10 +03:00
6e79ffea34 Fix clipboard not working on X11 2025-06-23 17:07:10 +03:00
4f03d37a17 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-23 12:03:41 +03:00
Wojtek Figat
ef5d45874a Fix compilation regression 2025-06-22 12:12:42 +02:00
Wojtek Figat
d7df403e5e Optimize ContainerControl.DisposeChildren 2025-06-20 09:05:41 +02:00
Wojtek Figat
d3a50cdacb Optimize Actor::DestroyChildren 2025-06-20 09:05:25 +02:00
Wojtek Figat
2e10d776e9 Optimize updating actor rendering entry with better thread locking that support async writes on actor update 2025-06-19 14:04:06 +02:00
Wojtek Figat
4ac870f701 Optimize physics transformation updates propagation in async via Job System 2025-06-19 13:57:50 +02:00
Wojtek Figat
6144f6c74e Optimize physics simulation with higher limit of 8 threads 2025-06-19 09:50:07 +02:00
Wojtek Figat
edb6884942 Optimize PhysX work dispatcher to be shared by all scenes 2025-06-19 08:24:26 +02:00
Wojtek Figat
62e329ac6e Add more memory tags for Navigation 2025-06-18 23:00:43 +02:00
Wojtek Figat
68dce7e4dd Merge remote-tracking branch 'origin/master' into 1.11 2025-06-18 22:46:10 +02:00
6fb8419b3c Disable window flashing on Wayland to fix focus issues 2025-06-17 14:23:20 +03:00
a4272d6ca9 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-17 14:19:46 +03:00
Wojtek Figat
bd2add7edd Tweak memory command tip 2025-06-16 23:15:58 +02:00
Wojtek Figat
986693757c Merge remote-tracking branch 'origin/master' into 1.11 2025-06-16 22:46:17 +02:00
4654117d5e Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-16 17:09:22 +03:00
6ff260d052 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-16 14:09:29 +03:00
88d2b72822 Update SDL to 3.3.x
Fixes popup focus issues on Linux
2025-06-16 14:09:04 +03:00
Wojtek Figat
766091045b Improve version parsing to share code 2025-06-12 18:21:12 +02:00
Wojtek Figat
e2d9452994 Add unified min Clang version 13 for Linux 2025-06-12 18:05:01 +02:00
Wojtek Figat
eadb4411ff Fix crash in GPU Memory profiler if resource went null 2025-06-12 17:35:02 +02:00
Wojtek Figat
bdc87c7bc6 Update min supported version of macOS to 13 and iOS to 15 2025-06-12 17:26:39 +02:00
Wojtek Figat
7606c9ac12 Update minimum CPU arch requirement on Windows to AVX2 with SSE4.2
94.48% support on PC according to Steam Hardware & Software Survey: May 2025 (https://store.steampowered.com/hwsurvey/)
2025-06-12 17:03:19 +02:00
Wojtek Figat
4240646ec7 Update minimum Windows version to 10 (to match .NET 8) 2025-06-12 08:31:32 +02:00
Wojtek Figat
0fa53f860a Add UseLogInRelease to engine config to disable logging in Release builds 2025-06-11 23:35:03 +02:00
Wojtek Figat
8ec138399a Add higher level streaming time budget in frame based on idle time 2025-06-11 18:40:35 +02:00
Wojtek Figat
5b6859a66f Add time slicing to Deserialization stage of async scenes loading to avoid hitching
#3261
2025-06-11 18:40:06 +02:00
Wojtek Figat
e9835766bc Add red color to Tracy profiler zones that cause CPU waiting to improve profiling 2025-06-11 14:56:43 +02:00
Wojtek Figat
d6eb647d59 Optimize async scene loading to run in separate stages with time-slicing 2025-06-11 14:33:47 +02:00
Wojtek Figat
b50f3fcb64 Refactor level actions to support time budget and time slicing 2025-06-11 00:01:46 +02:00
Wojtek Figat
d6b4992991 Optimize actors registration in SceneRendering to track free items 2025-06-10 20:08:20 +02:00
Wojtek Figat
cfd2f42b0c Optimize managed memory allocations in Editor profiler 2025-06-09 22:06:49 +02:00
Wojtek Figat
89c7f4b0a3 Fix ManagedDictionary cache to be cleared on hot-reload 2025-06-09 17:19:36 +02:00
Wojtek Figat
d7ff9fdade Optimize editor profiler native allocations when capturing data 2025-06-09 15:23:31 +02:00
Wojtek Figat
057ec9d41e Anothher fix 2025-06-09 10:48:02 +02:00
Wojtek Figat
7fa4efcac5 Fix compilation in Release 2025-06-09 10:17:51 +02:00
Wojtek Figat
6547e7ee9c Fix compilation with Clang 2025-06-08 23:58:33 +02:00
Wojtek Figat
907c593671 Fix typos in doc comments 2025-06-08 19:47:09 +02:00
29868531ad SDL workaround for window size and position not updating
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-06-08 18:57:57 +03:00
95dfb6fdc6 Reduce minimum default window size for CommandLineBox popup 2025-06-08 18:57:12 +03:00
Wojtek Figat
65ab42158d Update engine version 2025-06-08 00:58:39 +02:00
Wojtek Figat
99841e2e8d Fix crash when using invalid node index in skinned mesh 2025-06-08 00:58:31 +02:00
Wojtek Figat
73c30d3d89 Optimize asset references to support direct registration to reduce Delegate memory allocations and overhead 2025-06-08 00:58:15 +02:00
Wojtek Figat
bffb175a9b Code fixes 2025-06-07 01:25:22 +02:00
Wojtek Figat
125a973ff2 Rename Prefetch to MemoryPrefetch 2025-06-06 22:55:14 +02:00
Wojtek Figat
462f75abd0 Optimize memory allocation when reading animated model pose by cloth 2025-06-06 22:41:48 +02:00
Wojtek Figat
d95cd2f0be Optimize memory alloc on Animated Model init 2025-06-06 22:41:29 +02:00
Wojtek Figat
091f76bbf2 Add more improvements to usability of memory profiler 2025-06-06 22:40:43 +02:00
Wojtek Figat
e8b60060ab Fix memory profiler thread-local storage to avoid dynamic mem alloc due to recursive call 2025-06-06 14:52:27 +02:00
Wojtek Figat
cd637e8a7a Add more memory profiling coverage 2025-06-06 14:38:22 +02:00
Wojtek Figat
9d8e75caa3 Fix various code to improve quality 2025-06-06 11:19:32 +02:00
Wojtek Figat
0670c0bbd3 Fix compilation warnings 2025-06-05 18:32:36 +02:00
Wojtek Figat
f462a2187f Merge branch 'master' into 1.11 2025-06-05 18:03:17 +02:00
8df3999f85 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-04 19:30:30 +03:00
149b189629 Update SDL to 3.2.16 2025-06-02 19:23:34 +03:00
b1fd86e6b5 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-02 18:15:16 +03:00
5e8fdf879d Fix logging in gamepad related events 2025-06-02 18:15:10 +03:00
Wojtek Figat
8eff098850 Fix Linux build 2025-05-28 04:30:08 +02:00
Wojtek Figat
4fe9fdded6 Optimize redundant string allocation in managed binary module unload 2025-05-28 04:10:47 +02:00
Wojtek Figat
03d52d4eb9 Add support for building engine without logging 2025-05-28 04:05:12 +02:00
Wojtek Figat
ab61ed5a37 Add more memory profiling insights and groups 2025-05-28 04:03:44 +02:00
Wojtek Figat
72ee80242d Add integration with Tracy profiler to plot main memory categories 2025-05-26 05:37:53 +02:00
Wojtek Figat
9dc4dbc6d7 Add more memory profiler categories 2025-05-25 18:38:07 +02:00
Wojtek Figat
a74c5e7943 Another fix for iOS build 2025-05-25 18:01:30 +02:00
Wojtek Figat
8f9fa6995e Fix compilation issues 2025-05-25 17:40:00 +02:00
Wojtek Figat
98e59450f1 Add freeing managed assembly memory on reload/unload 2025-05-25 17:39:51 +02:00
Wojtek Figat
8c62f1120f Optimize dynamic memory allocations for managed runtime interop collections with a new Arena Allocation 2025-05-25 17:39:20 +02:00
Wojtek Figat
9aaba955d0 Fix profiler tables to use column headers aligned to center 2025-05-25 02:04:56 +02:00
Wojtek Figat
410ec0465c Optimize CSharp scripting runtime to use arena allocator per-assembly 2025-05-25 02:04:16 +02:00
Wojtek Figat
f9cb4ddae2 Add new Arena Allocator for optimized dynamic memory allocations with a shared lifetime 2025-05-24 05:08:32 +02:00
Wojtek Figat
bb855e2663 Add suport for Tracy profiler on Mac 2025-05-22 05:34:36 +02:00
Wojtek Figat
d24f9d1e1e Add warning when using memory profiler without enabled on startup 2025-05-22 05:18:56 +02:00
Wojtek Figat
c1b1f4afc4 Add process memory stats for Apple platforms 2025-05-22 04:49:48 +02:00
Wojtek Figat
c639a3103c Add memory profiling events to the main areas of the engine 2025-05-22 04:47:01 +02:00
Wojtek Figat
32bc73610f Fix debug command type detection when it's used with argument 2025-05-22 04:45:12 +02:00
Wojtek Figat
66dcfafa2e Fix Vulkan descriptor sets pooling 2025-05-22 04:42:01 +02:00
Wojtek Figat
9215f2662f Add missing memory alloc profiling for virtual pages 2025-05-22 04:41:01 +02:00
Wojtek Figat
2dc404cbd3 Add new memory profiler 2025-05-22 04:40:32 +02:00
84ada18299 Fix unlock mouse hotkey not unlocking CursorLockMode.Locked cursor 2025-04-21 23:13:23 +03:00
635fe8017e Fix EditorViewport buttons activating items on scene 2025-04-21 23:13:23 +03:00
834380ff05 Cleanup 2025-04-21 23:13:23 +03:00
dd65fc2289 Implement Wayland text clipboard data handling 2025-04-21 23:13:23 +03:00
86125bb625 Add support for VK_EXT_metal_surface extension 2025-04-21 23:13:23 +03:00
09a9d8b380 Update volk to 1.4.304 2025-04-21 23:13:23 +03:00
86b223ec93 Fix mouse relative mode activation triggering mouse move events on Mac 2025-04-21 23:13:23 +03:00
78f6080321 Initial support for building and running SDL platform on macOS 2025-04-21 23:13:23 +03:00
c7be6f6e0e Fix VC++ project file building on macOS Rider 2025-04-20 01:14:51 +03:00
c40c31fbb7 Reduce lock usage during window events 2025-04-19 21:14:13 +03:00
f5fbc1e32d Fix handling of WindowsManager locks 2025-04-19 21:14:13 +03:00
0a20378acd Handle Wayland dragging actions ending prematurely 2025-04-19 21:14:12 +03:00
d4e87877b6 Fix editor viewport activating while dragging a window 2025-04-19 21:14:12 +03:00
7eb7236b8a Fix occluded or hidden windows causing the engine to freeze on Wayland 2025-04-19 21:14:11 +03:00
dc0e4ffce2 Allow context menu to show when activating scene tree with right click 2025-04-19 21:14:11 +03:00
86fe93943d Fix crash when relative mode was left active on removed window 2025-04-19 21:14:10 +03:00
3258113ef8 Ensure mouse position is up-to-date during button down events 2025-04-19 21:14:10 +03:00
ebf3999cfe Fix rubber band selector activating outside of the viewport
This usually happens while trying to drag the window from window handle
2025-04-19 21:14:09 +03:00
a1ccbbb5b9 Use sensible window minimum size limits in editor windows 2025-04-19 21:14:09 +03:00
35072c40d8 Fix build on Linux 2025-04-19 21:14:08 +03:00
5c51021388 Fix merge 2025-04-19 21:14:08 +03:00
95fd527515 Apply drag and drop styling on refactored WindowDragHelper
Reapplies changes from commit 38b00f458c
2025-04-19 21:14:07 +03:00
113d851cea Fix cursor escaping constrained area when initially outside on Windows 2025-04-19 21:14:07 +03:00
72f0c460f9 Refix dragged maximized window generating wrong mouse position events 2025-04-19 21:14:06 +03:00
13ddd15b44 Focus viewports earlier to fix CursorLockMode not always activating 2025-04-19 21:14:06 +03:00
c9e24aaf5f Fix documentation generation 2025-04-19 21:14:05 +03:00
68c0ac0ffc Fix cursor locking bounds calculation 2025-04-19 21:14:05 +03:00
bebda275a9 Fix crash when Game viewport was hidden 2025-04-19 21:14:04 +03:00
287eaae850 Fix X11 external drag and drop not releasing consistently 2025-04-19 21:14:04 +03:00
7e59c3b9a7 Fix restoring locked cursor state when window gains focus again 2025-04-19 21:14:03 +03:00
38658a5b8c Fix CursorLockMode.Locked not working 2025-04-19 21:14:02 +03:00
88c75b8672 Fix dragged maximized window generating wrong mouse position events 2025-04-19 21:14:02 +03:00
17ab1e6830 Fix window handle clicking to not restore window from maximized state 2025-04-19 21:14:01 +03:00
1f45110e5f Fix rubber band selector activating when using Alt-key to orbit 2025-04-19 21:14:01 +03:00
5401166a07 Fix error when entering playmode while holding right mouse button 2025-04-19 21:14:00 +03:00
bfa8188782 Fix accepting drag and drop for files in Wayland 2025-04-19 21:14:00 +03:00
e777a71784 Fix error while dragging files around Content window 2025-04-19 21:13:59 +03:00
c13e91a0d0 Release rubber band selection when viewport starts controlling the view 2025-04-19 21:13:59 +03:00
aac5d57352 Update SDL to 3.2.10 2025-04-19 21:13:58 +03:00
ceca13c7b6 Avoid showing tooltips in inactive windows 2025-04-19 21:13:58 +03:00
2905470330 Update SDL3 to 3.2.4 2025-04-19 21:13:57 +03:00
2c17033822 Fix window dragging when not supported by Wayland compositor
(cherry picked from commit 3554747a67)
2025-04-19 21:13:57 +03:00
0a4cb9e9b1 Show current display server in Editor window tooltip
(cherry picked from commit 62968dd437)
2025-04-19 21:13:56 +03:00
096651f4c1 Properly mark floating windows with transparency support
(cherry picked from commit c660fac524)
2025-04-19 21:13:56 +03:00
275a08506b Enable transparency support in Vulkan swapchains
(cherry picked from commit 431a69e357)
2025-04-19 21:13:55 +03:00
4cd61cb381 Fix compilation for game builds
(cherry picked from commit f4fcc07288)
2025-04-19 21:13:54 +03:00
9ad1147581 Fix cloning SDL repository 2025-04-19 21:13:54 +03:00
35e09def1b Fix text input not working on X11 2025-04-19 21:13:53 +03:00
0469607a71 Fix button latching on Windows after drag and drop operation 2025-04-19 21:13:53 +03:00
d115d22ee6 Implement new window dragging system 2025-04-19 21:13:52 +03:00
8120ae621d Fix mouse resetting issues after ending relative mode 2025-04-19 21:13:52 +03:00
f873c2fa9f Fix frame stutter when window is focused 2025-04-19 21:13:51 +03:00
c3ffbe2f42 Fix error when docking to sides of tabbed panel 2025-04-19 21:13:51 +03:00
55a0a39881 Cleanup Linux SDL implementation 2025-04-19 21:13:50 +03:00
592215dd30 Support compiling third party library C files as C code 2025-04-19 21:13:50 +03:00
ef0c2a2785 Implement Wayland protocols module and file generation 2025-04-19 21:13:49 +03:00
37979e452a Fix mouse warping after ending relative mode 2025-04-19 21:13:49 +03:00
e9671bb727 Add git fetch method for dependencies 2025-04-19 21:13:48 +03:00
aa328bd591 Fix window ShowInTaskbar setting 2025-04-19 21:13:48 +03:00
188e4313f9 Fix various issues with child window positioning 2025-04-19 21:13:47 +03:00
df02c70e31 Add Window.IsAlwaysOnTop property 2025-04-19 21:13:47 +03:00
257f54b323 Use SDL locale 2025-04-19 21:13:46 +03:00
fb4b5b2575 Allow window with single tab to be dragged from tab area 2025-04-19 21:13:46 +03:00
4e1251276d Fix ValueBox mouse position resetting after releasing the button 2025-04-19 21:13:45 +03:00
a7b200dc57 Fix SDL build process on Linux 2025-04-19 21:13:44 +03:00
8cadbae80e Update SDL to 3.2.0 2025-04-19 21:13:44 +03:00
c3bae49aae Force cursor to center of Game Window when tab handle is clicked 2025-04-19 21:13:43 +03:00
Chandler Cox
6fa4fc6149 Fix rotation using SDL 2025-04-19 21:13:43 +03:00
f1ffe1acaa Fix Linux compilation without SDL 2025-04-19 21:13:42 +03:00
5c9ddf7f00 Fix compilation 2025-04-19 21:13:42 +03:00
431767a150 Update SDL3 2025-04-19 21:13:41 +03:00
8ba4e442a6 Fix compilation issues 2025-04-19 21:13:41 +03:00
2f8d19c267 Fix windows not being hidden initially 2025-04-19 21:13:40 +03:00
f2fd98f742 Fix parent window position handling with popup/tooltip windows 2025-04-19 21:13:40 +03:00
3ce35d6e81 Fix compilation errors in other platforms 2025-04-19 21:13:39 +03:00
796dbaa836 Fix CI for Linux 2025-04-19 21:13:39 +03:00
84b209ec7f Prevent building with SDL in unsupported platforms 2025-04-19 21:13:38 +03:00
fa976b34dc Fallback to X11 message box implementation when SDL fails 2025-04-19 21:13:38 +03:00
e7cda362b7 Fix popup and context menus not working on Wayland 2025-04-19 21:13:37 +03:00
a41a09c162 Hide warnings for unsupported SDL operations on Wayland 2025-04-19 21:13:37 +03:00
e82f84f0ab Log a warning for not implemented Wayland functionality 2025-04-19 21:13:36 +03:00
f1d387ceea Fix compilation in Linux 2025-04-19 21:13:36 +03:00
3893d4d1f8 Enable warning sound in question dialogs 2025-04-19 21:13:35 +03:00
cf7ac50faf Enable modern Windows dialog boxes 2025-04-19 21:13:35 +03:00
3f6bf15554 Implement relative mouse mode (raw input) for SDL platform 2025-04-19 21:13:34 +03:00
dac74829b4 Add flag for Window types 2025-04-19 21:13:34 +03:00
94d6f213a0 Enable native windowing system settings with SDL platform 2025-04-19 21:13:33 +03:00
8f2550ef61 Add command-line switches to force X11 and Wayland SDL drivers 2025-04-19 21:13:33 +03:00
b622a1cc5e Implement SDL platform, windowing and input handling 2025-04-19 21:13:32 +03:00
c83a3c32c7 Refactor application window class name 2025-04-19 21:13:32 +03:00
e7dcf7f7e7 Move Window related enums to separate header file 2025-04-19 21:13:31 +03:00
398785a2be Refactor Windows drag and drop implementation 2025-04-19 21:13:31 +03:00
05dba0f1f5 Refactor ScreenUtilities 2025-04-19 21:13:30 +03:00
39dbd32b2e Add more helper methods for managing Git repos 2025-04-19 21:13:30 +03:00
23a34a455a Fix centered window location on X11 2025-04-19 21:13:29 +03:00
4308328b48 Fix initial position of Tooltips 2025-04-19 21:13:29 +03:00
541 changed files with 106494 additions and 5036 deletions

View File

@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
- name: Setup Vulkan - name: Setup Vulkan
uses: ./.github/actions/vulkan uses: ./.github/actions/vulkan
- name: Setup .NET - name: Setup .NET
@@ -44,7 +44,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev libwayland-dev
- name: Setup Vulkan - name: Setup Vulkan
uses: ./.github/actions/vulkan uses: ./.github/actions/vulkan
- name: Setup .NET - name: Setup .NET

View File

@@ -28,7 +28,7 @@ jobs:
git lfs pull git lfs pull
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
- name: Build - name: Build
run: | run: |
./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8 ./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8

View File

@@ -28,6 +28,13 @@ TextureCube SkyLightTexture : register(t__SRV__);
Buffer<float4> ShadowsBuffer : register(t__SRV__); Buffer<float4> ShadowsBuffer : register(t__SRV__);
Texture2D<float> ShadowMap : register(t__SRV__); Texture2D<float> ShadowMap : register(t__SRV__);
@4// Forward Shading: Utilities @4// Forward Shading: Utilities
// Public accessors for lighting data, use them as data binding might change but those methods will remain.
LightData GetDirectionalLight() { return DirectionalLight; }
LightData GetSkyLight() { return SkyLight; }
ProbeData GetEnvironmentProbe() { return EnvironmentProbe; }
ExponentialHeightFogData GetExponentialHeightFog() { return ExponentialHeightFog; }
uint GetLocalLightsCount() { return LocalLightsCount; }
LightData GetLocalLight(uint i) { return LocalLights[i]; }
@5// Forward Shading: Shaders @5// Forward Shading: Shaders
// Pixel Shader function for Forward Pass // Pixel Shader function for Forward Pass
@@ -76,9 +83,8 @@ void PS_Forward(
gBuffer.ShadingModel = MATERIAL_SHADING_MODEL; gBuffer.ShadingModel = MATERIAL_SHADING_MODEL;
// Calculate lighting from a single directional light // Calculate lighting from a single directional light
float4 shadowMask = 1.0f;
ShadowSample shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, gBuffer); ShadowSample shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, gBuffer);
shadowMask = GetShadowMask(shadow); float4 shadowMask = GetShadowMask(shadow);
float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false); float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false);
// Calculate lighting from sky light // Calculate lighting from sky light

BIN
Content/Shaders/ProbesFilter.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -2,9 +2,9 @@
"Name": "Flax", "Name": "Flax",
"Version": { "Version": {
"Major": 1, "Major": 1,
"Minor": 10, "Minor": 11,
"Revision": 0, "Revision": 0,
"Build": 6705 "Build": 6800
}, },
"Company": "Flax", "Company": "Flax",
"Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.", "Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.",
@@ -13,6 +13,7 @@
"Configuration": { "Configuration": {
"UseCSharp": true, "UseCSharp": true,
"UseLargeWorlds": false, "UseLargeWorlds": false,
"UseDotNet": true "UseDotNet": true,
"UseSDL": true
} }
} }

View File

@@ -14,4 +14,4 @@ bash ./Development/Scripts/Linux/CallBuildTool.sh --genproject "$@"
# Build bindings for all editor configurations # Build bindings for all editor configurations
echo Building C# bindings... echo Building C# bindings...
# TODO: Detect the correct architecture here # TODO: Detect the correct architecture here
Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor #Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor

View File

@@ -57,9 +57,9 @@ Follow the instructions below to compile and run the engine from source.
* Arch: `sudo pacman -S git git-lfs` * Arch: `sudo pacman -S git git-lfs`
* `git-lfs install` * `git-lfs install`
* Install the required packages: * Install the required packages:
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev` * Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev zenity wayland-protocols libportal-dev`
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel` * Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel zenity wayland-protocols-devel libportal`
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib` * Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib zenity wayland-protocols libportal`
* Install Clang compiler (version 6 or later): * Install Clang compiler (version 6 or later):
* Ubuntu: `sudo apt-get install clang lldb lld` * Ubuntu: `sudo apt-get install clang lldb lld`
* Fedora: `sudo dnf install clang llvm lldb lld` * Fedora: `sudo dnf install clang llvm lldb lld`

View File

@@ -174,7 +174,9 @@ void EditorAnalytics::StartSession()
// Bind events // Bind events
GameCooker::OnEvent.Bind<RegisterGameCookingStart>(); GameCooker::OnEvent.Bind<RegisterGameCookingStart>();
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Bind<RegisterLightmapsBuildingStart>(); ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Bind<RegisterLightmapsBuildingStart>();
#if LOG_ENABLE
Log::Logger::OnError.Bind<RegisterError>(); Log::Logger::OnError.Bind<RegisterError>();
#endif
} }
void EditorAnalytics::EndSession() void EditorAnalytics::EndSession()
@@ -187,7 +189,9 @@ void EditorAnalytics::EndSession()
// Unbind events // Unbind events
GameCooker::OnEvent.Unbind<RegisterGameCookingStart>(); GameCooker::OnEvent.Unbind<RegisterGameCookingStart>();
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Unbind<RegisterLightmapsBuildingStart>(); ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Unbind<RegisterLightmapsBuildingStart>();
#if LOG_ENABLE
Log::Logger::OnError.Unbind<RegisterError>(); Log::Logger::OnError.Unbind<RegisterError>();
#endif
// End session // End session
{ {

View File

@@ -282,7 +282,7 @@ namespace FlaxEditor.Content
if (data is DragDataFiles) if (data is DragDataFiles)
return DragDropEffect.Copy; return DragDropEffect.Copy;
return _dragOverItems.Effect; return _dragOverItems?.Effect ?? DragDropEffect.None;
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -20,7 +20,7 @@ namespace FlaxEditor.Content
} }
/// <inheritdoc /> /// <inheritdoc />
public override string TypeDescription => Path.EndsWith(".h") ? "C++ Header File" : "C++ Source Code"; public override string TypeDescription => Path.EndsWith(".h") || Path.EndsWith(".hpp") ? "C++ Header File" : "C++ Source Code";
/// <inheritdoc /> /// <inheritdoc />
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CPPScript128; public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CPPScript128;

View File

@@ -30,6 +30,7 @@
#include "Engine/Scripting/ManagedCLR/MAssembly.h" #include "Engine/Scripting/ManagedCLR/MAssembly.h"
#include "Engine/Content/JsonAsset.h" #include "Engine/Content/JsonAsset.h"
#include "Engine/Content/AssetReference.h" #include "Engine/Content/AssetReference.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if PLATFORM_TOOLS_WINDOWS #if PLATFORM_TOOLS_WINDOWS
#include "Platform/Windows/WindowsPlatformTools.h" #include "Platform/Windows/WindowsPlatformTools.h"
#include "Engine/Platform/Windows/WindowsPlatformSettings.h" #include "Engine/Platform/Windows/WindowsPlatformSettings.h"
@@ -380,6 +381,7 @@ bool GameCooker::IsCancelRequested()
PlatformTools* GameCooker::GetTools(BuildPlatform platform) PlatformTools* GameCooker::GetTools(BuildPlatform platform)
{ {
PROFILE_MEM(Editor);
PlatformTools* result = nullptr; PlatformTools* result = nullptr;
if (!Tools.TryGet(platform, result)) if (!Tools.TryGet(platform, result))
{ {
@@ -471,6 +473,7 @@ bool GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration,
LOG(Error, "Build platform {0} is not supported.", ::ToString(platform)); LOG(Error, "Build platform {0} is not supported.", ::ToString(platform));
return true; return true;
} }
PROFILE_MEM(Editor);
// Setup // Setup
CancelFlag = 0; CancelFlag = 0;
@@ -624,6 +627,7 @@ void GameCookerImpl::ReportProgress(const String& info, float totalProgress)
void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets) void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
{ {
PROFILE_MEM(Editor);
if (Internal_OnCollectAssets == nullptr) if (Internal_OnCollectAssets == nullptr)
{ {
auto c = GameCooker::GetStaticClass(); auto c = GameCooker::GetStaticClass();
@@ -651,6 +655,7 @@ void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
bool GameCookerImpl::Build() bool GameCookerImpl::Build()
{ {
PROFILE_MEM(Editor);
CookingData& data = *Data; CookingData& data = *Data;
LOG(Info, "Starting Game Cooker..."); LOG(Info, "Starting Game Cooker...");
LOG(Info, "Platform: {0}, Configuration: {2}, Options: {1}", ::ToString(data.Platform), (int32)data.Options, ::ToString(data.Configuration)); LOG(Info, "Platform: {0}, Configuration: {2}, Options: {1}", ::ToString(data.Platform), (int32)data.Options, ::ToString(data.Configuration));
@@ -778,6 +783,8 @@ int32 GameCookerImpl::ThreadFunction()
bool GameCookerService::Init() bool GameCookerService::Init()
{ {
PROFILE_MEM(Editor);
auto editorAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly; auto editorAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
editorAssembly->Unloading.Bind(OnEditorAssemblyUnloading); editorAssembly->Unloading.Bind(OnEditorAssemblyUnloading);
GameCooker::OnCollectAssets.Bind(OnCollectAssets); GameCooker::OnCollectAssets.Bind(OnCollectAssets);
@@ -789,6 +796,7 @@ void GameCookerService::Update()
{ {
if (IsRunning) if (IsRunning)
{ {
PROFILE_MEM(Editor);
ScopeLock lock(ProgressLocker); ScopeLock lock(ProgressLocker);
if (ProgressMsg.HasChars()) if (ProgressMsg.HasChars())

View File

@@ -186,7 +186,7 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
ADD_ENTRY("CFBundlePackageType", "APPL"); ADD_ENTRY("CFBundlePackageType", "APPL");
ADD_ENTRY("NSPrincipalClass", "NSApplication"); ADD_ENTRY("NSPrincipalClass", "NSApplication");
ADD_ENTRY("LSApplicationCategoryType", "public.app-category.games"); ADD_ENTRY("LSApplicationCategoryType", "public.app-category.games");
ADD_ENTRY("LSMinimumSystemVersion", "10.15"); ADD_ENTRY("LSMinimumSystemVersion", "13");
ADD_ENTRY("CFBundleIconFile", "icon.icns"); ADD_ENTRY("CFBundleIconFile", "icon.icns");
ADD_ENTRY_STR("CFBundleExecutable", executableName); ADD_ENTRY_STR("CFBundleExecutable", executableName);
ADD_ENTRY_STR("CFBundleIdentifier", appIdentifier); ADD_ENTRY_STR("CFBundleIdentifier", appIdentifier);

View File

@@ -36,6 +36,7 @@
#include "Engine/Engine/Base/GameBase.h" #include "Engine/Engine/Base/GameBase.h"
#include "Engine/Engine/Globals.h" #include "Engine/Engine/Globals.h"
#include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Tools/TextureTool/TextureTool.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/Enums.h" #include "Engine/Scripting/Enums.h"
#if PLATFORM_TOOLS_WINDOWS #if PLATFORM_TOOLS_WINDOWS

View File

@@ -6,6 +6,8 @@
#include "Engine/Core/Types/TimeSpan.h" #include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Core/Types/Stopwatch.h" #include "Engine/Core/Types/Stopwatch.h"
#include "Engine/Core/Collections/Dictionary.h" #include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/BinaryModule.h" #include "Engine/Scripting/BinaryModule.h"
@@ -69,6 +71,7 @@ MTypeObject* CustomEditorsUtil::GetCustomEditor(MTypeObject* refType)
bool CustomEditorsUtilService::Init() bool CustomEditorsUtilService::Init()
{ {
PROFILE_MEM(Editor);
TRACK_ASSEMBLY(((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly); TRACK_ASSEMBLY(((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly);
Scripting::BinaryModuleLoaded.Bind(&OnBinaryModuleLoaded); Scripting::BinaryModuleLoaded.Bind(&OnBinaryModuleLoaded);
@@ -77,6 +80,8 @@ bool CustomEditorsUtilService::Init()
void OnAssemblyLoaded(MAssembly* assembly) void OnAssemblyLoaded(MAssembly* assembly)
{ {
PROFILE_CPU_NAMED("CustomEditors.OnAssemblyLoaded");
PROFILE_MEM(Editor);
Stopwatch stopwatch; Stopwatch stopwatch;
// Prepare FlaxEngine // Prepare FlaxEngine

View File

@@ -190,12 +190,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
foreach (var file in files) foreach (var file in files)
FindNewKeysCSharp(file, newKeys, allKeys); FindNewKeysCSharp(file, newKeys, allKeys);
// C++ // C/C++
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories); files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.c", SearchOption.AllDirectories)).ToArray();
filesCount += files.Length; filesCount += files.Length;
foreach (var file in files) foreach (var file in files)
FindNewKeysCpp(file, newKeys, allKeys); FindNewKeysCpp(file, newKeys, allKeys);
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories); files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.hpp", SearchOption.AllDirectories)).ToArray();;
filesCount += files.Length; filesCount += files.Length;
foreach (var file in files) foreach (var file in files)
FindNewKeysCpp(file, newKeys, allKeys); FindNewKeysCpp(file, newKeys, allKeys);

View File

@@ -20,6 +20,7 @@
#include "Engine/Engine/Engine.h" #include "Engine/Engine/Engine.h"
#include "Engine/ShadowsOfMordor/Builder.h" #include "Engine/ShadowsOfMordor/Builder.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "FlaxEngine.Gen.h" #include "FlaxEngine.Gen.h"
#if PLATFORM_LINUX #if PLATFORM_LINUX
#include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Tools/TextureTool/TextureTool.h"
@@ -47,6 +48,7 @@ void Editor::CloseSplashScreen()
bool Editor::CheckProjectUpgrade() bool Editor::CheckProjectUpgrade()
{ {
PROFILE_MEM(Editor);
const auto versionFilePath = Globals::ProjectCacheFolder / TEXT("version"); const auto versionFilePath = Globals::ProjectCacheFolder / TEXT("version");
// Load version cache file // Load version cache file
@@ -366,6 +368,8 @@ bool Editor::BackupProject()
int32 Editor::LoadProduct() int32 Editor::LoadProduct()
{ {
PROFILE_MEM(Editor);
// Flax Editor product // Flax Editor product
Globals::ProductName = TEXT("Flax Editor"); Globals::ProductName = TEXT("Flax Editor");
Globals::CompanyName = TEXT("Flax"); Globals::CompanyName = TEXT("Flax");
@@ -626,6 +630,7 @@ int32 Editor::LoadProduct()
Window* Editor::CreateMainWindow() Window* Editor::CreateMainWindow()
{ {
PROFILE_MEM(Editor);
Window* window = Managed->GetMainWindow(); Window* window = Managed->GetMainWindow();
#if PLATFORM_LINUX #if PLATFORM_LINUX
@@ -662,6 +667,7 @@ bool Editor::Init()
return true; return true;
} }
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Editor);
// If during last lightmaps baking engine crashed we could try to restore the progress // If during last lightmaps baking engine crashed we could try to restore the progress
ShadowsOfMordor::Builder::Instance()->CheckIfRestoreState(); ShadowsOfMordor::Builder::Instance()->CheckIfRestoreState();
@@ -693,11 +699,13 @@ bool Editor::Init()
void Editor::BeforeRun() void Editor::BeforeRun()
{ {
PROFILE_MEM(Editor);
Managed->BeforeRun(); Managed->BeforeRun();
} }
void Editor::BeforeExit() void Editor::BeforeExit()
{ {
PROFILE_MEM(Editor);
CloseSplashScreen(); CloseSplashScreen();
Managed->Exit(); Managed->Exit();
@@ -708,6 +716,8 @@ void Editor::BeforeExit()
void EditorImpl::OnUpdate() void EditorImpl::OnUpdate()
{ {
PROFILE_MEM(Editor);
// Update c# editor // Update c# editor
Editor::Managed->Update(); Editor::Managed->Update();

View File

@@ -51,7 +51,7 @@ namespace FlaxEditor.GUI
/// <summary> /// <summary>
/// The column title horizontal text alignment /// The column title horizontal text alignment
/// </summary> /// </summary>
public TextAlignment TitleAlignment = TextAlignment.Near; public TextAlignment TitleAlignment = TextAlignment.Center;
/// <summary> /// <summary>
/// The column title margin. /// The column title margin.

View File

@@ -1,7 +1,10 @@
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_SDL
#define USE_IS_FOREGROUND #define USE_IS_FOREGROUND
#else #else
#endif #endif
#if PLATFORM_SDL
#define USE_SDL_WORKAROUNDS
#endif
// Copyright (c) Wojciech Figat. All rights reserved. // Copyright (c) Wojciech Figat. All rights reserved.
using System.Collections.Generic; using System.Collections.Generic;
@@ -121,7 +124,7 @@ namespace FlaxEditor.GUI.ContextMenu
} }
/// <summary> /// <summary>
/// Shows the empty menu popup o na screen. /// Shows the empty menu popup on a screen.
/// </summary> /// </summary>
/// <param name="control">The target control.</param> /// <param name="control">The target control.</param>
/// <param name="area">The target control area to cover.</param> /// <param name="area">The target control area to cover.</param>
@@ -256,7 +259,9 @@ namespace FlaxEditor.GUI.ContextMenu
desc.AllowMaximize = false; desc.AllowMaximize = false;
desc.AllowDragAndDrop = false; desc.AllowDragAndDrop = false;
desc.IsTopmost = true; desc.IsTopmost = true;
desc.IsRegularWindow = false; desc.Type = WindowType.Popup;
desc.Parent = parentWin.Window;
desc.Title = "ContextMenu";
desc.HasSizingFrame = false; desc.HasSizingFrame = false;
OnWindowCreating(ref desc); OnWindowCreating(ref desc);
_window = Platform.CreateWindow(ref desc); _window = Platform.CreateWindow(ref desc);
@@ -265,6 +270,12 @@ namespace FlaxEditor.GUI.ContextMenu
_window.GotFocus += OnWindowGotFocus; _window.GotFocus += OnWindowGotFocus;
_window.LostFocus += OnWindowLostFocus; _window.LostFocus += OnWindowLostFocus;
} }
#if USE_IS_FOREGROUND && USE_SDL_WORKAROUNDS
// The focus between popup and parent windows doesn't change, force hide the popup when clicked on parent
parentWin.Window.MouseDown += OnWindowMouseDown;
_window.Closed += () => parentWin.Window.MouseDown -= OnWindowMouseDown;
#endif
// Attach to the window // Attach to the window
_parentCM = parent as ContextMenuBase; _parentCM = parent as ContextMenuBase;
@@ -440,6 +451,17 @@ namespace FlaxEditor.GUI.ContextMenu
} }
} }
#if USE_SDL_WORKAROUNDS
private void OnWindowGotFocus()
{
}
private void OnWindowMouseDown(ref Float2 mousePosition, MouseButton button, ref bool handled)
{
// The user clicked outside the popup window
Hide();
}
#else
private void OnWindowGotFocus() private void OnWindowGotFocus()
{ {
var child = _childCM; var child = _childCM;
@@ -453,6 +475,7 @@ namespace FlaxEditor.GUI.ContextMenu
}); });
} }
} }
#endif
private void OnWindowLostFocus() private void OnWindowLostFocus()
{ {
@@ -551,7 +574,12 @@ namespace FlaxEditor.GUI.ContextMenu
// Let root context menu to check if none of the popup windows // Let root context menu to check if none of the popup windows
if (_parentCM == null && UseVisibilityControl && !IsForeground) if (_parentCM == null && UseVisibilityControl && !IsForeground)
{ {
#if USE_SDL_WORKAROUNDS
if (!IsMouseOver)
Hide();
#else
Hide(); Hide();
#endif
} }
} }
#endif #endif

View File

@@ -326,8 +326,11 @@ namespace FlaxEditor.GUI.Dialogs
// Update eye dropper tool // Update eye dropper tool
if (_activeEyedropper) if (_activeEyedropper)
{ {
// Try reading the color under the cursor in realtime if supported by the platform
Float2 mousePosition = Platform.MousePosition; Float2 mousePosition = Platform.MousePosition;
SelectedColor = ScreenUtilities.GetColorAt(mousePosition); Color color = ScreenUtilities.GetColorAt(mousePosition);
if (color != Color.Transparent)
SelectedColor = color;
} }
} }

View File

@@ -1,545 +0,0 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI.Docking
{
/// <summary>
/// Helper class used to handle docking windows dragging and docking.
/// </summary>
public class DockHintWindow
{
private FloatWindowDockPanel _toMove;
private Float2 _dragOffset;
private Float2 _defaultWindowSize;
private Rectangle _rectDock;
private Rectangle _rectWindow;
private Float2 _mouse;
private DockState _toSet;
private DockPanel _toDock;
private bool _lateDragOffsetUpdate;
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
private DockHintWindow(FloatWindowDockPanel toMove)
{
_toMove = toMove;
_toSet = DockState.Float;
var window = toMove.Window.Window;
// Remove focus from drag target
_toMove.Focus();
_toMove.Defocus();
// Focus window
window.Focus();
// Check if window is maximized and restore window.
if (window.IsMaximized)
{
// Restore window and set position to mouse.
var mousePos = window.MousePosition;
var previousSize = window.Size;
window.Restore();
window.Position = Platform.MousePosition - mousePos * window.Size / previousSize;
}
// Calculate dragging offset and move window to the destination position
var mouseScreenPosition = Platform.MousePosition;
// If the _toMove window was not focused when initializing this window, the result vector only contains zeros
// and to prevent a failure, we need to perform an update for the drag offset at later time which will be done in the OnMouseMove event handler.
if (mouseScreenPosition != Float2.Zero)
CalculateDragOffset(mouseScreenPosition);
else
_lateDragOffsetUpdate = true;
// Get initial size
_defaultWindowSize = window.Size;
// Init proxy window
Proxy.Init(ref _defaultWindowSize);
// Bind events
Proxy.Window.MouseUp += OnMouseUp;
Proxy.Window.MouseMove += OnMouseMove;
Proxy.Window.LostFocus += OnLostFocus;
// Start tracking mouse
Proxy.Window.StartTrackingMouse(false);
// Update window GUI
Proxy.Window.GUI.PerformLayout();
// Update rectangles
UpdateRects();
// Hide base window
window.Hide();
// Enable hit window presentation
Proxy.Window.RenderingEnabled = true;
Proxy.Window.Show();
Proxy.Window.Focus();
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
public void Dispose()
{
// End tracking mouse
Proxy.Window.EndTrackingMouse();
// Disable rendering
Proxy.Window.RenderingEnabled = false;
// Unbind events
Proxy.Window.MouseUp -= OnMouseUp;
Proxy.Window.MouseMove -= OnMouseMove;
Proxy.Window.LostFocus -= OnLostFocus;
// Hide the proxy
Proxy.Hide();
if (_toMove == null)
return;
// Check if window won't be docked
if (_toSet == DockState.Float)
{
var window = _toMove.Window?.Window;
if (window == null)
return;
var mouse = Platform.MousePosition;
// Move base window
window.Position = mouse - _dragOffset;
// Show base window
window.Show();
}
else
{
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
// Check if window has only single tab
if (hasNoChildPanels && _toMove.TabsCount == 1)
{
// Dock window
_toMove.GetTab(0).Show(_toSet, _toDock);
}
// Check if dock as tab and has no child panels
else if (hasNoChildPanels && _toSet == DockState.DockFill)
{
// Dock all tabs
while (_toMove.TabsCount > 0)
{
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
}
}
else
{
var selectedTab = _toMove.SelectedTab;
// Dock the first tab into the target location
var firstTab = _toMove.GetTab(0);
firstTab.Show(_toSet, _toDock);
// Dock rest of the tabs
while (_toMove.TabsCount > 0)
{
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
}
// Keep selected tab being selected
selectedTab?.SelectTab();
}
// Focus target window
_toDock.Root.Focus();
}
_toMove = null;
}
/// <summary>
/// Creates the new dragging hit window.
/// </summary>
/// <param name="toMove">Floating dock panel to move.</param>
/// <returns>The dock hint window object.</returns>
public static DockHintWindow Create(FloatWindowDockPanel toMove)
{
if (toMove == null)
throw new ArgumentNullException();
return new DockHintWindow(toMove);
}
/// <summary>
/// Creates the new dragging hit window.
/// </summary>
/// <param name="toMove">Dock window to move.</param>
/// <returns>The dock hint window object.</returns>
public static DockHintWindow Create(DockWindow toMove)
{
if (toMove == null)
throw new ArgumentNullException();
// Show floating
toMove.ShowFloating();
// Move window to the mouse position (with some offset for caption bar)
var window = (WindowRootControl)toMove.Root;
var mouse = Platform.MousePosition;
window.Window.Position = mouse - new Float2(8, 8);
// Get floating panel
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
return new DockHintWindow(floatingPanelToMove);
}
/// <summary>
/// Calculates window rectangle in the dock window.
/// </summary>
/// <param name="state">Window dock state.</param>
/// <param name="rect">Dock panel rectangle.</param>
/// <returns>Calculated window rectangle.</returns>
public static Rectangle CalculateDockRect(DockState state, ref Rectangle rect)
{
Rectangle result = rect;
switch (state)
{
case DockState.DockFill:
result.Location.Y += DockPanel.DefaultHeaderHeight;
result.Size.Y -= DockPanel.DefaultHeaderHeight;
break;
case DockState.DockTop:
result.Size.Y *= DockPanel.DefaultSplitterValue;
break;
case DockState.DockLeft:
result.Size.X *= DockPanel.DefaultSplitterValue;
break;
case DockState.DockBottom:
result.Location.Y += result.Size.Y * (1 - DockPanel.DefaultSplitterValue);
result.Size.Y *= DockPanel.DefaultSplitterValue;
break;
case DockState.DockRight:
result.Location.X += result.Size.X * (1 - DockPanel.DefaultSplitterValue);
result.Size.X *= DockPanel.DefaultSplitterValue;
break;
}
return result;
}
private void CalculateDragOffset(Float2 mouseScreenPosition)
{
var baseWinPos = _toMove.Window.Window.Position;
_dragOffset = mouseScreenPosition - baseWinPos;
}
private void UpdateRects()
{
// Cache mouse position
_mouse = Platform.MousePosition;
// Check intersection with any dock panel
var uiMouse = _mouse;
_toDock = _toMove.MasterPanel.HitTest(ref uiMouse, _toMove);
// Check dock state to use
bool showProxyHints = _toDock != null;
bool showBorderHints = showProxyHints;
bool showCenterHint = showProxyHints;
if (showProxyHints)
{
// If moved window has not only tabs but also child panels disable docking as tab
if (_toMove.ChildPanelsCount > 0)
showCenterHint = false;
// Disable docking windows with one or more dock panels inside
if (_toMove.ChildPanelsCount > 0)
showBorderHints = false;
// Get dock area
_rectDock = _toDock.DockAreaBounds;
// Cache dock rectangles
var size = _rectDock.Size;
var offset = _rectDock.Location;
var borderMargin = 4.0f;
var hintWindowsSize = Proxy.HintWindowsSize * Platform.DpiScale;
var hintWindowsSize2 = hintWindowsSize * 0.5f;
var centerX = size.X * 0.5f;
var centerY = size.Y * 0.5f;
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
// Hit test
DockState toSet = DockState.Float;
if (showBorderHints)
{
if (_rUpper.Contains(_mouse))
toSet = DockState.DockTop;
else if (_rBottom.Contains(_mouse))
toSet = DockState.DockBottom;
else if (_rLeft.Contains(_mouse))
toSet = DockState.DockLeft;
else if (_rRight.Contains(_mouse))
toSet = DockState.DockRight;
}
if (showCenterHint && _rCenter.Contains(_mouse))
toSet = DockState.DockFill;
_toSet = toSet;
// Show proxy hint windows
Proxy.Down.Position = _rBottom.Location;
Proxy.Left.Position = _rLeft.Location;
Proxy.Right.Position = _rRight.Location;
Proxy.Up.Position = _rUpper.Location;
Proxy.Center.Position = _rCenter.Location;
}
else
{
_toSet = DockState.Float;
}
// Update proxy hint windows visibility
Proxy.Down.IsVisible = showProxyHints & showBorderHints;
Proxy.Left.IsVisible = showProxyHints & showBorderHints;
Proxy.Right.IsVisible = showProxyHints & showBorderHints;
Proxy.Up.IsVisible = showProxyHints & showBorderHints;
Proxy.Center.IsVisible = showProxyHints & showCenterHint;
// Calculate proxy/dock/window rectangles
if (_toDock == null)
{
// Floating window over nothing
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
}
else
{
if (_toSet == DockState.Float)
{
// Floating window over dock panel
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
}
else
{
// Use only part of the dock panel to show hint
_rectWindow = CalculateDockRect(_toSet, ref _rectDock);
}
}
// Update proxy window
Proxy.Window.ClientBounds = _rectWindow;
}
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
{
if (button == MouseButton.Left)
{
Dispose();
}
}
private void OnMouseMove(ref Float2 mousePos)
{
// Recalculate the drag offset because the current mouse screen position was invalid when we initialized the window
if (_lateDragOffsetUpdate)
{
// Calculate dragging offset and move window to the destination position
CalculateDragOffset(mousePos);
// Reset state
_lateDragOffsetUpdate = false;
}
UpdateRects();
}
private void OnLostFocus()
{
Dispose();
}
/// <summary>
/// Contains helper proxy windows shared across docking panels. They are used to visualize docking window locations.
/// </summary>
public static class Proxy
{
/// <summary>
/// The drag proxy window.
/// </summary>
public static Window Window;
/// <summary>
/// The left hint proxy window.
/// </summary>
public static Window Left;
/// <summary>
/// The right hint proxy window.
/// </summary>
public static Window Right;
/// <summary>
/// The up hint proxy window.
/// </summary>
public static Window Up;
/// <summary>
/// The down hint proxy window.
/// </summary>
public static Window Down;
/// <summary>
/// The center hint proxy window.
/// </summary>
public static Window Center;
/// <summary>
/// The hint windows size.
/// </summary>
public const float HintWindowsSize = 32.0f;
/// <summary>
/// Initializes the hit proxy windows. Those windows are used to indicate drag target areas (left, right, top, bottom, etc.).
/// </summary>
public static void InitHitProxy()
{
CreateProxy(ref Left, "DockHint.Left");
CreateProxy(ref Right, "DockHint.Right");
CreateProxy(ref Up, "DockHint.Up");
CreateProxy(ref Down, "DockHint.Down");
CreateProxy(ref Center, "DockHint.Center");
}
/// <summary>
/// Initializes the hint window.
/// </summary>
/// <param name="initSize">Initial size of the proxy window.</param>
public static void Init(ref Float2 initSize)
{
if (Window == null)
{
var settings = CreateWindowSettings.Default;
settings.Title = "DockHint.Window";
settings.Size = initSize;
settings.AllowInput = true;
settings.AllowMaximize = false;
settings.AllowMinimize = false;
settings.HasBorder = false;
settings.HasSizingFrame = false;
settings.IsRegularWindow = false;
settings.SupportsTransparency = true;
settings.ShowInTaskbar = false;
settings.ShowAfterFirstPaint = false;
settings.IsTopmost = true;
Window = Platform.CreateWindow(ref settings);
Window.Opacity = 0.6f;
Window.GUI.BackgroundColor = Style.Current.Selection;
Window.GUI.AddChild<DragVisuals>();
}
else
{
// Resize proxy
Window.ClientSize = initSize;
}
InitHitProxy();
}
private sealed class DragVisuals : Control
{
public DragVisuals()
{
AnchorPreset = AnchorPresets.StretchAll;
Offsets = Margin.Zero;
}
public override void Draw()
{
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.SelectionBorder);
}
}
private static void CreateProxy(ref Window win, string name)
{
if (win != null)
return;
var settings = CreateWindowSettings.Default;
settings.Title = name;
settings.Size = new Float2(HintWindowsSize * Platform.DpiScale);
settings.AllowInput = false;
settings.AllowMaximize = false;
settings.AllowMinimize = false;
settings.HasBorder = false;
settings.HasSizingFrame = false;
settings.IsRegularWindow = false;
settings.SupportsTransparency = true;
settings.ShowInTaskbar = false;
settings.ActivateWhenFirstShown = false;
settings.IsTopmost = true;
settings.ShowAfterFirstPaint = false;
win = Platform.CreateWindow(ref settings);
win.Opacity = 0.6f;
win.GUI.BackgroundColor = Style.Current.Selection;
win.GUI.AddChild<DragVisuals>();
}
/// <summary>
/// Hides proxy windows.
/// </summary>
public static void Hide()
{
HideProxy(ref Window);
HideProxy(ref Left);
HideProxy(ref Right);
HideProxy(ref Up);
HideProxy(ref Down);
HideProxy(ref Center);
}
private static void HideProxy(ref Window win)
{
if (win)
{
win.Hide();
}
}
/// <summary>
/// Releases proxy data and windows.
/// </summary>
public static void Dispose()
{
DisposeProxy(ref Window);
DisposeProxy(ref Left);
DisposeProxy(ref Right);
DisposeProxy(ref Up);
DisposeProxy(ref Down);
DisposeProxy(ref Center);
}
private static void DisposeProxy(ref Window win)
{
if (win)
{
win.Close(ClosingReason.User);
win = null;
}
}
}
}
}

View File

@@ -49,6 +49,11 @@ namespace FlaxEditor.GUI.Docking
/// The mouse position. /// The mouse position.
/// </summary> /// </summary>
public Float2 MousePosition = Float2.Minimum; public Float2 MousePosition = Float2.Minimum;
/// <summary>
/// The mouse position.
/// </summary>
public Float2 MouseStartPosition = Float2.Minimum;
/// <summary> /// <summary>
/// The start drag asynchronous window. /// The start drag asynchronous window.
@@ -165,7 +170,7 @@ namespace FlaxEditor.GUI.Docking
if (_panel.ChildPanelsCount == 0 && _panel.TabsCount == 1 && _panel.IsFloating) if (_panel.ChildPanelsCount == 0 && _panel.TabsCount == 1 && _panel.IsFloating)
{ {
// Create docking hint window but in an async manner // Create docking hint window but in an async manner
DockHintWindow.Create(_panel as FloatWindowDockPanel); WindowDragHelper.StartDragging(_panel as FloatWindowDockPanel);
} }
else else
{ {
@@ -176,7 +181,7 @@ namespace FlaxEditor.GUI.Docking
_panel.SelectTab(index - 1); _panel.SelectTab(index - 1);
// Create docking hint window // Create docking hint window
DockHintWindow.Create(win); WindowDragHelper.StartDragging(win, _panel.RootWindow.Window);
} }
} }
} }
@@ -355,6 +360,7 @@ namespace FlaxEditor.GUI.Docking
if (IsSingleFloatingWindow) if (IsSingleFloatingWindow)
return base.OnMouseDown(location, button); return base.OnMouseDown(location, button);
MouseDownWindow = GetTabAtPos(location, out IsMouseDownOverCross); MouseDownWindow = GetTabAtPos(location, out IsMouseDownOverCross);
MouseStartPosition = location;
// Check buttons // Check buttons
if (button == MouseButton.Left) if (button == MouseButton.Left)
@@ -441,6 +447,20 @@ namespace FlaxEditor.GUI.Docking
StartDrag(MouseDownWindow); StartDrag(MouseDownWindow);
MouseDownWindow = null; MouseDownWindow = null;
} }
// Check if single tab is tried to be moved
else if (MouseDownWindow != null && _panel.TabsCount <= 1)
{
if ((MousePosition - MouseStartPosition).Length > 3)
{
// Clear flag
IsMouseLeftButtonDown = false;
// Check tab under the mouse
if (!IsMouseDownOverCross && MouseDownWindow != null)
StartDrag(MouseDownWindow);
MouseDownWindow = null;
}
}
// Check if has more than one tab to change order // Check if has more than one tab to change order
else if (MouseDownWindow != null && _panel.TabsCount > 1) else if (MouseDownWindow != null && _panel.TabsCount > 1)
{ {

View File

@@ -182,6 +182,25 @@ namespace FlaxEditor.GUI.Docking
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param> /// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
/// <param name="position">Window location.</param> /// <param name="position">Window location.</param>
public void ShowFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent) public void ShowFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent)
{
CreateFloating(location, size, position, true);
}
/// <summary>
/// Creates the window in a floating state.
/// </summary>
public void CreateFloating()
{
CreateFloating(Float2.Zero, Float2.Zero);
}
/// <summary>
/// Creates the window in a floating state.
/// </summary>
/// <param name="location">Window location.</param>
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
/// <param name="position">Window location.</param>
/// <param name="showWindow">Window visibility.</param>
public void CreateFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent, bool showWindow = false)
{ {
Undock(); Undock();
@@ -199,14 +218,17 @@ namespace FlaxEditor.GUI.Docking
windowGUI.UnlockChildrenRecursive(); windowGUI.UnlockChildrenRecursive();
windowGUI.PerformLayout(); windowGUI.PerformLayout();
// Show if (showWindow)
window.Show(); {
window.BringToFront(); // Show
window.Focus(); window.Show();
OnShow(); window.BringToFront();
window.Focus();
OnShow();
// Perform layout again // Perform layout again
windowGUI.PerformLayout(); windowGUI.PerformLayout();
}
} }
/// <summary> /// <summary>

View File

@@ -11,6 +11,42 @@ namespace FlaxEditor.GUI.Docking
/// <seealso cref="DockPanel" /> /// <seealso cref="DockPanel" />
public class FloatWindowDockPanel : DockPanel public class FloatWindowDockPanel : DockPanel
{ {
private class FloatWindowDecorations : WindowDecorations
{
private FloatWindowDockPanel _panel;
public FloatWindowDecorations(FloatWindowDockPanel panel)
: base(panel.RootWindow)
{
_panel = panel;
}
/// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
{
_panel.BeginDrag();
return true;
}
return base.OnMouseDown(location, button);
}
#if !PLATFORM_WINDOWS
/// <inheritdoc />
protected override WindowHitCodes OnHitTest(ref Float2 mouse)
{
var hit = base.OnHitTest(ref mouse);
if (hit == WindowHitCodes.Caption)
{
// Override the system behaviour when interacting with the caption area
hit = WindowHitCodes.Client;
}
return hit;
}
#endif
}
private MasterDockPanel _masterPanel; private MasterDockPanel _masterPanel;
private WindowRootControl _window; private WindowRootControl _window;
@@ -40,6 +76,26 @@ namespace FlaxEditor.GUI.Docking
Parent = window; Parent = window;
_window.Window.Closing += OnClosing; _window.Window.Closing += OnClosing;
_window.Window.LeftButtonHit += OnLeftButtonHit; _window.Window.LeftButtonHit += OnLeftButtonHit;
if (Utilities.Utils.UseCustomWindowDecorations())
{
var decorations = Parent.AddChild(new FloatWindowDecorations(this));
decorations.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, false);
}
}
/// <inheritdoc />
protected override void PerformLayoutBeforeChildren()
{
base.PerformLayoutBeforeChildren();
var decorations = Parent.GetChild<FloatWindowDecorations>();
if (decorations != null)
{
// Apply offset for the title bar
foreach (var child in Children)
child.Bounds = child.Bounds with { Y = decorations.Height, Height = Parent.Height - decorations.Height };
}
} }
/// <summary> /// <summary>
@@ -52,7 +108,7 @@ namespace FlaxEditor.GUI.Docking
return; return;
// Create docking hint window // Create docking hint window
DockHintWindow.Create(this); WindowDragHelper.StartDragging(this);
} }
/// <summary> /// <summary>
@@ -71,22 +127,28 @@ namespace FlaxEditor.GUI.Docking
settings.Title = title; settings.Title = title;
settings.Size = size; settings.Size = size;
settings.Position = location; settings.Position = location;
settings.MinimumSize = new Float2(1); settings.MinimumSize = new Float2(200, 150);
settings.MaximumSize = Float2.Zero; // Unlimited size settings.MaximumSize = Float2.Zero; // Unlimited size
settings.Fullscreen = false; settings.Fullscreen = false;
settings.HasBorder = true; settings.HasBorder = true;
settings.SupportsTransparency = false; settings.SupportsTransparency = true;
settings.ActivateWhenFirstShown = true; settings.ActivateWhenFirstShown = true;
settings.AllowInput = true; settings.AllowInput = true;
settings.AllowMinimize = true; settings.AllowMinimize = true;
settings.AllowMaximize = true; settings.AllowMaximize = true;
settings.AllowDragAndDrop = true; settings.AllowDragAndDrop = true;
settings.IsTopmost = false; settings.IsTopmost = false;
settings.IsRegularWindow = true; settings.Type = WindowType.Regular;
settings.HasSizingFrame = true; settings.HasSizingFrame = true;
settings.ShowAfterFirstPaint = false; settings.ShowAfterFirstPaint = false;
settings.ShowInTaskbar = true; settings.ShowInTaskbar = true;
settings.StartPosition = startPosition; settings.StartPosition = startPosition;
if (Utilities.Utils.UseCustomWindowDecorations())
{
settings.HasBorder = false;
//settings.HasSizingFrame = false;
}
// Create window // Create window
return Platform.CreateWindow(ref settings); return Platform.CreateWindow(ref settings);

View File

@@ -81,7 +81,6 @@ namespace FlaxEditor.GUI.Docking
public DockPanel HitTest(ref Float2 position, FloatWindowDockPanel excluded) public DockPanel HitTest(ref Float2 position, FloatWindowDockPanel excluded)
{ {
// Check all floating windows // Check all floating windows
// TODO: gather windows order and take it into account when performing test
for (int i = 0; i < FloatingPanels.Count; i++) for (int i = 0; i < FloatingPanels.Count; i++)
{ {
var win = FloatingPanels[i]; var win = FloatingPanels[i];
@@ -94,9 +93,44 @@ namespace FlaxEditor.GUI.Docking
} }
// Base // Base
//if (!Root?.RootWindow.Window.IsFocused ?? false)
// return null;
return base.HitTest(ref position); return base.HitTest(ref position);
} }
/// <summary>
/// Performs hit test over dock panel.
/// </summary>
/// <param name="position">Window space position to test.</param>
/// <param name="excluded">Floating window to omit during searching (and all docked to that one).</param>
/// <param name="hitResults">Results of the hit test</param>
/// <returns>True if any dock panels were hit, otherwise false.</returns>
public bool HitTest(ref Float2 position, FloatWindowDockPanel excluded, out DockPanel[] hitResults)
{
// Check all floating windows
List<DockPanel> results = new(FloatingPanels.Count);
for (int i = 0; i < FloatingPanels.Count; i++)
{
var win = FloatingPanels[i];
if (win.Visible && win != excluded)
{
var result = win.HitTest(ref position);
if (result != null)
results.Add(result);
}
}
// Base
//if (!Root?.RootWindow.Window.IsFocused ?? false)
// return null;
var baseResult = base.HitTest(ref position);
if (baseResult != null)
results.Add(baseResult);
hitResults = results.ToArray();
return hitResults.Length > 0;
}
internal void LinkWindow(DockWindow window) internal void LinkWindow(DockWindow window)
{ {
// Add to the windows list // Add to the windows list

View File

@@ -0,0 +1,458 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI.Docking
{
/// <summary>
/// Helper class used to handle docking windows dragging and docking.
/// </summary>
public class WindowDragHelper
{
private FloatWindowDockPanel _toMove;
private Float2 _dragOffset;
private Rectangle _rectDock;
private Float2 _mouse;
private DockState _toSet;
private DockPanel _toDock;
private Window _dragSourceWindow;
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
private Control _dockHintDown, _dockHintUp, _dockHintLeft, _dockHintRight, _dockHintCenter;
/// <summary>
/// The hint control size.
/// </summary>
public const float HintControlSize = 32.0f;
/// <summary>
/// The opacity of the dragged window when hint controls are shown.
/// </summary>
public const float DragWindowOpacity = 0.4f;
/// <summary>
/// Returns true if any windows are being dragged.
/// </summary>
public static bool IsDragActive { get; private set; }
private WindowDragHelper(FloatWindowDockPanel toMove, Window dragSourceWindow)
{
IsDragActive = true;
_toMove = toMove;
_toSet = DockState.Float;
var window = toMove.Window.Window;
// Bind events
FlaxEngine.Scripting.Update += OnUpdate;
window.MouseUp += OnMouseUp;
// Update rectangles
UpdateRects(Platform.MousePosition);
// Ensure the dragged window stays on top of every other window
window.IsAlwaysOnTop = true;
_dragSourceWindow = dragSourceWindow;
if (_dragSourceWindow != null) // Detaching a tab from existing window
{
_dragOffset = new Float2(window.Size.X / 2, 10.0f);
_dragSourceWindow.MouseUp += OnMouseUp; // The mouse up event is sent to the source window on Windows
// TODO: when detaching tab in floating window (not main window), the drag source window is still main window?
var dragSourceWindowWayland = toMove.MasterPanel?.RootWindow.Window ?? Editor.Instance.Windows.MainWindow;
window.DoDragDrop(window.Title, _dragOffset, dragSourceWindowWayland);
}
else
{
_dragOffset = window.MousePosition;
window.DoDragDrop(window.Title, _dragOffset, window);
}
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
public void Dispose()
{
IsDragActive = false;
var window = _toMove?.Window?.Window;
// Unbind events
FlaxEngine.Scripting.Update -= OnUpdate;
if (window != null)
window.MouseUp -= OnMouseUp;
if (_dragSourceWindow != null)
_dragSourceWindow.MouseUp -= OnMouseUp;
RemoveDockHints();
if (_toMove == null)
return;
if (window != null)
{
window.Opacity = 1.0f;
window.IsAlwaysOnTop = false;
window.BringToFront();
}
// Check if window won't be docked
if (_toSet == DockState.Float)
{
if (window == null)
return;
// Show base window
window.Show();
}
else
{
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
// Check if window has only single tab
if (hasNoChildPanels && _toMove.TabsCount == 1)
{
// Dock window
_toMove.GetTab(0).Show(_toSet, _toDock);
}
// Check if dock as tab and has no child panels
else if (hasNoChildPanels && _toSet == DockState.DockFill)
{
// Dock all tabs
while (_toMove.TabsCount > 0)
{
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
}
}
else
{
var selectedTab = _toMove.SelectedTab;
// Dock the first tab into the target location
if (_toMove.TabsCount > 0)
{
var firstTab = _toMove.GetTab(0);
firstTab.Show(_toSet, _toDock);
// Dock rest of the tabs
while (_toMove.TabsCount > 0)
{
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
}
}
// Keep selected tab being selected
selectedTab?.SelectTab();
}
// Focus target window
_toDock.Root.Focus();
}
_toMove = null;
}
/// <summary>
/// Start dragging a floating dock panel.
/// </summary>
/// <param name="toMove">Floating dock panel to move.</param>
/// <returns>The window drag helper object.</returns>
public static WindowDragHelper StartDragging(FloatWindowDockPanel toMove)
{
if (toMove == null)
throw new ArgumentNullException();
return new WindowDragHelper(toMove, null);
}
/// <summary>
/// Start dragging a docked panel into a floating window.
/// </summary>
/// <param name="toMove">Dock window to move.</param>
/// <param name="dragSourceWindow">The window where dragging started from.</param>
/// <returns>The window drag helper object.</returns>
public static WindowDragHelper StartDragging(DockWindow toMove, Window dragSourceWindow)
{
if (toMove == null)
throw new ArgumentNullException();
// Create floating window
toMove.CreateFloating();
// Get floating panel
var window = (WindowRootControl)toMove.Root;
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
return new WindowDragHelper(floatingPanelToMove, dragSourceWindow);
}
private sealed class DragVisuals : Control
{
public DragVisuals()
{
AnchorPreset = AnchorPresets.StretchAll;
Offsets = Margin.Zero;
}
public override void Draw()
{
base.Draw();
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.SelectionBorder);
}
}
private void AddDockHints()
{
if (_toDock == null)
return;
if (_toDock.RootWindow.Window != _dragSourceWindow)
_toDock.RootWindow.Window.MouseUp += OnMouseUp;
_dockHintDown = AddHintControl(new Float2(0.5f, 1));
_dockHintUp = AddHintControl(new Float2(0.5f, 0));
_dockHintLeft = AddHintControl(new Float2(0, 0.5f));
_dockHintRight = AddHintControl(new Float2(1, 0.5f));
_dockHintCenter = AddHintControl(new Float2(0.5f, 0.5f));
Control AddHintControl(Float2 pivot)
{
DragVisuals hintControl = _toDock.AddChild<DragVisuals>();
hintControl.Size = new Float2(HintControlSize);
hintControl.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
hintControl.Pivot = pivot;
hintControl.PivotRelative = true;
return hintControl;
}
}
private void RemoveDockHints()
{
if (_toDock == null)
return;
if (_toDock.RootWindow.Window != _dragSourceWindow)
_toDock.RootWindow.Window.MouseUp -= OnMouseUp;
_dockHintDown?.Parent.RemoveChild(_dockHintDown);
_dockHintUp?.Parent.RemoveChild(_dockHintUp);
_dockHintLeft?.Parent.RemoveChild(_dockHintLeft);
_dockHintRight?.Parent.RemoveChild(_dockHintRight);
_dockHintCenter?.Parent.RemoveChild(_dockHintCenter);
_dockHintDown = _dockHintUp = _dockHintLeft = _dockHintRight = _dockHintCenter = null;
}
private void UpdateRects(Float2 mousePos)
{
// Cache mouse position
_mouse = mousePos;
// Check intersection with any dock panel
DockPanel dockPanel = null;
if (_toMove.MasterPanel.HitTest(ref _mouse, _toMove, out var hitResults))
{
dockPanel = hitResults[0];
// Prefer panel which currently has focus
foreach (var hit in hitResults)
{
if (hit.RootWindow.Window.IsFocused)
{
dockPanel = hit;
break;
}
}
// Prefer panel in the same window we hit earlier
if (dockPanel?.RootWindow != _toDock?.RootWindow)
{
foreach (var hit in hitResults)
{
if (hit.RootWindow == _toDock?.RootWindow)
{
dockPanel = _toDock;
break;
}
}
}
}
if (dockPanel != _toDock)
{
RemoveDockHints();
_toDock = dockPanel;
AddDockHints();
// Make sure the all the dock hint areas are not under other windows
_toDock?.RootWindow.Window.BringToFront();
//_toDock?.RootWindow.Window.Focus();
// Make the dragged window transparent when dock hints are visible
_toMove.Window.Window.Opacity = _toDock == null ? 1.0f : DragWindowOpacity;
}
// Check dock state to use
bool showProxyHints = _toDock != null;
bool showBorderHints = showProxyHints;
bool showCenterHint = showProxyHints;
Control hoveredHintControl = null;
Float2 hoveredSizeOverride = Float2.Zero;
if (showProxyHints)
{
// If moved window has not only tabs but also child panels disable docking as tab
if (_toMove.ChildPanelsCount > 0)
showCenterHint = false;
// Disable docking windows with one or more dock panels inside
if (_toMove.ChildPanelsCount > 0)
showBorderHints = false;
// Get dock area
_rectDock = _toDock.DockAreaBounds;
// Cache dock rectangles
var size = _rectDock.Size / Platform.DpiScale;
var offset = _toDock.PointFromScreen(_rectDock.Location);
var borderMargin = 4.0f;
var hintWindowsSize = HintControlSize;
var hintWindowsSize2 = hintWindowsSize * 0.5f;
var hintPreviewSize = new Float2(Math.Max(HintControlSize * 2, size.X * 0.5f), Math.Max(HintControlSize * 2, size.Y * 0.5f));
var centerX = size.X * 0.5f;
var centerY = size.Y * 0.5f;
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
// Hit test, and calculate the approximation for filled area when hovered over the hint
DockState toSet = DockState.Float;
if (showBorderHints)
{
if (_rUpper.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockTop;
hoveredHintControl = _dockHintUp;
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
}
else if (_rBottom.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockBottom;
hoveredHintControl = _dockHintDown;
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
}
else if (_rLeft.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockLeft;
hoveredHintControl = _dockHintLeft;
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
}
else if (_rRight.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockRight;
hoveredHintControl = _dockHintRight;
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
}
}
if (showCenterHint && _rCenter.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockFill;
hoveredHintControl = _dockHintCenter;
hoveredSizeOverride = new Float2(size.X, size.Y);
}
_toSet = toSet;
}
else
{
_toSet = DockState.Float;
}
// Update sizes and opacity of hint controls
if (_toDock != null)
{
if (hoveredHintControl != _dockHintDown)
{
_dockHintDown.Size = new Float2(HintControlSize);
_dockHintDown.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
}
if (hoveredHintControl != _dockHintLeft)
{
_dockHintLeft.Size = new Float2(HintControlSize);
_dockHintLeft.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
}
if (hoveredHintControl != _dockHintRight)
{
_dockHintRight.Size = new Float2(HintControlSize);
_dockHintRight.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
}
if (hoveredHintControl != _dockHintUp)
{
_dockHintUp.Size = new Float2(HintControlSize);
_dockHintUp.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
}
if (hoveredHintControl != _dockHintCenter)
{
_dockHintCenter.Size = new Float2(HintControlSize);
_dockHintCenter.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
}
if (_toSet != DockState.Float)
{
if (hoveredHintControl != null)
{
hoveredHintControl.BackgroundColor = Style.Current.Selection.AlphaMultiplied(1.0f);
hoveredHintControl.Size = hoveredSizeOverride;
}
}
}
// Update hint controls visibility and location
if (showProxyHints)
{
if (hoveredHintControl != _dockHintDown)
_dockHintDown.Location = _rBottom.Location;
if (hoveredHintControl != _dockHintLeft)
_dockHintLeft.Location = _rLeft.Location;
if (hoveredHintControl != _dockHintRight)
_dockHintRight.Location = _rRight.Location;
if (hoveredHintControl != _dockHintUp)
_dockHintUp.Location = _rUpper.Location;
if (hoveredHintControl != _dockHintCenter)
_dockHintCenter.Location = _rCenter.Location;
_dockHintDown.Visible = showProxyHints & showBorderHints;
_dockHintLeft.Visible = showProxyHints & showBorderHints;
_dockHintRight.Visible = showProxyHints & showBorderHints;
_dockHintUp.Visible = showProxyHints & showBorderHints;
_dockHintCenter.Visible = showProxyHints & showCenterHint;
}
}
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
{
if (button == MouseButton.Left)
Dispose();
}
private void OnUpdate()
{
var mousePos = Platform.MousePosition;
if (_mouse != mousePos)
OnMouseMove(mousePos);
}
private void OnMouseMove(Float2 mousePos)
{
if (_dragSourceWindow != null)
_toMove.Window.Window.Position = mousePos - _dragOffset;
UpdateRects(mousePos);
}
}
}

View File

@@ -266,6 +266,7 @@ namespace FlaxEditor.GUI.Input
return base.OnMouseDown(location, button); return base.OnMouseDown(location, button);
} }
#if !PLATFORM_SDL
/// <inheritdoc /> /// <inheritdoc />
public override void OnMouseMove(Float2 location) public override void OnMouseMove(Float2 location)
{ {
@@ -292,13 +293,45 @@ namespace FlaxEditor.GUI.Input
base.OnMouseMove(location); base.OnMouseMove(location);
} }
#else
/// <inheritdoc />
public override void OnMouseMoveRelative(Float2 mouseMotion)
{
var location = Root.TrackingMouseOffset;
if (_isSliding)
{
// Update sliding
ApplySliding(Root.TrackingMouseOffset.X * _slideSpeed);
return;
}
// Update cursor type so user knows they can slide value
if (CanUseSliding && SlideRect.Contains(location) && !_isSliding)
{
Cursor = CursorType.SizeWE;
_cursorChanged = true;
}
else if (_cursorChanged && !_isSliding)
{
Cursor = CursorType.Default;
_cursorChanged = false;
}
base.OnMouseMoveRelative(mouseMotion);
}
#endif
/// <inheritdoc /> /// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button) public override bool OnMouseUp(Float2 location, MouseButton button)
{ {
if (button == MouseButton.Left && _isSliding) if (button == MouseButton.Left && _isSliding)
{ {
#if !PLATFORM_SDL
// End sliding and return mouse to original location // End sliding and return mouse to original location
RootWindow.MousePosition = _mouseClickedPosition; RootWindow.MousePosition = _mouseClickedPosition;
#endif
EndSliding(); EndSliding();
return true; return true;
} }

View File

@@ -12,16 +12,6 @@ namespace FlaxEditor.GUI
/// <seealso cref="FlaxEngine.GUI.ContainerControl" /> /// <seealso cref="FlaxEngine.GUI.ContainerControl" />
public sealed class MainMenu : ContainerControl public sealed class MainMenu : ContainerControl
{ {
#if PLATFORM_WINDOWS
private bool _useCustomWindowSystem;
private Image _icon;
private Label _title;
private Button _closeButton;
private Button _minimizeButton;
private Button _maximizeButton;
private LocalizedString _charChromeRestore, _charChromeMaximize;
private Window _window;
#endif
private MainMenuButton _selected; private MainMenuButton _selected;
/// <summary> /// <summary>
@@ -60,200 +50,12 @@ namespace FlaxEditor.GUI
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="MainMenu"/> class. /// Initializes a new instance of the <see cref="MainMenu"/> class.
/// </summary> /// </summary>
/// <param name="mainWindow">The main window.</param> public MainMenu()
public MainMenu(RootControl mainWindow)
: base(0, 0, 0, 20) : base(0, 0, 0, 20)
{ {
AutoFocus = false; AutoFocus = false;
AnchorPreset = AnchorPresets.HorizontalStretchTop; AnchorPreset = AnchorPresets.HorizontalStretchTop;
BackgroundColor = Style.Current.LightBackground;
#if PLATFORM_WINDOWS
_useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
if (_useCustomWindowSystem)
{
BackgroundColor = Style.Current.LightBackground;
Height = 28;
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
Font iconFont = windowIconsFont?.CreateFont(9);
_window = mainWindow.RootWindow.Window;
_window.HitTest += OnHitTest;
_window.Closed += OnWindowClosed;
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
_icon = new Image
{
Margin = new Margin(6, 6, 6, 6),
Brush = new TextureBrush(windowIcon),
Color = Style.Current.Foreground,
KeepAspectRatio = false,
TooltipText = string.Format("{0}\nVersion {1}\nConfiguration {3}\nGraphics {2}", _window.Title, Globals.EngineVersion, GPUDevice.Instance.RendererType, configuration),
Parent = this,
};
_title = new Label(0, 0, Width, Height)
{
Text = _window.Title,
HorizontalAlignment = TextAlignment.Center,
VerticalAlignment = TextAlignment.Center,
ClipText = true,
TextColor = Style.Current.ForegroundGrey,
TextColorHighlighted = Style.Current.ForegroundGrey,
Parent = this,
};
_closeButton = new Button
{
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
Font = new FontReference(iconFont),
BackgroundColor = Color.Transparent,
BorderColor = Color.Transparent,
BorderColorHighlighted = Color.Transparent,
BorderColorSelected = Color.Transparent,
TextColor = Style.Current.Foreground,
Width = 46,
BackgroundColorHighlighted = Color.Red,
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
Parent = this,
};
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
_minimizeButton = new Button
{
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
Font = new FontReference(iconFont),
BackgroundColor = Color.Transparent,
BorderColor = Color.Transparent,
BorderColorHighlighted = Color.Transparent,
BorderColorSelected = Color.Transparent,
TextColor = Style.Current.Foreground,
Width = 46,
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
Parent = this,
};
_minimizeButton.Clicked += () => _window.Minimize();
_maximizeButton = new Button
{
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
Font = new FontReference(iconFont),
BackgroundColor = Color.Transparent,
BorderColor = Color.Transparent,
BorderColorHighlighted = Color.Transparent,
BorderColorSelected = Color.Transparent,
TextColor = Style.Current.Foreground,
Width = 46,
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
Parent = this,
};
_maximizeButton.Clicked += () =>
{
if (_window.IsMaximized)
_window.Restore();
else
_window.Maximize();
};
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
}
else
#endif
{
BackgroundColor = Style.Current.LightBackground;
}
}
#if PLATFORM_WINDOWS
/// <inheritdoc />
public override void Update(float deltaTime)
{
base.Update(deltaTime);
if (_maximizeButton != null)
{
_maximizeButton.Text = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
}
}
private void OnWindowClosed()
{
if (_window != null)
{
_window.HitTest = null;
_window = null;
}
}
private WindowHitCodes OnHitTest(ref Float2 mouse)
{
var dpiScale = _window.DpiScale;
if (_window.IsMinimized)
return WindowHitCodes.NoWhere;
if (!_window.IsMaximized)
{
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
var winSize = _window.Size;
// Distance from which the mouse is considered to be on the border/corner
float distance = 5.0f * dpiScale;
if (pos.Y > winSize.Y - distance && pos.X < distance)
return WindowHitCodes.BottomLeft;
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
return WindowHitCodes.BottomRight;
if (pos.Y < distance && pos.X < distance)
return WindowHitCodes.TopLeft;
if (pos.Y < distance && pos.X > winSize.X - distance)
return WindowHitCodes.TopRight;
if (pos.X > winSize.X - distance)
return WindowHitCodes.Right;
if (pos.X < distance)
return WindowHitCodes.Left;
if (pos.Y < distance)
return WindowHitCodes.Top;
if (pos.Y > winSize.Y - distance)
return WindowHitCodes.Bottom;
}
var mousePos = PointFromScreen(mouse * dpiScale);
var controlUnderMouse = GetChildAt(mousePos);
var isMouseOverSth = controlUnderMouse != null && controlUnderMouse != _title;
var rb = GetRightButton();
if (rb != null && _minimizeButton != null && new Rectangle(rb.UpperRight, _minimizeButton.BottomLeft - rb.UpperRight).Contains(ref mousePos) && !isMouseOverSth)
return WindowHitCodes.Caption;
return WindowHitCodes.Client;
}
#endif
/// <summary>
/// Return the rightmost button.
/// </summary>
/// <returns>Rightmost button, null if there is no <see cref="MainMenuButton"/></returns>
private MainMenuButton GetRightButton()
{
MainMenuButton b = null;
foreach (var control in Children)
{
if (b == null && control is MainMenuButton)
b = (MainMenuButton)control;
if (control is MainMenuButton && control.Right > b.Right)
b = (MainMenuButton)control;
}
return b;
} }
/// <summary> /// <summary>
@@ -298,26 +100,6 @@ namespace FlaxEditor.GUI
return result; return result;
} }
/// <inheritdoc />
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
{
if (base.OnMouseDoubleClick(location, button))
return true;
#if PLATFORM_WINDOWS
var child = GetChildAtRecursive(location);
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
{
if (_window.IsMaximized)
_window.Restore();
else
_window.Maximize();
}
#endif
return true;
}
/// <inheritdoc /> /// <inheritdoc />
public override bool OnKeyDown(KeyboardKeys key) public override bool OnKeyDown(KeyboardKeys key)
{ {
@@ -333,16 +115,8 @@ namespace FlaxEditor.GUI
protected override void PerformLayoutAfterChildren() protected override void PerformLayoutAfterChildren()
{ {
float x = 0; float x = 0;
WindowDecorations decorations = Parent.GetChild<WindowDecorations>();
#if PLATFORM_WINDOWS x += decorations?.Icon?.Width ?? 0;
if (_useCustomWindowSystem)
{
// Icon
_icon.X = x;
_icon.Size = new Float2(Height);
x += _icon.Width;
}
#endif
// Arrange controls // Arrange controls
MainMenuButton rightMostButton = null; MainMenuButton rightMostButton = null;
@@ -361,37 +135,21 @@ namespace FlaxEditor.GUI
x += b.Width; x += b.Width;
} }
} }
#if PLATFORM_WINDOWS // Fill the right side if title and buttons are not present
if (_useCustomWindowSystem) if (decorations?.Title == null)
{ Width = Parent.Width;
// Buttons else
_closeButton.Height = Height; Width = x;
_closeButton.X = Width - _closeButton.Width;
_maximizeButton.Height = Height;
_maximizeButton.X = _closeButton.X - _maximizeButton.Width;
_minimizeButton.Height = Height;
_minimizeButton.X = _maximizeButton.X - _minimizeButton.Width;
// Title
_title.Bounds = new Rectangle(x + 2, 0, _minimizeButton.Left - x - 4, Height);
//_title.Text = _title.Width < 300.0f ? Editor.Instance.ProjectInfo.Name : _window.Title;
}
#endif
} }
#if PLATFORM_WINDOWS
/// <inheritdoc /> /// <inheritdoc />
public override void OnDestroy() public override void OnDestroy()
{ {
base.OnDestroy(); base.OnDestroy();
if (_window != null) if (_selected != null)
{ Selected = null;
_window.Closed -= OnWindowClosed;
OnWindowClosed();
}
} }
#endif
} }
} }

View File

@@ -42,14 +42,12 @@ namespace FlaxEditor.GUI
Text = text; Text = text;
var style = Style.Current; var style = Style.Current;
#if PLATFORM_WINDOWS if (!Utilities.Utils.UseCustomWindowDecorations())
if (Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
{ {
BackgroundColorMouseOver = style.BackgroundHighlighted; BackgroundColorMouseOver = style.BackgroundHighlighted;
BackgroundColorMouseOverOpened = style.Background; BackgroundColorMouseOverOpened = style.Background;
} }
else else
#endif
{ {
BackgroundColorMouseOver = BackgroundColorMouseOverOpened = style.LightBackground * 1.3f; BackgroundColorMouseOver = BackgroundColorMouseOverOpened = style.LightBackground * 1.3f;
} }

View File

@@ -0,0 +1,342 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using FlaxEditor.GUI.Docking;
using FlaxEditor.Options;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI;
/// <summary>
/// Represents the title bar of the window with buttons.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
public class WindowDecorations : ContainerControl
{
private Image _icon;
private Label _title;
private Button _closeButton;
private Button _minimizeButton;
private Button _maximizeButton;
private LocalizedString _charChromeRestore, _charChromeMaximize;
private Window _window;
/// <summary>
/// The title label in the title bar.
/// </summary>
public Label Title => _title;
/// <summary>
/// The icon used in the title bar.
/// </summary>
public Image Icon => _icon;
/// <summary>
/// The tooltip shown when hovering over the icon.
/// </summary>
public string IconTooltipText
{
get => _icon?.TooltipText ?? null;
set
{
if (_icon != null)
_icon.TooltipText = value;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="WindowDecorations"/> class.
/// </summary>
/// <param name="window">The window.</param>
/// <param name="iconOnly">When set, omit drawing title and buttons.</param>
public WindowDecorations(RootControl window, bool iconOnly = false)
: base(0, 0, 0, 20)
{
_window = window.RootWindow.Window;
AutoFocus = false;
AnchorPreset = AnchorPresets.HorizontalStretchTop;
BackgroundColor = Color.Transparent;
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
_icon = new Image
{
Margin = new Margin(4, 4, 4, 4),
Brush = new TextureBrush(windowIcon),
Color = Style.Current.Foreground,
BackgroundColor = Style.Current.LightBackground,
KeepAspectRatio = false,
Parent = this,
};
if (!iconOnly)
{
_icon.Margin = new Margin(6, 6, 6, 6);
Height = 28;
_window.HitTest += OnHitTest;
_window.Closed += OnWindowClosed;
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
Font iconFont = windowIconsFont?.CreateFont(9);
_title = new Label(0, 0, Width, Height)
{
Text = _window.Title,
HorizontalAlignment = TextAlignment.Center,
VerticalAlignment = TextAlignment.Center,
ClipText = true,
TextColor = Style.Current.ForegroundGrey,
TextColorHighlighted = Style.Current.ForegroundGrey,
BackgroundColor = Style.Current.LightBackground,
Parent = this,
};
_closeButton = new Button
{
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
Font = new FontReference(iconFont),
BackgroundColor = Style.Current.LightBackground,
BorderColor = Color.Transparent,
BorderColorHighlighted = Color.Transparent,
BorderColorSelected = Color.Transparent,
TextColor = Style.Current.Foreground,
Width = 46,
BackgroundColorHighlighted = Color.Red,
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
Parent = this,
};
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
_minimizeButton = new Button
{
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
Font = new FontReference(iconFont),
BackgroundColor = Style.Current.LightBackground,
BorderColor = Color.Transparent,
BorderColorHighlighted = Color.Transparent,
BorderColorSelected = Color.Transparent,
TextColor = Style.Current.Foreground,
Width = 46,
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
Parent = this,
};
_minimizeButton.Clicked += () => _window.Minimize();
_maximizeButton = new Button
{
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
Font = new FontReference(iconFont),
BackgroundColor = Style.Current.LightBackground,
BorderColor = Color.Transparent,
BorderColorHighlighted = Color.Transparent,
BorderColorSelected = Color.Transparent,
TextColor = Style.Current.Foreground,
Width = 46,
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
Parent = this,
};
_maximizeButton.Clicked += () =>
{
if (_window.IsMaximized)
_window.Restore();
else
_window.Maximize();
};
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
}
}
/// <inheritdoc />
public override void Update(float deltaTime)
{
base.Update(deltaTime);
if (_maximizeButton != null)
{
var maximizeText = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
if (_maximizeButton.Text != maximizeText)
_maximizeButton.Text = maximizeText;
}
}
private void OnWindowClosed()
{
if (_window != null)
{
_window.HitTest -= OnHitTest;
_window = null;
}
}
/// <summary>
/// Perform hit test on the window.
/// </summary>
/// <param name="mouse">The mouse position</param>
/// <returns>The hit code for given position.</returns>
protected virtual WindowHitCodes OnHitTest(ref Float2 mouse)
{
if (_window.IsMinimized)
return WindowHitCodes.NoWhere;
var dpiScale = _window.DpiScale;
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
if (!_window.IsMaximized)
{
var winSize = _window.Size;
// Distance from which the mouse is considered to be on the border/corner
float distance = 5.0f * dpiScale;
if (pos.Y > winSize.Y - distance && pos.X < distance)
return WindowHitCodes.BottomLeft;
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
return WindowHitCodes.BottomRight;
if (pos.Y < distance && pos.X < distance)
return WindowHitCodes.TopLeft;
if (pos.Y < distance && pos.X > winSize.X - distance)
return WindowHitCodes.TopRight;
if (pos.X > winSize.X - distance)
return WindowHitCodes.Right;
if (pos.X < distance)
return WindowHitCodes.Left;
if (pos.Y < distance)
return WindowHitCodes.Top;
if (pos.Y > winSize.Y - distance)
return WindowHitCodes.Bottom;
}
var controlUnderMouse = GetChildAt(pos, control => control != _title);
if (_title.Bounds.Contains(pos) && controlUnderMouse == null)
return WindowHitCodes.Caption;
return WindowHitCodes.Client;
}
/// <inheritdoc />
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
{
// These may not work with main window due to SDL not passing mouse events
// when interacting with hit tests on caption area...
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
{
if (_window.IsMaximized)
_window.Restore();
else
_window.Maximize();
return true;
}
else if (Icon.Bounds.Contains(location) && button == MouseButton.Left)
{
_window.Close(ClosingReason.User);
return true;
}
return base.OnMouseDoubleClick(location, button);
}
/// <inheritdoc />
protected override void PerformLayoutAfterChildren()
{
// Calculate extents for title bounds area excluding the icon and main menu area
float x = 0;
// Icon
if (_icon != null)
{
_icon.X = x;
_icon.Size = new Float2(Height);
x += _icon.Width;
}
// Main menu if present
if (Parent.GetChild<MainMenu>() is MainMenu mainMenu)
{
for (int i = 0; i < mainMenu.Children.Count; i++)
{
var c = mainMenu.Children[i];
if (c is MainMenuButton b && c.Visible)
{
b.Bounds = new Rectangle(x, 0, b.Width, Height);
x += b.Width;
}
}
}
// Buttons
float rightMostButtonX = Width;
if (_closeButton != null)
{
_closeButton.Height = Height;
_closeButton.X = rightMostButtonX - _closeButton.Width;
rightMostButtonX = _closeButton.X;
}
if (_maximizeButton != null)
{
_maximizeButton.Height = Height;
_maximizeButton.X = rightMostButtonX - _maximizeButton.Width;
rightMostButtonX = _maximizeButton.X;
}
if (_minimizeButton != null)
{
_minimizeButton.Height = Height;
_minimizeButton.X = rightMostButtonX - _minimizeButton.Width;
rightMostButtonX = _minimizeButton.X;
}
// Title
if (_title != null)
{
_title.Text = _window.Title;
_title.Bounds = new Rectangle(x, 0, rightMostButtonX - x, Height);
}
}
/// <inheritdoc />
public override void Draw()
{
base.Draw();
DrawBorders();
}
/// <summary>
/// Draw borders around the window.
/// </summary>
public virtual void DrawBorders()
{
var win = RootWindow.Window;
if (win.IsMaximized)
return;
if (Editor.Instance.UI.StatusBar == null)
return;
const float thickness = 1.0f;
Color color = Editor.Instance.UI.StatusBar.StatusColor;
Rectangle rect = new Rectangle(thickness * 0.5f, thickness * 0.5f, Parent.Width - thickness, Parent.Height - thickness);
Render2D.DrawRectangle(rect, color);
}
/// <inheritdoc />
public override void OnDestroy()
{
base.OnDestroy();
if (_window != null)
{
_window.Closed -= OnWindowClosed;
OnWindowClosed();
}
}
}

View File

@@ -402,10 +402,11 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
break; break;
} }
} }
WindowsManager::WindowsLocker.Unlock();
for (const auto& e : inputEvents) for (const auto& e : inputEvents)
{ {
auto window = e.Target ? e.Target : defaultWindow; auto window = e.Target ? e.Target : defaultWindow;
if (!window) if (!window || window->IsClosed())
continue; continue;
switch (e.Type) switch (e.Type)
{ {
@@ -435,12 +436,14 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
case InputDevice::EventType::MouseMove: case InputDevice::EventType::MouseMove:
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position)); window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
break; break;
case InputDevice::EventType::MouseMoveRelative:
window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative);
break;
case InputDevice::EventType::MouseLeave: case InputDevice::EventType::MouseLeave:
window->OnMouseLeave(); window->OnMouseLeave();
break; break;
} }
} }
WindowsManager::WindowsLocker.Unlock();
} }
WindowsManager::WindowsLocker.Lock(); WindowsManager::WindowsLocker.Lock();
Array<Window*, InlinedAllocation<32>> windows; Array<Window*, InlinedAllocation<32>> windows;

View File

@@ -13,6 +13,7 @@
#include "Engine/Scripting/Internal/MainThreadManagedInvokeAction.h" #include "Engine/Scripting/Internal/MainThreadManagedInvokeAction.h"
#include "Engine/Content/Assets/VisualScript.h" #include "Engine/Content/Assets/VisualScript.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Level/Actor.h"
#include "Engine/CSG/CSGBuilder.h" #include "Engine/CSG/CSGBuilder.h"
#include "Engine/Engine/CommandLine.h" #include "Engine/Engine/CommandLine.h"
#include "Engine/Renderer/ProbesRenderer.h" #include "Engine/Renderer/ProbesRenderer.h"
@@ -74,7 +75,7 @@ void OnLightmapsBuildFinished(bool failed)
OnLightmapsBake(ShadowsOfMordor::BuildProgressStep::GenerateLightmapCharts, 0, 0, false); OnLightmapsBake(ShadowsOfMordor::BuildProgressStep::GenerateLightmapCharts, 0, 0, false);
} }
void OnBakeEvent(bool started, const ProbesRenderer::Entry& e) void OnBakeEvent(bool started, Actor* e)
{ {
if (Internal_EnvProbeBake == nullptr) if (Internal_EnvProbeBake == nullptr)
{ {
@@ -82,7 +83,7 @@ void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
ASSERT(Internal_EnvProbeBake); ASSERT(Internal_EnvProbeBake);
} }
MObject* probeObj = e.Actor ? e.Actor->GetManagedInstance() : nullptr; MObject* probeObj = e ? e->GetManagedInstance() : nullptr;
MainThreadManagedInvokeAction::ParamsBuilder params; MainThreadManagedInvokeAction::ParamsBuilder params;
params.AddParam(started); params.AddParam(started);
@@ -90,12 +91,12 @@ void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
MainThreadManagedInvokeAction::Invoke(Internal_EnvProbeBake, params); MainThreadManagedInvokeAction::Invoke(Internal_EnvProbeBake, params);
} }
void OnRegisterBake(const ProbesRenderer::Entry& e) void OnRegisterBake(Actor* e)
{ {
OnBakeEvent(true, e); OnBakeEvent(true, e);
} }
void OnFinishBake(const ProbesRenderer::Entry& e) void OnFinishBake(Actor* e)
{ {
OnBakeEvent(false, e); OnBakeEvent(false, e);
} }
@@ -156,7 +157,9 @@ ManagedEditor::ManagedEditor()
lightmapsBuilder->OnBuildProgress.Bind<OnLightmapsBuildProgress>(); lightmapsBuilder->OnBuildProgress.Bind<OnLightmapsBuildProgress>();
lightmapsBuilder->OnBuildFinished.Bind<OnLightmapsBuildFinished>(); lightmapsBuilder->OnBuildFinished.Bind<OnLightmapsBuildFinished>();
CSG::Builder::OnBrushModified.Bind<OnBrushModified>(); CSG::Builder::OnBrushModified.Bind<OnBrushModified>();
#if LOG_ENABLE
Log::Logger::OnMessage.Bind<OnLogMessage>(); Log::Logger::OnMessage.Bind<OnLogMessage>();
#endif
VisualScripting::DebugFlow.Bind<OnVisualScriptingDebugFlow>(); VisualScripting::DebugFlow.Bind<OnVisualScriptingDebugFlow>();
} }
@@ -172,7 +175,9 @@ ManagedEditor::~ManagedEditor()
lightmapsBuilder->OnBuildProgress.Unbind<OnLightmapsBuildProgress>(); lightmapsBuilder->OnBuildProgress.Unbind<OnLightmapsBuildProgress>();
lightmapsBuilder->OnBuildFinished.Unbind<OnLightmapsBuildFinished>(); lightmapsBuilder->OnBuildFinished.Unbind<OnLightmapsBuildFinished>();
CSG::Builder::OnBrushModified.Unbind<OnBrushModified>(); CSG::Builder::OnBrushModified.Unbind<OnBrushModified>();
#if LOG_ENABLE
Log::Logger::OnMessage.Unbind<OnLogMessage>(); Log::Logger::OnMessage.Unbind<OnLogMessage>();
#endif
VisualScripting::DebugFlow.Unbind<OnVisualScriptingDebugFlow>(); VisualScripting::DebugFlow.Unbind<OnVisualScriptingDebugFlow>();
} }

View File

@@ -1013,7 +1013,7 @@ namespace FlaxEditor.Modules
ContentItem item; ContentItem item;
if (path.EndsWith(".cs")) if (path.EndsWith(".cs"))
item = new CSharpScriptItem(path); item = new CSharpScriptItem(path);
else if (path.EndsWith(".cpp") || path.EndsWith(".h")) else if (path.EndsWith(".cpp") || path.EndsWith(".h") || path.EndsWith(".c") || path.EndsWith(".hpp"))
item = new CppScriptItem(path); item = new CppScriptItem(path);
else if (path.EndsWith(".shader") || path.EndsWith(".hlsl")) else if (path.EndsWith(".shader") || path.EndsWith(".hlsl"))
item = new ShaderSourceItem(path); item = new ShaderSourceItem(path);

View File

@@ -222,7 +222,7 @@ namespace FlaxEditor.Modules
outputExtension = extension; outputExtension = extension;
// Check if can place source files here // Check if can place source files here
if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h")) if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h" || extension == ".c" || extension == ".hpp"))
{ {
// Error // Error
Editor.LogWarning(string.Format("Cannot import \'{0}\' to \'{1}\'. The target directory cannot have scripts.", inputPath, targetLocation.Node.Path)); Editor.LogWarning(string.Format("Cannot import \'{0}\' to \'{1}\'. The target directory cannot have scripts.", inputPath, targetLocation.Node.Path));

View File

@@ -711,7 +711,11 @@ namespace FlaxEditor.Modules
private void OnActorChildNodesDispose(ActorNode node) private void OnActorChildNodesDispose(ActorNode node)
{ {
if (Selection.Count == 0)
return;
// TODO: cache if selection contains any actor child node and skip this loop if no need to iterate // TODO: cache if selection contains any actor child node and skip this loop if no need to iterate
// TODO: or build a hash set with selected nodes for quick O(1) checks (cached until selection changes)
// Deselect child nodes // Deselect child nodes
for (int i = 0; i < node.ChildNodes.Count; i++) for (int i = 0; i < node.ChildNodes.Count; i++)

View File

@@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph;
using FlaxEditor.SceneGraph.Actors; using FlaxEditor.SceneGraph.Actors;
using FlaxEngine; using FlaxEngine;
@@ -658,6 +659,48 @@ namespace FlaxEditor.Modules
//node?.TreeNode.OnActiveChanged(); //node?.TreeNode.OnActiveChanged();
} }
private void OnActorDestroyChildren(Actor actor)
{
// Instead of doing OnActorParentChanged for every child lets remove all of them at once from that actor
ActorNode node = GetActorNode(actor);
if (node != null)
{
if (Editor.SceneEditing.HasSthSelected)
{
// Clear selection if one of the removed actors is selected
var selection = new HashSet<Actor>();
foreach (var e in Editor.SceneEditing.Selection)
{
if (e is ActorNode q && q.Actor)
selection.Add(q.Actor);
}
var count = actor.ChildrenCount;
for (int i = 0; i < count; i++)
{
var child = actor.GetChild(i);
if (selection.Contains(child))
{
Editor.SceneEditing.Deselect();
break;
}
}
}
// Remove all child nodes (upfront remove all nodes to run faster)
for (int i = 0; i < node.ChildNodes.Count; i++)
{
if (node.ChildNodes[i] is ActorNode child)
child.parentNode = null;
}
node.TreeNode.DisposeChildren();
for (int i = 0; i < node.ChildNodes.Count; i++)
{
node.ChildNodes[i].Dispose();
}
node.ChildNodes.Clear();
}
}
/// <summary> /// <summary>
/// Gets the actor node. /// Gets the actor node.
/// </summary> /// </summary>
@@ -709,6 +752,7 @@ namespace FlaxEditor.Modules
Level.ActorOrderInParentChanged += OnActorOrderInParentChanged; Level.ActorOrderInParentChanged += OnActorOrderInParentChanged;
Level.ActorNameChanged += OnActorNameChanged; Level.ActorNameChanged += OnActorNameChanged;
Level.ActorActiveChanged += OnActorActiveChanged; Level.ActorActiveChanged += OnActorActiveChanged;
Level.ActorDestroyChildren += OnActorDestroyChildren;
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -726,6 +770,7 @@ namespace FlaxEditor.Modules
Level.ActorOrderInParentChanged -= OnActorOrderInParentChanged; Level.ActorOrderInParentChanged -= OnActorOrderInParentChanged;
Level.ActorNameChanged -= OnActorNameChanged; Level.ActorNameChanged -= OnActorNameChanged;
Level.ActorActiveChanged -= OnActorActiveChanged; Level.ActorActiveChanged -= OnActorActiveChanged;
Level.ActorDestroyChildren -= OnActorDestroyChildren;
// Cleanup graph // Cleanup graph
Root.Dispose(); Root.Dispose();

View File

@@ -267,7 +267,7 @@ namespace FlaxEditor.Modules
} }
/// <inheritdoc /> /// <inheritdoc />
public override void OnPlayBegin() public override void OnPlayBeginning()
{ {
Editor.Windows.FlashMainWindow(); Editor.Windows.FlashMainWindow();

View File

@@ -16,7 +16,6 @@ using FlaxEditor.Windows;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
using FlaxEngine.Json; using FlaxEngine.Json;
using DockHintWindow = FlaxEditor.GUI.Docking.DockHintWindow;
using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel; using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel;
using FlaxEditor.Content.Settings; using FlaxEditor.Content.Settings;
using FlaxEditor.Options; using FlaxEditor.Options;
@@ -29,6 +28,40 @@ namespace FlaxEditor.Modules
/// <seealso cref="FlaxEditor.Modules.EditorModule" /> /// <seealso cref="FlaxEditor.Modules.EditorModule" />
public sealed class UIModule : EditorModule public sealed class UIModule : EditorModule
{ {
private class MainWindowDecorations : WindowDecorations
{
public MainWindowDecorations(RootControl window, bool iconOnly)
: base(window, iconOnly)
{
}
/// <inheritdoc />
public override bool OnKeyDown(KeyboardKeys key)
{
if (base.OnKeyDown(key))
return true;
// Fallback to the edit window for shortcuts
var editor = Editor.Instance;
return editor.Windows.EditWin.InputActions.Process(editor, this, key);
}
/// <inheritdoc />
public override void DrawBorders()
{
// Draw main window borders if using a custom style
var win = RootWindow.Window;
if (win.IsMaximized)
return;
var color = Editor.Instance.UI.StatusBar.StatusColor;
var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight);
Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color);
Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color);
Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color);
}
}
private Label _progressLabel; private Label _progressLabel;
private ProgressBar _progressBar; private ProgressBar _progressBar;
private Button _outputLogButton; private Button _outputLogButton;
@@ -97,6 +130,11 @@ namespace FlaxEditor.Modules
/// </summary> /// </summary>
public MainMenu MainMenu; public MainMenu MainMenu;
/// <summary>
/// The window decorations (title bar with buttons)
/// </summary>
public WindowDecorations WindowDecorations;
/// <summary> /// <summary>
/// The tool strip control. /// The tool strip control.
/// </summary> /// </summary>
@@ -377,19 +415,11 @@ namespace FlaxEditor.Modules
InitToolstrip(mainWindow); InitToolstrip(mainWindow);
InitStatusBar(mainWindow); InitStatusBar(mainWindow);
InitDockPanel(mainWindow); InitDockPanel(mainWindow);
InitWindowDecorations(mainWindow);
Editor.Options.OptionsChanged += OnOptionsChanged; Editor.Options.OptionsChanged += OnOptionsChanged;
// Add dummy control for drawing the main window borders if using a custom style mainWindow.PerformLayout(true);
#if PLATFORM_WINDOWS
if (!Editor.Options.Options.Interface.UseNativeWindowSystem)
#endif
{
mainWindow.AddChild(new CustomWindowBorderControl
{
Size = Float2.Zero,
});
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -410,23 +440,6 @@ namespace FlaxEditor.Modules
} }
} }
private class CustomWindowBorderControl : Control
{
/// <inheritdoc />
public override void Draw()
{
var win = RootWindow.Window;
if (win.IsMaximized)
return;
var color = Editor.Instance.UI.StatusBar.StatusColor;
var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight);
Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color);
Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color);
Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color);
}
}
/// <inheritdoc /> /// <inheritdoc />
public override void OnEndInit() public override void OnEndInit()
{ {
@@ -458,13 +471,6 @@ namespace FlaxEditor.Modules
UpdateToolstrip(); UpdateToolstrip();
} }
/// <inheritdoc />
public override void OnExit()
{
// Cleanup dock panel hint proxy windows (Flax will destroy them by var but it's better to clear them earlier)
DockHintWindow.Proxy.Dispose();
}
private IColorPickerDialog ShowPickColorDialog(Control targetControl, Color initialValue, ColorValueBox.ColorPickerEvent colorChanged, ColorValueBox.ColorPickerClosedEvent pickerClosed, bool useDynamicEditing) private IColorPickerDialog ShowPickColorDialog(Control targetControl, Color initialValue, ColorValueBox.ColorPickerEvent colorChanged, ColorValueBox.ColorPickerClosedEvent pickerClosed, bool useDynamicEditing)
{ {
var dialog = new ColorPickerDialog(initialValue, colorChanged, pickerClosed, useDynamicEditing); var dialog = new ColorPickerDialog(initialValue, colorChanged, pickerClosed, useDynamicEditing);
@@ -518,10 +524,12 @@ namespace FlaxEditor.Modules
private void InitMainMenu(RootControl mainWindow) private void InitMainMenu(RootControl mainWindow)
{ {
MainMenu = new MainMenu(mainWindow) MainMenu = new MainMenu()
{ {
Parent = mainWindow Parent = mainWindow
}; };
if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
MainMenu.Height = 28;
var inputOptions = Editor.Options.Options.Input; var inputOptions = Editor.Options.Options.Input;
@@ -661,6 +669,20 @@ namespace FlaxEditor.Modules
cm.AddButton("Information about Flax", () => new AboutDialog().Show()); cm.AddButton("Information about Flax", () => new AboutDialog().Show());
} }
private void InitWindowDecorations(RootControl mainWindow)
{
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
var driver = Platform.DisplayServer;
if (!string.IsNullOrEmpty(driver))
driver = $" ({driver})";
WindowDecorations = new MainWindowDecorations(mainWindow, !Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
{
Parent = mainWindow,
IconTooltipText = $"{mainWindow.RootWindow.Title}\nVersion {Globals.EngineVersion}\nConfiguration {configuration}\nGraphics {GPUDevice.Instance.RendererType}{driver}",
};
}
private void OnOptionsChanged(EditorOptions options) private void OnOptionsChanged(EditorOptions options)
{ {
var inputOptions = options.Input; var inputOptions = options.Input;
@@ -1070,6 +1092,7 @@ namespace FlaxEditor.Modules
{ {
// Clear UI references (GUI cannot be used after window closing) // Clear UI references (GUI cannot be used after window closing)
MainMenu = null; MainMenu = null;
WindowDecorations = null;
ToolStrip = null; ToolStrip = null;
MasterPanel = null; MasterPanel = null;
StatusBar = null; StatusBar = null;

View File

@@ -10,7 +10,6 @@ using System.Text;
using System.Xml; using System.Xml;
using FlaxEditor.Content; using FlaxEditor.Content;
using FlaxEditor.GUI.Dialogs; using FlaxEditor.GUI.Dialogs;
using FlaxEditor.GUI.Docking;
using FlaxEditor.Windows; using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets; using FlaxEditor.Windows.Assets;
using FlaxEditor.Windows.Profiler; using FlaxEditor.Windows.Profiler;
@@ -758,17 +757,19 @@ namespace FlaxEditor.Modules
var settings = CreateWindowSettings.Default; var settings = CreateWindowSettings.Default;
settings.Title = "Flax Editor"; settings.Title = "Flax Editor";
settings.Size = Platform.DesktopSize * 0.75f; settings.Size = Platform.DesktopSize * 0.75f;
settings.MinimumSize = new Float2(200, 150);
settings.StartPosition = WindowStartPosition.CenterScreen; settings.StartPosition = WindowStartPosition.CenterScreen;
settings.ShowAfterFirstPaint = true; settings.ShowAfterFirstPaint = true;
#if PLATFORM_WINDOWS
if (!Editor.Instance.Options.Options.Interface.UseNativeWindowSystem) if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
{ {
settings.HasBorder = false; settings.HasBorder = false;
#if PLATFORM_WINDOWS && !PLATFORM_SDL
// Skip OS sizing frame and implement it using LeftButtonHit // Skip OS sizing frame and implement it using LeftButtonHit
settings.HasSizingFrame = false; settings.HasSizingFrame = false;
#endif
} }
#elif PLATFORM_LINUX #if PLATFORM_LINUX && !PLATFORM_SDL
settings.HasBorder = false; settings.HasBorder = false;
#endif #endif
MainWindow = Platform.CreateWindow(ref settings); MainWindow = Platform.CreateWindow(ref settings);

View File

@@ -160,21 +160,47 @@ namespace FlaxEditor.Options
GameWindowThenRestore, GameWindowThenRestore,
} }
/// <summary>
/// Options for type of window decorations to use.
/// </summary>
public enum WindowDecorationsType
{
/// <summary>
/// Determined automatically based on the system and any known compatibility issues with native decorations.
/// </summary>
Auto,
/// <summary>
/// Automatically choose most compatible window decorations for child windows, prefer custom decorations on main window.
/// </summary>
[EditorDisplay(Name = "Auto (Child Only)")]
AutoChildOnly,
/// <summary>
/// Use native system window decorations on all windows.
/// </summary>
Native,
/// <summary>
/// Use custom client-side window decorations on all windows.
/// </summary>
[EditorDisplay(Name = "Client-side")]
ClientSide,
}
/// <summary> /// <summary>
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required. /// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
/// </summary> /// </summary>
[DefaultValue(1.0f), Limit(0.1f, 10.0f)] [DefaultValue(1.0f), Limit(0.1f, 10.0f)]
[EditorDisplay("Interface"), EditorOrder(10), Tooltip("Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.")] [EditorDisplay("Interface"), EditorOrder(10), Tooltip("Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.")]
public float InterfaceScale { get; set; } = 1.0f; public float InterfaceScale { get; set; } = 1.0f;
#if PLATFORM_WINDOWS
/// <summary> /// <summary>
/// Gets or sets a value indicating whether use native window title bar. Editor restart required. /// Gets or sets a value indicating whether use native window title bar decorations in child windows. Editor restart required.
/// </summary> /// </summary>
[DefaultValue(false)] [DefaultValue(WindowDecorationsType.AutoChildOnly)]
[EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar. Editor restart required.")] [EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar decorations. Editor restart required.")]
public bool UseNativeWindowSystem { get; set; } = false; public WindowDecorationsType WindowDecorations { get; set; } = WindowDecorationsType.AutoChildOnly;
#endif
/// <summary> /// <summary>
/// Gets or sets a value indicating whether show selected camera preview in the editor window. /// Gets or sets a value indicating whether show selected camera preview in the editor window.

View File

@@ -6,6 +6,7 @@
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Engine/Globals.h" #include "Engine/Engine/Globals.h"
#include "Engine/Core/Math/Quaternion.h" #include "Engine/Core/Math/Quaternion.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Serialization/JsonWriters.h" #include "Engine/Serialization/JsonWriters.h"
#include "Engine/Serialization/JsonTools.h" #include "Engine/Serialization/JsonTools.h"
#include <ThirdParty/pugixml/pugixml.hpp> #include <ThirdParty/pugixml/pugixml.hpp>
@@ -327,6 +328,7 @@ ProjectInfo* ProjectInfo::Load(const String& path)
} }
// Load // Load
PROFILE_MEM(Editor);
auto project = New<ProjectInfo>(); auto project = New<ProjectInfo>();
if (project->LoadProject(path)) if (project->LoadProject(path))
{ {

View File

@@ -27,7 +27,7 @@ namespace FlaxEditor.SceneGraph
/// <summary> /// <summary>
/// The parent node. /// The parent node.
/// </summary> /// </summary>
protected SceneGraphNode parentNode; internal SceneGraphNode parentNode;
/// <summary> /// <summary>
/// Gets the children list. /// Gets the children list.

View File

@@ -13,6 +13,7 @@
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Platform/Thread.h" #include "Engine/Platform/Thread.h"
#include "Engine/Threading/IRunnable.h" #include "Engine/Threading/IRunnable.h"
#include "Engine/Profiler/ProfilerMemory.h"
void OnAsyncBegin(Thread* thread); void OnAsyncBegin(Thread* thread);
void OnAsyncEnd(); void OnAsyncEnd();
@@ -232,6 +233,8 @@ void OnAsyncEnd()
bool CodeEditingManagerService::Init() bool CodeEditingManagerService::Init()
{ {
PROFILE_MEM(Editor);
// Try get editors // Try get editors
#if USE_VISUAL_STUDIO_DTE #if USE_VISUAL_STUDIO_DTE
VisualStudioEditor::FindEditors(&CodeEditors); VisualStudioEditor::FindEditors(&CodeEditors);

View File

@@ -23,6 +23,7 @@
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Script.h" #include "Engine/Scripting/Script.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Level/Level.h" #include "Engine/Level/Level.h"
#include "FlaxEngine.Gen.h" #include "FlaxEngine.Gen.h"
@@ -77,7 +78,7 @@ namespace ScriptsBuilderImpl
void onScriptsReloadEnd(); void onScriptsReloadEnd();
void onScriptsLoaded(); void onScriptsLoaded();
void GetClassName(const StringAnsi& fullname, StringAnsi& className); void GetClassName(const StringAnsiView fullname, StringAnsi& className);
void onCodeEditorAsyncOpenBegin() void onCodeEditorAsyncOpenBegin()
{ {
@@ -120,9 +121,13 @@ void ScriptsBuilderImpl::sourceDirEvent(const String& path, FileSystemAction act
// Discard non-source files or generated files // Discard non-source files or generated files
if ((!path.EndsWith(TEXT(".cs")) && if ((!path.EndsWith(TEXT(".cs")) &&
!path.EndsWith(TEXT(".cpp")) && !path.EndsWith(TEXT(".cpp")) &&
!path.EndsWith(TEXT(".c")) &&
!path.EndsWith(TEXT(".hpp")) &&
!path.EndsWith(TEXT(".h"))) || !path.EndsWith(TEXT(".h"))) ||
path.EndsWith(TEXT(".Gen.cs"))) path.EndsWith(TEXT(".Gen.cs")))
{
return; return;
}
ScopeLock scopeLock(_locker); ScopeLock scopeLock(_locker);
_lastSourceCodeEdited = DateTime::Now(); _lastSourceCodeEdited = DateTime::Now();
@@ -272,7 +277,7 @@ bool ScriptsBuilder::GenerateProject(const StringView& customArgs)
return RunBuildTool(args); return RunBuildTool(args);
} }
void ScriptsBuilderImpl::GetClassName(const StringAnsi& fullname, StringAnsi& className) void ScriptsBuilderImpl::GetClassName(const StringAnsiView fullname, StringAnsi& className)
{ {
const auto lastDotIndex = fullname.FindLast('.'); const auto lastDotIndex = fullname.FindLast('.');
if (lastDotIndex != -1) if (lastDotIndex != -1)
@@ -413,6 +418,7 @@ void ScriptsBuilder::GetBinariesConfiguration(const Char*& target, const Char*&
bool ScriptsBuilderImpl::compileGameScriptsAsyncInner() bool ScriptsBuilderImpl::compileGameScriptsAsyncInner()
{ {
PROFILE_MEM(Editor);
LOG(Info, "Starting scripts compilation..."); LOG(Info, "Starting scripts compilation...");
CallEvent(EventType::CompileStarted); CallEvent(EventType::CompileStarted);
@@ -519,6 +525,8 @@ void ScriptsBuilderImpl::onEditorAssemblyUnloading(MAssembly* assembly)
bool ScriptsBuilderImpl::compileGameScriptsAsync() bool ScriptsBuilderImpl::compileGameScriptsAsync()
{ {
PROFILE_MEM(Editor);
// Start // Start
{ {
ScopeLock scopeLock(_locker); ScopeLock scopeLock(_locker);
@@ -562,6 +570,7 @@ bool ScriptsBuilderService::Init()
// Check flag // Check flag
if (_isInited) if (_isInited)
return false; return false;
PROFILE_MEM(Editor);
_isInited = true; _isInited = true;
// Link for Editor assembly unload event to clear cached Internal_OnCompilationEnd to prevent errors // Link for Editor assembly unload event to clear cached Internal_OnCompilationEnd to prevent errors
@@ -659,6 +668,9 @@ bool ScriptsBuilderService::Init()
void ScriptsBuilderService::Update() void ScriptsBuilderService::Update()
{ {
PROFILE_CPU();
PROFILE_MEM(Editor);
// Send compilation events // Send compilation events
{ {
ScopeLock scopeLock(_compileEventsLocker); ScopeLock scopeLock(_compileEventsLocker);

View File

@@ -6,6 +6,7 @@ using FlaxEditor.Scripting;
using FlaxEditor.Surface.Elements; using FlaxEditor.Surface.Elements;
using FlaxEditor.Windows.Assets; using FlaxEditor.Windows.Assets;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.Surface.Archetypes namespace FlaxEditor.Surface.Archetypes
{ {
@@ -123,7 +124,8 @@ namespace FlaxEditor.Surface.Archetypes
case MaterialDomain.Particle: case MaterialDomain.Particle:
case MaterialDomain.Deformable: case MaterialDomain.Deformable:
{ {
bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit; bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit && info.ShadingModel != MaterialShadingModel.CustomLit;
bool isOpaque = info.BlendMode == MaterialBlendMode.Opaque;
bool withTess = info.TessellationMode != TessellationMethod.None; bool withTess = info.TessellationMode != TessellationMethod.None;
GetBox(MaterialNodeBoxes.Color).IsActive = isNotUnlit; GetBox(MaterialNodeBoxes.Color).IsActive = isNotUnlit;
@@ -134,8 +136,8 @@ namespace FlaxEditor.Surface.Archetypes
GetBox(MaterialNodeBoxes.Roughness).IsActive = isNotUnlit; GetBox(MaterialNodeBoxes.Roughness).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.AmbientOcclusion).IsActive = isNotUnlit; GetBox(MaterialNodeBoxes.AmbientOcclusion).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.Normal).IsActive = isNotUnlit; GetBox(MaterialNodeBoxes.Normal).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || info.BlendMode != MaterialBlendMode.Opaque; GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || !isOpaque;
GetBox(MaterialNodeBoxes.Refraction).IsActive = info.BlendMode != MaterialBlendMode.Opaque; GetBox(MaterialNodeBoxes.Refraction).IsActive = !isOpaque;
GetBox(MaterialNodeBoxes.PositionOffset).IsActive = true; GetBox(MaterialNodeBoxes.PositionOffset).IsActive = true;
GetBox(MaterialNodeBoxes.TessellationMultiplier).IsActive = withTess; GetBox(MaterialNodeBoxes.TessellationMultiplier).IsActive = withTess;
GetBox(MaterialNodeBoxes.WorldDisplacement).IsActive = withTess; GetBox(MaterialNodeBoxes.WorldDisplacement).IsActive = withTess;
@@ -260,6 +262,211 @@ namespace FlaxEditor.Surface.Archetypes
} }
} }
#if false // TODO: finish code editor based on RichTextBoxBase with text block parsing for custom styling
internal sealed class CustomCodeTextBox : RichTextBoxBase
{
protected override void OnParseTextBlocks()
{
base.OnParseTextBlocks();
// Single block for a whole text
// TODO: implement code parsing with HLSL syntax
var font = Style.Current.FontMedium;
var style = new TextBlockStyle
{
Font = new FontReference(font),
Color = Style.Current.Foreground,
BackgroundSelectedBrush = new SolidColorBrush(Style.Current.BackgroundSelected),
};
_textBlocks.Clear();
_textBlocks.Add(new TextBlock
{
Range = new TextRange
{
StartIndex = 0,
EndIndex = TextLength,
},
Style = style,
Bounds = new Rectangle(Float2.Zero, font.MeasureText(Text)),
});
}
#else
internal sealed class CustomCodeTextBox : TextBox
{
#endif
public override void Draw()
{
base.Draw();
// Draw border
if (!IsFocused)
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.BorderNormal);
}
}
internal sealed class CustomCodeNode : SurfaceNode
{
private Rectangle _resizeButtonRect;
private Float2 _startResizingSize;
private Float2 _startResizingCornerOffset;
private bool _isResizing;
private CustomCodeTextBox _textBox;
private int SizeValueIndex => Archetype.TypeID == 8 ? 1 : 3; // Index of the Size stored in Values array
private Float2 SizeValue
{
get => (Float2)Values[SizeValueIndex];
set => SetValue(SizeValueIndex, value, false);
}
public CustomCodeNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
: base(id, context, nodeArch, groupArch)
{
Float2 pos = new Float2(FlaxEditor.Surface.Constants.NodeMarginX, FlaxEditor.Surface.Constants.NodeMarginY + FlaxEditor.Surface.Constants.NodeHeaderSize), size;
if (nodeArch.TypeID == 8)
{
pos += new Float2(60, 0);
size = new Float2(172, 200);
}
else
{
pos += new Float2(0, 40);
size = new Float2(300, 200);
}
_textBox = new CustomCodeTextBox
{
IsMultiline = true,
Location = pos,
Size = size,
Parent = this,
AnchorMax = Float2.One,
};
_textBox.EditEnd += () => SetValue(0, _textBox.Text);
}
public override bool CanSelect(ref Float2 location)
{
return base.CanSelect(ref location) && !_resizeButtonRect.MakeOffsetted(Location).Contains(ref location);
}
public override void OnSurfaceLoaded(SurfaceNodeActions action)
{
base.OnSurfaceLoaded(action);
_textBox.Text = (string)Values[0];
var size = SizeValue;
if (Surface != null && Surface.GridSnappingEnabled)
size = Surface.SnapToGrid(size, true);
Resize(size.X, size.Y);
}
public override void OnValuesChanged()
{
base.OnValuesChanged();
var size = SizeValue;
Resize(size.X, size.Y);
_textBox.Text = (string)Values[0];
}
protected override void UpdateRectangles()
{
base.UpdateRectangles();
const float buttonMargin = FlaxEditor.Surface.Constants.NodeCloseButtonMargin;
const float buttonSize = FlaxEditor.Surface.Constants.NodeCloseButtonSize;
_resizeButtonRect = new Rectangle(_closeButtonRect.Left, Height - buttonSize - buttonMargin - 4, buttonSize, buttonSize);
}
public override void Draw()
{
base.Draw();
var style = Style.Current;
if (_isResizing)
{
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
}
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
}
public override void OnLostFocus()
{
if (_isResizing)
EndResizing();
base.OnLostFocus();
}
public override void OnEndMouseCapture()
{
if (_isResizing)
EndResizing();
base.OnEndMouseCapture();
}
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (base.OnMouseDown(location, button))
return true;
if (button == MouseButton.Left && _resizeButtonRect.Contains(ref location) && Surface.CanEdit)
{
// Start sliding
_isResizing = true;
_startResizingSize = Size;
_startResizingCornerOffset = Size - location;
StartMouseCapture();
Cursor = CursorType.SizeNWSE;
return true;
}
return false;
}
public override void OnMouseMove(Float2 location)
{
if (_isResizing)
{
var emptySize = CalculateNodeSize(0, 0);
var size = Float2.Max(location - emptySize + _startResizingCornerOffset, new Float2(240, 160));
Resize(size.X, size.Y);
}
else
{
base.OnMouseMove(location);
}
}
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (button == MouseButton.Left && _isResizing)
{
EndResizing();
return true;
}
return base.OnMouseUp(location, button);
}
private void EndResizing()
{
Cursor = CursorType.Default;
EndMouseCapture();
_isResizing = false;
if (_startResizingSize != Size)
{
var emptySize = CalculateNodeSize(0, 0);
SizeValue = Size - emptySize;
Surface.MarkAsEdited(false);
}
}
}
internal enum MaterialTemplateInputsMapping internal enum MaterialTemplateInputsMapping
{ {
/// <summary> /// <summary>
@@ -410,13 +617,15 @@ namespace FlaxEditor.Surface.Archetypes
new NodeArchetype new NodeArchetype
{ {
TypeID = 8, TypeID = 8,
Create = (id, context, arch, groupArch) => new CustomCodeNode(id, context, arch, groupArch),
Title = "Custom Code", Title = "Custom Code",
Description = "Custom HLSL shader code expression", Description = "Custom HLSL shader code expression",
Flags = NodeFlags.MaterialGraph, Flags = NodeFlags.MaterialGraph,
Size = new Float2(300, 200), Size = new Float2(300, 200),
DefaultValues = new object[] DefaultValues = new object[]
{ {
"// Here you can add HLSL code\nOutput0 = Input0;" "// Here you can add HLSL code\nOutput0 = Input0;",
new Float2(300, 200),
}, },
Elements = new[] Elements = new[]
{ {
@@ -433,8 +642,6 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Output(1, "Output1", typeof(Float4), 9), NodeElementArchetype.Factory.Output(1, "Output1", typeof(Float4), 9),
NodeElementArchetype.Factory.Output(2, "Output2", typeof(Float4), 10), NodeElementArchetype.Factory.Output(2, "Output2", typeof(Float4), 10),
NodeElementArchetype.Factory.Output(3, "Output3", typeof(Float4), 11), NodeElementArchetype.Factory.Output(3, "Output3", typeof(Float4), 11),
NodeElementArchetype.Factory.TextBox(60, 0, 175, 200, 0),
} }
}, },
new NodeArchetype new NodeArchetype
@@ -874,6 +1081,7 @@ namespace FlaxEditor.Surface.Archetypes
new NodeArchetype new NodeArchetype
{ {
TypeID = 38, TypeID = 38,
Create = (id, context, arch, groupArch) => new CustomCodeNode(id, context, arch, groupArch),
Title = "Custom Global Code", Title = "Custom Global Code",
Description = "Custom global HLSL shader code expression (placed before material shader code). Can contain includes to shader utilities or declare functions to reuse later.", Description = "Custom global HLSL shader code expression (placed before material shader code). Can contain includes to shader utilities or declare functions to reuse later.",
Flags = NodeFlags.MaterialGraph, Flags = NodeFlags.MaterialGraph,
@@ -883,6 +1091,7 @@ namespace FlaxEditor.Surface.Archetypes
"// Here you can add HLSL code\nfloat4 GetCustomColor()\n{\n\treturn float4(1, 0, 0, 1);\n}", "// Here you can add HLSL code\nfloat4 GetCustomColor()\n{\n\treturn float4(1, 0, 0, 1);\n}",
true, true,
(int)MaterialTemplateInputsMapping.Utilities, (int)MaterialTemplateInputsMapping.Utilities,
new Float2(300, 240),
}, },
Elements = new[] Elements = new[]
{ {
@@ -890,7 +1099,6 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Text(20, 0, "Enabled"), NodeElementArchetype.Factory.Text(20, 0, "Enabled"),
NodeElementArchetype.Factory.Text(0, 20, "Location"), NodeElementArchetype.Factory.Text(0, 20, "Location"),
NodeElementArchetype.Factory.Enum(50, 20, 120, 2, typeof(MaterialTemplateInputsMapping)), NodeElementArchetype.Factory.Enum(50, 20, 120, 2, typeof(MaterialTemplateInputsMapping)),
NodeElementArchetype.Factory.TextBox(0, 40, 300, 200, 0),
} }
}, },
new NodeArchetype new NodeArchetype

View File

@@ -1,131 +0,0 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#include "ScreenUtilities.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Log.h"
#include "Engine/Profiler/ProfilerCPU.h"
Delegate<Color32> ScreenUtilities::PickColorDone;
#if PLATFORM_WINDOWS
#include <Windows.h>
#pragma comment(lib, "Gdi32.lib")
static HHOOK MouseCallbackHook;
LRESULT CALLBACK OnScreenUtilsMouseCallback(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
if (nCode >= 0 && wParam == WM_LBUTTONDOWN)
{
UnhookWindowsHookEx(MouseCallbackHook);
// Push event with the picked color
const Float2 cursorPos = Platform::GetMousePosition();
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
ScreenUtilities::PickColorDone(colorPicked);
return 1;
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
{
PROFILE_CPU();
HDC deviceContext = GetDC(NULL);
COLORREF color = GetPixel(deviceContext, (int)pos.X, (int)pos.Y);
ReleaseDC(NULL, deviceContext);
return Color32(GetRValue(color), GetGValue(color), GetBValue(color), 255);
}
void ScreenUtilities::PickColor()
{
MouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, OnScreenUtilsMouseCallback, NULL, NULL);
if (MouseCallbackHook == NULL)
{
LOG(Warning, "Failed to set mouse hook.");
LOG(Warning, "Error: {0}", GetLastError());
}
}
#elif PLATFORM_LINUX
#include "Engine/Platform/Linux/LinuxPlatform.h"
#include "Engine/Platform/Linux/IncludeX11.h"
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
{
X11::XColor color;
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
int defaultScreen = X11::XDefaultScreen(display);
X11::XImage* image;
image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), (int)pos.X, (int)pos.Y, 1, 1, AllPlanes, XYPixmap);
color.pixel = XGetPixel(image, 0, 0);
X11::XFree(image);
X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color);
Color32 outputColor;
outputColor.R = color.red / 256;
outputColor.G = color.green / 256;
outputColor.B = color.blue / 256;
outputColor.A = 255;
return outputColor;
}
void OnScreenUtilsXEventCallback(void* eventPtr)
{
X11::XEvent* event = (X11::XEvent*) eventPtr;
X11::Display* display = (X11::Display*)LinuxPlatform::GetXDisplay();
if (event->type == ButtonPress)
{
const Float2 cursorPos = Platform::GetMousePosition();
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
X11::XUngrabPointer(display, CurrentTime);
ScreenUtilities::PickColorDone(colorPicked);
LinuxPlatform::xEventRecieved.Unbind(OnScreenUtilsXEventCallback);
}
}
void ScreenUtilities::PickColor()
{
PROFILE_CPU();
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
X11::Cursor cursor = XCreateFontCursor(display, 130);
int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime);
if (grabbedPointer != GrabSuccess)
{
LOG(Error, "Failed to grab cursor for events.");
X11::XFreeCursor(display, cursor);
return;
}
X11::XFreeCursor(display, cursor);
LinuxPlatform::xEventRecieved.Bind(OnScreenUtilsXEventCallback);
}
#elif PLATFORM_MAC
#include <Cocoa/Cocoa.h>
#include <AppKit/AppKit.h>
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
{
// TODO: implement ScreenUtilities for macOS
return { 0, 0, 0, 255 };
}
void ScreenUtilities::PickColor()
{
// This is what C# calls to start the color picking sequence
// This should stop mouse clicks from working for one click, and that click is on the selected color
// There is a class called NSColorSample that might implement that for you, but maybe not.
}
#endif

View File

@@ -2,32 +2,4 @@
#pragma once #pragma once
#include "Engine/Core/Types/BaseTypes.h" #include "Engine/Platform/ScreenUtilities.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
/// <summary>
/// Platform-dependent screen utilities.
/// </summary>
API_CLASS(Static) class FLAXENGINE_API ScreenUtilities
{
DECLARE_SCRIPTING_TYPE_MINIMAL(ScreenUtilities);
/// <summary>
/// Gets the pixel color at the specified coordinates.
/// </summary>
/// <param name="pos">Screen-space coordinate to read.</param>
/// <returns>Pixel color at the specified coordinates.</returns>
API_FUNCTION() static Color32 GetColorAt(const Float2& pos);
/// <summary>
/// Starts async color picking. Color will be returned through PickColorDone event when the actions ends (user selected the final color with a mouse). When action is active, GetColorAt can be used to read the current value.
/// </summary>
API_FUNCTION() static void PickColor();
/// <summary>
/// Called when PickColor action is finished.
/// </summary>
API_EVENT() static Delegate<Color32> PickColorDone;
};

View File

@@ -1559,5 +1559,17 @@ namespace FlaxEditor.Utilities
c = c.Parent; c = c.Parent;
return c as ISceneEditingContext; return c as ISceneEditingContext;
} }
internal static bool UseCustomWindowDecorations(bool isMainWindow = false)
{
return Editor.Instance.Options.Options.Interface.WindowDecorations switch
{
Options.InterfaceOptions.WindowDecorationsType.Auto => !Platform.SupportsNativeDecorations,
Options.InterfaceOptions.WindowDecorationsType.AutoChildOnly => !isMainWindow ? !Platform.SupportsNativeDecorations : true,
Options.InterfaceOptions.WindowDecorationsType.Native => false,
Options.InterfaceOptions.WindowDecorationsType.ClientSide => true,
_ => throw new ArgumentOutOfRangeException()
};
}
} }
} }

View File

@@ -5,6 +5,7 @@
#include "Engine/Content/Assets/Model.h" #include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Assets/MaterialInstance.h" #include "Engine/Content/Assets/MaterialInstance.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Level/Level.h" #include "Engine/Level/Level.h"
#include "Engine/Level/Scene/Scene.h" #include "Engine/Level/Scene/Scene.h"
#include "Engine/Level/Actors/PointLight.h" #include "Engine/Level/Actors/PointLight.h"
@@ -263,6 +264,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
bool ViewportIconsRendererService::Init() bool ViewportIconsRendererService::Init()
{ {
PROFILE_MEM(Editor);
QuadModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Quad")); QuadModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Quad"));
#define INIT(type, path) \ #define INIT(type, path) \
InstanceBuffers[static_cast<int32>(IconTypes::type)].Setup(1); \ InstanceBuffers[static_cast<int32>(IconTypes::type)].Setup(1); \

View File

@@ -77,7 +77,7 @@ namespace FlaxEditor.Viewport
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root); public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
/// <inheritdoc /> /// <inheritdoc />
public Float2 MouseDelta => _mouseDelta; public Float2 MouseDelta => FlaxEngine.Input.MousePositionDelta;
/// <inheritdoc /> /// <inheritdoc />
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false; public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;

View File

@@ -4,6 +4,7 @@ using System;
using System.Linq; using System.Linq;
using FlaxEditor.Content.Settings; using FlaxEditor.Content.Settings;
using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Docking;
using FlaxEditor.GUI.Input; using FlaxEditor.GUI.Input;
using FlaxEditor.Options; using FlaxEditor.Options;
using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Cameras;
@@ -158,18 +159,22 @@ namespace FlaxEditor.Viewport
private float _movementSpeed; private float _movementSpeed;
private float _minMovementSpeed; private float _minMovementSpeed;
private float _maxMovementSpeed; private float _maxMovementSpeed;
#if !PLATFORM_SDL
private float _mouseAccelerationScale; private float _mouseAccelerationScale;
private bool _useMouseFiltering; private bool _useMouseFiltering;
private bool _useMouseAcceleration; private bool _useMouseAcceleration;
#endif
// Input // Input
internal bool _disableInputUpdate; internal bool _disableInputUpdate;
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown; private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
private int _deltaFilteringStep;
private Float2 _startPos; private Float2 _startPos;
#if !PLATFORM_SDL
private Float2 _mouseDeltaLast; private Float2 _mouseDeltaLast;
private int _deltaFilteringStep;
private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames]; private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames];
#endif
/// <summary> /// <summary>
/// The previous input (from the previous update). /// The previous input (from the previous update).
@@ -522,10 +527,11 @@ namespace FlaxEditor.Viewport
: base(task) : base(task)
{ {
_editor = Editor.Instance; _editor = Editor.Instance;
#if !PLATFORM_SDL
_mouseAccelerationScale = 0.1f; _mouseAccelerationScale = 0.1f;
_useMouseFiltering = false; _useMouseFiltering = false;
_useMouseAcceleration = false; _useMouseAcceleration = false;
#endif
_camera = camera; _camera = camera;
if (_camera != null) if (_camera != null)
_camera.Viewport = this; _camera.Viewport = this;
@@ -1520,7 +1526,9 @@ namespace FlaxEditor.Viewport
// Hide cursor and start tracking mouse movement // Hide cursor and start tracking mouse movement
win.StartTrackingMouse(false); win.StartTrackingMouse(false);
win.Cursor = CursorType.Hidden; win.Cursor = CursorType.Hidden;
win.MouseMoveRelative += OnMouseMoveRelative;
#if !PLATFORM_SDL
// Center mouse position if it's too close to the edge // Center mouse position if it's too close to the edge
var size = Size; var size = Size;
var center = Float2.Round(size * 0.5f); var center = Float2.Round(size * 0.5f);
@@ -1529,6 +1537,7 @@ namespace FlaxEditor.Viewport
_viewMousePos = center; _viewMousePos = center;
win.MousePosition = PointToWindow(_viewMousePos); win.MousePosition = PointToWindow(_viewMousePos);
} }
#endif
} }
/// <summary> /// <summary>
@@ -1540,6 +1549,7 @@ namespace FlaxEditor.Viewport
// Restore cursor and stop tracking mouse movement // Restore cursor and stop tracking mouse movement
win.Cursor = CursorType.Default; win.Cursor = CursorType.Default;
win.EndTrackingMouse(); win.EndTrackingMouse();
win.MouseMoveRelative -= OnMouseMoveRelative;
} }
/// <summary> /// <summary>
@@ -1621,18 +1631,15 @@ namespace FlaxEditor.Viewport
// Get parent window // Get parent window
var win = (WindowRootControl)Root; var win = (WindowRootControl)Root;
if (win.IsFocused)
// Get current mouse position in the view
{ {
// When the window is not focused, the position in window does not return sane values // Get current mouse position in the view
Float2 pos = PointFromWindow(win.MousePosition); _viewMousePos = PointFromWindow(win.MousePosition);
if (!float.IsInfinity(pos.LengthSquared))
_viewMousePos = pos;
} }
// Update input // Update input
var window = win.Window; var window = win.Window;
var canUseInput = window != null && window.IsFocused && window.IsForegroundWindow; var canUseInput = window != null && window.IsFocused && window.IsForegroundWindow && !WindowDragHelper.IsDragActive;
{ {
// Get input buttons and keys (skip if viewport has no focus or mouse is over a child control) // Get input buttons and keys (skip if viewport has no focus or mouse is over a child control)
var isViewportControllingMouse = canUseInput && IsControllingMouse; var isViewportControllingMouse = canUseInput && IsControllingMouse;
@@ -1644,9 +1651,17 @@ namespace FlaxEditor.Viewport
else else
EndMouseCapture(); EndMouseCapture();
} }
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
_prevInput = _input; _prevInput = _input;
#if PLATFORM_SDL
bool useMouse = IsControllingMouse || ContainsPoint(ref _viewMousePos) || _prevInput.IsControllingMouse;
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot)); var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
if (_prevInput.IsControllingMouse)
hit = null;
#else
bool useMouse = IsControllingMouse || ContainsPoint(ref _viewMousePos);
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
#endif
if (canUseInput && ContainsFocus && hit == null) if (canUseInput && ContainsFocus && hit == null)
_input.Gather(win.Window, useMouse, ref _prevInput); _input.Gather(win.Window, useMouse, ref _prevInput);
else else
@@ -1759,6 +1774,10 @@ namespace FlaxEditor.Viewport
if (_input.IsControlDown) if (_input.IsControlDown)
moveDelta *= 0.3f; moveDelta *= 0.3f;
#if PLATFORM_SDL
var mouseDelta = _mouseDelta;
_mouseDelta = Float2.Zero;
#else
// Calculate smooth mouse delta not dependant on viewport size // Calculate smooth mouse delta not dependant on viewport size
var offset = _viewMousePos - _startPos; var offset = _viewMousePos - _startPos;
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown) if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown)
@@ -1800,6 +1819,7 @@ namespace FlaxEditor.Viewport
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale; mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
_mouseDeltaLast = currentDelta; _mouseDeltaLast = currentDelta;
} }
#endif
// Update // Update
moveDelta *= dt * (60.0f * 4.0f); moveDelta *= dt * (60.0f * 4.0f);
@@ -1808,12 +1828,14 @@ namespace FlaxEditor.Viewport
mouseDelta *= new Float2(1, -1); mouseDelta *= new Float2(1, -1);
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse); UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
#if !PLATFORM_SDL
// Move mouse back to the root position // Move mouse back to the root position
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown)) if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown))
{ {
var center = PointToWindow(_startPos); var center = PointToWindow(_startPos);
win.MousePosition = center; win.MousePosition = center;
} }
#endif
// Change Ortho size on mouse scroll // Change Ortho size on mouse scroll
if (_isOrtho && !rmbWheel) if (_isOrtho && !rmbWheel)
@@ -1825,6 +1847,8 @@ namespace FlaxEditor.Viewport
} }
else else
{ {
#if PLATFORM_SDL
#else
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown) if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
{ {
// Calculate smooth mouse delta not dependant on viewport size // Calculate smooth mouse delta not dependant on viewport size
@@ -1839,6 +1863,7 @@ namespace FlaxEditor.Viewport
_mouseDelta = Float2.Zero; _mouseDelta = Float2.Zero;
} }
_mouseDeltaLast = Float2.Zero; _mouseDeltaLast = Float2.Zero;
#endif
if (ContainsFocus) if (ContainsFocus)
{ {
@@ -1888,6 +1913,12 @@ namespace FlaxEditor.Viewport
_input.MouseWheelDelta = 0; _input.MouseWheelDelta = 0;
} }
/// <inheritdoc />
public void OnMouseMoveRelative(ref Float2 mouseMotion)
{
_mouseDelta += mouseMotion;
}
/// <inheritdoc /> /// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button) public override bool OnMouseDown(Float2 location, MouseButton button)
{ {
@@ -1953,7 +1984,8 @@ namespace FlaxEditor.Viewport
if (_isControllingMouse) if (_isControllingMouse)
{ {
OnControlMouseEnd(RootWindow.Window); if (RootWindow?.Window != null)
OnControlMouseEnd(RootWindow.Window);
_isControllingMouse = false; _isControllingMouse = false;
_isVirtualMouseRightDown = false; _isVirtualMouseRightDown = false;
} }

View File

@@ -611,16 +611,25 @@ namespace FlaxEditor.Viewport
// Don't allow rubber band selection when gizmo is controlling mouse, vertex painting mode, or cloth painting is enabled // Don't allow rubber band selection when gizmo is controlling mouse, vertex painting mode, or cloth painting is enabled
bool canStart = !(IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown) && bool canStart = !(IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown) &&
Gizmos?.Active is TransformGizmo && !Gizmos.Active.IsControllingMouse; Gizmos?.Active is TransformGizmo;
_rubberBandSelector.TryCreateRubberBand(canStart, _viewMousePos); _rubberBandSelector.TryCreateRubberBand(canStart, _viewMousePos);
} }
/// <inheritdoc />
protected override void OnControlMouseBegin(Window win)
{
_rubberBandSelector.ReleaseRubberBandSelection();
base.OnControlMouseBegin(win);
}
/// <inheritdoc /> /// <inheritdoc />
protected override void OnLeftMouseButtonDown() protected override void OnLeftMouseButtonDown()
{ {
base.OnLeftMouseButtonDown(); base.OnLeftMouseButtonDown();
_rubberBandSelector.TryStartingRubberBandSelection(); if (!IsAltKeyDown)
_rubberBandSelector.TryStartingRubberBandSelection();
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -129,6 +129,9 @@ namespace FlaxEditor.Windows
"Mono Project - www.mono-project.com", "Mono Project - www.mono-project.com",
#if USE_NETCORE #if USE_NETCORE
".NET - www.dotnet.microsoft.com", ".NET - www.dotnet.microsoft.com",
#endif
#if PLATFORM_SDL
"Simple DirectMedia Layer - www.libsdl.org",
#endif #endif
"FreeType Project - www.freetype.org", "FreeType Project - www.freetype.org",
"Assimp - www.assimp.sourceforge.net", "Assimp - www.assimp.sourceforge.net",

View File

@@ -1063,7 +1063,7 @@ namespace FlaxEditor.Windows
_cursorVisible = Screen.CursorVisible; _cursorVisible = Screen.CursorVisible;
_cursorLockMode = Screen.CursorLock; _cursorLockMode = Screen.CursorLock;
Screen.CursorVisible = true; Screen.CursorVisible = true;
if (Screen.CursorLock == CursorLockMode.Clipped) if (Screen.CursorLock == CursorLockMode.Clipped || Screen.CursorLock == CursorLockMode.Locked)
Screen.CursorLock = CursorLockMode.None; Screen.CursorLock = CursorLockMode.None;
// Defocus // Defocus
@@ -1152,8 +1152,11 @@ namespace FlaxEditor.Windows
if (Editor.StateMachine.IsPlayMode && !Editor.StateMachine.PlayingState.IsPaused) if (Editor.StateMachine.IsPlayMode && !Editor.StateMachine.PlayingState.IsPaused)
{ {
// Make sure the cursor is always in the viewport when cursor is locked
bool forceCenter = _cursorLockMode != CursorLockMode.None && !IsMouseOver;
// Center mouse in play mode // Center mouse in play mode
if (CenterMouseOnFocus) if (CenterMouseOnFocus || forceCenter)
{ {
var center = PointToWindow(Size * 0.5f); var center = PointToWindow(Size * 0.5f);
Root.MousePosition = center; Root.MousePosition = center;
@@ -1178,9 +1181,10 @@ namespace FlaxEditor.Windows
_cursorVisible = Screen.CursorVisible; _cursorVisible = Screen.CursorVisible;
_cursorLockMode = Screen.CursorLock; _cursorLockMode = Screen.CursorLock;
// Restore cursor visibility (could be hidden by the game) // Restore cursor state, could be hidden or locked by the game
if (!_cursorVisible) if (!_cursorVisible)
Screen.CursorVisible = true; Screen.CursorVisible = true;
Screen.CursorLock = CursorLockMode.None;
} }
} }

View File

@@ -34,6 +34,7 @@ namespace FlaxEditor.Windows.Profiler
private List<ClickableRow> _tableRowsCache; private List<ClickableRow> _tableRowsCache;
private Dictionary<Guid, Resource> _resourceCache; private Dictionary<Guid, Resource> _resourceCache;
private StringBuilder _stringBuilder; private StringBuilder _stringBuilder;
private Asset[] _assetsCache;
public Assets() public Assets()
: base("Assets") : base("Assets")
@@ -138,12 +139,12 @@ namespace FlaxEditor.Windows.Profiler
_stringBuilder = new StringBuilder(); _stringBuilder = new StringBuilder();
// Capture current assets usage info // Capture current assets usage info
var assets = FlaxEngine.Content.Assets; FlaxEngine.Content.GetAssets(ref _assetsCache, out var count);
var resources = new Resource[assets.Length]; var resources = new Resource[count];
ulong totalMemoryUsage = 0; ulong totalMemoryUsage = 0;
for (int i = 0; i < resources.Length; i++) for (int i = 0; i < count; i++)
{ {
var asset = assets[i]; var asset = _assetsCache[i];
ref var resource = ref resources[i]; ref var resource = ref resources[i];
if (!asset) if (!asset)
continue; continue;
@@ -179,6 +180,7 @@ namespace FlaxEditor.Windows.Profiler
if (_resources == null) if (_resources == null)
_resources = new SamplesBuffer<Resource[]>(); _resources = new SamplesBuffer<Resource[]>();
_resources.Add(resources); _resources.Add(resources);
Array.Clear(_assetsCache);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -200,6 +202,7 @@ namespace FlaxEditor.Windows.Profiler
_resourceCache?.Clear(); _resourceCache?.Clear();
_tableRowsCache?.Clear(); _tableRowsCache?.Clear();
_stringBuilder?.Clear(); _stringBuilder?.Clear();
_assetsCache = null;
base.OnDestroy(); base.OnDestroy();
} }

View File

@@ -2,6 +2,8 @@
#if USE_PROFILER #if USE_PROFILER
using System; using System;
using System.Collections.Generic;
using FlaxEditor.GUI;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
@@ -13,9 +15,22 @@ namespace FlaxEditor.Windows.Profiler
/// <seealso cref="FlaxEditor.Windows.Profiler.ProfilerMode" /> /// <seealso cref="FlaxEditor.Windows.Profiler.ProfilerMode" />
internal sealed class Memory : ProfilerMode internal sealed class Memory : ProfilerMode
{ {
private struct FrameData
{
public ProfilerMemory.GroupsArray Usage;
public ProfilerMemory.GroupsArray Peek;
public ProfilerMemory.GroupsArray Count;
}
private readonly SingleChart _nativeAllocationsChart; private readonly SingleChart _nativeAllocationsChart;
private readonly SingleChart _managedAllocationsChart; private readonly SingleChart _managedAllocationsChart;
private readonly Table _table;
private SamplesBuffer<FrameData> _frames;
private List<Row> _tableRowsCache;
private string[] _groupNames;
private int[] _groupOrder;
private Label _warningText;
public Memory() public Memory()
: base("Memory") : base("Memory")
{ {
@@ -50,6 +65,70 @@ namespace FlaxEditor.Windows.Profiler
Parent = layout, Parent = layout,
}; };
_managedAllocationsChart.SelectedSampleChanged += OnSelectedSampleChanged; _managedAllocationsChart.SelectedSampleChanged += OnSelectedSampleChanged;
// Warning text
if (!ProfilerMemory.Enabled)
{
_warningText = new Label
{
Text = "Detailed memory profiling is disabled. Run with command line '-mem'",
TextColor = Color.Red,
Visible = false,
Parent = layout,
};
}
// Table
var style = Style.Current;
var headerColor = style.LightBackground;
var textColor = style.Foreground;
_table = new Table
{
Columns = new[]
{
new ColumnDefinition
{
UseExpandCollapseMode = true,
CellAlignment = TextAlignment.Near,
Title = "Group",
TitleBackgroundColor = headerColor,
TitleColor = textColor,
},
new ColumnDefinition
{
Title = "Usage",
TitleBackgroundColor = headerColor,
FormatValue = FormatCellBytes,
TitleColor = textColor,
},
new ColumnDefinition
{
Title = "Peek",
TitleBackgroundColor = headerColor,
FormatValue = FormatCellBytes,
TitleColor = textColor,
},
new ColumnDefinition
{
Title = "Count",
TitleBackgroundColor = headerColor,
TitleColor = textColor,
},
},
Parent = layout,
};
_table.Splits = new[]
{
0.5f,
0.2f,
0.2f,
0.1f,
};
}
private string FormatCellBytes(object x)
{
return Utilities.Utils.FormatBytesCount(Convert.ToUInt64(x));
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -57,6 +136,7 @@ namespace FlaxEditor.Windows.Profiler
{ {
_nativeAllocationsChart.Clear(); _nativeAllocationsChart.Clear();
_managedAllocationsChart.Clear(); _managedAllocationsChart.Clear();
_frames?.Clear();
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -84,6 +164,19 @@ namespace FlaxEditor.Windows.Profiler
_nativeAllocationsChart.AddSample(nativeMemoryAllocation); _nativeAllocationsChart.AddSample(nativeMemoryAllocation);
_managedAllocationsChart.AddSample(managedMemoryAllocation); _managedAllocationsChart.AddSample(managedMemoryAllocation);
// Gather memory profiler stats for groups
var frame = new FrameData
{
Usage = ProfilerMemory.GetGroups(0),
Peek = ProfilerMemory.GetGroups(1),
Count = ProfilerMemory.GetGroups(2),
};
if (_frames == null)
_frames = new SamplesBuffer<FrameData>();
if (_groupNames == null)
_groupNames = ProfilerMemory.GetGroupNames();
_frames.Add(frame);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -91,6 +184,112 @@ namespace FlaxEditor.Windows.Profiler
{ {
_nativeAllocationsChart.SelectedSampleIndex = selectedFrame; _nativeAllocationsChart.SelectedSampleIndex = selectedFrame;
_managedAllocationsChart.SelectedSampleIndex = selectedFrame; _managedAllocationsChart.SelectedSampleIndex = selectedFrame;
UpdateTable(selectedFrame);
}
/// <inheritdoc />
public override void OnDestroy()
{
_tableRowsCache?.Clear();
_groupNames = null;
_groupOrder = null;
base.OnDestroy();
}
private void UpdateTable(int selectedFrame)
{
if (_frames == null)
return;
if (_tableRowsCache == null)
_tableRowsCache = new List<Row>();
_table.IsLayoutLocked = true;
RecycleTableRows(_table, _tableRowsCache);
UpdateTableInner(selectedFrame);
_table.UnlockChildrenRecursive();
_table.PerformLayout();
}
private unsafe void UpdateTableInner(int selectedFrame)
{
if (_frames.Count == 0)
return;
if (_warningText != null)
_warningText.Visible = true;
var frame = _frames.Get(selectedFrame);
var totalUage = frame.Usage.Values0[(int)ProfilerMemory.Groups.TotalTracked];
var totalPeek = frame.Peek.Values0[(int)ProfilerMemory.Groups.TotalTracked];
var totalCount = frame.Count.Values0[(int)ProfilerMemory.Groups.TotalTracked];
// Sort by memory size
if (_groupOrder == null)
_groupOrder = new int[(int)ProfilerMemory.Groups.MAX];
for (int i = 0; i < (int)ProfilerMemory.Groups.MAX; i++)
_groupOrder[i] = i;
Array.Sort(_groupOrder, (x, y) =>
{
var tmp = _frames.Get(selectedFrame);
return tmp.Usage.Values0[y].CompareTo(tmp.Usage.Values0[x]);
});
// Add rows
var rowColor2 = Style.Current.Background * 1.4f;
for (int i = 0; i < (int)ProfilerMemory.Groups.MAX; i++)
{
var group = _groupOrder[i];
var groupUsage = frame.Usage.Values0[group];
if (groupUsage <= 0)
continue;
var groupPeek = frame.Peek.Values0[group];
var groupCount = frame.Count.Values0[group];
Row row;
if (_tableRowsCache.Count != 0)
{
var last = _tableRowsCache.Count - 1;
row = _tableRowsCache[last];
_tableRowsCache.RemoveAt(last);
}
else
{
row = new Row
{
Values = new object[4],
BackgroundColors = new Color[4],
};
}
{
// Group
row.Values[0] = _groupNames[group];
// Usage
row.Values[1] = groupUsage;
row.BackgroundColors[1] = Color.Red.AlphaMultiplied(Mathf.Min(1, (float)groupUsage / totalUage) * 0.5f);
// Peek
row.Values[2] = groupPeek;
row.BackgroundColors[2] = Color.Red.AlphaMultiplied(Mathf.Min(1, (float)groupPeek / totalPeek) * 0.5f);
// Count
row.Values[3] = groupCount;
row.BackgroundColors[3] = Color.Red.AlphaMultiplied(Mathf.Min(1, (float)groupCount / totalCount) * 0.5f);
}
row.Width = _table.Width;
row.BackgroundColor = i % 2 == 1 ? rowColor2 : Color.Transparent;
row.Parent = _table;
var useBackground = group != (int)ProfilerMemory.Groups.Total &&
group != (int)ProfilerMemory.Groups.TotalTracked &&
group != (int)ProfilerMemory.Groups.Malloc;
if (!useBackground)
{
for (int k = 1; k < row.BackgroundColors.Length; k++)
row.BackgroundColors[k] = Color.Transparent;
}
}
} }
} }
} }

View File

@@ -35,6 +35,7 @@ namespace FlaxEditor.Windows.Profiler
private Dictionary<string, Guid> _assetPathToId; private Dictionary<string, Guid> _assetPathToId;
private Dictionary<Guid, Resource> _resourceCache; private Dictionary<Guid, Resource> _resourceCache;
private StringBuilder _stringBuilder; private StringBuilder _stringBuilder;
private GPUResource[] _gpuResourcesCached;
public MemoryGPU() public MemoryGPU()
: base("GPU Memory") : base("GPU Memory")
@@ -138,13 +139,15 @@ namespace FlaxEditor.Windows.Profiler
// Capture current GPU resources usage info // Capture current GPU resources usage info
var contentDatabase = Editor.Instance.ContentDatabase; var contentDatabase = Editor.Instance.ContentDatabase;
var gpuResources = GPUDevice.Instance.Resources; GPUDevice.Instance.GetResources(ref _gpuResourcesCached, out var count);
var resources = new Resource[gpuResources.Length]; var resources = new Resource[count];
var sb = _stringBuilder; var sb = _stringBuilder;
for (int i = 0; i < resources.Length; i++) for (int i = 0; i < count; i++)
{ {
var gpuResource = gpuResources[i]; var gpuResource = _gpuResourcesCached[i];
ref var resource = ref resources[i]; ref var resource = ref resources[i];
if (!gpuResource)
continue;
// Try to reuse cached resource info // Try to reuse cached resource info
var gpuResourceId = gpuResource.ID; var gpuResourceId = gpuResource.ID;
@@ -219,6 +222,7 @@ namespace FlaxEditor.Windows.Profiler
if (_resources == null) if (_resources == null)
_resources = new SamplesBuffer<Resource[]>(); _resources = new SamplesBuffer<Resource[]>();
_resources.Add(resources); _resources.Add(resources);
Array.Clear(_gpuResourcesCached);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -255,6 +259,7 @@ namespace FlaxEditor.Windows.Profiler
_assetPathToId?.Clear(); _assetPathToId?.Clear();
_tableRowsCache?.Clear(); _tableRowsCache?.Clear();
_stringBuilder?.Clear(); _stringBuilder?.Clear();
_gpuResourcesCached = null;
base.OnDestroy(); base.OnDestroy();
} }

View File

@@ -26,7 +26,6 @@ namespace FlaxEditor.Windows
private Tree _tree; private Tree _tree;
private Panel _sceneTreePanel; private Panel _sceneTreePanel;
private bool _isUpdatingSelection; private bool _isUpdatingSelection;
private bool _isMouseDown;
private bool _blockSceneTreeScroll = false; private bool _blockSceneTreeScroll = false;
private DragAssets _dragAssets; private DragAssets _dragAssets;
@@ -374,10 +373,7 @@ namespace FlaxEditor.Windows
return true; return true;
if (buttons == MouseButton.Right) if (buttons == MouseButton.Right)
{
_isMouseDown = true;
return true; return true;
}
return false; return false;
} }
@@ -388,10 +384,8 @@ namespace FlaxEditor.Windows
if (base.OnMouseUp(location, buttons)) if (base.OnMouseUp(location, buttons))
return true; return true;
if (_isMouseDown && buttons == MouseButton.Right) if (buttons == MouseButton.Right)
{ {
_isMouseDown = false;
if (Editor.StateMachine.CurrentState.CanEditScene) if (Editor.StateMachine.CurrentState.CanEditScene)
{ {
// Show context menu // Show context menu
@@ -416,14 +410,6 @@ namespace FlaxEditor.Windows
return false; return false;
} }
/// <inheritdoc />
public override void OnLostFocus()
{
_isMouseDown = false;
base.OnLostFocus();
}
/// <inheritdoc /> /// <inheritdoc />
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
{ {

View File

@@ -28,7 +28,7 @@ const Char* SplashScreenQuotes[] =
#elif PLATFORM_LINUX #elif PLATFORM_LINUX
TEXT("Try it on a Raspberry"), TEXT("Try it on a Raspberry"),
TEXT("Trying to exit vim"), TEXT("Trying to exit vim"),
TEXT("Sudo flax --loadproject"), TEXT("sudo flax --project HelloWorld.flaxproj"),
#elif PLATFORM_MAC #elif PLATFORM_MAC
TEXT("don't compare Macbooks to oranges."), TEXT("don't compare Macbooks to oranges."),
TEXT("Why does macbook heat up?\nBecause it doesn't have windows"), TEXT("Why does macbook heat up?\nBecause it doesn't have windows"),
@@ -104,6 +104,7 @@ const Char* SplashScreenQuotes[] =
TEXT("You have my bow.\nAnd my axe!"), TEXT("You have my bow.\nAnd my axe!"),
TEXT("To the bridge of Khazad-dum."), TEXT("To the bridge of Khazad-dum."),
TEXT("One ring to rule them all.\nOne ring to find them."), TEXT("One ring to rule them all.\nOne ring to find them."),
TEXT("Where there's a whip, there's a way."),
TEXT("That's what she said"), TEXT("That's what she said"),
TEXT("We could be compiling shaders here"), TEXT("We could be compiling shaders here"),
TEXT("Hello There"), TEXT("Hello There"),
@@ -164,7 +165,7 @@ void SplashScreen::Show()
settings.AllowMaximize = false; settings.AllowMaximize = false;
settings.AllowDragAndDrop = false; settings.AllowDragAndDrop = false;
settings.IsTopmost = false; settings.IsTopmost = false;
settings.IsRegularWindow = false; settings.Type = WindowType::Utility;
settings.HasSizingFrame = false; settings.HasSizingFrame = false;
settings.ShowAfterFirstPaint = true; settings.ShowAfterFirstPaint = true;
settings.StartPosition = WindowStartPosition::CenterScreen; settings.StartPosition = WindowStartPosition::CenterScreen;

View File

@@ -7,6 +7,7 @@
#include "Engine/Engine/Time.h" #include "Engine/Engine/Time.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Threading/TaskGraph.h" #include "Engine/Threading/TaskGraph.h"
class BehaviorSystem : public TaskGraphSystem class BehaviorSystem : public TaskGraphSystem
@@ -38,6 +39,7 @@ TaskGraphSystem* Behavior::System = nullptr;
void BehaviorSystem::Job(int32 index) void BehaviorSystem::Job(int32 index)
{ {
PROFILE_CPU_NAMED("Behavior.Job"); PROFILE_CPU_NAMED("Behavior.Job");
PROFILE_MEM(AI);
Behaviors[index]->UpdateAsync(); Behaviors[index]->UpdateAsync();
} }
@@ -57,6 +59,7 @@ void BehaviorSystem::Execute(TaskGraph* graph)
bool BehaviorService::Init() bool BehaviorService::Init()
{ {
PROFILE_MEM(AI);
Behavior::System = New<BehaviorSystem>(); Behavior::System = New<BehaviorSystem>();
Engine::UpdateGraph->AddSystem(Behavior::System); Engine::UpdateGraph->AddSystem(Behavior::System);
return false; return false;
@@ -70,9 +73,9 @@ void BehaviorService::Dispose()
Behavior::Behavior(const SpawnParams& params) Behavior::Behavior(const SpawnParams& params)
: Script(params) : Script(params)
, Tree(this)
{ {
_knowledge.Behavior = this; _knowledge.Behavior = this;
Tree.Changed.Bind<Behavior, &Behavior::ResetLogic>(this);
} }
void Behavior::UpdateAsync() void Behavior::UpdateAsync()
@@ -172,6 +175,19 @@ void Behavior::OnDisable()
BehaviorServiceInstance.UpdateList.Remove(this); BehaviorServiceInstance.UpdateList.Remove(this);
} }
void Behavior::OnAssetChanged(Asset* asset, void* caller)
{
ResetLogic();
}
void Behavior::OnAssetLoaded(Asset* asset, void* caller)
{
}
void Behavior::OnAssetUnloaded(Asset* asset, void* caller)
{
}
#if USE_EDITOR #if USE_EDITOR
bool Behavior::GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior) bool Behavior::GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior)

View File

@@ -11,7 +11,7 @@
/// <summary> /// <summary>
/// Behavior instance script that runs Behavior Tree execution. /// Behavior instance script that runs Behavior Tree execution.
/// </summary> /// </summary>
API_CLASS(Attributes="Category(\"Flax Engine\")") class FLAXENGINE_API Behavior : public Script API_CLASS(Attributes="Category(\"Flax Engine\")") class FLAXENGINE_API Behavior : public Script, private IAssetReference
{ {
API_AUTO_SERIALIZATION(); API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE(Behavior); DECLARE_SCRIPTING_TYPE(Behavior);
@@ -92,6 +92,11 @@ public:
void OnDisable() override; void OnDisable() override;
private: private:
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
#if USE_EDITOR #if USE_EDITOR
// Editor-only utilities to debug nodes state. // Editor-only utilities to debug nodes state.
API_FUNCTION(Internal) static bool GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior); API_FUNCTION(Internal) static bool GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior);

View File

@@ -4,6 +4,7 @@
#include "BehaviorTree.h" #include "BehaviorTree.h"
#include "BehaviorTreeNodes.h" #include "BehaviorTreeNodes.h"
#include "BehaviorKnowledgeSelector.h" #include "BehaviorKnowledgeSelector.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/BinaryModule.h" #include "Engine/Scripting/BinaryModule.h"
#include "Engine/Scripting/ManagedCLR/MProperty.h" #include "Engine/Scripting/ManagedCLR/MProperty.h"
@@ -144,6 +145,7 @@ BehaviorKnowledge::~BehaviorKnowledge()
void BehaviorKnowledge::InitMemory(BehaviorTree* tree) void BehaviorKnowledge::InitMemory(BehaviorTree* tree)
{ {
PROFILE_MEM(AI);
if (Tree) if (Tree)
FreeMemory(); FreeMemory();
if (!tree) if (!tree)

View File

@@ -10,6 +10,7 @@
#include "Engine/Serialization/JsonSerializer.h" #include "Engine/Serialization/JsonSerializer.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "FlaxEngine.Gen.h" #include "FlaxEngine.Gen.h"
#if USE_EDITOR #if USE_EDITOR
#include "Engine/Level/Level.h" #include "Engine/Level/Level.h"
@@ -275,6 +276,7 @@ Asset::LoadResult BehaviorTree::load()
if (surfaceChunk == nullptr) if (surfaceChunk == nullptr)
return LoadResult::MissingDataChunk; return LoadResult::MissingDataChunk;
MemoryReadStream surfaceStream(surfaceChunk->Get(), surfaceChunk->Size()); MemoryReadStream surfaceStream(surfaceChunk->Get(), surfaceChunk->Size());
PROFILE_MEM(AI);
if (Graph.Load(&surfaceStream, true)) if (Graph.Load(&surfaceStream, true))
{ {
LOG(Warning, "Failed to load graph \'{0}\'", ToString()); LOG(Warning, "Failed to load graph \'{0}\'", ToString());

View File

@@ -4,6 +4,7 @@
#include "AnimEvent.h" #include "AnimEvent.h"
#include "Engine/Engine/Engine.h" #include "Engine/Engine/Engine.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Level/Actors/AnimatedModel.h" #include "Engine/Level/Actors/AnimatedModel.h"
#include "Engine/Engine/Time.h" #include "Engine/Engine/Time.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
@@ -69,6 +70,7 @@ AnimContinuousEvent::AnimContinuousEvent(const SpawnParams& params)
bool AnimationsService::Init() bool AnimationsService::Init()
{ {
PROFILE_MEM(Animations);
Animations::System = New<AnimationsSystem>(); Animations::System = New<AnimationsSystem>();
Engine::UpdateGraph->AddSystem(Animations::System); Engine::UpdateGraph->AddSystem(Animations::System);
return false; return false;
@@ -83,6 +85,7 @@ void AnimationsService::Dispose()
void AnimationsSystem::Job(int32 index) void AnimationsSystem::Job(int32 index)
{ {
PROFILE_CPU_NAMED("Animations.Job"); PROFILE_CPU_NAMED("Animations.Job");
PROFILE_MEM(Animations);
auto animatedModel = AnimationManagerInstance.UpdateList[index]; auto animatedModel = AnimationManagerInstance.UpdateList[index];
if (CanUpdateModel(animatedModel)) if (CanUpdateModel(animatedModel))
{ {
@@ -147,6 +150,7 @@ void AnimationsSystem::PostExecute(TaskGraph* graph)
if (!Active) if (!Active)
return; return;
PROFILE_CPU_NAMED("Animations.PostExecute"); PROFILE_CPU_NAMED("Animations.PostExecute");
PROFILE_MEM(Animations);
// Update gameplay // Update gameplay
for (int32 index = 0; index < AnimationManagerInstance.UpdateList.Count(); index++) for (int32 index = 0; index < AnimationManagerInstance.UpdateList.Count(); index++)

View File

@@ -6,6 +6,7 @@
#include "Engine/Content/Assets/SkinnedModel.h" #include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Graphics/Models/SkeletonData.h" #include "Engine/Graphics/Models/SkeletonData.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Threading/Threading.h"
extern void RetargetSkeletonPose(const SkeletonData& sourceSkeleton, const SkeletonData& targetSkeleton, const SkinnedModel::SkeletonMapping& mapping, const Transform* sourceNodes, Transform* targetNodes); extern void RetargetSkeletonPose(const SkeletonData& sourceSkeleton, const SkeletonData& targetSkeleton, const SkinnedModel::SkeletonMapping& mapping, const Transform* sourceNodes, Transform* targetNodes);

View File

@@ -7,6 +7,7 @@
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Content/Deprecated.h" #include "Engine/Content/Deprecated.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Audio/AudioClip.h" #include "Engine/Audio/AudioClip.h"
#include "Engine/Graphics/PostProcessSettings.h" #include "Engine/Graphics/PostProcessSettings.h"
#if USE_EDITOR #if USE_EDITOR
@@ -249,6 +250,7 @@ bool SceneAnimation::Save(const StringView& path)
Asset::LoadResult SceneAnimation::load() Asset::LoadResult SceneAnimation::load()
{ {
TrackStatesCount = 0; TrackStatesCount = 0;
PROFILE_MEM(AnimationsData);
// Get the data chunk // Get the data chunk
if (LoadChunk(0)) if (LoadChunk(0))

View File

@@ -12,6 +12,7 @@
#include "Engine/Audio/AudioSource.h" #include "Engine/Audio/AudioSource.h"
#include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderTask.h"
#include "Engine/Renderer/RenderList.h" #include "Engine/Renderer/RenderList.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Script.h" #include "Engine/Scripting/Script.h"
#include "Engine/Scripting/ManagedCLR/MException.h" #include "Engine/Scripting/ManagedCLR/MException.h"
@@ -151,6 +152,7 @@ void SceneAnimationPlayer::Tick(float dt)
SceneAnimation* anim = Animation.Get(); SceneAnimation* anim = Animation.Get();
if (!anim || !anim->IsLoaded()) if (!anim || !anim->IsLoaded())
return; return;
PROFILE_MEM(Animations);
// Setup state // Setup state
if (_tracks.Count() != anim->TrackStatesCount) if (_tracks.Count() != anim->TrackStatesCount)
@@ -229,6 +231,7 @@ void SceneAnimationPlayer::MapTrack(const StringView& from, const Guid& to)
SceneAnimation* anim = Animation.Get(); SceneAnimation* anim = Animation.Get();
if (!anim || !anim->IsLoaded()) if (!anim || !anim->IsLoaded())
return; return;
PROFILE_MEM(Animations);
for (int32 j = 0; j < anim->Tracks.Count(); j++) for (int32 j = 0; j < anim->Tracks.Count(); j++)
{ {
const auto& track = anim->Tracks[j]; const auto& track = anim->Tracks[j];

View File

@@ -8,6 +8,7 @@
#include "Engine/Scripting/BinaryModule.h" #include "Engine/Scripting/BinaryModule.h"
#include "Engine/Level/Level.h" #include "Engine/Level/Level.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Engine/Engine.h" #include "Engine/Engine/Engine.h"
#include "Engine/Engine/CommandLine.h" #include "Engine/Engine/CommandLine.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
@@ -151,6 +152,7 @@ void Audio::SetEnableHRTF(bool value)
bool AudioService::Init() bool AudioService::Init()
{ {
PROFILE_CPU_NAMED("Audio.Init"); PROFILE_CPU_NAMED("Audio.Init");
PROFILE_MEM(Audio);
const auto settings = AudioSettings::Get(); const auto settings = AudioSettings::Get();
const bool mute = CommandLine::Options.Mute.IsTrue() || settings->DisableAudio; const bool mute = CommandLine::Options.Mute.IsTrue() || settings->DisableAudio;
@@ -211,6 +213,7 @@ bool AudioService::Init()
void AudioService::Update() void AudioService::Update()
{ {
PROFILE_CPU_NAMED("Audio.Update"); PROFILE_CPU_NAMED("Audio.Update");
PROFILE_MEM(Audio);
// Update the master volume // Update the master volume
float masterVolume = MasterVolume; float masterVolume = MasterVolume;

View File

@@ -10,6 +10,7 @@
#include "Engine/Scripting/ManagedCLR/MUtils.h" #include "Engine/Scripting/ManagedCLR/MUtils.h"
#include "Engine/Streaming/StreamingGroup.h" #include "Engine/Streaming/StreamingGroup.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h" #include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
#include "Engine/Tools/AudioTool/AudioTool.h" #include "Engine/Tools/AudioTool/AudioTool.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
@@ -18,6 +19,7 @@ REGISTER_BINARY_ASSET_WITH_UPGRADER(AudioClip, "FlaxEngine.AudioClip", AudioClip
bool AudioClip::StreamingTask::Run() bool AudioClip::StreamingTask::Run()
{ {
PROFILE_MEM(Audio);
AssetReference<AudioClip> ref = _asset.Get(); AssetReference<AudioClip> ref = _asset.Get();
if (ref == nullptr || AudioBackend::Instance == nullptr) if (ref == nullptr || AudioBackend::Instance == nullptr)
return true; return true;
@@ -318,6 +320,7 @@ bool AudioClip::init(AssetInitData& initData)
Asset::LoadResult AudioClip::load() Asset::LoadResult AudioClip::load()
{ {
PROFILE_MEM(Audio);
#if !COMPILE_WITH_OGG_VORBIS #if !COMPILE_WITH_OGG_VORBIS
if (AudioHeader.Format == AudioFormat::Vorbis) if (AudioHeader.Format == AudioFormat::Vorbis)
{ {

View File

@@ -21,9 +21,8 @@ AudioSource::AudioSource(const SpawnParams& params)
, _playOnStart(false) , _playOnStart(false)
, _startTime(0.0f) , _startTime(0.0f)
, _allowSpatialization(true) , _allowSpatialization(true)
, Clip(this)
{ {
Clip.Changed.Bind<AudioSource, &AudioSource::OnClipChanged>(this);
Clip.Loaded.Bind<AudioSource, &AudioSource::OnClipLoaded>(this);
} }
void AudioSource::SetVolume(float value) void AudioSource::SetVolume(float value)
@@ -264,7 +263,7 @@ void AudioSource::RequestStreamingBuffersUpdate()
_needToUpdateStreamingBuffers = true; _needToUpdateStreamingBuffers = true;
} }
void AudioSource::OnClipChanged() void AudioSource::OnAssetChanged(Asset* asset, void* caller)
{ {
Stop(); Stop();
@@ -276,7 +275,7 @@ void AudioSource::OnClipChanged()
} }
} }
void AudioSource::OnClipLoaded() void AudioSource::OnAssetLoaded(Asset* asset, void* caller)
{ {
if (!SourceID) if (!SourceID)
return; return;
@@ -302,6 +301,10 @@ void AudioSource::OnClipLoaded()
} }
} }
void AudioSource::OnAssetUnloaded(Asset* asset, void* caller)
{
}
bool AudioSource::UseStreaming() const bool AudioSource::UseStreaming() const
{ {
if (Clip == nullptr || Clip->WaitForLoaded()) if (Clip == nullptr || Clip->WaitForLoaded())

View File

@@ -13,7 +13,7 @@
/// Whether or not an audio source is spatial is controlled by the assigned AudioClip.The volume and the pitch of a spatial audio source is controlled by its position and the AudioListener's position/direction/velocity. /// Whether or not an audio source is spatial is controlled by the assigned AudioClip.The volume and the pitch of a spatial audio source is controlled by its position and the AudioListener's position/direction/velocity.
/// </remarks> /// </remarks>
API_CLASS(Attributes="ActorContextMenu(\"New/Audio/Audio Source\"), ActorToolbox(\"Other\")") API_CLASS(Attributes="ActorContextMenu(\"New/Audio/Audio Source\"), ActorToolbox(\"Other\")")
class FLAXENGINE_API AudioSource : public Actor class FLAXENGINE_API AudioSource : public Actor, IAssetReference
{ {
DECLARE_SCENE_OBJECT(AudioSource); DECLARE_SCENE_OBJECT(AudioSource);
friend class AudioStreamingHandler; friend class AudioStreamingHandler;
@@ -293,8 +293,10 @@ public:
void RequestStreamingBuffersUpdate(); void RequestStreamingBuffersUpdate();
private: private:
void OnClipChanged(); // [IAssetReference]
void OnClipLoaded(); void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
/// <summary> /// <summary>
/// Plays the audio source. Should have buffer(s) binded before. /// Plays the audio source. Should have buffer(s) binded before.

View File

@@ -9,6 +9,7 @@
#include "Engine/Tools/AudioTool/AudioTool.h" #include "Engine/Tools/AudioTool/AudioTool.h"
#include "Engine/Engine/Units.h" #include "Engine/Engine/Units.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Audio/Audio.h" #include "Engine/Audio/Audio.h"
#include "Engine/Audio/AudioListener.h" #include "Engine/Audio/AudioListener.h"
#include "Engine/Audio/AudioSource.h" #include "Engine/Audio/AudioSource.h"
@@ -321,6 +322,8 @@ void AudioBackendOAL::Listener_ReinitializeAll()
uint32 AudioBackendOAL::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler) uint32 AudioBackendOAL::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
{ {
PROFILE_MEM(Audio);
uint32 sourceID = 0; uint32 sourceID = 0;
ALC::Source::Rebuild(sourceID, position, orientation, volume, pitch, pan, loop, spatial, attenuation, minDistance, doppler); ALC::Source::Rebuild(sourceID, position, orientation, volume, pitch, pan, loop, spatial, attenuation, minDistance, doppler);
@@ -516,6 +519,7 @@ void AudioBackendOAL::Buffer_Delete(uint32 bufferID)
void AudioBackendOAL::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info) void AudioBackendOAL::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info)
{ {
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Audio);
// Pick the format for the audio data (it might not be supported natively) // Pick the format for the audio data (it might not be supported natively)
ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth); ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
@@ -625,6 +629,8 @@ AudioBackend::FeatureFlags AudioBackendOAL::Base_Features()
void AudioBackendOAL::Base_OnActiveDeviceChanged() void AudioBackendOAL::Base_OnActiveDeviceChanged()
{ {
PROFILE_MEM(Audio);
// Cleanup // Cleanup
Array<ALC::AudioSourceState> states; Array<ALC::AudioSourceState> states;
states.EnsureCapacity(Audio::Sources.Count()); states.EnsureCapacity(Audio::Sources.Count());

View File

@@ -9,6 +9,7 @@
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Audio/Audio.h" #include "Engine/Audio/Audio.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS
// Tweak Win ver // Tweak Win ver
@@ -232,6 +233,7 @@ void AudioBackendXAudio2::Listener_ReinitializeAll()
uint32 AudioBackendXAudio2::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler) uint32 AudioBackendXAudio2::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
{ {
PROFILE_MEM(Audio);
ScopeLock lock(XAudio2::Locker); ScopeLock lock(XAudio2::Locker);
// Get first free source // Get first free source
@@ -580,6 +582,7 @@ void AudioBackendXAudio2::Source_DequeueProcessedBuffers(uint32 sourceID)
uint32 AudioBackendXAudio2::Buffer_Create() uint32 AudioBackendXAudio2::Buffer_Create()
{ {
PROFILE_MEM(Audio);
uint32 bufferID; uint32 bufferID;
ScopeLock lock(XAudio2::Locker); ScopeLock lock(XAudio2::Locker);
@@ -618,6 +621,7 @@ void AudioBackendXAudio2::Buffer_Delete(uint32 bufferID)
void AudioBackendXAudio2::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info) void AudioBackendXAudio2::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info)
{ {
PROFILE_MEM(Audio);
CHECK(info.NumChannels <= MAX_INPUT_CHANNELS); CHECK(info.NumChannels <= MAX_INPUT_CHANNELS);
XAudio2::Locker.Lock(); XAudio2::Locker.Lock();

View File

@@ -9,6 +9,7 @@
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/LogContext.h" #include "Engine/Core/LogContext.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/ManagedCLR/MCore.h" #include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Threading/MainThreadTask.h" #include "Engine/Threading/MainThreadTask.h"
#include "Engine/Threading/ThreadLocal.h" #include "Engine/Threading/ThreadLocal.h"
@@ -34,15 +35,18 @@ bool ContentDeprecated::Clear(bool newValue)
#endif #endif
AssetReferenceBase::AssetReferenceBase(IAssetReference* owner)
: _owner(owner)
{
}
AssetReferenceBase::~AssetReferenceBase() AssetReferenceBase::~AssetReferenceBase()
{ {
Asset* asset = _asset; Asset* asset = _asset;
if (asset) if (asset)
{ {
_asset = nullptr; _asset = nullptr;
asset->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this); asset->RemoveReference(this);
asset->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
asset->RemoveReference();
} }
} }
@@ -51,52 +55,60 @@ String AssetReferenceBase::ToString() const
return _asset ? _asset->ToString() : TEXT("<null>"); return _asset ? _asset->ToString() : TEXT("<null>");
} }
void AssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
{
if (_owner)
_owner->OnAssetChanged(asset, this);
}
void AssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
Loaded();
if (_owner)
_owner->OnAssetLoaded(asset, this);
}
void AssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
Unload();
OnSet(nullptr);
if (_owner)
_owner->OnAssetUnloaded(asset, this);
}
void AssetReferenceBase::OnSet(Asset* asset) void AssetReferenceBase::OnSet(Asset* asset)
{ {
auto e = _asset; auto e = _asset;
if (e != asset) if (e != asset)
{ {
if (e) if (e)
{ e->RemoveReference(this);
e->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
e->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
e->RemoveReference();
}
_asset = e = asset; _asset = e = asset;
if (e) if (e)
{ e->AddReference(this);
e->AddReference();
e->OnLoaded.Bind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
e->OnUnloaded.Bind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
}
Changed(); Changed();
if (_owner)
_owner->OnAssetChanged(asset, this);
if (e && e->IsLoaded()) if (e && e->IsLoaded())
{
Loaded(); Loaded();
if (_owner)
_owner->OnAssetLoaded(asset, this);
}
} }
} }
void AssetReferenceBase::OnLoaded(Asset* asset)
{
if (_asset != asset)
return;
Loaded();
}
void AssetReferenceBase::OnUnloaded(Asset* asset)
{
if (_asset != asset)
return;
Unload();
OnSet(nullptr);
}
WeakAssetReferenceBase::~WeakAssetReferenceBase() WeakAssetReferenceBase::~WeakAssetReferenceBase()
{ {
Asset* asset = _asset; Asset* asset = _asset;
if (asset) if (asset)
{ {
_asset = nullptr; _asset = nullptr;
asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this); asset->RemoveReference(this, true);
} }
} }
@@ -105,36 +117,43 @@ String WeakAssetReferenceBase::ToString() const
return _asset ? _asset->ToString() : TEXT("<null>"); return _asset ? _asset->ToString() : TEXT("<null>");
} }
void WeakAssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
{
}
void WeakAssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
{
}
void WeakAssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
Unload();
asset->RemoveReference(this, true);
_asset = nullptr;
}
void WeakAssetReferenceBase::OnSet(Asset* asset) void WeakAssetReferenceBase::OnSet(Asset* asset)
{ {
auto e = _asset; auto e = _asset;
if (e != asset) if (e != asset)
{ {
if (e) if (e)
e->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this); e->RemoveReference(this, true);
_asset = e = asset; _asset = e = asset;
if (e) if (e)
e->OnUnloaded.Bind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this); e->AddReference(this, true);
} }
} }
void WeakAssetReferenceBase::OnUnloaded(Asset* asset)
{
if (_asset != asset)
return;
Unload();
asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
_asset = nullptr;
}
SoftAssetReferenceBase::~SoftAssetReferenceBase() SoftAssetReferenceBase::~SoftAssetReferenceBase()
{ {
Asset* asset = _asset; Asset* asset = _asset;
if (asset) if (asset)
{ {
_asset = nullptr; _asset = nullptr;
asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this); asset->RemoveReference(this);
asset->RemoveReference();
} }
#if !BUILD_RELEASE #if !BUILD_RELEASE
_id = Guid::Empty; _id = Guid::Empty;
@@ -146,22 +165,34 @@ String SoftAssetReferenceBase::ToString() const
return _asset ? _asset->ToString() : (_id.IsValid() ? _id.ToString() : TEXT("<null>")); return _asset ? _asset->ToString() : (_id.IsValid() ? _id.ToString() : TEXT("<null>"));
} }
void SoftAssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
{
}
void SoftAssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
{
}
void SoftAssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
_asset->RemoveReference(this);
_asset = nullptr;
_id = Guid::Empty;
Changed();
}
void SoftAssetReferenceBase::OnSet(Asset* asset) void SoftAssetReferenceBase::OnSet(Asset* asset)
{ {
if (_asset == asset) if (_asset == asset)
return; return;
if (_asset) if (_asset)
{ _asset->RemoveReference(this);
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->RemoveReference();
}
_asset = asset; _asset = asset;
_id = asset ? asset->GetID() : Guid::Empty; _id = asset ? asset->GetID() : Guid::Empty;
if (asset) if (asset)
{ asset->AddReference(this);
asset->AddReference();
asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
}
Changed(); Changed();
} }
@@ -170,10 +201,7 @@ void SoftAssetReferenceBase::OnSet(const Guid& id)
if (_id == id) if (_id == id)
return; return;
if (_asset) if (_asset)
{ _asset->RemoveReference(this);
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->RemoveReference();
}
_asset = nullptr; _asset = nullptr;
_id = id; _id = id;
Changed(); Changed();
@@ -184,21 +212,7 @@ void SoftAssetReferenceBase::OnResolve(const ScriptingTypeHandle& type)
ASSERT(!_asset); ASSERT(!_asset);
_asset = ::LoadAsset(_id, type); _asset = ::LoadAsset(_id, type);
if (_asset) if (_asset)
{ _asset->AddReference(this);
_asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->AddReference();
}
}
void SoftAssetReferenceBase::OnUnloaded(Asset* asset)
{
if (_asset != asset)
return;
_asset->RemoveReference();
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset = nullptr;
_id = Guid::Empty;
Changed();
} }
Asset::Asset(const SpawnParams& params, const AssetInfo* info) Asset::Asset(const SpawnParams& params, const AssetInfo* info)
@@ -216,6 +230,39 @@ int32 Asset::GetReferencesCount() const
return (int32)Platform::AtomicRead(const_cast<int64 volatile*>(&_refCount)); return (int32)Platform::AtomicRead(const_cast<int64 volatile*>(&_refCount));
} }
void Asset::AddReference()
{
Platform::InterlockedIncrement(&_refCount);
}
void Asset::AddReference(IAssetReference* ref, bool week)
{
if (!week)
Platform::InterlockedIncrement(&_refCount);
if (ref)
{
//PROFILE_MEM(EngineDelegate); // Include references tracking memory within Delegate memory
ScopeLock lock(_referencesLocker);
_references.Add(ref);
}
}
void Asset::RemoveReference()
{
Platform::InterlockedDecrement(&_refCount);
}
void Asset::RemoveReference(IAssetReference* ref, bool week)
{
if (ref)
{
ScopeLock lock(_referencesLocker);
_references.Remove(ref);
}
if (!week)
Platform::InterlockedDecrement(&_refCount);
}
String Asset::ToString() const String Asset::ToString() const
{ {
return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath()); return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath());
@@ -354,6 +401,7 @@ uint64 Asset::GetMemoryUsage() const
if (Platform::AtomicRead(&_loadingTask)) if (Platform::AtomicRead(&_loadingTask))
result += sizeof(ContentLoadTask); result += sizeof(ContentLoadTask);
result += (OnLoaded.Capacity() + OnReloading.Capacity() + OnUnloaded.Capacity()) * sizeof(EventType::FunctionType); result += (OnLoaded.Capacity() + OnReloading.Capacity() + OnUnloaded.Capacity()) * sizeof(EventType::FunctionType);
result += _references.Capacity() * sizeof(HashSet<IAssetReference*>::Bucket);
Locker.Unlock(); Locker.Unlock();
return result; return result;
} }
@@ -444,6 +492,9 @@ bool Asset::WaitForLoaded(double timeoutInMilliseconds) const
} }
PROFILE_CPU(); PROFILE_CPU();
ZoneColor(TracyWaitZoneColor);
const StringView path(GetPath());
ZoneText(*path, path.Length());
Content::WaitForTask(loadingTask, timeoutInMilliseconds); Content::WaitForTask(loadingTask, timeoutInMilliseconds);
@@ -528,6 +579,7 @@ ContentLoadTask* Asset::createLoadingTask()
void Asset::startLoading() void Asset::startLoading()
{ {
PROFILE_MEM(ContentAssets);
ASSERT(!IsLoaded()); ASSERT(!IsLoaded());
ASSERT(Platform::AtomicRead(&_loadingTask) == 0); ASSERT(Platform::AtomicRead(&_loadingTask) == 0);
auto loadingTask = createLoadingTask(); auto loadingTask = createLoadingTask();
@@ -627,6 +679,9 @@ void Asset::onLoaded_MainThread()
ASSERT(IsInMainThread()); ASSERT(IsInMainThread());
// Send event // Send event
ScopeLock lock(_referencesLocker);
for (const auto& e : _references)
e.Item->OnAssetLoaded(this, this);
OnLoaded(this); OnLoaded(this);
} }
@@ -640,6 +695,9 @@ void Asset::onUnload_MainThread()
CancelStreaming(); CancelStreaming();
// Send event // Send event
ScopeLock lock(_referencesLocker);
for (const auto& e : _references)
e.Item->OnAssetUnloaded(this, this);
OnUnloaded(this); OnUnloaded(this);
} }

View File

@@ -7,6 +7,7 @@
#include "Engine/Core/Types/String.h" #include "Engine/Core/Types/String.h"
#include "Engine/Platform/CriticalSection.h" #include "Engine/Platform/CriticalSection.h"
#include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Threading/ConcurrentSystemLocker.h"
#include "Config.h" #include "Config.h"
#include "Types.h" #include "Types.h"
@@ -18,6 +19,20 @@
public: \ public: \
explicit type(const SpawnParams& params, const AssetInfo* info) explicit type(const SpawnParams& params, const AssetInfo* info)
// Utility interface for objects that reference asset and want to get notified about asset reference changes.
class FLAXENGINE_API IAssetReference
{
public:
virtual ~IAssetReference() = default;
// Asset reference got changed.
virtual void OnAssetChanged(Asset* asset, void* caller) = 0;
// Asset got loaded.
virtual void OnAssetLoaded(Asset* asset, void* caller) = 0;
// Asset gets unloaded.
virtual void OnAssetUnloaded(Asset* asset, void* caller) = 0;
};
/// <summary> /// <summary>
/// Asset objects base class. /// Asset objects base class.
/// </summary> /// </summary>
@@ -48,6 +63,9 @@ protected:
int8 _deleteFileOnUnload : 1; // Indicates that asset source file should be removed on asset unload int8 _deleteFileOnUnload : 1; // Indicates that asset source file should be removed on asset unload
int8 _isVirtual : 1; // Indicates that asset is pure virtual (generated or temporary, has no storage so won't be saved) int8 _isVirtual : 1; // Indicates that asset is pure virtual (generated or temporary, has no storage so won't be saved)
HashSet<IAssetReference*> _references;
CriticalSection _referencesLocker; // TODO: convert into a single interlocked exchange for the current thread owning lock
public: public:
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Asset"/> class. /// Initializes a new instance of the <see cref="Asset"/> class.
@@ -88,18 +106,22 @@ public:
/// <summary> /// <summary>
/// Adds reference to that asset. /// Adds reference to that asset.
/// </summary> /// </summary>
FORCE_INLINE void AddReference() void AddReference();
{
Platform::InterlockedIncrement(&_refCount); /// <summary>
} /// Adds reference to that asset.
/// </summary>
void AddReference(IAssetReference* ref, bool week = false);
/// <summary> /// <summary>
/// Removes reference from that asset. /// Removes reference from that asset.
/// </summary> /// </summary>
FORCE_INLINE void RemoveReference() void RemoveReference();
{
Platform::InterlockedDecrement(&_refCount); /// <summary>
} /// Removes reference from that asset.
/// </summary>
void RemoveReference(IAssetReference* ref, bool week = false);
public: public:
/// <summary> /// <summary>

View File

@@ -7,10 +7,11 @@
/// <summary> /// <summary>
/// Asset reference utility. Keeps reference to the linked asset object and handles load/unload events. /// Asset reference utility. Keeps reference to the linked asset object and handles load/unload events.
/// </summary> /// </summary>
class FLAXENGINE_API AssetReferenceBase class FLAXENGINE_API AssetReferenceBase : public IAssetReference
{ {
protected: protected:
Asset* _asset = nullptr; Asset* _asset = nullptr;
IAssetReference* _owner = nullptr;
public: public:
/// <summary> /// <summary>
@@ -36,6 +37,12 @@ public:
/// </summary> /// </summary>
AssetReferenceBase() = default; AssetReferenceBase() = default;
/// <summary>
/// Initializes a new instance of the <see cref="AssetReferenceBase"/> class.
/// </summary>
/// <param name="owner">The reference owner to keep notified about asset changes.</param>
AssetReferenceBase(IAssetReference* owner);
/// <summary> /// <summary>
/// Finalizes an instance of the <see cref="AssetReferenceBase"/> class. /// Finalizes an instance of the <see cref="AssetReferenceBase"/> class.
/// </summary> /// </summary>
@@ -63,10 +70,14 @@ public:
/// </summary> /// </summary>
String ToString() const; String ToString() const;
public:
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
protected: protected:
void OnSet(Asset* asset); void OnSet(Asset* asset);
void OnLoaded(Asset* asset);
void OnUnloaded(Asset* asset);
}; };
/// <summary> /// <summary>
@@ -87,6 +98,13 @@ public:
{ {
} }
/// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>
explicit AssetReference(decltype(__nullptr))
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class. /// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary> /// </summary>
@@ -96,6 +114,15 @@ public:
OnSet((Asset*)asset); OnSet((Asset*)asset);
} }
/// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>
/// <param name="owner">The reference owner to keep notified about asset changes.</param>
explicit AssetReference(IAssetReference* owner)
: AssetReferenceBase(owner)
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class. /// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary> /// </summary>

View File

@@ -9,6 +9,7 @@
#include "Engine/Animations/Animations.h" #include "Engine/Animations/Animations.h"
#include "Engine/Animations/SceneAnimations/SceneAnimation.h" #include "Engine/Animations/SceneAnimations/SceneAnimation.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#if USE_EDITOR #if USE_EDITOR
@@ -598,6 +599,7 @@ void Animation::OnScriptingDispose()
Asset::LoadResult Animation::load() Asset::LoadResult Animation::load()
{ {
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Get stream with animations data // Get stream with animations data

View File

@@ -9,6 +9,7 @@
#include "Engine/Core/Types/DataContainer.h" #include "Engine/Core/Types/DataContainer.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Animations/Animations.h" #include "Engine/Animations/Animations.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
@@ -25,6 +26,7 @@ AnimationGraph::AnimationGraph(const SpawnParams& params, const AssetInfo* info)
Asset::LoadResult AnimationGraph::load() Asset::LoadResult AnimationGraph::load()
{ {
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Get stream with graph data // Get stream with graph data
@@ -83,6 +85,7 @@ bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, b
Log::ArgumentNullException(); Log::ArgumentNullException();
return true; return true;
} }
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Create Graph data // Create Graph data

View File

@@ -8,6 +8,7 @@
#include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Serialization/MemoryWriteStream.h"
#endif #endif
#include "Engine/Animations/Animations.h" #include "Engine/Animations/Animations.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
@@ -20,6 +21,7 @@ AnimationGraphFunction::AnimationGraphFunction(const SpawnParams& params, const
Asset::LoadResult AnimationGraphFunction::load() Asset::LoadResult AnimationGraphFunction::load()
{ {
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Get graph data from chunk // Get graph data from chunk

View File

@@ -165,9 +165,13 @@ Asset::LoadResult Material::load()
MaterialGenerator generator; MaterialGenerator generator;
generator.Error.Bind(&OnGeneratorError); generator.Error.Bind(&OnGeneratorError);
if (_shaderHeader.Material.GraphVersion != MATERIAL_GRAPH_VERSION) if (_shaderHeader.Material.GraphVersion != MATERIAL_GRAPH_VERSION)
{
LOG(Info, "Converting material \'{0}\', from version {1} to {2}...", name, _shaderHeader.Material.GraphVersion, MATERIAL_GRAPH_VERSION); LOG(Info, "Converting material \'{0}\', from version {1} to {2}...", name, _shaderHeader.Material.GraphVersion, MATERIAL_GRAPH_VERSION);
}
else else
{
LOG(Info, "Updating material \'{0}\'...", name); LOG(Info, "Updating material \'{0}\'...", name);
}
// Load or create material surface // Load or create material surface
MaterialLayer* layer; MaterialLayer* layer;
@@ -410,16 +414,18 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
// Prepare // Prepare
auto& info = _shaderHeader.Material.Info; auto& info = _shaderHeader.Material.Info;
const bool isSurfaceOrTerrainOrDeformable = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain || info.Domain == MaterialDomain::Deformable; const bool isSurfaceOrTerrainOrDeformable = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain || info.Domain == MaterialDomain::Deformable;
const bool isOpaque = info.BlendMode == MaterialBlendMode::Opaque;
const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage; const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage;
const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && info.BlendMode != MaterialBlendMode::Opaque) || info.Domain == MaterialDomain::Particle; const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && !isOpaque) || info.Domain == MaterialDomain::Particle;
const bool useTess = const bool useTess =
info.TessellationMode != TessellationMethod::None && info.TessellationMode != TessellationMethod::None &&
RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrainOrDeformable; RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrainOrDeformable;
const bool useDistortion = const bool useDistortion =
(info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) && (info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) &&
info.BlendMode != MaterialBlendMode::Opaque && !isOpaque &&
EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseRefraction) && EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseRefraction) &&
(info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None; (info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None;
const MaterialShadingModel shadingModel = info.ShadingModel == MaterialShadingModel::CustomLit ? MaterialShadingModel::Unlit : info.ShadingModel;
// @formatter:off // @formatter:off
static const char* Numbers[] = static const char* Numbers[] =
@@ -431,7 +437,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
// Setup shader macros // Setup shader macros
options.Macros.Add({ "MATERIAL_DOMAIN", Numbers[(int32)info.Domain] }); options.Macros.Add({ "MATERIAL_DOMAIN", Numbers[(int32)info.Domain] });
options.Macros.Add({ "MATERIAL_BLEND", Numbers[(int32)info.BlendMode] }); options.Macros.Add({ "MATERIAL_BLEND", Numbers[(int32)info.BlendMode] });
options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)info.ShadingModel] }); options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)shadingModel] });
options.Macros.Add({ "MATERIAL_MASKED", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseMask) ? 1 : 0] }); options.Macros.Add({ "MATERIAL_MASKED", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseMask) ? 1 : 0] });
options.Macros.Add({ "DECAL_BLEND_MODE", Numbers[(int32)info.DecalBlendingMode] }); options.Macros.Add({ "DECAL_BLEND_MODE", Numbers[(int32)info.DecalBlendingMode] });
options.Macros.Add({ "USE_EMISSIVE", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseEmissive) ? 1 : 0] }); options.Macros.Add({ "USE_EMISSIVE", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseEmissive) ? 1 : 0] });
@@ -488,7 +494,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] }); options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] });
options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] }); options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] });
options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] }); options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] });
options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] }); options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && isOpaque ? 1 : 0] });
options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] }); options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] });
#endif #endif
} }

View File

@@ -18,6 +18,7 @@
#include "Engine/Graphics/Models/MeshDeformation.h" #include "Engine/Graphics/Models/MeshDeformation.h"
#include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Renderer/DrawCall.h" #include "Engine/Renderer/DrawCall.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Tools/ModelTool/ModelTool.h" #include "Engine/Tools/ModelTool/ModelTool.h"
@@ -304,6 +305,7 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
Log::ArgumentOutOfRangeException(); Log::ArgumentOutOfRangeException();
return true; return true;
} }
PROFILE_MEM(GraphicsMeshes);
// Dispose previous data and disable streaming (will start data uploading tasks manually) // Dispose previous data and disable streaming (will start data uploading tasks manually)
StopStreaming(); StopStreaming();
@@ -343,6 +345,7 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
bool Model::LoadHeader(ReadStream& stream, byte& headerVersion) bool Model::LoadHeader(ReadStream& stream, byte& headerVersion)
{ {
PROFILE_MEM(GraphicsMeshes);
if (ModelBase::LoadHeader(stream, headerVersion)) if (ModelBase::LoadHeader(stream, headerVersion))
return true; return true;
@@ -509,6 +512,7 @@ bool Model::Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk
void Model::SetupMaterialSlots(int32 slotsCount) void Model::SetupMaterialSlots(int32 slotsCount)
{ {
PROFILE_MEM(GraphicsMeshes);
ModelBase::SetupMaterialSlots(slotsCount); ModelBase::SetupMaterialSlots(slotsCount);
// Adjust meshes indices for slots // Adjust meshes indices for slots
@@ -584,6 +588,8 @@ int32 Model::GetAllocatedResidency() const
Asset::LoadResult Model::load() Asset::LoadResult Model::load()
{ {
PROFILE_MEM(GraphicsMeshes);
// Get header chunk // Get header chunk
auto chunk0 = GetChunk(0); auto chunk0 = GetChunk(0);
if (chunk0 == nullptr || chunk0->IsMissing()) if (chunk0 == nullptr || chunk0->IsMissing())

View File

@@ -5,10 +5,12 @@
#include "Engine/Core/Math/Transform.h" #include "Engine/Core/Math/Transform.h"
#include "Engine/Content/WeakAssetReference.h" #include "Engine/Content/WeakAssetReference.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Graphics/Config.h" #include "Engine/Graphics/Config.h"
#include "Engine/Graphics/Models/MeshBase.h" #include "Engine/Graphics/Models/MeshBase.h"
#include "Engine/Graphics/Models/MeshDeformation.h" #include "Engine/Graphics/Models/MeshDeformation.h"
#include "Engine/Graphics/Shaders/GPUVertexLayout.h" #include "Engine/Graphics/Shaders/GPUVertexLayout.h"
#include "Engine/Threading/Threading.h"
#if GPU_ENABLE_ASYNC_RESOURCES_CREATION #if GPU_ENABLE_ASYNC_RESOURCES_CREATION
#include "Engine/Threading/ThreadPoolTask.h" #include "Engine/Threading/ThreadPoolTask.h"
#define STREAM_TASK_BASE ThreadPoolTask #define STREAM_TASK_BASE ThreadPoolTask
@@ -51,6 +53,7 @@ public:
AssetReference<ModelBase> model = _model.Get(); AssetReference<ModelBase> model = _model.Get();
if (model == nullptr) if (model == nullptr)
return true; return true;
PROFILE_MEM(GraphicsMeshes);
// Get data // Get data
BytesContainer data; BytesContainer data;
@@ -334,6 +337,8 @@ bool ModelBase::LoadHeader(ReadStream& stream, byte& headerVersion)
bool ModelBase::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly) bool ModelBase::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly)
{ {
PROFILE_MEM(GraphicsMeshes);
// Load descriptor // Load descriptor
static_assert(MODEL_MESH_VERSION == 2, "Update code"); static_assert(MODEL_MESH_VERSION == 2, "Update code");
uint32 vertices, triangles; uint32 vertices, triangles;

View File

@@ -6,6 +6,7 @@
#include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Upgraders/SkeletonMaskUpgrader.h" #include "Engine/Content/Upgraders/SkeletonMaskUpgrader.h"
#include "Engine/Threading/Threading.h"
REGISTER_BINARY_ASSET_WITH_UPGRADER(SkeletonMask, "FlaxEngine.SkeletonMask", SkeletonMaskUpgrader, true); REGISTER_BINARY_ASSET_WITH_UPGRADER(SkeletonMask, "FlaxEngine.SkeletonMask", SkeletonMaskUpgrader, true);

View File

@@ -18,6 +18,7 @@
#include "Engine/Content/Upgraders/SkinnedModelAssetUpgrader.h" #include "Engine/Content/Upgraders/SkinnedModelAssetUpgrader.h"
#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h" #include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Renderer/DrawCall.h" #include "Engine/Renderer/DrawCall.h"
#if USE_EDITOR #if USE_EDITOR
#include "Engine/Graphics/Models/ModelData.h" #include "Engine/Graphics/Models/ModelData.h"
@@ -458,6 +459,7 @@ bool SkinnedModel::Init(const Span<int32>& meshesCountPerLod)
Log::ArgumentOutOfRangeException(); Log::ArgumentOutOfRangeException();
return true; return true;
} }
PROFILE_MEM(GraphicsMeshes);
// Dispose previous data and disable streaming (will start data uploading tasks manually) // Dispose previous data and disable streaming (will start data uploading tasks manually)
StopStreaming(); StopStreaming();
@@ -501,6 +503,7 @@ void BlendShape::LoadHeader(ReadStream& stream, byte headerVersion)
void BlendShape::Load(ReadStream& stream, byte meshVersion) void BlendShape::Load(ReadStream& stream, byte meshVersion)
{ {
PROFILE_MEM(GraphicsMeshes);
UseNormals = stream.ReadBool(); UseNormals = stream.ReadBool();
stream.ReadUint32(&MinVertexIndex); stream.ReadUint32(&MinVertexIndex);
stream.ReadUint32(&MaxVertexIndex); stream.ReadUint32(&MaxVertexIndex);
@@ -531,6 +534,7 @@ void BlendShape::Save(WriteStream& stream) const
bool SkinnedModel::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly) bool SkinnedModel::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly)
{ {
PROFILE_MEM(GraphicsMeshes);
if (ModelBase::LoadMesh(stream, meshVersion, mesh, dataIfReadOnly)) if (ModelBase::LoadMesh(stream, meshVersion, mesh, dataIfReadOnly))
return true; return true;
static_assert(MODEL_MESH_VERSION == 2, "Update code"); static_assert(MODEL_MESH_VERSION == 2, "Update code");
@@ -560,6 +564,7 @@ bool SkinnedModel::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase
bool SkinnedModel::LoadHeader(ReadStream& stream, byte& headerVersion) bool SkinnedModel::LoadHeader(ReadStream& stream, byte& headerVersion)
{ {
PROFILE_MEM(GraphicsMeshes);
if (ModelBase::LoadHeader(stream, headerVersion)) if (ModelBase::LoadHeader(stream, headerVersion))
return true; return true;
static_assert(MODEL_HEADER_VERSION == 2, "Update code"); static_assert(MODEL_HEADER_VERSION == 2, "Update code");
@@ -861,6 +866,7 @@ uint64 SkinnedModel::GetMemoryUsage() const
void SkinnedModel::SetupMaterialSlots(int32 slotsCount) void SkinnedModel::SetupMaterialSlots(int32 slotsCount)
{ {
PROFILE_MEM(GraphicsMeshes);
ModelBase::SetupMaterialSlots(slotsCount); ModelBase::SetupMaterialSlots(slotsCount);
// Adjust meshes indices for slots // Adjust meshes indices for slots
@@ -954,6 +960,7 @@ Asset::LoadResult SkinnedModel::load()
if (chunk0 == nullptr || chunk0->IsMissing()) if (chunk0 == nullptr || chunk0->IsMissing())
return LoadResult::MissingDataChunk; return LoadResult::MissingDataChunk;
MemoryReadStream headerStream(chunk0->Get(), chunk0->Size()); MemoryReadStream headerStream(chunk0->Get(), chunk0->Size());
PROFILE_MEM(GraphicsMeshes);
// Load asset data (anything but mesh contents that use streaming) // Load asset data (anything but mesh contents that use streaming)
byte headerVersion; byte headerVersion;

View File

@@ -18,6 +18,7 @@
#include "Engine/Serialization/Serialization.h" #include "Engine/Serialization/Serialization.h"
#include "Engine/Serialization/JsonWriter.h" #include "Engine/Serialization/JsonWriter.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Utilities/StringConverter.h" #include "Engine/Utilities/StringConverter.h"
#include "Engine/Threading/MainThreadTask.h" #include "Engine/Threading/MainThreadTask.h"
#include "Engine/Level/SceneObject.h" #include "Engine/Level/SceneObject.h"
@@ -37,10 +38,12 @@ namespace
void PrintStack(LogType type) void PrintStack(LogType type)
{ {
#if LOG_ENABLE
const String stack = VisualScripting::GetStackTrace(); const String stack = VisualScripting::GetStackTrace();
Log::Logger::Write(type, TEXT("Visual Script stack trace:")); Log::Logger::Write(type, TEXT("Visual Script stack trace:"));
Log::Logger::Write(type, stack); Log::Logger::Write(type, stack);
Log::Logger::Write(type, TEXT("")); Log::Logger::Write(type, TEXT(""));
#endif
} }
bool SerializeValue(const Variant& a, const Variant& b) bool SerializeValue(const Variant& a, const Variant& b)
@@ -1340,6 +1343,8 @@ bool VisualScript::Save(const StringView& path)
Asset::LoadResult VisualScript::load() Asset::LoadResult VisualScript::load()
{ {
PROFILE_MEM(ScriptingVisual);
// Build Visual Script typename that is based on asset id // Build Visual Script typename that is based on asset id
String typeName = _id.ToString(); String typeName = _id.ToString();
StringUtils::ConvertUTF162ANSI(typeName.Get(), _typenameChars, 32); StringUtils::ConvertUTF162ANSI(typeName.Get(), _typenameChars, 32);
@@ -1532,6 +1537,7 @@ Asset::LoadResult VisualScript::load()
void VisualScript::unload(bool isReloading) void VisualScript::unload(bool isReloading)
{ {
PROFILE_MEM(ScriptingVisual);
#if USE_EDITOR #if USE_EDITOR
if (isReloading) if (isReloading)
{ {
@@ -1588,6 +1594,7 @@ AssetChunksFlag VisualScript::getChunksToPreload() const
void VisualScript::CacheScriptingType() void VisualScript::CacheScriptingType()
{ {
PROFILE_MEM(ScriptingVisual);
ScopeLock lock(VisualScriptingBinaryModule::Locker); ScopeLock lock(VisualScriptingBinaryModule::Locker);
auto& binaryModule = VisualScriptingModule; auto& binaryModule = VisualScriptingModule;
@@ -1723,6 +1730,7 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
VisualScript* visualScript = VisualScriptingModule.Scripts[params.Type.TypeIndex]; VisualScript* visualScript = VisualScriptingModule.Scripts[params.Type.TypeIndex];
// Initialize instance data // Initialize instance data
PROFILE_MEM(ScriptingVisual);
ScopeLock lock(visualScript->Locker); ScopeLock lock(visualScript->Locker);
auto& instanceParams = visualScript->_instances[object->GetID()].Params; auto& instanceParams = visualScript->_instances[object->GetID()].Params;
instanceParams.Resize(visualScript->Graph.Parameters.Count()); instanceParams.Resize(visualScript->Graph.Parameters.Count());
@@ -1747,6 +1755,8 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
void VisualScriptingBinaryModule::OnScriptsReloading() void VisualScriptingBinaryModule::OnScriptsReloading()
{ {
PROFILE_MEM(ScriptingVisual);
// Clear any cached types from that module across all loaded Visual Scripts // Clear any cached types from that module across all loaded Visual Scripts
for (auto& script : Scripts) for (auto& script : Scripts)
{ {
@@ -1795,6 +1805,7 @@ void VisualScriptingBinaryModule::OnScriptsReloading()
void VisualScriptingBinaryModule::OnEvent(ScriptingObject* object, Span<Variant> parameters, ScriptingTypeHandle eventType, StringView eventName) void VisualScriptingBinaryModule::OnEvent(ScriptingObject* object, Span<Variant> parameters, ScriptingTypeHandle eventType, StringView eventName)
{ {
PROFILE_MEM(ScriptingVisual);
if (object) if (object)
{ {
// Object event // Object event
@@ -1900,9 +1911,13 @@ bool VisualScriptingBinaryModule::InvokeMethod(void* method, const Variant& inst
if (!instanceObject || instanceObject->GetTypeHandle() != vsMethod->Script->GetScriptingType()) if (!instanceObject || instanceObject->GetTypeHandle() != vsMethod->Script->GetScriptingType())
{ {
if (!instanceObject) if (!instanceObject)
{
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(vsMethod->Script->GetScriptTypeName()), String(vsMethod->Name), vsMethod->ParamNames.Count()); LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(vsMethod->Script->GetScriptTypeName()), String(vsMethod->Name), vsMethod->ParamNames.Count());
}
else else
{
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) with invalid object instance of type '{3}'", String(vsMethod->Script->GetScriptTypeName()), String(vsMethod->Name), vsMethod->ParamNames.Count(), String(instanceObject->GetType().Fullname)); LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) with invalid object instance of type '{3}'", String(vsMethod->Script->GetScriptTypeName()), String(vsMethod->Name), vsMethod->ParamNames.Count(), String(instanceObject->GetType().Fullname));
}
return true; return true;
} }
} }
@@ -1952,6 +1967,7 @@ bool VisualScriptingBinaryModule::GetFieldValue(void* field, const Variant& inst
bool VisualScriptingBinaryModule::SetFieldValue(void* field, const Variant& instance, Variant& value) bool VisualScriptingBinaryModule::SetFieldValue(void* field, const Variant& instance, Variant& value)
{ {
PROFILE_MEM(ScriptingVisual);
const auto vsFiled = (VisualScript::Field*)field; const auto vsFiled = (VisualScript::Field*)field;
const auto instanceObject = (ScriptingObject*)instance; const auto instanceObject = (ScriptingObject*)instance;
if (!instanceObject) if (!instanceObject)
@@ -2038,6 +2054,7 @@ void VisualScriptingBinaryModule::SerializeObject(JsonWriter& stream, ScriptingO
void VisualScriptingBinaryModule::DeserializeObject(ISerializable::DeserializeStream& stream, ScriptingObject* object, ISerializeModifier* modifier) void VisualScriptingBinaryModule::DeserializeObject(ISerializable::DeserializeStream& stream, ScriptingObject* object, ISerializeModifier* modifier)
{ {
PROFILE_MEM(ScriptingVisual);
ASSERT(stream.IsObject()); ASSERT(stream.IsObject());
Locker.Lock(); Locker.Lock();
const auto asset = Scripts[object->GetTypeHandle().TypeIndex].Get(); const auto asset = Scripts[object->GetTypeHandle().TypeIndex].Get();
@@ -2161,6 +2178,7 @@ const Variant& VisualScript::GetScriptInstanceParameterValue(const StringView& n
void VisualScript::SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, const Variant& value) void VisualScript::SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, const Variant& value)
{ {
PROFILE_MEM(ScriptingVisual);
CHECK(instance); CHECK(instance);
for (int32 paramIndex = 0; paramIndex < Graph.Parameters.Count(); paramIndex++) for (int32 paramIndex = 0; paramIndex < Graph.Parameters.Count(); paramIndex++)
{ {
@@ -2182,6 +2200,7 @@ void VisualScript::SetScriptInstanceParameterValue(const StringView& name, Scrip
void VisualScript::SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, Variant&& value) void VisualScript::SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, Variant&& value)
{ {
PROFILE_MEM(ScriptingVisual);
CHECK(instance); CHECK(instance);
for (int32 paramIndex = 0; paramIndex < Graph.Parameters.Count(); paramIndex++) for (int32 paramIndex = 0; paramIndex < Graph.Parameters.Count(); paramIndex++)
{ {
@@ -2379,6 +2398,7 @@ VisualScriptingBinaryModule* VisualScripting::GetBinaryModule()
Variant VisualScripting::Invoke(VisualScript::Method* method, ScriptingObject* instance, Span<Variant> parameters) Variant VisualScripting::Invoke(VisualScript::Method* method, ScriptingObject* instance, Span<Variant> parameters)
{ {
PROFILE_MEM(ScriptingVisual);
CHECK_RETURN(method && method->Script->IsLoaded(), Variant::Zero); CHECK_RETURN(method && method->Script->IsLoaded(), Variant::Zero);
PROFILE_CPU_SRC_LOC(method->ProfilerData); PROFILE_CPU_SRC_LOC(method->ProfilerData);
@@ -2419,6 +2439,7 @@ bool VisualScripting::Evaluate(VisualScript* script, ScriptingObject* instance,
const auto box = node->GetBox(boxId); const auto box = node->GetBox(boxId);
if (!box) if (!box)
return false; return false;
PROFILE_MEM(ScriptingVisual);
// Add to the calling stack // Add to the calling stack
ScopeContext scope; ScopeContext scope;

View File

@@ -10,6 +10,7 @@
#include "Engine/Serialization/JsonTools.h" #include "Engine/Serialization/JsonTools.h"
#include "Engine/Debug/Exceptions/JsonParseException.h" #include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Threading/ThreadPoolTask.h" #include "Engine/Threading/ThreadPoolTask.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if USE_EDITOR #if USE_EDITOR
#include "Engine/Platform/FileSystem.h" #include "Engine/Platform/FileSystem.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
@@ -527,6 +528,7 @@ protected:
auto storage = ref->Storage; auto storage = ref->Storage;
auto factory = (BinaryAssetFactoryBase*)Content::GetAssetFactory(ref->GetTypeName()); auto factory = (BinaryAssetFactoryBase*)Content::GetAssetFactory(ref->GetTypeName());
ASSERT(factory); ASSERT(factory);
PROFILE_MEM(ContentAssets);
// Here we should open storage and extract AssetInitData // Here we should open storage and extract AssetInitData
// This would also allow to convert/upgrade data // This would also allow to convert/upgrade data

View File

@@ -28,6 +28,7 @@
#include "Engine/Engine/Globals.h" #include "Engine/Engine/Globals.h"
#include "Engine/Level/Types.h" #include "Engine/Level/Types.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/ManagedCLR/MClass.h" #include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#if USE_EDITOR #if USE_EDITOR
@@ -117,6 +118,8 @@ ContentService ContentServiceInstance;
bool ContentService::Init() bool ContentService::Init()
{ {
PROFILE_MEM(Content);
// Load assets registry // Load assets registry
Cache.Init(); Cache.Init();
@@ -159,6 +162,7 @@ void ContentService::Update()
void ContentService::LateUpdate() void ContentService::LateUpdate()
{ {
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Content);
// Check if need to perform an update of unloading assets // Check if need to perform an update of unloading assets
const TimeSpan timeNow = Time::Update.UnscaledTime; const TimeSpan timeNow = Time::Update.UnscaledTime;
@@ -324,6 +328,7 @@ String LoadingThread::ToString() const
int32 LoadingThread::Run() int32 LoadingThread::Run()
{ {
PROFILE_MEM(Content);
#if USE_EDITOR && PLATFORM_WINDOWS #if USE_EDITOR && PLATFORM_WINDOWS
// Initialize COM // Initialize COM
// TODO: maybe add sth to Thread::Create to indicate that thread will use COM stuff // TODO: maybe add sth to Thread::Create to indicate that thread will use COM stuff
@@ -416,6 +421,7 @@ bool Content::GetAssetInfo(const Guid& id, AssetInfo& info)
if (Cache.FindAsset(id, info)) if (Cache.FindAsset(id, info))
return true; return true;
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Content);
// Locking injects some stalls but we need to make it safe (only one thread can pass though it at once) // Locking injects some stalls but we need to make it safe (only one thread can pass though it at once)
ScopeLock lock(WorkspaceDiscoveryLocker); ScopeLock lock(WorkspaceDiscoveryLocker);
@@ -465,6 +471,7 @@ bool Content::GetAssetInfo(const StringView& path, AssetInfo& info)
if (!FileSystem::FileExists(path)) if (!FileSystem::FileExists(path))
return false; return false;
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Content);
const auto extension = FileSystem::GetExtension(path).ToLower(); const auto extension = FileSystem::GetExtension(path).ToLower();
@@ -593,6 +600,7 @@ Asset* Content::LoadAsyncInternal(const StringView& internalPath, const MClass*
Asset* Content::LoadAsyncInternal(const StringView& internalPath, const ScriptingTypeHandle& type) Asset* Content::LoadAsyncInternal(const StringView& internalPath, const ScriptingTypeHandle& type)
{ {
PROFILE_MEM(Content);
#if USE_EDITOR #if USE_EDITOR
const String path = Globals::EngineContentFolder / internalPath + ASSET_FILES_EXTENSION_WITH_DOT; const String path = Globals::EngineContentFolder / internalPath + ASSET_FILES_EXTENSION_WITH_DOT;
if (!FileSystem::FileExists(path)) if (!FileSystem::FileExists(path))
@@ -635,6 +643,8 @@ Asset* Content::LoadAsync(const StringView& path, const MClass* type)
Asset* Content::LoadAsync(const StringView& path, const ScriptingTypeHandle& type) Asset* Content::LoadAsync(const StringView& path, const ScriptingTypeHandle& type)
{ {
PROFILE_MEM(Content);
// Ensure path is in a valid format // Ensure path is in a valid format
String pathNorm(path); String pathNorm(path);
ContentStorageManager::FormatPath(pathNorm); ContentStorageManager::FormatPath(pathNorm);
@@ -687,7 +697,6 @@ Asset* Content::GetAsset(const StringView& outputPath)
{ {
if (outputPath.IsEmpty()) if (outputPath.IsEmpty())
return nullptr; return nullptr;
ScopeLock lock(AssetsLocker); ScopeLock lock(AssetsLocker);
for (auto i = Assets.Begin(); i.IsNotEnd(); ++i) for (auto i = Assets.Begin(); i.IsNotEnd(); ++i)
{ {
@@ -791,6 +800,23 @@ void Content::deleteFileSafety(const StringView& path, const Guid& id)
#endif #endif
} }
#if !COMPILE_WITHOUT_CSHARP
#include "Engine/Scripting/ManagedCLR/MUtils.h"
void* Content::GetAssetsInternal()
{
AssetsLocker.Lock();
MArray* result = MCore::Array::New(Asset::TypeInitializer.GetClass(), Assets.Count());
int32 i = 0;
for (const auto& e : Assets)
MCore::GC::WriteArrayRef(result, e.Value->GetOrCreateManagedInstance(), i++);
AssetsLocker.Unlock();
return result;
}
#endif
#if USE_EDITOR #if USE_EDITOR
bool Content::RenameAsset(const StringView& oldPath, const StringView& newPath) bool Content::RenameAsset(const StringView& oldPath, const StringView& newPath)
@@ -1023,6 +1049,7 @@ Asset* Content::CreateVirtualAsset(const MClass* type)
Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type) Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type)
{ {
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Content);
auto& assetType = type.GetType(); auto& assetType = type.GetType();
// Init mock asset info // Init mock asset info
@@ -1045,7 +1072,9 @@ Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type)
} }
// Create asset object // Create asset object
PROFILE_MEM_BEGIN(ContentAssets);
auto asset = factory->NewVirtual(info); auto asset = factory->NewVirtual(info);
PROFILE_MEM_END();
if (asset == nullptr) if (asset == nullptr)
{ {
LOG(Error, "Cannot create virtual asset object."); LOG(Error, "Cannot create virtual asset object.");
@@ -1054,7 +1083,9 @@ Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type)
asset->RegisterObject(); asset->RegisterObject();
// Call initializer function // Call initializer function
PROFILE_MEM_BEGIN(ContentAssets);
asset->InitAsVirtual(); asset->InitAsVirtual();
PROFILE_MEM_END();
// Register asset // Register asset
AssetsLocker.Lock(); AssetsLocker.Lock();
@@ -1097,6 +1128,8 @@ void Content::WaitForTask(ContentLoadTask* loadingTask, double timeoutInMillisec
localQueue.Clear(); localQueue.Clear();
} }
PROFILE_CPU_NAMED("Inline");
ZoneColor(0xffaaaaaa);
thread->Run(tmp); thread->Run(tmp);
} }
else else
@@ -1209,6 +1242,7 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
{ {
if (!id.IsValid()) if (!id.IsValid())
return nullptr; return nullptr;
PROFILE_MEM(Content);
// Check if asset has been already loaded // Check if asset has been already loaded
Asset* result = nullptr; Asset* result = nullptr;
@@ -1277,7 +1311,9 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
} }
// Create asset object // Create asset object
PROFILE_MEM_BEGIN(ContentAssets);
result = factory->New(assetInfo); result = factory->New(assetInfo);
PROFILE_MEM_END();
if (result == nullptr) if (result == nullptr)
{ {
LOG(Error, "Cannot create asset object. Info: {0}", assetInfo.ToString()); LOG(Error, "Cannot create asset object. Info: {0}", assetInfo.ToString());

View File

@@ -1,5 +1,6 @@
// Copyright (c) Wojciech Figat. All rights reserved. // Copyright (c) Wojciech Figat. All rights reserved.
using FlaxEngine.Interop;
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -7,6 +8,33 @@ namespace FlaxEngine
{ {
partial class Content partial class Content
{ {
/// <summary>
/// Gets the assets (loaded or during load).
/// </summary>
public static Asset[] Assets
{
get
{
IntPtr ptr = Internal_GetAssetsInternal();
ManagedArray array = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(ptr).Target);
return NativeInterop.GCHandleArrayToManagedArray<Asset>(array);
}
}
/// <summary>
/// Gets the assets (loaded or during load).
/// </summary>
/// <param name="buffer">Output buffer to fill with asset pointers. Can be provided by a user to avoid memory allocation. Buffer might be larger than actual list size. Use <paramref name="count"/> for actual item count.></param>
/// <param name="count">Amount of valid items inside <paramref name="buffer"/>.</param>
public static void GetAssets(ref Asset[] buffer, out int count)
{
count = 0;
IntPtr ptr = Internal_GetAssetsInternal();
ManagedArray array = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(ptr).Target);
buffer = NativeInterop.GCHandleArrayToManagedArray<Asset>(array, buffer);
count = buffer.Length;
}
/// <summary> /// <summary>
/// Loads asset to the Content Pool and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async. /// Loads asset to the Content Pool and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async.
/// </summary> /// </summary>

View File

@@ -122,7 +122,7 @@ public:
/// Gets the assets (loaded or during load). /// Gets the assets (loaded or during load).
/// </summary> /// </summary>
/// <returns>The collection of assets.</returns> /// <returns>The collection of assets.</returns>
API_PROPERTY() static Array<Asset*, HeapAllocation> GetAssets(); static Array<Asset*, HeapAllocation> GetAssets();
/// <summary> /// <summary>
/// Gets the raw dictionary of assets (loaded or during load). /// Gets the raw dictionary of assets (loaded or during load).
@@ -368,4 +368,9 @@ private:
static void onAssetUnload(Asset* asset); static void onAssetUnload(Asset* asset);
static void onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId); static void onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId);
static void deleteFileSafety(const StringView& path, const Guid& id); static void deleteFileSafety(const StringView& path, const Guid& id);
// Internal bindings
#if !COMPILE_WITHOUT_CSHARP
API_FUNCTION(NoProxy) static void* GetAssetsInternal();
#endif
}; };

View File

@@ -20,6 +20,7 @@
#include "Engine/Core/Cache.h" #include "Engine/Core/Cache.h"
#include "Engine/Debug/Exceptions/JsonParseException.h" #include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/ManagedCLR/MClass.h" #include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/ManagedCLR/MField.h" #include "Engine/Scripting/ManagedCLR/MField.h"
@@ -39,6 +40,7 @@ String JsonAssetBase::GetData() const
if (Data == nullptr) if (Data == nullptr)
return String::Empty; return String::Empty;
PROFILE_CPU_NAMED("JsonAsset.GetData"); PROFILE_CPU_NAMED("JsonAsset.GetData");
PROFILE_MEM(ContentAssets);
rapidjson_flax::StringBuffer buffer; rapidjson_flax::StringBuffer buffer;
OnGetData(buffer); OnGetData(buffer);
return String((const char*)buffer.GetString(), (int32)buffer.GetSize()); return String((const char*)buffer.GetString(), (int32)buffer.GetSize());
@@ -49,6 +51,7 @@ void JsonAssetBase::SetData(const StringView& value)
if (!IsLoaded()) if (!IsLoaded())
return; return;
PROFILE_CPU_NAMED("JsonAsset.SetData"); PROFILE_CPU_NAMED("JsonAsset.SetData");
PROFILE_MEM(ContentAssets);
const StringAnsi dataJson(value); const StringAnsi dataJson(value);
ScopeLock lock(Locker); ScopeLock lock(Locker);
const StringView dataTypeName = DataTypeName; const StringView dataTypeName = DataTypeName;
@@ -60,6 +63,7 @@ void JsonAssetBase::SetData(const StringView& value)
bool JsonAssetBase::Init(const StringView& dataTypeName, const StringAnsiView& dataJson) bool JsonAssetBase::Init(const StringView& dataTypeName, const StringAnsiView& dataJson)
{ {
PROFILE_MEM(ContentAssets);
unload(true); unload(true);
DataTypeName = dataTypeName; DataTypeName = dataTypeName;
DataEngineBuild = FLAXENGINE_VERSION_BUILD; DataEngineBuild = FLAXENGINE_VERSION_BUILD;
@@ -239,6 +243,7 @@ Asset::LoadResult JsonAssetBase::loadAsset()
{ {
if (IsVirtual() || _isVirtualDocument) if (IsVirtual() || _isVirtualDocument)
return LoadResult::Ok; return LoadResult::Ok;
PROFILE_MEM(ContentAssets);
// Load data (raw json file in editor, cooked asset in build game) // Load data (raw json file in editor, cooked asset in build game)
#if USE_EDITOR #if USE_EDITOR
@@ -305,6 +310,7 @@ Asset::LoadResult JsonAssetBase::loadAsset()
void JsonAssetBase::unload(bool isReloading) void JsonAssetBase::unload(bool isReloading)
{ {
PROFILE_MEM(ContentAssets);
ISerializable::SerializeDocument tmp; ISerializable::SerializeDocument tmp;
Document.Swap(tmp); Document.Swap(tmp);
Data = nullptr; Data = nullptr;
@@ -453,6 +459,7 @@ bool JsonAsset::CreateInstance()
ScopeLock lock(Locker); ScopeLock lock(Locker);
if (Instance) if (Instance)
return false; return false;
PROFILE_MEM(ContentAssets);
// Try to scripting type for this data // Try to scripting type for this data
const StringAsANSI<> dataTypeNameAnsi(DataTypeName.Get(), DataTypeName.Length()); const StringAsANSI<> dataTypeNameAnsi(DataTypeName.Get(), DataTypeName.Length());

View File

@@ -19,6 +19,15 @@ API_STRUCT(NoDefault, Template, MarshalAs=JsonAsset*) struct JsonAssetReference
OnSet(asset); OnSet(asset);
} }
explicit JsonAssetReference(decltype(__nullptr))
{
}
explicit JsonAssetReference(IAssetReference* owner)
: AssetReference<JsonAsset>(owner)
{
}
/// <summary> /// <summary>
/// Gets the deserialized native object instance of the given type. Returns null if asset is not loaded or loaded object has different type. /// Gets the deserialized native object instance of the given type. Returns null if asset is not loaded or loaded object has different type.
/// </summary> /// </summary>

View File

@@ -8,6 +8,7 @@
#include "Engine/Content/WeakAssetReference.h" #include "Engine/Content/WeakAssetReference.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
/// <summary> /// <summary>
/// Asset loading task object. /// Asset loading task object.
@@ -44,6 +45,7 @@ protected:
Result run() override Result run() override
{ {
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(ContentAssets);
// Keep valid ref to the asset // Keep valid ref to the asset
AssetReference<::Asset> ref = Asset.Get(); AssetReference<::Asset> ref = Asset.Get();

View File

@@ -7,7 +7,7 @@
/// <summary> /// <summary>
/// The asset soft reference. Asset gets referenced (loaded) on actual use (ID reference is resolving it). /// The asset soft reference. Asset gets referenced (loaded) on actual use (ID reference is resolving it).
/// </summary> /// </summary>
class FLAXENGINE_API SoftAssetReferenceBase class FLAXENGINE_API SoftAssetReferenceBase : public IAssetReference
{ {
protected: protected:
Asset* _asset = nullptr; Asset* _asset = nullptr;
@@ -46,11 +46,16 @@ public:
/// </summary> /// </summary>
String ToString() const; String ToString() const;
public:
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
protected: protected:
void OnSet(Asset* asset); void OnSet(Asset* asset);
void OnSet(const Guid& id); void OnSet(const Guid& id);
void OnResolve(const ScriptingTypeHandle& type); void OnResolve(const ScriptingTypeHandle& type);
void OnUnloaded(Asset* asset);
}; };
/// <summary> /// <summary>
@@ -71,6 +76,13 @@ public:
{ {
} }
/// <summary>
/// Initializes a new instance of the <see cref="SoftAssetReference"/> class.
/// </summary>
explicit SoftAssetReference(decltype(__nullptr))
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SoftAssetReference"/> class. /// Initializes a new instance of the <see cref="SoftAssetReference"/> class.
/// </summary> /// </summary>

View File

@@ -182,10 +182,5 @@ public:
/// Clones this chunk data (doesn't copy location in file). /// Clones this chunk data (doesn't copy location in file).
/// </summary> /// </summary>
/// <returns>The cloned chunk.</returns> /// <returns>The cloned chunk.</returns>
FlaxChunk* Clone() const FlaxChunk* Clone() const;
{
auto chunk = New<FlaxChunk>();
chunk->Data.Copy(Data);
return chunk;
}
}; };

View File

@@ -8,6 +8,7 @@
#include "Engine/Core/Types/TimeSpan.h" #include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Platform/File.h" #include "Engine/Platform/File.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Serialization/FileWriteStream.h" #include "Engine/Serialization/FileWriteStream.h"
#include "Engine/Content/Asset.h" #include "Engine/Content/Asset.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
@@ -63,6 +64,14 @@ void FlaxChunk::RegisterUsage()
LastAccessTime = Platform::GetTimeSeconds(); LastAccessTime = Platform::GetTimeSeconds();
} }
FlaxChunk* FlaxChunk::Clone() const
{
PROFILE_MEM(ContentFiles);
auto chunk = New<FlaxChunk>();
chunk->Data.Copy(Data);
return chunk;
}
const int32 FlaxStorage::MagicCode = 1180124739; const int32 FlaxStorage::MagicCode = 1180124739;
FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr); FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr);
@@ -281,19 +290,12 @@ uint32 FlaxStorage::GetMemoryUsage() const
bool FlaxStorage::Load() bool FlaxStorage::Load()
{ {
// Check if was already loaded
if (IsLoaded()) if (IsLoaded())
{
return false; return false;
} PROFILE_MEM(ContentFiles);
// Prevent loading by more than one thread
ScopeLock lock(_loadLocker); ScopeLock lock(_loadLocker);
if (IsLoaded()) if (IsLoaded())
{
// Other thread loaded it
return false; return false;
}
ASSERT(GetEntriesCount() == 0); ASSERT(GetEntriesCount() == 0);
// Open file // Open file
@@ -693,6 +695,7 @@ bool FlaxStorage::LoadAssetHeader(const Guid& id, AssetInitData& data)
bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk) bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
{ {
PROFILE_MEM(ContentFiles);
ASSERT(IsLoaded()); ASSERT(IsLoaded());
ASSERT(chunk != nullptr && _chunks.Contains(chunk)); ASSERT(chunk != nullptr && _chunks.Contains(chunk));
@@ -866,6 +869,7 @@ FlaxChunk* FlaxStorage::AllocateChunk()
{ {
if (AllowDataModifications()) if (AllowDataModifications())
{ {
PROFILE_MEM(ContentFiles);
auto chunk = New<FlaxChunk>(); auto chunk = New<FlaxChunk>();
_chunks.Add(chunk); _chunks.Add(chunk);
return chunk; return chunk;
@@ -1125,6 +1129,7 @@ bool FlaxStorage::Save(const AssetInitData& data, bool silentMode)
bool FlaxStorage::LoadAssetHeader(const Entry& e, AssetInitData& data) bool FlaxStorage::LoadAssetHeader(const Entry& e, AssetInitData& data)
{ {
PROFILE_MEM(ContentFiles);
ASSERT(IsLoaded()); ASSERT(IsLoaded());
auto lock = Lock(); auto lock = Lock();
@@ -1396,6 +1401,8 @@ FileReadStream* FlaxStorage::OpenFile()
auto& stream = _file.Get(); auto& stream = _file.Get();
if (stream == nullptr) if (stream == nullptr)
{ {
PROFILE_MEM(ContentFiles);
// Open file // Open file
auto file = File::Open(_path, FileMode::OpenExisting, FileAccess::Read, FileShare::Read); auto file = File::Open(_path, FileMode::OpenExisting, FileAccess::Read, FileShare::Read);
if (file == nullptr) if (file == nullptr)
@@ -1418,6 +1425,7 @@ bool FlaxStorage::CloseFileHandles()
return false; return false;
} }
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(ContentFiles);
// Note: this is usually called by the content manager when this file is not used or on exit // Note: this is usually called by the content manager when this file is not used or on exit
// In those situations all the async tasks using this storage should be cancelled externally // In those situations all the async tasks using this storage should be cancelled externally

View File

@@ -7,7 +7,7 @@
/// <summary> /// <summary>
/// Asset reference utility that doesn't add reference to that asset. Handles asset unload event. /// Asset reference utility that doesn't add reference to that asset. Handles asset unload event.
/// </summary> /// </summary>
API_CLASS(InBuild) class WeakAssetReferenceBase API_CLASS(InBuild) class WeakAssetReferenceBase : public IAssetReference
{ {
public: public:
typedef Delegate<> EventType; typedef Delegate<> EventType;
@@ -56,9 +56,14 @@ public:
/// </summary> /// </summary>
String ToString() const; String ToString() const;
public:
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
protected: protected:
void OnSet(Asset* asset); void OnSet(Asset* asset);
void OnUnloaded(Asset* asset);
}; };
/// <summary> /// <summary>
@@ -72,7 +77,13 @@ public:
/// Initializes a new instance of the <see cref="WeakAssetReference"/> class. /// Initializes a new instance of the <see cref="WeakAssetReference"/> class.
/// </summary> /// </summary>
WeakAssetReference() WeakAssetReference()
: WeakAssetReferenceBase() {
}
/// <summary>
/// Initializes a new instance of the <see cref="WeakAssetReference"/> class.
/// </summary>
explicit WeakAssetReference(decltype(__nullptr))
{ {
} }
@@ -81,7 +92,6 @@ public:
/// </summary> /// </summary>
/// <param name="asset">The asset to set.</param> /// <param name="asset">The asset to set.</param>
WeakAssetReference(T* asset) WeakAssetReference(T* asset)
: WeakAssetReferenceBase()
{ {
OnSet(asset); OnSet(asset);
} }

View File

@@ -13,6 +13,7 @@
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Platform/FileSystem.h" #include "Engine/Platform/FileSystem.h"
#include "Engine/Platform/Platform.h" #include "Engine/Platform/Platform.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Engine/Globals.h" #include "Engine/Engine/Globals.h"
#include "ImportTexture.h" #include "ImportTexture.h"
#include "ImportModel.h" #include "ImportModel.h"
@@ -151,6 +152,7 @@ bool CreateAssetContext::AllocateChunk(int32 index)
} }
// Create new chunk // Create new chunk
PROFILE_MEM(ContentFiles);
Data.Header.Chunks[index] = New<FlaxChunk>(); Data.Header.Chunks[index] = New<FlaxChunk>();
return false; return false;
} }

View File

@@ -3,7 +3,7 @@
#include "Cache.h" #include "Cache.h"
#include "FlaxEngine.Gen.h" #include "FlaxEngine.Gen.h"
CollectionPoolCache<ISerializeModifier, Cache::ISerializeModifierClearCallback> Cache::ISerializeModifier; Cache::ISerializeModifierCache Cache::ISerializeModifier;
void Cache::ISerializeModifierClearCallback(::ISerializeModifier* obj) void Cache::ISerializeModifierClearCallback(::ISerializeModifier* obj)
{ {

View File

@@ -15,11 +15,12 @@ public:
static void ISerializeModifierClearCallback(ISerializeModifier* obj); static void ISerializeModifierClearCallback(ISerializeModifier* obj);
public: public:
typedef CollectionPoolCache<ISerializeModifier, ISerializeModifierClearCallback> ISerializeModifierCache;
/// <summary> /// <summary>
/// Gets the ISerializeModifier lookup cache. Safe allocation, per thread, uses caching. /// Gets the ISerializeModifier lookup cache. Safe allocation, per thread, uses caching.
/// </summary> /// </summary>
static CollectionPoolCache<ISerializeModifier, ISerializeModifierClearCallback> ISerializeModifier; static ISerializeModifierCache ISerializeModifier;
public: public:

View File

@@ -20,6 +20,7 @@ API_CLASS(InBuild) class Array
public: public:
using ItemType = T; using ItemType = T;
using AllocationData = typename AllocationType::template Data<T>; using AllocationData = typename AllocationType::template Data<T>;
using AllocationTag = typename AllocationType::Tag;
private: private:
int32 _count; int32 _count;
@@ -36,6 +37,17 @@ public:
{ {
} }
/// <summary>
/// Initializes an empty <see cref="Array"/> without reserving any space.
/// </summary>
/// <param name="tag">The custom allocation tag.</param>
Array(AllocationTag tag)
: _count(0)
, _capacity(0)
, _allocation(tag)
{
}
/// <summary> /// <summary>
/// Initializes <see cref="Array"/> by reserving space. /// Initializes <see cref="Array"/> by reserving space.
/// </summary> /// </summary>

View File

@@ -30,13 +30,17 @@
#endif #endif
// Enable logging service (saving log to file, can be disabled using -nolog command line) // Enable logging service (saving log to file, can be disabled using -nolog command line)
#ifndef LOG_ENABLE
#define LOG_ENABLE 1 #define LOG_ENABLE 1
#endif
// Enable crash reporting service (stack trace and crash dump collecting) // Enable crash reporting service (stack trace and crash dump collecting)
#define CRASH_LOG_ENABLE (!BUILD_RELEASE) #define CRASH_LOG_ENABLE (!BUILD_RELEASE)
// Enable/disable assertion // Enable/disable assertion
#ifndef ENABLE_ASSERTION
#define ENABLE_ASSERTION (!BUILD_RELEASE) #define ENABLE_ASSERTION (!BUILD_RELEASE)
#endif
// Enable/disable assertion for Engine low layers // Enable/disable assertion for Engine low layers
#define ENABLE_ASSERTION_LOW_LAYERS ENABLE_ASSERTION && (BUILD_DEBUG || FLAX_TESTS) #define ENABLE_ASSERTION_LOW_LAYERS ENABLE_ASSERTION && (BUILD_DEBUG || FLAX_TESTS)

Some files were not shown because too many files have changed in this diff Show More