2019-12-26 15:32:37 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2015-08-10 08:14:45 -04:00
2021-04-28 16:22:18 -04:00
# include "Algo/Accumulate.h"
2021-11-10 13:08:00 -05:00
# include "Algo/AllOf.h"
2022-02-07 20:06:56 -05:00
# include "Algo/Compare.h"
2021-11-10 13:08:00 -05:00
# include "Algo/StableSort.h"
# include "Algo/Transform.h"
# include "Async/Async.h"
# include "Containers/StaticBitArray.h"
# include "DerivedDataBackendInterface.h"
# include "DerivedDataCacheInterface.h"
# include "DerivedDataCacheMaintainer.h"
2022-01-07 23:22:29 -05:00
# include "DerivedDataCachePrivate.h"
2021-11-10 13:08:00 -05:00
# include "DerivedDataCacheRecord.h"
# include "DerivedDataCacheUsageStats.h"
# include "DerivedDataChunk.h"
2022-05-04 16:23:17 -04:00
# include "DerivedDataRequestOwner.h"
2022-01-06 11:05:57 -05:00
# include "DerivedDataValue.h"
2021-11-10 13:08:00 -05:00
# include "Experimental/Async/LazyEvent.h"
# include "Features/IModularFeatures.h"
# include "HAL/Event.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
# include "HAL/FileManager.h"
2021-11-10 13:08:00 -05:00
# include "HAL/Thread.h"
# include "Hash/xxhash.h"
# include "HashingArchiveProxy.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
# include "Misc/CommandLine.h"
2021-11-10 14:44:18 -05:00
# include "Misc/ConfigCacheIni.h"
2021-11-10 13:08:00 -05:00
# include "Misc/CoreMisc.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
# include "Misc/FileHelper.h"
2021-11-10 13:08:00 -05:00
# include "Misc/Guid.h"
# include "Misc/MessageDialog.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
# include "Misc/Paths.h"
2021-04-28 16:22:18 -04:00
# include "Misc/PathViews.h"
# include "Misc/ScopeExit.h"
Copying //UE4/Dev-Editor to //UE4/Dev-Main (Source: //UE4/Dev-Editor @ 3739701)
#lockdown Nick.Penwarden
#rb none
============================
MAJOR FEATURES & CHANGES
============================
Change 3358367 by tim.gautier
Submitting resaved QAGame assets - Materials, Material Instances, Material Functions and Parameters
Change 3624848 by Jamie.Dale
Added a composite font for the editor (and Slate core)
This is defined in FLegacySlateFontInfoCache::GetDefaultFont and uses our default Roboto fonts (and the culture specific fallback fonts), and is now used as the default font for Slate and the editor.
This change removes all the manual TTF/OTF file references from the various Slate styles, as well as updating 200+ hard-coded font references to use the new default font.
This fixes various rendering issues with fonts in the editor when using different languages, and clears a big barrier for removing the legacy localized fallback font support.
Change 3654993 by Jamie.Dale
'Native' (now called 'FNativeFuncPtr') is now a function pointer that takes a UObject* context, rather than a UObject member function pointer
This avoids ambiguity when binding a native function pointer to a type that doesn't match the context pointer, as you could end up getting a function called with an incorrect 'this' pointer
Breaking changes:
- Native has been renamed to FNativeFuncPtr.
- The signature of a native function has changed (use the DECLARE_FUNCTION and DEFINE_FUNCTION macro pair).
- Use P_THIS if you were previously using the 'this' pointer in your native function.
Change 3699591 by Jamie.Dale
Added support for displaying and editing numbers in a culture correct way
Numeric input boxes in Slate will now display and accept numbers using the culture correct decimal separators. This is enabled by default, and can be disabled by setting "ShouldUseLocalizedNumericInput" to "False" in XEditorSettings.ini (for the editor), or XGameUserSettings.ini (for a game).
#jira UE-4028
Change 3719568 by Jamie.Dale
Allow platforms to override the default ICU timezone calculation
Change 3622366 by Bradut.Palas
#jira UE-46677
Don't allow OnLevelRemovedFromWorld to reset the transaction buffer if we're in PIE mode.
Also, remove one undo barrier in case the event was triggered in PIE mode or else we block the user from undoing previous actions.
Change 3622378 by Bradut.Palas
#jira UE-46590
we have a general bug with detecting the size of the last column, but the clamping prevents it from appearing with the other resize modes. The Content Browser is the only one to use fixed width.
The bug is that the size of the last element is incorrectly reported, after we drag back and forth.
Fixed by not reading the size real time, but reading it from the SlotInfo structure that is created earlier, which holds the correct value.
Change 3622552 by Jamie.Dale
Added support for per-culture sub-fonts within a composite font
This allows you to do things like create a Japanese specific Han sub-font to override the Han characters used in a CJK font (previously you needed to create a localized font asset to achieve this).
Change 3623170 by Jamie.Dale
Fixing warning
Change 3624846 by Jamie.Dale
Composite font cache optimizations
- Converted a typically small sized map to a sorted array + binary search.
- Converted the already sorted range array to use binary search.
- Contiguous ranges using the same typeface are now merged in the cache.
Change 3625576 by Cody.Albert
We now only set the widget tree to transient instead of passing the flag through StaticDuplicateObject. This was causing instanced subobjects to be flagged with RF_DuplicateTransient, preventing them from properly being duplicated when an array of instanced subobjects was modified.
#jira UE-47971
Change 3626057 by Matt.Kuhlenschmidt
Expose EUmgSequencePlayMode to blueprints
#jira UE-49255
Change 3626556 by Matt.Kuhlenschmidt
Fix window size and position adjustment not accounting for primary monitor not being a high DPI monitor when a secondary monitor is. Causes flickering and incorrect window positioning.
#jira UE-48922, UE-48957
Change 3627692 by Matt.Kuhlenschmidt
PR #3977: Source control submenu menu customization (Contributed by Kryofenix)
Change 3628600 by Arciel.Rekman
Added AutoCheckout to FAssetRenameManager for commandlet usage.
Change 3630561 by Richard.Hinckley
Deprecating the version of UFunctionalTestingManager::RunAllFunctionalTests that feature an unused bool parameter, replacing with a new version without that parameter.
Change 3630656 by Richard.Hinckley
Compile fix.
Change 3630964 by Arciel.Rekman
Fix CrashReporterClient headless build.
Change 3631050 by Matt.Kuhlenschmidt
Back out revision 9 from //UE4/Dev-Editor/Engine/Source/Runtime/Slate/Private/Widgets/Layout/SSplitter.cpp
Causes major problems with resizing splitters in editor
Change 3631140 by Arciel.Rekman
OpenAL: update Linux version to 1.18.1 (UETOOL-1253)
- Also remove a hack for RPATH and make it use a generic RPATH mechanism.
- Bulk of the change from Cengiz.Terzibas
#jira UETOOL-1253
Change 3632924 by Jamie.Dale
Added support for a catch-all fallback font within composite fonts
This allows you to provide broad "font of last resort" behavior on a per-composite font basis, in a way that can also work with different font styles.
Change 3633055 by Jamie.Dale
Fixed some refresh issues in the font editor
Change 3633062 by Jamie.Dale
Fixed localization commands being reported as unknown
Change 3633906 by Nick.Darnell
UMG - You can now store refrences to widgets in the same UserWidget. If you need to create links between widgets this is valuable. Will likely introduce new ways to utilize this in the future, for now just getting it working.
Change 3634070 by Arciel.Rekman
Display actually used values of material overrides.
Change 3634254 by Arciel.Rekman
Fix ResavePackages working poorly with projects on other drives (UE-49465).
#jira UE-49465
Change 3635985 by Matt.Kuhlenschmidt
Fixed typo in function name used by maps
PR #3975: Add tooltip to Arrays in Editor (Contributed by projectgheist)
Change 3636012 by Matt.Kuhlenschmidt
PR #3982: Unhide mouse cursor after using Ansel (Contributed by projectgheist)
Change 3636706 by Lauren.Ridge
Epic Friday: Save parameters to child or sibling instance functionality
Change 3638706 by Jamie.Dale
Added an improved Japanese font to the editor
This is only used when displaying Japanese text when the editor is set to Japanese, and uses a font with Japanese-style unified Han characters (our default fallback font uses Chinese-style unified Han characters).
#jira UE-33268
Change 3639438 by Arciel.Rekman
Linux: Repaired ARM server build (UE-49635).
- Made Steam* plugins compile.
- Disabled OpenEXR as the libs aren't compiled (need to be done separately).
(Edigrating CL 3639429 from Release-4.17 to Dev-Editor)
Change 3640625 by Matt.Kuhlenschmidt
PR #4012: FSlateApplication::ProcessReply use &Reply (Contributed by projectgheist)
Change 3640626 by Matt.Kuhlenschmidt
PR #4011: Remove space from filename (Contributed by projectgheist)
Change 3640697 by Matt.Kuhlenschmidt
PR #4010: PNG alpha fix (Contributed by mmdanggg2)
Change 3641137 by Jamie.Dale
Fixed an issue where a culture specific sub-font could produce incorrect measurements during a culture switch
It would fallback to the last resort font for a frame or two while the font cache flushed. This has it update the ranges immediately.
Change 3641351 by Jamie.Dale
Fixing incorrect weights on the Japanese sub-font
Change 3641356 by Jamie.Dale
Fixing inconsistent font sizes between CoreStyle and EditorStyle
Change 3641710 by Jamie.Dale
Fixed pure-virtual function call on UMulticastDelegateProperty
Change 3641941 by Lauren.Ridge
Adding a Parameter Details tab to the Material Editor so users can change default parameter details
Change 3644141 by Jamie.Dale
Added an improved Korean font to the editor
This is only used when displaying Korean text when the editor is set to Korean
Change 3644213 by Arciel.Rekman
Fix the side effects of a fix for UE-49465.
- Default materials were apparently not being found while building DDC (e.g. making an installed build), now they are
and we should not reset loaders on them lest we trigger HasDefaultMaterialsPostLoaded() assert later.
#jira UE-49465
Change 3644777 by Jamie.Dale
Reverting Korean editor font back to NanumGothic as NanumBarunGothic looked too squished
Change 3644879 by tim.gautier
QAGame: Optimized assets for Procedural Foliage testing
- Added camera bookmarks to Stations in QA-Foliage
- Renamed QA-FoliageTypeInst assets to ProcFoliage_Shape
- Fixed up redirectors
Change 3645109 by Matt.Kuhlenschmidt
PR #3990: Git plugin: fix status of renamed, removed, missing, untracked assets (Contributed by SRombauts)
Change 3645114 by Matt.Kuhlenschmidt
PR #3991: Git Plugin: Fix RunDumpToFile() leaking Process handles (Contributed by SRombauts)
Change 3645116 by Matt.Kuhlenschmidt
PR #3996: Git Plugin: run an "UpdateStatus" at "Connect" time to populate the Source Control cache (Contributed by SRombauts)
Change 3645118 by Matt.Kuhlenschmidt
PR #4005: Git Plugin: Expand the size of the Button "Initialize project with Git" (Contributed by SRombauts)
Change 3645876 by Arciel.Rekman
Linux: fix submenus of context menu not working (UE-47639).
- Change by icculus (Ryan Gordon).
- QA-ClickHUD seems to be not affected by this change (it is already broken alas).
#jira UE-47639
Change 3648088 by Jamie.Dale
Fixed some case-sensitivity issues with FText format argument names/pins
These were originally case-sensitive, but that was lost somewhere along the way. This change restores their original behavior.
#jira UE-47122
Change 3648097 by Jamie.Dale
Moved common macOS/iOS localization implementation into FApplePlatformMisc
#jira UE-49940
Change 3650858 by Arciel.Rekman
UBT: improve CodeLite project generator (UE-49400).
- PR #3987 submitted by yaakuro (Cengiz Terzibas).
#jira UE-49400
Change 3651231 by Arciel.Rekman
Linux: default to SM5 for Vulkan.
- Change by Timothee.Bessett.
Change 3653627 by Matt.Kuhlenschmidt
PR #4020: Source Control Submit Files now interprets Escape key as if the user clicked cancel (Contributed by SRombauts)
Change 3653628 by Matt.Kuhlenschmidt
PR #4022: Add New C++ Class dialog remember previously selected module. (Contributed by Koderz)
Change 3653984 by Jamie.Dale
Fixed some redundant string construction
Change 3658528 by Joe.Graf
UE-45141 - Added CMAKE_CXX_COMPILER and CMAKE_C_COMPILER settings to the generated CMake files
Change 3658594 by Jamie.Dale
Zipping in UAT now always uses UTF-8 encoding to prevent Unicode issues
#jira UE-27263
Change 3659643 by Michael.Trepka
Added a call to FCoreDelegates::ApplicationWillTerminateDelegate.Broadcast(); in Mac RequestExit() to match Windows behavior
#jira UETOOL-1238
Change 3661908 by Matt.Kuhlenschmidt
USD asset importing improvements
Change 3664100 by Matt.Kuhlenschmidt
Fix static analysis
Change 3664107 by Matt.Kuhlenschmidt
PR #4051: UE-49448: FPropertyChangedEvent to include TopLevelObjects (Contributed by projectgheist)
Change 3664125 by Matt.Kuhlenschmidt
PR #4036: Add missing GRAPHEDITOR_API (Contributed by projectgheist)
Change 3664340 by Jamie.Dale
PR #3648: Prevent GatherTextFromSource from failing the commandlet (Contributed by projectgheist)
Change 3664403 by Jamie.Dale
PR #3769: Fixes UE-46973 - Drag and Dropping Folders with Names (Contributed by LordNed)
Change 3664539 by Jamie.Dale
PR #3280: Added EditableText functionality (Contributed by projectgheist)
Change 3665433 by Alexis.Matte
When we finish importing morph target we must re-initialise the render resources since we now use GPU morph target.
#jira UE-50231
Change 3666747 by Cody.Albert
Change 3669280 by Jamie.Dale
PR #4060: UE-50455: Verify folder is newly created before removing from tree (Contributed by projectgheist)
Change 3669718 by Jamie.Dale
PR #4061: Clear Content Browser folder search box on escape key (Contributed by projectgheist)
Change 3670838 by Alexis.Matte
Fix crash when deleting a skeletal mesh LOD and the mouse is over the "reimport" button.
#jira UE-50387
Change 3671559 by Matt.Kuhlenschmidt
Update SimpleUI automation test ground truth
#jira UE-50325
Change 3671587 by Alexis.Matte
Fix fbx importer scale not always apply. A cache array was not reset when opening a fbx file.
#jira UE-50147
Change 3671730 by Jamie.Dale
Added PostInitInstance to UClass to allow class types to perform construction time initialization of their instances
Change 3672104 by Michael.Dupuis
#jira UE-50427: Update the volume visibility list of the editor viewport when changing the procedural foliage settings
Change 3674906 by Alexis.Matte
Make sure the export LOD option is taken in consideration when exporting a level or the current level selection
#jira UE-50248
Change 3674942 by Matt.Kuhlenschmidt
Fix static analysis
Change 3675401 by Alexis.Matte
-fix export animation, do not truncate the last frame anymore
-fix the import animation, there was a display issue in the progress bar. Also a floorToInt sometime truncate the last valid frame. We also have a better way to calculate the time increment we use to sample the fbx curves.
#jira UE-48231
Change 3675990 by Alexis.Matte
Remove morph target when doing a re-import, so morph will be remove if they do not exist anymore in the fbx.
This is to avoid driving random vertex with old morph target.
#jira UE-50391
Change 3676169 by Alexis.Matte
When we re-import with dialog the option, "Override Full Name" was set to false and save with the option dialog. We now not set it to false, since it was not use during re-import.
Change 3676396 by Alexis.Matte
Make all LOD 0 name consistent in staticmesh editor
#jira UE-49461
Change 3677730 by Cody.Albert
Enable locking of Persistent Level in Levels tab
#jira UE-50686
Change 3677838 by Jamie.Dale
Replaced broken version of Roboto Light
Change 3679619 by Alexis.Matte
Integrate GitHub pr #4029 to fix import fbx chunk material assignation.
#jira UE-50001
Change 3680093 by Alexis.Matte
Fix the skeletal mesh so the vertex color is part of the vertex equality like with the static mesh.
Change 3680931 by Arciel.Rekman
SlateDialogs: show image icon for *.tga (UE-25106).
- Also reworked the logic somewhat.
#jira UE-25106
Change 3681966 by Yannick.Lange
MaterialEditor post-process preview.
#jira UE-45307
Change 3682407 by Lauren.Ridge
Fixes for material editor compile errors
Change 3682628 by Lauren.Ridge
Content browser filters for Material Layers, Blends, and their instances
Change 3682725 by Lauren.Ridge
Adding filter assets and instance assets to Material Layers and Material Layer Blends. Turning Material Layering on by default
Change 3682921 by Lauren.Ridge
Fix for instance layers not initializing fully
Change 3682954 by Lauren.Ridge
Creating Material Layer Test Assets
Change 3683582 by Alexis.Matte
Fix static analysis build
Change 3683614 by Matt.Kuhlenschmidt
PR #4062: Git Plugin: Fix UE-44637: Deleting an asset is unsuccessful if the asset is marked for add (Contributed by SRombauts)
Change 3684130 by Lauren.Ridge
Allow visible parameter retrieval to correctly recurse through internally called functions. Previous check was intended to prevent function previews from leaving their graph through unhooked inputs, but unintentionally blocked all function inputs.
Change 3686289 by Arciel.Rekman
Remove the pessimization (UE-23791).
Change 3686455 by Lauren.Ridge
Fixes for adding/removing a layer parameter from the parent not updating the child
Change 3686829 by Jamie.Dale
No longer include trailing whitespace in the justification calculation for soft-wrapped lines
#jira UE-50266
Change 3686970 by Lauren.Ridge
Making material parameter preview work for functions as well
Change 3687077 by Jamie.Dale
Fixed crash using FActorDetails with the struct details panel
Change 3687152 by Jamie.Dale
Fixed the row structure tag not appearing in the Content Browser for Data Table assets
The CDO is used to filter these tags, and the CDO was omiting that tag which caused it to be filtered for all Data Tables.
#jira UE-48691
Change 3687174 by Lauren.Ridge
Fix for material layer sub-parameters showing up in the default material parameters panel
Change 3688100 by Lauren.Ridge
Fixing static analysis error
Change 3688317 by Jamie.Dale
Fixed crash using the widget reflector in a cooked game
Editor-style isn't available in cooked games. Core-style should be used instead for the widget reflector.
Change 3689054 by Jamie.Dale
Reference Viewer can now show/copy references lists for nodes with multiple objects, or multiple selected nodes
#jira UE-45751
Change 3689513 by Jamie.Dale
Fixed justification bug with RTL text caused by CL# 3686829
Also implemented the same alignment fix for visually left-aligned RTL text.
#jira UE-50266
Change 3690231 by Lauren.Ridge
Added Material Layers Parameters Preview (all editing disabled) panel to the Material Editor
Change 3690234 by Lauren.Ridge
Adding Material Layers Function Parameter to Static Parameter Compare
Change 3690750 by Chris.Bunner
Potential nullptr crash.
Change 3690751 by Chris.Bunner
Fixed logic on overridden vector parameter retrieval for material instances checking a function owned parameter.
Change 3691010 by Jamie.Dale
Fixed some clipping issues that could occur with right-aligned text
FTextBlockLayout::OnPaint was passing an unscaled offset to SetVisibleRegion, and it also wasn't correctly adjusting the offset for RTL text with left-alignment (which becomes a visual right-alignment)
#jira UE-46760
Change 3691091 by Jamie.Dale
Renamed FTextBlockLayout to FSlateTextBlockLayout to reflect that it's a Slate specific type
Change 3691134 by Alexis.Matte
Make sure we instance also the collision mesh when exporting a level to fbx file.
#jira UE-51066
Change 3691157 by Lauren.Ridge
Fix for reset to default not refreshing sub-parameters
Change 3691192 by Jamie.Dale
Fixed Content Browser selection resetting when changing certain view settings
#jira UE-49611
Change 3691204 by Alexis.Matte
Remove fbx export file version 2010 compatibility. The 2018 fbx sdk refuse to export earlier then 2011.
#jira UE-51023
Change 3692335 by Lauren.Ridge
Setting displayed asset to equal filter asset if no instance has been selected
Change 3692479 by Jamie.Dale
Fixed whitespace
Change 3692508 by Alexis.Matte
Make sure we warn the user that there is nothing to export when exporting to fbx using "export selected" or "export All" from the file menu.
We also prevent the export dialog to show
#jira UE-50973
Change 3692639 by Jamie.Dale
Translation Editor now shows stale translations as "Untranslated"
Change 3692743 by Lauren.Ridge
Smaller blend icons, added icon size override to FObjectEntryBox
Change 3692830 by Alexis.Matte
Fix linux build
Change 3692894 by Lauren.Ridge
Tooltip on "Parent" in material layers
Change 3693141 by Jamie.Dale
Removed dead code
FastDecimalFormat made this redundant
Change 3693580 by Jamie.Dale
Added AlwaysSign number formatting option
#jira UE-10310
Change 3693784 by Jamie.Dale
Fixed assert extracting the number formatting rules for Arabic
It uses a character outside the BMP for its plus and minus sign, so we need these to be a string to handle that.
#jira UE-10310
Change 3694428 by Arciel.Rekman
Linux: make directory watch request a warning so they don't block cooking.
- See https://answers.unrealengine.com/questions/715206/cook-error-on-linux.html
Change 3694458 by Matt.Kuhlenschmidt
Made duplicate keybinding warning non-fatal
Change 3694496 by Alexis.Matte
fix static analysis build
Change 3694515 by Jamie.Dale
Added support for culture correct parsing of decimal numbers
#jira UE-4028
Change 3694621 by Jamie.Dale
Added a variant of FastDecimalFormat::StringToNumber that takes a string length
This can be useful if you want to convert a number from within a non-null terminated string
#jira UE-4028
Change 3694958 by Jamie.Dale
Added a parsed length output to FastDecimalFormat::StringToNumber to allow permissive parsing
You can test this rather than the result if you want to attempt to parse a number from a string that may have other data after it. This also fixes the sign-suffix causing the parsing to fail.
#jira UE-4028
Change 3695083 by Alexis.Matte
Optimisation of the morph target import
- We now compute only the normal for the shape the tangent are not necessary
- The async tasks are create when there is some available cpu thread to avoid filling the memory
- When we re-import the morph target are deleted in bulk avoiding to initialize the morph map for every morphs targets
#jira UE-50945
Change 3695122 by Jamie.Dale
GetCultureAgnosticFormattingRules no longer returns a copy
Change 3695835 by Arciel.Rekman
TestPAL: greatly expanded malloc test.
Change 3695918 by Arciel.Rekman
TestPAL: Added thread priority test.
Change 3696589 by Arciel.Rekman
TestPAL: tweak thread priorities test (better readability).
Change 3697345 by Alexis.Matte
Fix reorder of material when importing a LOD with new material
#jira UE-51135
Change 3699590 by Jamie.Dale
Updated SGraphPinNum to use a numeric editor
#jira UE-4028
Change 3699698 by Matt.Kuhlenschmidt
Fix crash opening the level viewport context menu if the actor-component selection is out of sync
#jira UE-48444
Change 3700158 by Arciel.Rekman
Enable packaging for Android Vulkan on Linux (UETOOL-1232).
- Change by Cengiz Terzibas
Change 3700224 by Arciel.Rekman
TestPAL: fixed a memory leak.
Change 3700775 by Cody.Albert
Don't need to initialize EnvironmentCubeMap twice.
Change 3700866 by Michael.Trepka
PR #3223: Remove unnecessary reallocation. (Contributed by foollbar)
#jira UE-41643
Change 3701132 by Michael.Trepka
Copy of CL 3671538
Fixed issues with editor's game mode in high DPI on Mac.
#jira UE-49947, UE-51063
Change 3701421 by Michael.Trepka
Fixed a crash in FScreenShotManager caused by an attempt to access a deleted FString in async lambda expression
Change 3701495 by Alexis.Matte
Fix fbx importer "import normals" option when mix with "mikkt" tangent build it was recomputing the normals instead of importing them.
#jira UE-UE-51359
Change 3702982 by Jamie.Dale
Cleaned up some localization setting names
These now have consistent names and avoid double negatives. This also fixes needing to restart the editor when changing the "ShouldUseLocalizedPropertyNames" setting.
Change 3703517 by Arciel.Rekman
TestPAL: improved thread test.
- Changed the counter to a normal variable to reduce possible contentions (threads used to share the counter in an early prototype, hence the usage of an atomic).
Change 3704378 by Michael.Trepka
Disable Zoom button on Mac if project requests a resizeable window without it.
#jira UE-51335
Change 3706316 by Jamie.Dale
Fixed the asset search suggestions list closing if you clicked on its scrollbar
#jira UE-28885
Change 3706855 by Alexis.Matte
Support importing animation that has some keys with negative time
#jira UE-51305
Change 3709634 by Matt.Kuhlenschmidt
PR #4146: Null access check on ForceLOD in FViewport::HighResScreenshot (Contributed by projectgheist)
Change 3711085 by Michael.Trepka
Reenabled UBT makefiles on Mac
Change 3713049 by Josh.Engebretson
The ConfigPropertyEditor now generates a unique runtime UClass. It uses the outer name on the property instead of a unique ID as a unique id would generate a new UClass every time (and these are RF_Standalone). I also removed some static qualifiers for Section and Property names which were incorrect.
#jira UE-51319
Change 3713144 by Lauren.Ridge
Fixing automated test error
#jira UE-50982
Change 3713395 by Alexis.Matte
Fix auto import mountpoint
#jira UE-51524
Change 3713881 by Michael.Trepka
Added -buildscw to Mac Build.sh script to build ShaderCompileWorker in addition to the requested target. Xcode passes it to the script when building non-program targets.
#jira UE-31093
Change 3714197 by Michael.Trepka
Send IMM key down event to the main window instead of Cocoa key window, as that's what the Slate's active window is. This solves problems with IMM not working in context menu text edit fields.
#jira UE-47915
Change 3714911 by Joe.Graf
Merge of cmake changes from Dev-Rendering
Change 3715973 by Michael.Trepka
Disable OS close button on Windows if project settings request that
#jira UE-45522
Change 3716390 by Lauren.Ridge
The color picker summoned when double-clicking vector3 nodes now has its intended "do not refresh until OK is clicked" behavior.
#jira UE-50916
Change 3716529 by Josh.Engebretson
Content Browser: Clamp "Assets to Load at Once Before Warning" so it cannot be set below 1
#jira UE-51341
Change 3716885 by Josh.Engebretson
Tracking transactions such as a duplication operation can modify a selection which differs from the initial one. Added package state tracking to restore unmodified state when necessary.
#jira UE-48572
Change 3716929 by Josh.Engebretson
Unshelved from pending changelist '3364093':
PR #3420: Exe's icons and properties (Contributed by projectgheist)
Change 3716937 by Josh.Engebretson
Unshelved from pending changelist '3647428':
PR #4026: Fixed memory leaks for pipe writes and added data pipe writes (Contributed by Hemofektik)
Change 3717002 by Josh.Engebretson
Fix FileReference/string conversion
Change 3717355 by Joe.Graf
Fixed CMake file generation on Windows including Engine/Source/ThirdParty source
Change 3718256 by Arciel.Rekman
TestPAL: slight mod to the malloc test.
- Touch the allocated memory to check actual resident usage.
Change 3718290 by Arciel.Rekman
BAFO: place descriptor after the allocation to save some VIRT memory.
- We're relying on passing correct "Size" argument to Free() anyway, and this modification makes use of that extra information to save on memory for the descriptor.
Change 3718508 by Michael.Trepka
Fixed vsnprintf on platforms that use our custom implementation in StandardPlatformString.cpp to ignore length modifier for certain types (floating point, pointer)
#jira UE-46148
Change 3718855 by Lauren.Ridge
Adding content browser favorite folders. Add or remove folders from the favorite list in the folder's right-click context menu, and hide or show the favorites list in the Content Browser options.
Change 3718932 by Cody.Albert
Update ActorSequence plugin loading phase to PreDefault
#jira UE-51612
Change 3719378 by tim.gautier
QAGame: Renamed multiTxt_Justification > UMG_TextJustification.
Added additional Text Widgets for testing
Change 3719413 by Lauren.Ridge
Resubmit of content browser favorites
Change 3719803 by Yannick.Lange
VREditor: Fix crash with null GEditor
#jira UE-50103
Change 3721127 by tim.gautier
QAGame: Fixed up a ton of redirectors within /Content and /Content/Materials
- Added M_ParamDefaults and MF_ParamDefaults
- Moved legacy MeshPaint materials into /Content/Materials/MeshPaint
- Renamed ColorPulse assets from MatFunction_ > MF_, moved into /Content/Materials/Functions
Change 3721255 by Alexis.Matte
Replace skeletal mesh import option "keep overlapping vertex" by 3 float thresholds allowing the user to control the welding thresholds.
#jira UE-51363
Change 3721594 by Lauren.Ridge
Material Blends now have plane mesh previews in their icons.
Change 3722072 by tim.gautier
QAGame: Updated MF_ParamDefaults - using red channel as roughness
Updated M_ParamDefaults - tweaked Scalar values
Change 3722180 by Michael.Trepka
Updated Xcode project generator to sort projects in the navigator by name (within folders) and also sort the list of schemes so that their order matches the order of projects in the navigator.
#jira UE-25941
Change 3722220 by Michael.Trepka
Fixed a problem with Xcode project generator not handling quoted preprocessor definitions correctly
#jira UE-40246
Change 3722806 by Lauren.Ridge
Fixing non-editor compiles
Change 3722914 by Alexis.Matte
Fbx importer: Add new attribute type(eSkeleton) for staticmesh socket import.
#jira UE-51665
Change 3723446 by Michael.Trepka
Copy of CL 3688862 from 4.18 + one more fix for a deadlock related to window resizing when using IME
Don't do anything in Mac window's windowWillResize: if we're simply chaning the z order of windows. This way we avoid a rare dead lock when hiding the window.
#jira UE-48257
Change 3723505 by Matt.Kuhlenschmidt
Fix duplicate actors being created for USD primitives that specify a custom actor class
Change 3723555 by Matt.Kuhlenschmidt
Fix crash loading the gameplayabilities module
#jira UE-51693
Change 3723557 by Matt.Kuhlenschmidt
Fixed tooltip on viewport dpi scaling option
Change 3723870 by Lauren.Ridge
Fixing incorrect reset to default visibility, adding clear behavior to fields
Change 3723917 by Arciel.Rekman
Linux: fix compilation with glibc 2.26+ (UE-51699).
- Fixes compilation on Ubuntu 17.10 among others.
(Merging 3723489 from //UE4/Release-4.18/... to //UE4/Dev-Editor/...)
Change 3723918 by Arciel.Rekman
Linux: do not test for popcnt presence unnecessarily (UE-51677).
(Merging 3723904 from //UE4/Release-4.18/... to //UE4/Dev-Editor/...)
Change 3724229 by Arciel.Rekman
Fix FOutputDeviceStdOutput to use printf() on Unix platforms.
Change 3724261 by Arciel.Rekman
TestPAL: fix thread priority test (zero the counter).
Change 3724978 by Arciel.Rekman
Linux: fix priority calculation.
- Rlimit values are always positive, so this was completely broken when the RLIMIT_NICE is non-0.
Change 3725382 by Matt.Kuhlenschmidt
Guard against crashes and add more logging when actor creation fails.
Looks like it could be manual garbage collections triggered before conversion is complete so those have been removed
#jira UE-47464
Change 3725559 by Matt.Kuhlenschmidt
Added a setting to enable/disable high dpi support in editor. This currently only functions in Windows.
Moved some files around for better consistency
Change 3725640 by Arciel.Rekman
Fix Linux thread/process priorities.
- Should also speed up SCW on Linux by deprioritizing them less.
Change 3726101 by Matt.Kuhlenschmidt
Fix logic bug in USD child "kind" type resolving
Change 3726244 by Joe.Graf
Added an option to generate a minimal set of targets for cmake files
Added shader and config files to cmake file generation for searching within IDEs
Change 3726506 by Arciel.Rekman
Fix compile issue after DPI change.
Change 3726549 by Matt.Kuhlenschmidt
Remove unnecessary indirection to cached widgets in the hit test grid
Change 3726660 by Arciel.Rekman
Enable DPI switch on Linux.
Change 3726763 by Arciel.Rekman
Fix mismatching "noperspective" qualifier (UE-50807).
- Pull request #4080 by TTimo.
Change 3727080 by Michael.Trepka
Added support for editor's EnableHighDPIAwareness setting on Mac
Change 3727658 by Matt.Kuhlenschmidt
Fix shutdown crash if level editor is still referenced after the object system has been gc'd
#jira UE-51630
Change 3728270 by Matt.Kuhlenschmidt
Remove propertyeditor dependency from editorstyle
Change 3728291 by Arciel.Rekman
Linux: fix for a crash on a headless system (UE-51714).
- Preliminary change before merging to 4.18.
Change 3728293 by Arciel.Rekman
Linux: remove unneeded dependency on CEF.
- Old workaround should no longer be needed, while this dependency makes UE4 depend on a ton of external libs.
Change 3728524 by Michael.Trepka
Copy of CL 3725570
Removed Enable Fullscreen option from editor's Window menu on Mac. Windowed fullscreen mode is currently unavailable on Mac in editor mode as supporting it properly would require it to work with multiple spaces and split screen, which we currently don't handle (requested in UE-27240)
#jira UE-51709
Change 3728875 by Michael.Trepka
Fixed compile error in Mac SlateOpenGLContext.cpp
Change 3728880 by Matt.Kuhlenschmidt
Guard against invalid worlds in thumbnail renderers
Change 3728924 by Michael.Trepka
Don't defer MacApplication->CloseWindow() call. This should fix a rare problem with deferred call executing during Slate's PrepassWindowAndChildren call.
#jira UE-51711
Change 3729288 by Joe.Graf
Added the .idea/misc.xml file generation to speed up CLion indexing
Change 3729935 by Michael.Dupuis
#jira UE-51722: Hide from UI invalid enum values
Change 3730234 by Matt.Kuhlenschmidt
Fix "Game Gets Mouse Control" setting no longer functioning and instead the mouse was always captured.
#jira UE-51801
Change 3730349 by Michael.Dupuis
#jira UE-51324: Clear the UI selection when rebuilding the palette, as we destroyed all items and recreate them, so selection is on invalid item
Change 3730438 by Lauren.Ridge
Cleaning up material layering UI functions
Change 3730723 by Jamie.Dale
Fixed FastDecimalFormat::StringToNumber incorrectly reporting that number-like sequences that lacked digits had been parsed as numbers
#jira UE-51799
Change 3731008 by Lauren.Ridge
Changing Layers and Blends from proxy assets to real assets
Change 3731026 by Arciel.Rekman
libelf: make elf_end() visible (UE-51843).
- This repairs compilation for a case when CUDA is being used.
- Also added some missing files for ARM 32-bit.
Change 3731081 by Lauren.Ridge
New material layer test assets
Change 3731186 by Josh.Engebretson
Adding camera speed scalar setting and Toolbar UI to increase range on camera speed presets
#jira UE-50104
Change 3731188 by Mike.Erwin
Improve responsiveness of Open Asset dialog.
On large projects, there's a noticeable delay when opening and searching/filtering assets.
Stopwatch measurements on my machine (seconds for ~122,000 assets):
before with this CL
ctrl-P 1.4 0.45
search 1.8 0.55
CollectionManagerModule was the main culprit for search/filter slowness.
Open Asset delay was due to filtering out plugin content. We were doing a lot of redundant work for what is essentially a read-only operation.
Change 3731682 by Arciel.Rekman
UnrealEd: Allow unattended commandlets to rename/save packages.
Change 3732305 by Michael.Dupuis
#jira UE-48434 : Only register if the foliage type still has a valid mesh
Change 3732361 by Matt.Kuhlenschmidt
Fix two settings objects being created in the transient package with the same name
#jira UE-51891
Change 3732895 by Josh.Engebretson
https://jira.it.epicgames.net/browse/UE-51706
If a shared DDC is not being used, present a notification to the licensee with a link on how to setup a shared DDC.
Adds DDC notification events for check/put and query for whether a shared DDC is in use.
#jira UE-51706
Change 3733025 by Arciel.Rekman
UBT: make sure new clang versions are invoked.
Change 3733311 by Mike.Erwin
Fix Linux compile warning from CL 3731188
It didn't like mixing && and || without parentheses. Reworked logic to do one test at a time, put cheaper tests first to avoid calls to more expensive IsPluginFolder.
Change 3733658 by Josh.Engebretson
Add a missing #undef LOCTEXT_NAMESPACE
Change 3734003 by Arciel.Rekman
Fix Windows attempting to use printf %ls and crashing at that (UE-51934).
Change 3734039 by Michael.Trepka
Fixed a couple of merge issues in Mac ApplicationCore
Change 3734052 by Michael.Trepka
One more Mac ApplicationCore fix
Change 3734244 by Lauren.Ridge
Fix for accessing Slate window on render thread
Change 3734950 by Josh.Engebretson
Fixing clang warning
Change 3734978 by Jamie.Dale
Relaxed enum property importing to allow valid numeric values to be imported too
This was previously made more strict which caused a regression in Data Table importing
#jira UE-51848
Change 3734999 by Arciel.Rekman
Linux: add LTO support and more.
- Adds ability to use link-time opitimization (reusing current target property bAllowLTCG).
- Supports using llvm-ar and lld instead of ar/ranlib and ld.
- More build information printed (and in a better organized way).
- Native scripts updated to install packages with the appropriate tools on supported systems
- AutoSDKs updated to require a new toolchain (already checked in).
- Required disabling OpenAL due to https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=219089
Change 3735268 by Matt.Kuhlenschmidt
Added support for canvas based DPI scaling.
-Scene canvas is by default not scaled as this could severely impact any game using a canvas based UI
-The debug canvas for stats is always dpi scaled in editor and pie.
-Eliminated text scaling workaround now that the entire canvas is properly scaled
-Enabled canvas scaling in cascade UI
Change 3735329 by Matt.Kuhlenschmidt
Fix potential crash if an asset editor has an object deleted out from under it
#jira UE-51941
Change 3735502 by Arciel.Rekman
Fix compile issue (bShouldUpdateScreenPercentage).
Change 3735878 by Jamie.Dale
Updated FString::SanitizeFloat to allow you to specify the min number of fractional digits to have in the resultant string
This defaults to 1 as that was the old behavior of FString::SanitizeFloat, but can also be set to 0 to prevent adding .0 to whole numbers.
Change 3735881 by Jamie.Dale
JsonValue no longer stringifies whole numbers as floats
Change 3735884 by Jamie.Dale
Only allow enums to import integral values
Change 3735912 by Josh.Engebretson
Improving cook process error/warning handling including asset warning/error content browser links and manual dismiss for cook error notifications
#jira UE-48131
Change 3736280 by Matt.Kuhlenschmidt
Fix 0 dpi scale for canvases
#jira UE-51995
Change 3736298 by Matt.Kuhlenschmidt
Force focus of game viewports in vr mode
Change 3736374 by Jamie.Dale
Fixed some places where input chords were being used without testing that they had a valid key set
#jira UE-51799
Change 3738543 by Matt.Kuhlenschmidt
Better fix for edit condition crashes
#jira UE-51886
Change 3738603 by Lauren.Ridge
Copy over of drag and drop non-array onto array fix
Change 3739701 by Chris.Babcock
Fix crashlytics merge error
#jira UE-52064
#ue4
#android
[CL 3739980 by Matt Kuhlenschmidt in Main branch]
2017-11-06 18:22:01 -05:00
# include "Misc/ScopeLock.h"
2021-04-28 16:22:18 -04:00
# include "Misc/StringBuilder.h"
2021-11-10 13:08:00 -05:00
# include "ProfilingDebugging/CookStats.h"
2021-05-18 18:50:43 -04:00
# include "ProfilingDebugging/CountersTrace.h"
2021-11-10 13:08:00 -05:00
# include "ProfilingDebugging/CpuProfilerTrace.h"
2021-04-28 16:22:18 -04:00
# include "Serialization/CompactBinary.h"
2021-11-10 13:08:00 -05:00
# include "Serialization/CompactBinaryPackage.h"
2021-04-28 16:22:18 -04:00
# include "Serialization/CompactBinaryValidation.h"
# include "Serialization/CompactBinaryWriter.h"
2021-11-10 13:08:00 -05:00
# include "Templates/Greater.h"
Copying //UE4/Orion-Staging to //UE4/Main (Origin //Orion/Dev-General @ 2870388)
#lockdown Nick.Penwarden
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2870336 on 2016/02/17 by Marc.Audy
Continued splitting up Orion Build
* Restructure from platform based MakeBuild steps in to a PS4, Server, and Windows Client MakeBuild
* Cook server data only once for both Windows and Linux (windows reuses Linux server data)
* Split compilation of Win64 Client and Server such that MakeBuild_Server only builds Server and MakeBuild_WindowsClient only builds Client
#jira UEB-580
#rb Ben.Marsh
#tests Preflight and generated Windows Client and Server work to play game
Change 2870026 on 2016/02/17 by Wes.Hunt
Don't allow array shrinking when removing the corruption wrapper trailer.
#rb none
Updating CIS Counter
Change 2869725 on 2016/02/17 by Dmitry.Rekman
More analytics and QoS stats added for 0.19.
#rb none
#tests Ran Windows client and Linux server on compatible content.
Change 2869705 on 2016/02/16 by Ryan.Gerleve
Fix replicated properties and call RepNotifies of startup actors when scrubbing in replays.
This is the engine support for fixing OR-6817, towers not respawning when rewinding replays.
#rb john.pollard
#tests golden path, replays, ps4 nomcp
Change 2869644 on 2016/02/16 by Jason.Bestimt
#ORION_DEV - Merge MAIN (0.18) at CL# 2869635
#Tests:none
#RB:none
Change 2869586 on 2016/02/16 by Marcus.Wassmer
Fix texturestreaming RHI flushes.
#rb none
#test goldenpath
#codereview Gil.Gribb
Change 2869279 on 2016/02/16 by Lukasz.Furman
fixed minion hit reaction directions
#orion OR-13953
#rb Mieszko.Zielinski
#tests PIE: hit minions with various abilities from different angles, checked velocity of death particles when killed by abilities and towers
#codereview Dan.Youhon
Change 2869277 on 2016/02/16 by Wes.Hunt
During cook, when a package is not ready to save, actually early out of the saving code. Saves somewhere in the 130s to 200s range for cooks.
#rb daniel.lamb
#tests local windows cooks, preflight PS4 cooks
Change 2869132 on 2016/02/16 by Mieszko.Zielinski
Added a function to AISenseConfig allowing native-code MaxAge configuration #UE4
#rb Lukasz.Furman
#test none required
Change 2868981 on 2016/02/16 by Wes.Hunt
remove -LogCookStats cmdline check, always log cook stats. -SendCookAnalytics flag is still used.
This was requested by NickP.
#rb none
#tests local windows cooks
Change 2868975 on 2016/02/16 by Wes.Hunt
Don't submit DDC usage stats for zero-sized events.
#rb none
#tests local windows cook
Change 2868956 on 2016/02/16 by Jason.Bestimt
#ORION_DEV - Merge MAIN (0.18) at CL# 2868926
#RB:none
#Tests:none
Change 2868889 on 2016/02/16 by Max.Chen
Sequencer: Only allow transport control binding when editing level editor sequencers.
#rb none
#tests none
Change 2868663 on 2016/02/16 by David.Ratti
downgrade warning to display
#rb none
#tests compile
Change 2868624 on 2016/02/16 by Marcus.Wassmer
Re-Enable Defrag validation for devgeneral
#rb none
#test none
Change 2868493 on 2016/02/16 by Benn.Gallagher
Added a few more stats to morph target updates to try and narrow down hitches
#rb Bruce.Nesbit
#tests pie, -game Win64
Change 2868445 on 2016/02/16 by Dmitry.Rekman
Linux: report crashes due to stack overflow (OR-14519).
- Reserve memory for alternative stack for signal handlers. Adds about 128KB memory per thread.
- Force process spawning to use vfork() when no pipes are needed.
- Ignore all signals except explicitly handled.
- Prevent signals from being raised while another one is handled.
- Added "debug threadrecurse" and "debug threadstackoverflow" to test that.
[CL 2873763 by Andrew Grant in Main branch]
2016-02-19 12:03:17 -05:00
2022-02-14 14:43:39 -05:00
namespace UE : : DerivedData
2021-04-28 16:22:18 -04:00
{
2021-05-18 18:50:43 -04:00
TRACE_DECLARE_INT_COUNTER ( FileSystemDDC_Get , TEXT ( " FileSystemDDC Get " ) ) ;
TRACE_DECLARE_INT_COUNTER ( FileSystemDDC_GetHit , TEXT ( " FileSystemDDC Get Hit " ) ) ;
TRACE_DECLARE_INT_COUNTER ( FileSystemDDC_Put , TEXT ( " FileSystemDDC Put " ) ) ;
TRACE_DECLARE_INT_COUNTER ( FileSystemDDC_PutHit , TEXT ( " FileSystemDDC Put Hit " ) ) ;
TRACE_DECLARE_INT_COUNTER ( FileSystemDDC_BytesRead , TEXT ( " FileSystemDDC Bytes Read " ) ) ;
TRACE_DECLARE_INT_COUNTER ( FileSystemDDC_BytesWritten , TEXT ( " FileSystemDDC Bytes Written " ) ) ;
2021-11-10 13:08:00 -05:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2022-06-07 11:17:10 -04:00
# if PLATFORM_LINUX
// PATH_MAX on Linux is 4096 (getconf PATH_MAX /, also see limits.h), so this value can be larger (note that it is still arbitrary).
// This should not affect sharing the cache between platforms as the absolute paths will be different anyway.
static constexpr int32 GMaxCacheRootLen = 3119 ;
# else
static constexpr int32 GMaxCacheRootLen = 119 ;
# endif // PLATFORM_LINUX
static constexpr int32 GMaxCacheKeyLen =
FCacheBucket : : MaxNameLen + // Name
sizeof ( FIoHash ) * 2 + // Hash
4 + // Separators /<Name>/<Hash01>/<Hash23>/<Hash4-40>
4 ; // Extension (.udd)
2022-03-17 10:29:53 -04:00
static const TCHAR * GBucketsDirectoryName = TEXT ( " Buckets " ) ;
static const TCHAR * GContentDirectoryName = TEXT ( " Content " ) ;
2021-11-10 13:08:00 -05:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2022-02-14 14:43:39 -05:00
void BuildPathForCachePackage ( const FCacheKey & CacheKey , FStringBuilderBase & Path )
2021-04-28 16:22:18 -04:00
{
2021-11-10 13:08:00 -05:00
const FIoHash : : ByteArray & Bytes = CacheKey . Hash . GetBytes ( ) ;
Path . Appendf ( TEXT ( " %s/%hs/%02x/%02x/ " ) , GBucketsDirectoryName , CacheKey . Bucket . ToCString ( ) , Bytes [ 0 ] , Bytes [ 1 ] ) ;
UE : : String : : BytesToHexLower ( MakeArrayView ( Bytes ) . RightChop ( 2 ) , Path ) ;
2022-02-25 15:48:16 -05:00
Path < < TEXTVIEW ( " .udd " ) ;
2021-04-28 16:22:18 -04:00
}
2022-02-14 14:43:39 -05:00
void BuildPathForCacheContent ( const FIoHash & RawHash , FStringBuilderBase & Path )
2021-04-28 16:22:18 -04:00
{
2021-11-10 13:08:00 -05:00
const FIoHash : : ByteArray & Bytes = RawHash . GetBytes ( ) ;
Path . Appendf ( TEXT ( " %s/%02x/%02x/ " ) , GContentDirectoryName , Bytes [ 0 ] , Bytes [ 1 ] ) ;
UE : : String : : BytesToHexLower ( MakeArrayView ( Bytes ) . RightChop ( 2 ) , Path ) ;
2022-02-25 15:48:16 -05:00
Path < < TEXTVIEW ( " .udd " ) ;
2021-04-28 16:22:18 -04:00
}
2021-11-10 13:08:00 -05:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static uint64 RandFromGuid ( )
2020-03-24 19:12:36 -04:00
{
2021-11-10 13:08:00 -05:00
const FGuid Guid = FGuid : : NewGuid ( ) ;
return FXxHash64 : : HashBuffer ( & Guid , sizeof ( FGuid ) ) . Hash ;
2020-03-24 19:12:36 -04:00
}
2021-11-10 13:08:00 -05:00
/** A LCG in which the modulus is a power of two where the exponent is the bit width of T. */
template < typename T , T Modulus = 0 >
class TLinearCongruentialGenerator
2020-03-24 19:12:36 -04:00
{
2021-11-10 13:08:00 -05:00
static_assert ( ! TIsSigned < T > : : Value ) ;
static_assert ( ( Modulus & ( Modulus - 1 ) ) = = 0 , " Modulus must be a power of two. " ) ;
public :
constexpr inline TLinearCongruentialGenerator ( T InMultiplier , T InIncrement )
: Multiplier ( InMultiplier )
, Increment ( InIncrement )
{
}
constexpr inline T GetNext ( T & Value )
{
Value = ( Value * Multiplier + Increment ) & ( Modulus - 1 ) ;
return Value ;
}
private :
const T Multiplier ;
const T Increment ;
} ;
class FRandomStream
{
public :
2021-11-10 14:44:18 -05:00
inline explicit FRandomStream ( const uint32 Seed )
2021-11-10 13:08:00 -05:00
: Random ( 1103515245 , 12345 ) // From ANSI C
, Value ( Seed )
{
}
/** Returns a random value in [Min, Max). */
2021-11-10 14:44:18 -05:00
inline uint32 GetRandRange ( const uint32 Min , const uint32 Max )
2021-11-10 13:08:00 -05:00
{
return Min + uint32 ( ( uint64 ( Max - Min ) * Random . GetNext ( Value ) ) > > 32 ) ;
}
private :
TLinearCongruentialGenerator < uint32 > Random ;
uint32 Value ;
} ;
template < uint32 Modulus , uint32 Count = Modulus >
class TRandomOrder
{
static_assert ( ( Modulus & ( Modulus - 1 ) ) = = 0 & & Modulus > 16 , " Modulus must be a power of two greater than 16. " ) ;
static_assert ( Count > 0 & & Count < = Modulus , " Count must be in the range (0, Modulus]. " ) ;
public :
inline explicit TRandomOrder ( FRandomStream & Stream )
: Random ( Stream . GetRandRange ( 0 , Modulus / 16 ) * 8 + 5 , 12345 )
, First ( Stream . GetRandRange ( 0 , Count ) )
, Value ( First )
{
}
inline uint32 GetFirst ( ) const
{
return First ;
}
inline uint32 GetNext ( )
{
if constexpr ( Count < Modulus )
{
for ( ; ; )
{
if ( const uint32 Next = Random . GetNext ( Value ) ; Next < Count )
{
return Next ;
}
}
}
return Random . GetNext ( Value ) ;
}
private :
TLinearCongruentialGenerator < uint32 , Modulus > Random ;
uint32 First ;
uint32 Value ;
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct FFileSystemCacheStoreMaintainerParams
{
/** Files older than this will be deleted. */
FTimespan MaxFileAge = FTimespan : : FromDays ( 15.0 ) ;
2022-04-29 16:18:09 -04:00
/** Limits the number of paths scanned in one second. */
uint32 MaxScanRate = MAX_uint32 ;
2021-11-10 13:08:00 -05:00
/** Limits the number of directories scanned in each cache bucket or content root. */
uint32 MaxDirectoryScanCount = MAX_uint32 ;
2021-11-15 13:56:02 -05:00
/** Minimum duration between the start of consecutive scans. Use MaxValue to scan only once. */
2021-11-10 13:08:00 -05:00
FTimespan ScanFrequency = FTimespan : : FromHours ( 1.0 ) ;
/** Time to wait after initialization before maintenance begins. */
FTimespan TimeToWaitAfterInit = FTimespan : : FromMinutes ( 1.0 ) ;
} ;
class FFileSystemCacheStoreMaintainer final : public ICacheStoreMaintainer
{
public :
FFileSystemCacheStoreMaintainer ( const FFileSystemCacheStoreMaintainerParams & Params , FStringView CachePath ) ;
~ FFileSystemCacheStoreMaintainer ( ) ;
bool IsIdle ( ) const final { return bIdle ; }
void WaitForIdle ( ) const { IdleEvent . Wait ( ) ; }
void BoostPriority ( ) final ;
private :
2021-11-15 13:56:02 -05:00
void Tick ( ) ;
2021-11-10 13:08:00 -05:00
void Loop ( ) ;
void Scan ( ) ;
void CreateContentRoot ( ) ;
void CreateBucketRoots ( ) ;
void ScanHashRoot ( uint32 RootIndex ) ;
2021-11-10 14:44:18 -05:00
TStaticBitArray < 256 > ScanHashDirectory ( FStringBuilderBase & BasePath ) ;
2021-11-10 13:08:00 -05:00
2021-11-10 14:44:18 -05:00
TStaticBitArray < 10 > ScanLegacyDirectory ( FStringBuilderBase & BasePath ) ;
2021-11-10 13:08:00 -05:00
void CreateLegacyRoot ( ) ;
void ScanLegacyRoot ( ) ;
void ResetRoots ( ) ;
2022-04-29 16:18:09 -04:00
void ProcessDirectory ( const TCHAR * Path ) ;
void ProcessFile ( const TCHAR * Path , const FFileStatData & Stat , bool & bOutDeletedFile ) ;
void ProcessWait ( ) ;
void DeleteDirectory ( const TCHAR * Path ) ;
2021-11-10 13:08:00 -05:00
private :
struct FRoot ;
struct FLegacyRoot ;
FFileSystemCacheStoreMaintainerParams Params ;
/** Path to the root of the cache store. */
FString CachePath ;
/** True when there is no active maintenance scan. */
bool bIdle = false ;
2021-11-15 13:56:02 -05:00
/** True when maintenance is expected to exit as soon as possible. */
bool bExit = false ;
/** True when maintenance is expected to exit at the end of the scan. */
bool bExitAfterScan = false ;
2022-04-29 16:18:09 -04:00
/** Ignore the scan rate for one maintenance scan. */
bool bIgnoreScanRate = false ;
2021-11-10 13:08:00 -05:00
uint32 ProcessCount = 0 ;
uint32 DeleteCount = 0 ;
uint64 DeleteSize = 0 ;
double BatchStartTime = 0.0 ;
IFileManager & FileManager = IFileManager : : Get ( ) ;
mutable FLazyEvent IdleEvent ;
FEventRef WaitEvent ;
FThread Thread ;
TArray < TUniquePtr < FRoot > > Roots ;
TUniquePtr < FLegacyRoot > LegacyRoot ;
FRandomStream Random { uint32 ( RandFromGuid ( ) ) } ;
2021-11-15 13:56:02 -05:00
static constexpr double MaxScanFrequencyDays = 365.0 ;
2021-11-10 13:08:00 -05:00
} ;
struct FFileSystemCacheStoreMaintainer : : FRoot
{
2021-11-10 14:44:18 -05:00
inline FRoot ( const FStringView RootPath , FRandomStream & Stream )
2021-11-10 13:08:00 -05:00
: Order ( Stream )
{
Path . Append ( RootPath ) ;
}
TStringBuilder < 256 > Path ;
TRandomOrder < 256 * 256 > Order ;
TStaticBitArray < 256 > ScannedLevel0 ;
TStaticBitArray < 256 > ExistsLevel0 ;
TStaticBitArray < 256 > ExistsLevel1 [ 256 ] ;
uint32 DirectoryScanCount = 0 ;
bool bScannedRoot = false ;
} ;
struct FFileSystemCacheStoreMaintainer : : FLegacyRoot
{
inline explicit FLegacyRoot ( FRandomStream & Stream )
: Order ( Stream )
{
}
TRandomOrder < 1024 , 1000 > Order ;
TStaticBitArray < 10 > ScannedLevel0 ;
TStaticBitArray < 10 > ScannedLevel1 [ 10 ] ;
TStaticBitArray < 10 > ExistsLevel0 ;
TStaticBitArray < 10 > ExistsLevel1 [ 10 ] ;
TStaticBitArray < 10 > ExistsLevel2 [ 10 ] [ 10 ] ;
uint32 DirectoryScanCount = 0 ;
} ;
FFileSystemCacheStoreMaintainer : : FFileSystemCacheStoreMaintainer (
const FFileSystemCacheStoreMaintainerParams & InParams ,
2021-11-10 14:44:18 -05:00
const FStringView InCachePath )
2021-11-10 13:08:00 -05:00
: Params ( InParams )
, CachePath ( InCachePath )
2021-11-15 13:56:02 -05:00
, bExitAfterScan ( Params . ScanFrequency . GetTotalDays ( ) > MaxScanFrequencyDays )
2021-11-10 13:08:00 -05:00
, IdleEvent ( EEventMode : : ManualReset )
, WaitEvent ( EEventMode : : AutoReset )
, Thread (
TEXT ( " FileSystemCacheStoreMaintainer " ) ,
[ this ] { Loop ( ) ; } ,
2021-11-15 13:56:02 -05:00
[ this ] { Tick ( ) ; } ,
2021-11-10 13:08:00 -05:00
/*StackSize*/ 32 * 1024 ,
TPri_BelowNormal )
{
IModularFeatures : : Get ( ) . RegisterModularFeature ( FeatureName , this ) ;
2020-03-24 19:12:36 -04:00
}
2021-11-10 13:08:00 -05:00
FFileSystemCacheStoreMaintainer : : ~ FFileSystemCacheStoreMaintainer ( )
{
bExit = true ;
IModularFeatures : : Get ( ) . UnregisterModularFeature ( FeatureName , this ) ;
WaitEvent - > Trigger ( ) ;
Thread . Join ( ) ;
}
void FFileSystemCacheStoreMaintainer : : BoostPriority ( )
{
2022-04-29 16:18:09 -04:00
bIgnoreScanRate = true ;
2021-11-10 13:08:00 -05:00
WaitEvent - > Trigger ( ) ;
}
2021-11-15 13:56:02 -05:00
void FFileSystemCacheStoreMaintainer : : Tick ( )
{
// Scan once and exit if the priority has been boosted.
2022-04-29 16:18:09 -04:00
if ( bIgnoreScanRate )
2021-11-15 13:56:02 -05:00
{
bExitAfterScan = true ;
Loop ( ) ;
}
bIdle = true ;
IdleEvent . Trigger ( ) ;
}
2021-11-10 13:08:00 -05:00
void FFileSystemCacheStoreMaintainer : : Loop ( )
{
WaitEvent - > Wait ( Params . TimeToWaitAfterInit , /*bIgnoreThreadIdleStats*/ true ) ;
while ( ! bExit )
{
const FDateTime ScanStart = FDateTime : : Now ( ) ;
DeleteCount = 0 ;
DeleteSize = 0 ;
IdleEvent . Reset ( ) ;
bIdle = false ;
Scan ( ) ;
bIdle = true ;
IdleEvent . Trigger ( ) ;
2022-04-29 16:18:09 -04:00
bIgnoreScanRate = false ;
2021-11-10 13:08:00 -05:00
const FDateTime ScanEnd = FDateTime : : Now ( ) ;
UE_LOG ( LogDerivedDataCache , Log ,
TEXT ( " %s: Maintenance finished in %s and deleted %u file(s) with total size % " UINT64_FMT " MiB. " ) ,
* CachePath , * ( ScanEnd - ScanStart ) . ToString ( ) , DeleteCount , DeleteSize / 1024 / 1024 ) ;
2021-11-15 13:56:02 -05:00
if ( bExit | | bExitAfterScan )
2021-11-10 13:08:00 -05:00
{
break ;
}
const FDateTime ScanTime = ScanStart + Params . ScanFrequency ;
2021-11-15 13:56:02 -05:00
UE_CLOG ( ScanEnd < ScanTime , LogDerivedDataCache , Verbose ,
TEXT ( " %s: Maintenance is paused until the next scan at %s. " ) , * CachePath , * ScanTime . ToString ( ) ) ;
2021-11-10 13:08:00 -05:00
for ( FDateTime Now = ScanEnd ; ! bExit & & Now < ScanTime ; Now = FDateTime : : Now ( ) )
{
WaitEvent - > Wait ( ScanTime - Now , /*bIgnoreThreadIdleStats*/ true ) ;
}
}
2021-11-15 13:56:02 -05:00
bIdle = true ;
IdleEvent . Trigger ( ) ;
2021-11-10 13:08:00 -05:00
}
void FFileSystemCacheStoreMaintainer : : Scan ( )
{
CreateContentRoot ( ) ;
CreateBucketRoots ( ) ;
CreateLegacyRoot ( ) ;
while ( ! bExit )
{
const uint32 RootCount = uint32 ( Roots . Num ( ) ) ;
const uint32 TotalRootCount = uint32 ( RootCount + LegacyRoot . IsValid ( ) ) ;
if ( TotalRootCount = = 0 )
{
break ;
}
if ( const uint32 RootIndex = Random . GetRandRange ( 0 , TotalRootCount ) ; RootIndex < RootCount )
{
ScanHashRoot ( RootIndex ) ;
}
else
{
ScanLegacyRoot ( ) ;
}
}
ResetRoots ( ) ;
}
void FFileSystemCacheStoreMaintainer : : CreateContentRoot ( )
{
TStringBuilder < 256 > ContentPath ;
FPathViews : : Append ( ContentPath , CachePath , GContentDirectoryName ) ;
if ( FileManager . DirectoryExists ( * ContentPath ) )
{
Roots . Add ( MakeUnique < FRoot > ( ContentPath , Random ) ) ;
}
}
void FFileSystemCacheStoreMaintainer : : CreateBucketRoots ( )
{
TStringBuilder < 256 > BucketsPath ;
FPathViews : : Append ( BucketsPath , CachePath , GBucketsDirectoryName ) ;
2021-11-10 14:44:18 -05:00
FileManager . IterateDirectoryStat ( * BucketsPath , [ this ] ( const TCHAR * Path , const FFileStatData & Stat ) - > bool
2021-11-10 13:08:00 -05:00
{
2021-11-10 14:44:18 -05:00
if ( Stat . bIsDirectory )
2021-11-10 13:08:00 -05:00
{
Roots . Add ( MakeUnique < FRoot > ( Path , Random ) ) ;
}
return ! bExit ;
} ) ;
}
2021-11-10 14:44:18 -05:00
void FFileSystemCacheStoreMaintainer : : ScanHashRoot ( const uint32 RootIndex )
2021-11-10 13:08:00 -05:00
{
FRoot & Root = * Roots [ int32 ( RootIndex ) ] ;
const uint32 DirectoryIndex = Root . Order . GetNext ( ) ;
const uint32 IndexLevel0 = DirectoryIndex / 256 ;
const uint32 IndexLevel1 = DirectoryIndex % 256 ;
bool bScanned = false ;
ON_SCOPE_EXIT
{
if ( ( DirectoryIndex = = Root . Order . GetFirst ( ) ) | |
( bScanned & & + + Root . DirectoryScanCount > = Params . MaxDirectoryScanCount ) )
{
2021-11-10 14:44:18 -05:00
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
TEXT ( " %s: Maintenance finished scanning %s. " ) , * CachePath , * Root . Path ) ;
2021-11-10 13:08:00 -05:00
Roots . RemoveAt ( int32 ( RootIndex ) ) ;
}
} ;
if ( ! Root . bScannedRoot )
{
Root . ExistsLevel0 = ScanHashDirectory ( Root . Path ) ;
Root . bScannedRoot = true ;
}
if ( ! Root . ExistsLevel0 [ IndexLevel0 ] )
{
return ;
}
if ( ! Root . ScannedLevel0 [ IndexLevel0 ] )
{
TStringBuilder < 256 > Path ;
Path . Appendf ( TEXT ( " %s/%02x " ) , * Root . Path , IndexLevel0 ) ;
Root . ExistsLevel1 [ IndexLevel0 ] = ScanHashDirectory ( Path ) ;
Root . ScannedLevel0 [ IndexLevel0 ] = true ;
}
if ( ! Root . ExistsLevel1 [ IndexLevel0 ] [ IndexLevel1 ] )
{
return ;
}
TStringBuilder < 256 > Path ;
Path . Appendf ( TEXT ( " %s/%02x/%02x " ) , * Root . Path , IndexLevel0 , IndexLevel1 ) ;
2022-04-29 16:18:09 -04:00
ProcessDirectory ( * Path ) ;
2021-11-10 13:08:00 -05:00
bScanned = true ;
}
2021-11-10 14:44:18 -05:00
TStaticBitArray < 256 > FFileSystemCacheStoreMaintainer : : ScanHashDirectory ( FStringBuilderBase & BasePath )
2021-11-10 13:08:00 -05:00
{
TStaticBitArray < 256 > Exists ;
2021-11-10 14:44:18 -05:00
FileManager . IterateDirectoryStat ( * BasePath , [ this , & Exists ] ( const TCHAR * Path , const FFileStatData & Stat ) - > bool
2021-11-10 13:08:00 -05:00
{
2021-11-10 14:44:18 -05:00
FStringView View = FPathViews : : GetCleanFilename ( Path ) ;
if ( Stat . bIsDirectory & & View . Len ( ) = = 2 & & Algo : : AllOf ( View , FChar : : IsHexDigit ) )
2021-11-10 13:08:00 -05:00
{
uint8 Byte ;
2021-11-10 14:44:18 -05:00
if ( String : : HexToBytes ( View , & Byte ) = = 1 )
2021-11-10 13:08:00 -05:00
{
Exists [ Byte ] = true ;
}
}
return ! bExit ;
} ) ;
2022-04-29 16:18:09 -04:00
if ( Exists . FindFirstSetBit ( ) = = INDEX_NONE )
{
DeleteDirectory ( * BasePath ) ;
}
2021-11-10 13:08:00 -05:00
return Exists ;
}
2021-11-10 14:44:18 -05:00
TStaticBitArray < 10 > FFileSystemCacheStoreMaintainer : : ScanLegacyDirectory ( FStringBuilderBase & BasePath )
2021-11-10 13:08:00 -05:00
{
TStaticBitArray < 10 > Exists ;
2021-11-10 14:44:18 -05:00
FileManager . IterateDirectoryStat ( * BasePath , [ this , & Exists ] ( const TCHAR * Path , const FFileStatData & Stat ) - > bool
2021-11-10 13:08:00 -05:00
{
2021-11-10 14:44:18 -05:00
FStringView View = FPathViews : : GetCleanFilename ( Path ) ;
if ( Stat . bIsDirectory & & View . Len ( ) = = 1 & & Algo : : AllOf ( View , FChar : : IsDigit ) )
2021-11-10 13:08:00 -05:00
{
2021-11-10 14:44:18 -05:00
Exists [ FChar : : ConvertCharDigitToInt ( View [ 0 ] ) ] = true ;
2021-11-10 13:08:00 -05:00
}
return ! bExit ;
} ) ;
2022-04-29 16:18:09 -04:00
if ( Exists . FindFirstSetBit ( ) = = INDEX_NONE & & BasePath . Len ( ) > CachePath . Len ( ) )
{
DeleteDirectory ( * BasePath ) ;
}
2021-11-10 13:08:00 -05:00
return Exists ;
}
void FFileSystemCacheStoreMaintainer : : CreateLegacyRoot ( )
{
TStringBuilder < 256 > Path ;
FPathViews : : Append ( Path , CachePath ) ;
TStaticBitArray < 10 > Exists = ScanLegacyDirectory ( Path ) ;
if ( Exists . FindFirstSetBit ( ) ! = INDEX_NONE )
{
LegacyRoot = MakeUnique < FLegacyRoot > ( Random ) ;
LegacyRoot - > ExistsLevel0 = Exists ;
}
}
void FFileSystemCacheStoreMaintainer : : ScanLegacyRoot ( )
{
FLegacyRoot & Root = * LegacyRoot ;
const uint32 DirectoryIndex = Root . Order . GetNext ( ) ;
const int32 IndexLevel0 = int32 ( DirectoryIndex / 100 ) % 10 ;
const int32 IndexLevel1 = int32 ( DirectoryIndex / 10 ) % 10 ;
const int32 IndexLevel2 = int32 ( DirectoryIndex / 1 ) % 10 ;
bool bScanned = false ;
ON_SCOPE_EXIT
{
if ( ( DirectoryIndex = = Root . Order . GetFirst ( ) ) | |
( bScanned & & + + Root . DirectoryScanCount > = Params . MaxDirectoryScanCount ) )
{
LegacyRoot . Reset ( ) ;
}
} ;
if ( ! Root . ExistsLevel0 [ IndexLevel0 ] )
{
return ;
}
if ( ! Root . ScannedLevel0 [ IndexLevel0 ] )
{
TStringBuilder < 256 > Path ;
FPathViews : : Append ( Path , CachePath , IndexLevel0 ) ;
Root . ExistsLevel1 [ IndexLevel0 ] = ScanLegacyDirectory ( Path ) ;
Root . ScannedLevel0 [ IndexLevel0 ] = true ;
}
if ( ! Root . ExistsLevel1 [ IndexLevel0 ] [ IndexLevel1 ] )
{
return ;
}
if ( ! Root . ScannedLevel1 [ IndexLevel0 ] [ IndexLevel1 ] )
{
TStringBuilder < 256 > Path ;
FPathViews : : Append ( Path , CachePath , IndexLevel0 , IndexLevel1 ) ;
Root . ExistsLevel2 [ IndexLevel0 ] [ IndexLevel1 ] = ScanLegacyDirectory ( Path ) ;
Root . ScannedLevel1 [ IndexLevel0 ] [ IndexLevel1 ] = true ;
}
if ( ! Root . ExistsLevel2 [ IndexLevel0 ] [ IndexLevel1 ] [ IndexLevel2 ] )
{
return ;
}
TStringBuilder < 256 > Path ;
FPathViews : : Append ( Path , CachePath , IndexLevel0 , IndexLevel1 , IndexLevel2 ) ;
2022-04-29 16:18:09 -04:00
ProcessDirectory ( * Path ) ;
2021-11-10 13:08:00 -05:00
bScanned = true ;
}
void FFileSystemCacheStoreMaintainer : : ResetRoots ( )
{
Roots . Empty ( ) ;
LegacyRoot . Reset ( ) ;
}
2022-04-29 16:18:09 -04:00
void FFileSystemCacheStoreMaintainer : : ProcessDirectory ( const TCHAR * const Path )
2021-11-10 13:08:00 -05:00
{
2022-04-29 16:18:09 -04:00
bool bTryDelete = true ;
FileManager . IterateDirectoryStat ( Path , [ this , & bTryDelete ] ( const TCHAR * const Path , const FFileStatData & Stat ) - > bool
{
bool bDeletedFile = false ;
ProcessFile ( Path , Stat , bDeletedFile ) ;
bTryDelete & = bDeletedFile ;
return ! bExit ;
} ) ;
if ( bTryDelete )
{
DeleteDirectory ( Path ) ;
}
ProcessWait ( ) ;
}
void FFileSystemCacheStoreMaintainer : : ProcessFile ( const TCHAR * const Path , const FFileStatData & Stat , bool & bOutDeletedFile )
{
bOutDeletedFile = false ;
2021-11-10 14:44:18 -05:00
if ( Stat . bIsDirectory )
2021-11-10 13:08:00 -05:00
{
return ;
}
2021-11-10 14:44:18 -05:00
const FDateTime Now = FDateTime : : UtcNow ( ) ;
if ( Stat . ModificationTime + Params . MaxFileAge < Now & & Stat . AccessTime + Params . MaxFileAge < Now )
2021-11-10 13:08:00 -05:00
{
+ + DeleteCount ;
2021-11-10 14:44:18 -05:00
DeleteSize + = Stat . FileSize > 0 ? uint64 ( Stat . FileSize ) : 0 ;
2021-11-10 13:08:00 -05:00
if ( FileManager . Delete ( Path , /*bRequireExists*/ false , /*bEvenReadOnly*/ false , /*bQuiet*/ true ) )
{
2022-04-29 16:18:09 -04:00
bOutDeletedFile = true ;
2021-11-10 13:08:00 -05:00
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
TEXT ( " %s: Maintenance deleted file %s that was last modified at %s. " ) ,
2021-11-10 14:44:18 -05:00
* CachePath , Path , * Stat . ModificationTime . ToIso8601 ( ) ) ;
2021-11-10 13:08:00 -05:00
}
else
{
UE_LOG ( LogDerivedDataCache , Verbose ,
TEXT ( " %s: Maintenance failed to delete file %s that was last modified at %s. " ) ,
2021-11-10 14:44:18 -05:00
* CachePath , Path , * Stat . ModificationTime . ToIso8601 ( ) ) ;
2021-11-10 13:08:00 -05:00
}
}
2022-04-29 16:18:09 -04:00
ProcessWait ( ) ;
}
void FFileSystemCacheStoreMaintainer : : ProcessWait ( )
{
if ( ! bExit & & ! bIgnoreScanRate & & Params . MaxScanRate & & + + ProcessCount % Params . MaxScanRate = = 0 )
2021-11-10 13:08:00 -05:00
{
const double BatchEndTime = FPlatformTime : : Seconds ( ) ;
if ( const double BatchWaitTime = 1.0 - ( BatchEndTime - BatchStartTime ) ; BatchWaitTime > 0.0 )
{
WaitEvent - > Wait ( FTimespan : : FromSeconds ( BatchWaitTime ) , /*bIgnoreThreadIdleStats*/ true ) ;
BatchStartTime = FPlatformTime : : Seconds ( ) ;
}
else
{
BatchStartTime = BatchEndTime ;
}
}
}
2022-04-29 16:18:09 -04:00
void FFileSystemCacheStoreMaintainer : : DeleteDirectory ( const TCHAR * Path )
{
if ( FileManager . DeleteDirectory ( Path ) )
{
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " %s: Maintenance deleted empty directory %s. " ) , * CachePath , Path ) ;
}
}
2021-11-10 13:08:00 -05:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2021-11-10 14:44:18 -05:00
class FAccessLogWriter
2015-08-10 08:14:45 -04:00
{
public :
2021-11-10 14:44:18 -05:00
FAccessLogWriter ( const TCHAR * FileName , const FString & CachePath ) ;
void Append ( const FIoHash & RawHash , FStringView Path ) ;
void Append ( const FCacheKey & CacheKey , FStringView Path ) ;
private :
void AppendPath ( FStringView Path ) ;
TUniquePtr < FArchive > Archive ;
FString BasePath ;
FCriticalSection CriticalSection ;
TSet < FIoHash > ContentKeys ;
TSet < FCacheKey > RecordKeys ;
} ;
FAccessLogWriter : : FAccessLogWriter ( const TCHAR * const FileName , const FString & CachePath )
: Archive ( IFileManager : : Get ( ) . CreateFileWriter ( FileName , FILEWRITE_AllowRead ) )
, BasePath ( CachePath / TEXT ( " " ) )
{
}
void FAccessLogWriter : : Append ( const FIoHash & RawHash , const FStringView Path )
{
FScopeLock Lock ( & CriticalSection ) ;
bool bIsAlreadyInSet = false ;
ContentKeys . FindOrAdd ( RawHash , & bIsAlreadyInSet ) ;
if ( ! bIsAlreadyInSet )
2020-03-24 19:12:36 -04:00
{
2021-11-10 14:44:18 -05:00
AppendPath ( Path ) ;
2020-03-24 19:12:36 -04:00
}
2021-11-10 14:44:18 -05:00
}
2020-03-24 19:12:36 -04:00
2021-11-10 14:44:18 -05:00
void FAccessLogWriter : : Append ( const FCacheKey & CacheKey , const FStringView Path )
{
FScopeLock Lock ( & CriticalSection ) ;
bool bIsAlreadyInSet = false ;
RecordKeys . FindOrAdd ( CacheKey , & bIsAlreadyInSet ) ;
if ( ! bIsAlreadyInSet )
2021-10-12 21:21:22 -04:00
{
2021-11-10 14:44:18 -05:00
AppendPath ( Path ) ;
2021-10-12 21:21:22 -04:00
}
2021-11-10 14:44:18 -05:00
}
2021-10-12 21:21:22 -04:00
2021-11-10 14:44:18 -05:00
void FAccessLogWriter : : AppendPath ( const FStringView Path )
{
if ( Path . StartsWith ( BasePath ) )
{
2021-12-15 17:03:40 -05:00
const FTCHARToUTF8 PathUtf8 ( Path . RightChop ( BasePath . Len ( ) ) ) ;
2021-11-10 14:44:18 -05:00
Archive - > Serialize ( const_cast < ANSICHAR * > ( PathUtf8 . Get ( ) ) , PathUtf8 . Length ( ) ) ;
Archive - > Serialize ( const_cast < ANSICHAR * > ( LINE_TERMINATOR_ANSI ) , sizeof ( LINE_TERMINATOR_ANSI ) - 1 ) ;
2015-08-10 08:14:45 -04:00
}
2021-11-10 14:44:18 -05:00
}
2015-08-10 08:14:45 -04:00
2021-11-10 14:44:18 -05:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2015-08-10 08:14:45 -04:00
2022-06-07 11:17:10 -04:00
class FFileSystemCacheStore final : public ILegacyCacheStore
2021-11-10 14:44:18 -05:00
{
public :
2022-06-07 11:17:10 -04:00
FFileSystemCacheStore (
const TCHAR * CachePath ,
const TCHAR * Params ,
const TCHAR * AccessLogPath ,
ECacheStoreFlags & OutFlags ) ;
2020-03-13 15:35:22 -04:00
2021-11-10 14:44:18 -05:00
bool RunSpeedTest (
double InSkipTestsIfSeeksExceedMS ,
double & OutSeekTimeMS ,
double & OutReadSpeedMBs ,
double & OutWriteSpeedMBs ) const ;
2020-03-13 15:35:22 -04:00
2021-11-10 14:44:18 -05:00
// ICacheStore Interface
2021-04-28 16:22:18 -04:00
2021-11-10 14:44:18 -05:00
void Put (
2021-12-13 13:32:28 -05:00
TConstArrayView < FCachePutRequest > Requests ,
2021-08-04 18:08:50 -04:00
IRequestOwner & Owner ,
2021-11-10 14:44:18 -05:00
FOnCachePutComplete & & OnComplete ) final ;
void Get (
2021-12-13 13:32:28 -05:00
TConstArrayView < FCacheGetRequest > Requests ,
2021-08-04 18:08:50 -04:00
IRequestOwner & Owner ,
2021-11-10 14:44:18 -05:00
FOnCacheGetComplete & & OnComplete ) final ;
2022-01-10 12:13:18 -05:00
void PutValue (
TConstArrayView < FCachePutValueRequest > Requests ,
IRequestOwner & Owner ,
FOnCachePutValueComplete & & OnComplete ) final ;
void GetValue (
TConstArrayView < FCacheGetValueRequest > Requests ,
IRequestOwner & Owner ,
FOnCacheGetValueComplete & & OnComplete ) final ;
2021-11-10 14:44:18 -05:00
void GetChunks (
2022-01-18 04:47:59 -05:00
TConstArrayView < FCacheGetChunkRequest > Requests ,
2021-08-04 18:08:50 -04:00
IRequestOwner & Owner ,
2022-01-18 04:47:59 -05:00
FOnCacheGetChunkComplete & & OnComplete ) final ;
2021-04-28 16:22:18 -04:00
2022-06-07 11:17:10 -04:00
// ILegacyCacheStore
void LegacyStats ( FDerivedDataCacheStatsNode & OutNode ) final ;
bool LegacyDebugOptions ( FBackendDebugOptions & Options ) final ;
2021-04-28 16:22:18 -04:00
private :
2022-01-10 12:13:18 -05:00
[[nodiscard]] bool PutCacheRecord ( FStringView Name , const FCacheRecord & Record , const FCacheRecordPolicy & Policy , uint64 & OutWriteSize ) ;
2021-04-28 16:22:18 -04:00
2022-01-10 12:13:18 -05:00
[[nodiscard]] FOptionalCacheRecord GetCacheRecordOnly (
2022-01-07 23:22:29 -05:00
FStringView Name ,
2021-10-07 09:11:32 -04:00
const FCacheKey & Key ,
2021-11-10 14:44:18 -05:00
const FCacheRecordPolicy & Policy ) ;
2022-01-10 12:13:18 -05:00
[[nodiscard]] FOptionalCacheRecord GetCacheRecord (
2022-01-07 23:22:29 -05:00
FStringView Name ,
2021-10-07 09:11:32 -04:00
const FCacheKey & Key ,
const FCacheRecordPolicy & Policy ,
2021-11-10 14:44:18 -05:00
EStatus & OutStatus ) ;
2022-01-10 12:13:18 -05:00
[[nodiscard]] bool PutCacheValue ( FStringView Name , const FCacheKey & Key , const FValue & Value , ECachePolicy Policy , uint64 & OutWriteSize ) ;
[[nodiscard]] bool GetCacheValueOnly ( FStringView Name , const FCacheKey & Key , ECachePolicy Policy , FValue & OutValue ) ;
[[nodiscard]] bool GetCacheValue ( FStringView Name , const FCacheKey & Key , ECachePolicy Policy , FValue & OutValue ) ;
[[nodiscard]] bool PutCacheContent ( FStringView Name , const FCompressedBuffer & Content , uint64 & OutWriteSize ) const ;
[[nodiscard]] bool GetCacheContentExists ( const FCacheKey & Key , const FIoHash & RawHash ) const ;
[[nodiscard]] bool GetCacheContent (
2022-01-07 23:22:29 -05:00
FStringView Name ,
2021-12-08 17:15:36 -05:00
const FCacheKey & Key ,
2022-01-10 12:13:18 -05:00
const FValueId & Id ,
const FValue & Value ,
ECachePolicy Policy ,
FValue & OutValue ) const ;
2021-11-10 14:44:18 -05:00
void GetCacheContent (
2022-01-07 23:22:29 -05:00
FStringView Name ,
2021-10-08 11:01:21 -04:00
const FCacheKey & Key ,
2022-01-10 12:13:18 -05:00
const FValueId & Id ,
const FValue & Value ,
2021-12-08 17:15:36 -05:00
ECachePolicy Policy ,
FCompressedBufferReader & Reader ,
TUniquePtr < FArchive > & OutArchive ) const ;
2021-11-10 13:08:00 -05:00
2022-01-10 12:13:18 -05:00
void BuildCachePackagePath ( const FCacheKey & CacheKey , FStringBuilderBase & Path ) const ;
2021-11-10 14:44:18 -05:00
void BuildCacheContentPath ( const FIoHash & RawHash , FStringBuilderBase & Path ) const ;
2021-11-10 13:08:00 -05:00
2022-01-31 11:52:13 -05:00
[[nodiscard]] bool SaveFileWithHash ( FStringBuilderBase & Path , FStringView DebugName , TFunctionRef < void ( FArchive & ) > WriteFunction , bool bReplaceExisting = false ) const ;
2022-01-10 12:13:18 -05:00
[[nodiscard]] bool LoadFileWithHash ( FStringBuilderBase & Path , FStringView DebugName , TFunctionRef < void ( FArchive & ) > ReadFunction ) const ;
2022-01-31 11:52:13 -05:00
[[nodiscard]] bool SaveFile ( FStringBuilderBase & Path , FStringView DebugName , TFunctionRef < void ( FArchive & ) > WriteFunction , bool bReplaceExisting = false ) const ;
2022-01-10 12:13:18 -05:00
[[nodiscard]] bool LoadFile ( FStringBuilderBase & Path , FStringView DebugName , TFunctionRef < void ( FArchive & ) > ReadFunction ) const ;
2022-04-29 16:18:09 -04:00
[[nodiscard]] TUniquePtr < FArchive > OpenFileWrite ( FStringBuilderBase & Path , FStringView DebugName ) const ;
[[nodiscard]] TUniquePtr < FArchive > OpenFileRead ( FStringBuilderBase & Path , FStringView DebugName ) const ;
2021-04-28 16:22:18 -04:00
2022-01-10 12:13:18 -05:00
[[nodiscard]] bool FileExists ( FStringBuilderBase & Path ) const ;
2021-04-28 16:22:18 -04:00
2015-08-10 08:14:45 -04:00
private :
2021-11-10 14:44:18 -05:00
/** Base path we are storing the cache files in. */
2015-08-10 08:14:45 -04:00
FString CachePath ;
2022-06-07 11:17:10 -04:00
/** Class of this cache. */
EBackendSpeedClass SpeedClass ;
2021-11-10 13:08:00 -05:00
/** If true, do not attempt to write to this cache. */
2015-08-10 08:14:45 -04:00
bool bReadOnly ;
/** If true, CachedDataProbablyExists will update the file timestamps. */
bool bTouch ;
/** Age of file when it should be deleted from DDC cache. */
2021-11-10 13:08:00 -05:00
double DaysToDeleteUnusedFiles ;
2020-03-24 19:12:36 -04:00
2021-11-10 13:08:00 -05:00
/** Maximum total size of compressed data stored within a record package with multiple attachments. */
uint64 MaxRecordSizeKB = 256 ;
/** Maximum total size of compressed data stored within a value package, or a record package with one attachment. */
uint64 MaxValueSizeKB = 1024 ;
2020-03-24 19:12:36 -04:00
2020-03-13 15:35:22 -04:00
/** Access log to write to */
TUniquePtr < FAccessLogWriter > AccessLogWriter ;
2020-03-24 19:12:36 -04:00
2020-03-26 20:55:39 -04:00
/** Debug Options */
FBackendDebugOptions DebugOptions ;
2021-11-10 14:44:18 -05:00
FDerivedDataCacheUsageStats UsageStats ;
2022-05-31 05:11:05 -04:00
/** Speed stats */
FDerivedDataCacheSpeedStats SpeedStats ;
2021-11-10 13:08:00 -05:00
TUniquePtr < FFileSystemCacheStoreMaintainer > Maintainer ;
2015-08-10 08:14:45 -04:00
} ;
2021-11-10 14:44:18 -05:00
FFileSystemCacheStore : : FFileSystemCacheStore (
const TCHAR * const InCachePath ,
const TCHAR * const InParams ,
2022-06-07 11:17:10 -04:00
const TCHAR * const InAccessLogPath ,
ECacheStoreFlags & OutFlags )
2021-11-10 14:44:18 -05:00
: CachePath ( InCachePath )
2022-06-07 11:17:10 -04:00
, SpeedClass ( EBackendSpeedClass : : Unknown )
2021-11-10 14:44:18 -05:00
, bReadOnly ( false )
, bTouch ( false )
, DaysToDeleteUnusedFiles ( 15.0 )
2015-08-10 08:14:45 -04:00
{
2021-11-10 14:44:18 -05:00
// If we find a platform that has more stringent limits, this needs to be rethought.
2022-06-07 11:17:10 -04:00
checkf ( GMaxCacheRootLen + GMaxCacheKeyLen < = FPlatformMisc : : GetMaxPathLength ( ) ,
2021-11-10 14:44:18 -05:00
TEXT ( " Not enough room left for cache keys in max path. " ) ) ;
2020-03-24 19:12:36 -04:00
2021-11-10 14:44:18 -05:00
check ( CachePath . Len ( ) ) ;
FPaths : : NormalizeFilename ( CachePath ) ;
// Params that override our instance defaults
FParse : : Bool ( InParams , TEXT ( " ReadOnly= " ) , bReadOnly ) ;
FParse : : Bool ( InParams , TEXT ( " Touch= " ) , bTouch ) ;
FParse : : Value ( InParams , TEXT ( " UnusedFileAge= " ) , DaysToDeleteUnusedFiles ) ;
FParse : : Value ( InParams , TEXT ( " MaxRecordSizeKB= " ) , MaxRecordSizeKB ) ;
FParse : : Value ( InParams , TEXT ( " MaxValueSizeKB= " ) , MaxValueSizeKB ) ;
// Flush the cache if requested.
bool bFlush = false ;
if ( ! bReadOnly & & FParse : : Bool ( InParams , TEXT ( " Flush= " ) , bFlush ) & & bFlush )
2015-08-10 08:14:45 -04:00
{
2022-04-28 15:28:49 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( FileSystemDDC_Flush ) ;
2021-11-10 14:44:18 -05:00
IFileManager : : Get ( ) . DeleteDirectory ( * ( CachePath / TEXT ( " " ) ) , /*bRequireExists*/ false , /*bTree*/ true ) ;
2015-08-10 08:14:45 -04:00
}
2020-03-24 19:12:36 -04:00
2021-11-10 14:44:18 -05:00
// check latency and speed. Read values should always be valid
2022-05-31 05:11:05 -04:00
2021-11-10 14:44:18 -05:00
/* Speeds faster than this are considered local*/
const float ConsiderFastAtMS = 10 ;
/* Speeds faster than this are ok. Everything else is slow. This value can be overridden in the ini file */
float ConsiderSlowAtMS = 50 ;
FParse : : Value ( InParams , TEXT ( " ConsiderSlowAt= " ) , ConsiderSlowAtMS ) ;
// can skip the speed test so everything acts as local (e.g. 4.25 and earlier behavior).
bool SkipSpeedTest = ! WITH_EDITOR | | FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " DDCSkipSpeedTest " ) ) ;
if ( SkipSpeedTest )
{
2022-05-31 05:11:05 -04:00
SpeedStats . ReadSpeedMBs = 999 ;
SpeedStats . WriteSpeedMBs = 999 ;
SpeedStats . LatencyMS = 0 ;
2021-11-10 14:44:18 -05:00
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " Skipping speed test to %s. Assuming local performance " ) , * CachePath ) ;
}
2022-05-31 05:11:05 -04:00
if ( ! SkipSpeedTest & & ! RunSpeedTest ( ConsiderSlowAtMS * 2 , SpeedStats . LatencyMS , SpeedStats . ReadSpeedMBs , SpeedStats . WriteSpeedMBs ) )
2021-11-10 14:44:18 -05:00
{
2022-06-07 11:17:10 -04:00
OutFlags = ECacheStoreFlags : : None ;
2021-11-10 14:44:18 -05:00
UE_LOG ( LogDerivedDataCache , Warning , TEXT ( " No read or write access to %s " ) , * CachePath ) ;
}
else
{
2022-05-31 05:11:05 -04:00
bool bReadTestPassed = SpeedStats . ReadSpeedMBs > 0.0 ;
bool bWriteTestPassed = SpeedStats . WriteSpeedMBs > 0.0 ;
2021-11-10 14:44:18 -05:00
// if we failed writes mark this as read only
bReadOnly = bReadOnly | | ! bWriteTestPassed ;
// classify and report on these times
2022-05-31 05:11:05 -04:00
if ( SpeedStats . LatencyMS < 1 )
2021-11-10 14:44:18 -05:00
{
2022-06-07 11:17:10 -04:00
SpeedClass = EBackendSpeedClass : : Local ;
2021-11-10 14:44:18 -05:00
}
2022-05-31 05:11:05 -04:00
else if ( SpeedStats . LatencyMS < = ConsiderFastAtMS )
2021-11-10 14:44:18 -05:00
{
2022-06-07 11:17:10 -04:00
SpeedClass = EBackendSpeedClass : : Fast ;
2021-11-10 14:44:18 -05:00
}
2022-05-31 05:11:05 -04:00
else if ( SpeedStats . LatencyMS > = ConsiderSlowAtMS )
2021-11-10 14:44:18 -05:00
{
2022-06-07 11:17:10 -04:00
SpeedClass = EBackendSpeedClass : : Slow ;
2021-11-10 14:44:18 -05:00
}
else
{
2022-06-07 11:17:10 -04:00
SpeedClass = EBackendSpeedClass : : Ok ;
2021-11-10 14:44:18 -05:00
}
UE_LOG ( LogDerivedDataCache , Display ,
2022-06-14 12:46:34 -04:00
TEXT ( " Performance to %s: Latency=%.02fms. RandomReadSpeed=%.02fMBs, RandomWriteSpeed=%.02fMBs. "
" Assigned SpeedClass '%s' " ) ,
2022-05-31 05:11:05 -04:00
* CachePath , SpeedStats . LatencyMS , SpeedStats . ReadSpeedMBs , SpeedStats . WriteSpeedMBs , LexToString ( SpeedClass ) ) ;
2021-11-10 14:44:18 -05:00
2022-06-07 11:17:10 -04:00
if ( SpeedClass < = EBackendSpeedClass : : Slow & & ! bReadOnly )
2021-11-10 14:44:18 -05:00
{
if ( GIsBuildMachine )
{
2022-06-14 12:46:34 -04:00
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " Access to %s appears to be slow. "
" 'Touch' will be disabled and queries/writes will be limited. " ) , * CachePath ) ;
2021-11-10 14:44:18 -05:00
}
else
{
2022-06-14 12:46:34 -04:00
UE_LOG ( LogDerivedDataCache , Warning , TEXT ( " Access to %s appears to be slow. "
" 'Touch' will be disabled and queries/writes will be limited. " ) , * CachePath ) ;
2021-11-10 14:44:18 -05:00
}
bTouch = false ;
//bReadOnly = true;
}
if ( ! bReadOnly )
{
if ( FString ( FCommandLine : : Get ( ) ) . Contains ( TEXT ( " Run=DerivedDataCache " ) ) )
{
bTouch = true ; // we always touch files when running the DDC commandlet
}
// The command line (-ddctouch) enables touch on all filesystem backends if specified.
bTouch = bTouch | | FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " DDCTOUCH " ) ) ;
if ( bTouch )
{
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " Files in %s will be touched. " ) , * CachePath ) ;
}
bool bClean = false ;
bool bDeleteUnused = true ;
FParse : : Bool ( InParams , TEXT ( " Clean= " ) , bClean ) ;
FParse : : Bool ( InParams , TEXT ( " DeleteUnused= " ) , bDeleteUnused ) ;
bDeleteUnused = bDeleteUnused & & ! FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " NODDCCLEANUP " ) ) ;
if ( bClean | | bDeleteUnused )
{
FFileSystemCacheStoreMaintainerParams MaintainerParams ;
MaintainerParams . MaxFileAge = FTimespan : : FromDays ( DaysToDeleteUnusedFiles ) ;
if ( bDeleteUnused )
{
2022-04-29 16:18:09 -04:00
if ( ! FParse : : Value ( InParams , TEXT ( " MaxFileChecksPerSec= " ) , MaintainerParams . MaxScanRate ) )
2021-11-10 14:44:18 -05:00
{
int32 MaxFileScanRate ;
if ( GConfig - > GetInt ( TEXT ( " DDCCleanup " ) , TEXT ( " MaxFileChecksPerSec " ) , MaxFileScanRate , GEngineIni ) )
{
2022-04-29 16:18:09 -04:00
MaintainerParams . MaxScanRate = uint32 ( MaxFileScanRate ) ;
2021-11-10 14:44:18 -05:00
}
}
FParse : : Value ( InParams , TEXT ( " FoldersToClean= " ) , MaintainerParams . MaxDirectoryScanCount ) ;
}
else
{
2021-11-15 13:56:02 -05:00
MaintainerParams . ScanFrequency = FTimespan : : MaxValue ( ) ;
2021-11-10 14:44:18 -05:00
}
double TimeToWaitAfterInit ;
if ( bClean )
{
MaintainerParams . TimeToWaitAfterInit = FTimespan : : Zero ( ) ;
}
else if ( GConfig - > GetDouble ( TEXT ( " DDCCleanup " ) , TEXT ( " TimeToWaitAfterInit " ) , TimeToWaitAfterInit , GEngineIni ) )
{
MaintainerParams . TimeToWaitAfterInit = FTimespan : : FromSeconds ( TimeToWaitAfterInit ) ;
}
Maintainer = MakeUnique < FFileSystemCacheStoreMaintainer > ( MaintainerParams , CachePath ) ;
if ( bClean )
{
Maintainer - > BoostPriority ( ) ;
Maintainer - > WaitForIdle ( ) ;
}
}
}
2022-06-07 11:17:10 -04:00
if ( InAccessLogPath & & * InAccessLogPath )
2021-11-10 14:44:18 -05:00
{
AccessLogWriter . Reset ( new FAccessLogWriter ( InAccessLogPath , CachePath ) ) ;
}
2022-06-07 11:17:10 -04:00
ECacheStoreFlags Flags = ECacheStoreFlags : : Query ;
Flags | = bReadOnly ? ECacheStoreFlags : : None : ECacheStoreFlags : : Store ;
Flags | = SpeedClass = = EBackendSpeedClass : : Local ? ECacheStoreFlags : : Local : ECacheStoreFlags : : Remote ;
OutFlags = Flags ;
2021-11-10 14:44:18 -05:00
}
}
bool FFileSystemCacheStore : : RunSpeedTest (
const double InSkipTestsIfSeeksExceedMS ,
double & OutSeekTimeMS ,
double & OutReadSpeedMBs ,
double & OutWriteSpeedMBs ) const
{
SCOPED_BOOT_TIMING ( " RunSpeedTest " ) ;
// files of increasing size. Most DDC data falls within this range so we don't want to skew by reading
// large amounts of data. Ultimately we care most about latency anyway.
const int FileSizes [ ] = { 4 , 8 , 16 , 64 , 128 , 256 } ;
const int NumTestFolders = 2 ; //(0-9)
const int FileSizeCount = UE_ARRAY_COUNT ( FileSizes ) ;
bool bWriteTestPassed = true ;
bool bReadTestPassed = true ;
bool bTestDataExists = true ;
double TotalSeekTime = 0 ;
double TotalReadTime = 0 ;
double TotalWriteTime = 0 ;
int TotalDataRead = 0 ;
int TotalDataWritten = 0 ;
const FString AbsoluteCachePath = IFileManager : : Get ( ) . ConvertToAbsolutePathForExternalAppForRead ( * CachePath ) ;
2022-06-07 11:17:10 -04:00
if ( AbsoluteCachePath . Len ( ) > = GMaxCacheRootLen )
2021-11-10 14:44:18 -05:00
{
2022-01-21 16:35:58 -05:00
const FText ErrorMessage = FText : : Format ( NSLOCTEXT ( " DerivedDataCache " , " PathTooLong " , " Cache path {0} is longer than {1} characters... please adjust [DerivedDataBackendGraph] paths to be shorter (this leaves more room for cache keys). " ) ,
2022-06-07 11:17:10 -04:00
FText : : FromString ( AbsoluteCachePath ) , FText : : AsNumber ( GMaxCacheRootLen ) ) ;
2021-11-10 14:44:18 -05:00
FMessageDialog : : Open ( EAppMsgType : : Ok , ErrorMessage ) ;
UE_LOG ( LogDerivedDataCache , Fatal , TEXT ( " %s " ) , * ErrorMessage . ToString ( ) ) ;
}
TArray < FString > Paths ;
TArray < FString > MissingFiles ;
MissingFiles . Reserve ( NumTestFolders * FileSizeCount ) ;
const FString TestDataPath = FPaths : : Combine ( CachePath , TEXT ( " TestData " ) ) ;
// create an upfront map of paths to data size in bytes
// create the paths we'll use. <path>/0/TestData.dat, <path>/1/TestData.dat etc. If those files don't exist we'll
// create them which will likely give an invalid result when measuring them now but not in the future...
TMap < FString , int > TestFileEntries ;
for ( int iSize = 0 ; iSize < FileSizeCount ; iSize + + )
{
// make sure we dont stat/read/write to consecuting files in folders
for ( int iFolder = 0 ; iFolder < NumTestFolders ; iFolder + + )
{
int FileSizeKB = FileSizes [ iSize ] ;
FString Path = FPaths : : Combine ( CachePath , TEXT ( " TestData " ) , * FString : : FromInt ( iFolder ) ,
* FString : : Printf ( TEXT ( " TestData_%dkb.dat " ) , FileSizeKB ) ) ;
TestFileEntries . Add ( Path , FileSizeKB * 1024 ) ;
}
}
// measure latency by checking for the presence of all these files. We'll also track which don't exist..
const double StatStartTime = FPlatformTime : : Seconds ( ) ;
for ( auto & KV : TestFileEntries )
{
FFileStatData StatData = IFileManager : : Get ( ) . GetStatData ( * KV . Key ) ;
if ( ! StatData . bIsValid | | StatData . FileSize ! = KV . Value )
{
MissingFiles . Add ( KV . Key ) ;
}
}
// save total stat time
TotalSeekTime = ( FPlatformTime : : Seconds ( ) - StatStartTime ) ;
// calculate seek time here
OutSeekTimeMS = ( TotalSeekTime / TestFileEntries . Num ( ) ) * 1000 ;
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " Stat tests to %s took %.02f seconds " ) , * CachePath , TotalSeekTime ) ;
// if seek times are very slow do a single read/write test just to confirm access
/*if (OutSeekTimeMS >= InSkipTestsIfSeeksExceedMS)
{
UE_LOG ( LogDerivedDataCache , Warning ,
TEXT ( " Limiting read/write speed tests due to seek times of %.02f exceeding %.02fms. " )
TEXT ( " Values will be inaccurate. " ) , OutSeekTimeMS , InSkipTestsIfSeeksExceedMS ) ;
FString Path = TestFileEntries . begin ( ) - > Key ;
int Size = TestFileEntries . begin ( ) - > Value ;
TestFileEntries . Reset ( ) ;
TestFileEntries . Add ( Path , Size ) ;
} */
// create any files that were missing
if ( ! bReadOnly )
{
TArray < uint8 > Data ;
for ( auto & File : MissingFiles )
{
const int DesiredSize = TestFileEntries [ File ] ;
Data . SetNumUninitialized ( DesiredSize ) ;
if ( ! FFileHelper : : SaveArrayToFile ( Data , * File , & IFileManager : : Get ( ) , FILEWRITE_Silent ) )
{
// Handle the case where something else may have created the path at the same time.
// This is less about multiple users and more about things like SCW's / UnrealPak
// that can spin up multiple instances at once.
if ( ! IFileManager : : Get ( ) . FileExists ( * File ) )
{
uint32 ErrorCode = FPlatformMisc : : GetLastError ( ) ;
TCHAR ErrorBuffer [ 1024 ] ;
FPlatformMisc : : GetSystemErrorMessage ( ErrorBuffer , 1024 , ErrorCode ) ;
UE_LOG ( LogDerivedDataCache , Warning ,
2022-06-14 12:46:34 -04:00
TEXT ( " Fail to create %s, derived data cache to this directory will be read only. "
" WriteError: %u (%s) " ) , * File , ErrorCode , ErrorBuffer ) ;
2021-11-10 14:44:18 -05:00
bTestDataExists = false ;
bWriteTestPassed = false ;
break ;
}
}
}
}
// now read all sizes from random folders
{
const int ArraySize = UE_ARRAY_COUNT ( FileSizes ) ;
TArray < uint8 > TempData ;
TempData . Empty ( FileSizes [ ArraySize - 1 ] * 1024 ) ;
const double ReadStartTime = FPlatformTime : : Seconds ( ) ;
for ( auto & KV : TestFileEntries )
{
const int FileSize = KV . Value ;
const FString & FilePath = KV . Key ;
if ( ! FFileHelper : : LoadFileToArray ( TempData , * FilePath , FILEREAD_Silent ) )
{
uint32 ErrorCode = FPlatformMisc : : GetLastError ( ) ;
TCHAR ErrorBuffer [ 1024 ] ;
FPlatformMisc : : GetSystemErrorMessage ( ErrorBuffer , 1024 , ErrorCode ) ;
UE_LOG ( LogDerivedDataCache , Warning ,
TEXT ( " Fail to read from %s, derived data cache will be disabled. ReadError: %u (%s) " ) ,
* FilePath , ErrorCode , ErrorBuffer ) ;
bReadTestPassed = false ;
break ;
}
TotalDataRead + = TempData . Num ( ) ;
}
TotalReadTime = FPlatformTime : : Seconds ( ) - ReadStartTime ;
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " Read tests %s on %s and took %.02f seconds " ) ,
bReadTestPassed ? TEXT ( " passed " ) : TEXT ( " failed " ) , * CachePath , TotalReadTime ) ;
}
// do write tests if or read tests passed and our seeks were below the cut-off
if ( bReadTestPassed & & ! bReadOnly )
{
// do write tests but use a unique folder that is cleaned up afterwards
FString CustomPath = FPaths : : Combine ( CachePath , TEXT ( " TestData " ) , * FGuid : : NewGuid ( ) . ToString ( ) ) ;
const int ArraySize = UE_ARRAY_COUNT ( FileSizes ) ;
TArray < uint8 > TempData ;
TempData . Empty ( FileSizes [ ArraySize - 1 ] * 1024 ) ;
const double WriteStartTime = FPlatformTime : : Seconds ( ) ;
for ( auto & KV : TestFileEntries )
{
const int FileSize = KV . Value ;
FString FilePath = KV . Key ;
TempData . SetNumUninitialized ( FileSize ) ;
FilePath = FilePath . Replace ( * CachePath , * CustomPath ) ;
if ( ! FFileHelper : : SaveArrayToFile ( TempData , * FilePath , & IFileManager : : Get ( ) , FILEWRITE_Silent ) )
{
uint32 ErrorCode = FPlatformMisc : : GetLastError ( ) ;
TCHAR ErrorBuffer [ 1024 ] ;
FPlatformMisc : : GetSystemErrorMessage ( ErrorBuffer , 1024 , ErrorCode ) ;
UE_LOG ( LogDerivedDataCache , Warning ,
TEXT ( " Fail to write to %s, derived data cache will be disabled. ReadError: %u (%s) " ) ,
* FilePath , ErrorCode , ErrorBuffer ) ;
bWriteTestPassed = false ;
break ;
}
TotalDataWritten + = TempData . Num ( ) ;
}
TotalWriteTime = FPlatformTime : : Seconds ( ) - WriteStartTime ;
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " write tests %s on %s and took %.02f seconds " ) ,
bWriteTestPassed ? TEXT ( " passed " ) : TEXT ( " failed " ) , * CachePath , TotalReadTime )
// remove the custom path but do it async as this can be slow on remote drives
AsyncTask ( ENamedThreads : : AnyThread , [ CustomPath ] ( ) {
IFileManager : : Get ( ) . DeleteDirectory ( * CustomPath , false , true ) ;
} ) ;
}
const double TotalTestTime = FPlatformTime : : Seconds ( ) - StatStartTime ;
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " Speed tests for %s took %.02f seconds " ) , * CachePath , TotalTestTime ) ;
// check latency and speed. Read values should always be valid
OutReadSpeedMBs = ( bReadTestPassed ? ( TotalDataRead / TotalReadTime ) : 0 ) / ( 1024 * 1024 ) ;
OutWriteSpeedMBs = ( bWriteTestPassed ? ( TotalDataWritten / TotalWriteTime ) : 0 ) / ( 1024 * 1024 ) ;
return bWriteTestPassed | | bReadTestPassed ;
}
2022-06-07 11:17:10 -04:00
void FFileSystemCacheStore : : LegacyStats ( FDerivedDataCacheStatsNode & OutNode )
2021-11-10 14:44:18 -05:00
{
2022-06-07 11:17:10 -04:00
OutNode = { TEXT ( " File System " ) , * CachePath , SpeedClass = = EBackendSpeedClass : : Local } ;
OutNode . UsageStats . Add ( TEXT ( " " ) , UsageStats ) ;
OutNode . SpeedStats = SpeedStats ;
2021-11-10 14:44:18 -05:00
}
2022-06-07 11:17:10 -04:00
bool FFileSystemCacheStore : : LegacyDebugOptions ( FBackendDebugOptions & InOptions )
2021-11-10 14:44:18 -05:00
{
DebugOptions = InOptions ;
return true ;
}
void FFileSystemCacheStore : : Put (
2021-12-13 13:32:28 -05:00
const TConstArrayView < FCachePutRequest > Requests ,
2021-11-10 14:44:18 -05:00
IRequestOwner & Owner ,
FOnCachePutComplete & & OnComplete )
{
2021-12-13 13:32:28 -05:00
for ( const FCachePutRequest & Request : Requests )
2021-11-10 14:44:18 -05:00
{
2022-03-17 09:07:20 -04:00
bool bOk ;
2021-11-10 14:44:18 -05:00
{
2022-03-17 09:07:20 -04:00
const FCacheRecord & Record = Request . Record ;
TRACE_CPUPROFILER_EVENT_SCOPE ( FileSystemDDC_Put ) ;
TRACE_COUNTER_INCREMENT ( FileSystemDDC_Put ) ;
COOK_STAT ( auto Timer = UsageStats . TimePut ( ) ) ;
uint64 WriteSize = 0 ;
bOk = PutCacheRecord ( Request . Name , Record , Request . Policy , WriteSize ) ;
if ( bOk )
{
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache put complete for %s from '%s' " ) ,
* CachePath , * WriteToString < 96 > ( Record . GetKey ( ) ) , * Request . Name ) ;
TRACE_COUNTER_INCREMENT ( FileSystemDDC_PutHit ) ;
TRACE_COUNTER_ADD ( FileSystemDDC_BytesWritten , WriteSize ) ;
if ( WriteSize )
{
COOK_STAT ( Timer . AddHit ( WriteSize ) ) ;
}
}
2021-11-10 14:44:18 -05:00
}
2022-03-17 09:07:20 -04:00
OnComplete ( Request . MakeResponse ( bOk ? EStatus : : Ok : EStatus : : Error ) ) ;
2021-11-10 14:44:18 -05:00
}
}
void FFileSystemCacheStore : : Get (
2021-12-13 13:32:28 -05:00
const TConstArrayView < FCacheGetRequest > Requests ,
2021-11-10 14:44:18 -05:00
IRequestOwner & Owner ,
FOnCacheGetComplete & & OnComplete )
{
2021-12-13 13:32:28 -05:00
for ( const FCacheGetRequest & Request : Requests )
2021-11-10 14:44:18 -05:00
{
2022-03-17 09:07:20 -04:00
EStatus Status = EStatus : : Error ;
FOptionalCacheRecord Record ;
2021-11-10 14:44:18 -05:00
{
2022-03-17 09:07:20 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( FileSystemDDC_Get ) ;
TRACE_COUNTER_INCREMENT ( FileSystemDDC_Get ) ;
COOK_STAT ( auto Timer = UsageStats . TimeGet ( ) ) ;
if ( ( Record = GetCacheRecord ( Request . Name , Request . Key , Request . Policy , Status ) ) )
{
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache hit for %s from '%s' " ) ,
* CachePath , * WriteToString < 96 > ( Request . Key ) , * Request . Name ) ;
TRACE_COUNTER_INCREMENT ( FileSystemDDC_GetHit ) ;
TRACE_COUNTER_ADD ( FileSystemDDC_BytesRead , Private : : GetCacheRecordCompressedSize ( Record . Get ( ) ) ) ;
COOK_STAT ( Timer . AddHit ( Private : : GetCacheRecordCompressedSize ( Record . Get ( ) ) ) ) ;
}
else
{
Record = FCacheRecordBuilder ( Request . Key ) . Build ( ) ;
}
2021-11-10 14:44:18 -05:00
}
2022-03-17 09:07:20 -04:00
OnComplete ( { Request . Name , MoveTemp ( Record ) . Get ( ) , Request . UserData , Status } ) ;
2021-11-10 14:44:18 -05:00
}
}
2022-01-10 12:13:18 -05:00
void FFileSystemCacheStore : : PutValue (
const TConstArrayView < FCachePutValueRequest > Requests ,
IRequestOwner & Owner ,
FOnCachePutValueComplete & & OnComplete )
{
for ( const FCachePutValueRequest & Request : Requests )
{
2022-03-17 09:07:20 -04:00
bool bOk ;
2022-01-10 12:13:18 -05:00
{
2022-03-17 09:07:20 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( FileSystemDDC_PutValue ) ;
TRACE_COUNTER_INCREMENT ( FileSystemDDC_Put ) ;
COOK_STAT ( auto Timer = UsageStats . TimePut ( ) ) ;
uint64 WriteSize = 0 ;
bOk = PutCacheValue ( Request . Name , Request . Key , Request . Value , Request . Policy , WriteSize ) ;
if ( bOk )
{
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache put complete for %s from '%s' " ) ,
* CachePath , * WriteToString < 96 > ( Request . Key ) , * Request . Name ) ;
TRACE_COUNTER_INCREMENT ( FileSystemDDC_PutHit ) ;
TRACE_COUNTER_ADD ( FileSystemDDC_BytesWritten , WriteSize ) ;
if ( WriteSize )
{
COOK_STAT ( Timer . AddHit ( WriteSize ) ) ;
}
}
2022-01-10 12:13:18 -05:00
}
2022-03-17 09:07:20 -04:00
OnComplete ( Request . MakeResponse ( bOk ? EStatus : : Ok : EStatus : : Error ) ) ;
2022-01-10 12:13:18 -05:00
}
}
void FFileSystemCacheStore : : GetValue (
const TConstArrayView < FCacheGetValueRequest > Requests ,
IRequestOwner & Owner ,
FOnCacheGetValueComplete & & OnComplete )
{
for ( const FCacheGetValueRequest & Request : Requests )
{
2022-03-17 09:07:20 -04:00
bool bOk ;
2022-01-10 12:13:18 -05:00
FValue Value ;
{
2022-03-17 09:07:20 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( FileSystemDDC_GetValue ) ;
TRACE_COUNTER_INCREMENT ( FileSystemDDC_Get ) ;
COOK_STAT ( auto Timer = UsageStats . TimeGet ( ) ) ;
bOk = GetCacheValue ( Request . Name , Request . Key , Request . Policy , Value ) ;
if ( bOk )
{
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache hit for %s from '%s' " ) ,
* CachePath , * WriteToString < 96 > ( Request . Key ) , * Request . Name ) ;
TRACE_COUNTER_INCREMENT ( FileSystemDDC_GetHit ) ;
TRACE_COUNTER_ADD ( FileSystemDDC_BytesRead , Value . GetData ( ) . GetCompressedSize ( ) ) ;
COOK_STAT ( Timer . AddHit ( Value . GetData ( ) . GetCompressedSize ( ) ) ) ;
}
2022-01-10 12:13:18 -05:00
}
2022-03-17 09:07:20 -04:00
OnComplete ( { Request . Name , Request . Key , Value , Request . UserData , bOk ? EStatus : : Ok : EStatus : : Error } ) ;
2022-01-10 12:13:18 -05:00
}
}
2021-11-10 14:44:18 -05:00
void FFileSystemCacheStore : : GetChunks (
2022-01-18 04:47:59 -05:00
const TConstArrayView < FCacheGetChunkRequest > Requests ,
2021-11-10 14:44:18 -05:00
IRequestOwner & Owner ,
2022-01-18 04:47:59 -05:00
FOnCacheGetChunkComplete & & OnComplete )
2021-11-10 14:44:18 -05:00
{
2022-01-18 04:47:59 -05:00
TArray < FCacheGetChunkRequest , TInlineAllocator < 16 > > SortedRequests ( Requests ) ;
2022-01-04 09:26:33 -05:00
SortedRequests . StableSort ( TChunkLess ( ) ) ;
2021-11-10 14:44:18 -05:00
2022-01-10 12:13:18 -05:00
bool bHasValue = false ;
FValue Value ;
FValueId ValueId ;
2022-01-06 11:05:57 -05:00
FCacheKey ValueKey ;
TUniquePtr < FArchive > ValueAr ;
FCompressedBufferReader ValueReader ;
2021-11-10 14:44:18 -05:00
FOptionalCacheRecord Record ;
2022-01-18 04:47:59 -05:00
for ( const FCacheGetChunkRequest & Request : SortedRequests )
2021-11-10 14:44:18 -05:00
{
2022-03-17 09:07:20 -04:00
EStatus Status = EStatus : : Error ;
FSharedBuffer Buffer ;
uint64 RawSize = 0 ;
2021-11-10 14:44:18 -05:00
{
2022-03-17 09:07:20 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( FileSystemDDC_GetChunks ) ;
TRACE_COUNTER_INCREMENT ( FileSystemDDC_Get ) ;
const bool bExistsOnly = EnumHasAnyFlags ( Request . Policy , ECachePolicy : : SkipData ) ;
COOK_STAT ( auto Timer = bExistsOnly ? UsageStats . TimeProbablyExists ( ) : UsageStats . TimeGet ( ) ) ;
if ( ! ( bHasValue & & ValueKey = = Request . Key & & ValueId = = Request . Id ) | | ValueReader . HasSource ( ) < ! bExistsOnly )
2021-11-10 14:44:18 -05:00
{
2022-03-17 09:07:20 -04:00
ValueReader . ResetSource ( ) ;
ValueAr . Reset ( ) ;
ValueKey = { } ;
ValueId . Reset ( ) ;
Value . Reset ( ) ;
bHasValue = false ;
if ( Request . Id . IsValid ( ) )
2022-01-10 12:13:18 -05:00
{
2022-03-17 09:07:20 -04:00
if ( ! ( Record & & Record . Get ( ) . GetKey ( ) = = Request . Key ) )
2022-02-14 14:43:39 -05:00
{
2022-03-17 09:07:20 -04:00
FCacheRecordPolicyBuilder PolicyBuilder ( ECachePolicy : : None ) ;
PolicyBuilder . AddValuePolicy ( Request . Id , Request . Policy ) ;
Record . Reset ( ) ;
Record = GetCacheRecordOnly ( Request . Name , Request . Key , PolicyBuilder . Build ( ) ) ;
}
if ( Record )
{
if ( const FValueWithId & ValueWithId = Record . Get ( ) . GetValue ( Request . Id ) )
{
bHasValue = true ;
Value = ValueWithId ;
ValueId = Request . Id ;
ValueKey = Request . Key ;
GetCacheContent ( Request . Name , Request . Key , ValueId , Value , Request . Policy , ValueReader , ValueAr ) ;
}
}
}
else
{
ValueKey = Request . Key ;
bHasValue = GetCacheValueOnly ( Request . Name , Request . Key , Request . Policy , Value ) ;
if ( bHasValue )
{
GetCacheContent ( Request . Name , Request . Key , Request . Id , Value , Request . Policy , ValueReader , ValueAr ) ;
2022-02-14 14:43:39 -05:00
}
2022-01-10 12:13:18 -05:00
}
2021-11-10 14:44:18 -05:00
}
2022-03-17 09:07:20 -04:00
if ( bHasValue )
2021-12-08 17:15:36 -05:00
{
2022-03-17 09:07:20 -04:00
const uint64 RawOffset = FMath : : Min ( Value . GetRawSize ( ) , Request . RawOffset ) ;
RawSize = FMath : : Min ( Value . GetRawSize ( ) - RawOffset , Request . RawSize ) ;
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache hit for %s from '%s' " ) ,
* CachePath , * WriteToString < 96 > ( Request . Key , ' / ' , Request . Id ) , * Request . Name ) ;
TRACE_COUNTER_INCREMENT ( FileSystemDDC_GetHit ) ;
TRACE_COUNTER_ADD ( FileSystemDDC_BytesRead , ! bExistsOnly ? RawSize : 0 ) ;
COOK_STAT ( Timer . AddHit ( ! bExistsOnly ? RawSize : 0 ) ) ;
if ( ! bExistsOnly )
2022-02-14 14:43:39 -05:00
{
2022-03-17 09:07:20 -04:00
Buffer = ValueReader . Decompress ( RawOffset , RawSize ) ;
2022-02-14 14:43:39 -05:00
}
2022-03-17 09:07:20 -04:00
Status = bExistsOnly | | Buffer . GetSize ( ) = = RawSize ? EStatus : : Ok : EStatus : : Error ;
2021-12-08 17:15:36 -05:00
}
}
2022-03-17 09:07:20 -04:00
OnComplete ( { Request . Name , Request . Key , Request . Id , Request . RawOffset ,
RawSize , Value . GetRawHash ( ) , MoveTemp ( Buffer ) , Request . UserData , Status } ) ;
2021-11-10 14:44:18 -05:00
}
}
bool FFileSystemCacheStore : : PutCacheRecord (
2022-01-07 23:22:29 -05:00
const FStringView Name ,
2021-11-10 14:44:18 -05:00
const FCacheRecord & Record ,
2021-12-16 19:57:53 -05:00
const FCacheRecordPolicy & Policy ,
uint64 & OutWriteSize )
2021-11-10 14:44:18 -05:00
{
2022-06-07 11:17:10 -04:00
if ( bReadOnly )
2021-11-10 14:44:18 -05:00
{
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
2021-12-15 09:30:12 -05:00
TEXT ( " %s: Skipped put of %s from '%.*s' because this cache store is read-only " ) ,
2022-01-07 23:22:29 -05:00
* CachePath , * WriteToString < 96 > ( Record . GetKey ( ) ) , Name . Len ( ) , Name . GetData ( ) ) ;
2021-11-10 14:44:18 -05:00
return false ;
}
const FCacheKey & Key = Record . GetKey ( ) ;
2022-01-31 11:52:13 -05:00
const ECachePolicy RecordPolicy = Policy . GetRecordPolicy ( ) ;
2021-11-10 14:44:18 -05:00
// Skip the request if storing to the cache is disabled.
2022-06-07 11:17:10 -04:00
const ECachePolicy StoreFlag = SpeedClass = = EBackendSpeedClass : : Local ? ECachePolicy : : StoreLocal : ECachePolicy : : StoreRemote ;
2022-01-31 11:52:13 -05:00
if ( ! EnumHasAnyFlags ( RecordPolicy , StoreFlag ) )
2021-11-10 14:44:18 -05:00
{
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " %s: Skipped put of %s from '%.*s' due to cache policy " ) ,
2022-01-07 23:22:29 -05:00
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
2021-11-10 14:44:18 -05:00
return false ;
}
2022-02-23 17:04:41 -05:00
if ( DebugOptions . ShouldSimulatePutMiss ( Key ) )
2021-11-10 14:44:18 -05:00
{
2022-02-23 17:04:41 -05:00
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Simulated miss for put of %s from '%.*s' " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
return false ;
2021-11-10 14:44:18 -05:00
}
TStringBuilder < 256 > Path ;
2022-01-10 12:13:18 -05:00
BuildCachePackagePath ( Key , Path ) ;
2022-02-07 20:06:56 -05:00
// Check if there is an existing record package.
FCbPackage ExistingPackage ;
2022-06-07 11:17:10 -04:00
const ECachePolicy QueryFlag = SpeedClass = = EBackendSpeedClass : : Local ? ECachePolicy : : QueryLocal : ECachePolicy : : QueryRemote ;
2022-02-07 20:06:56 -05:00
bool bReplaceExisting = ! EnumHasAnyFlags ( RecordPolicy , QueryFlag ) ;
2022-04-14 10:32:14 -04:00
bool bSavePackage = bReplaceExisting ;
if ( const bool bLoadPackage = ! bReplaceExisting | | ! Algo : : AllOf ( Record . GetValues ( ) , & FValue : : HasData ) )
2021-11-10 14:44:18 -05:00
{
2022-04-14 10:32:14 -04:00
// Load the existing package to take its attachments into account.
// Save the new package if there is no existing package or it fails to load.
bSavePackage | = ! LoadFileWithHash ( Path , Name , [ & ExistingPackage ] ( FArchive & Ar ) { ExistingPackage . TryLoad ( Ar ) ; } ) ;
if ( ! bSavePackage )
2022-02-07 20:06:56 -05:00
{
2022-04-14 10:32:14 -04:00
// Save the new package if the existing package is invalid.
2022-02-07 20:06:56 -05:00
const FOptionalCacheRecord ExistingRecord = FCacheRecord : : Load ( ExistingPackage ) ;
2022-04-14 10:32:14 -04:00
bSavePackage | = ! ExistingRecord ;
2022-02-07 20:06:56 -05:00
const auto MakeValueTuple = [ ] ( const FValueWithId & Value ) - > TTuple < FValueId , FIoHash >
{
return MakeTuple ( Value . GetId ( ) , Value . GetRawHash ( ) ) ;
} ;
if ( ExistingRecord & & ! Algo : : CompareBy ( ExistingRecord . Get ( ) . GetValues ( ) , Record . GetValues ( ) , MakeValueTuple ) )
{
2022-04-14 10:32:14 -04:00
// Content differs between the existing record and the new record.
2022-02-07 20:06:56 -05:00
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: Cache put found non-deterministic record for %s from '%.*s' " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
const auto HasValueContent = [ this , Name , & Key ] ( const FValueWithId & Value ) - > bool
{
if ( ! Value . HasData ( ) & & ! GetCacheContentExists ( Key , Value . GetRawHash ( ) ) )
{
UE_LOG ( LogDerivedDataCache , Log ,
2022-06-14 12:46:34 -04:00
TEXT ( " %s: Cache put of non-deterministic record will overwrite existing record due to "
" missing value %s with hash %s for %s from '%.*s' " ) ,
2022-02-07 20:06:56 -05:00
* CachePath , * WriteToString < 16 > ( Value . GetId ( ) ) , * WriteToString < 48 > ( Value . GetRawHash ( ) ) ,
* WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
return false ;
}
return true ;
} ;
2022-04-14 10:32:14 -04:00
// Save the new package because the existing package differs and is missing content.
bSavePackage | = ! Algo : : AllOf ( ExistingRecord . Get ( ) . GetValues ( ) , HasValueContent ) ;
2022-02-07 20:06:56 -05:00
}
2022-04-14 10:32:14 -04:00
bReplaceExisting | = bSavePackage ;
2022-02-07 20:06:56 -05:00
}
2021-11-10 14:44:18 -05:00
}
2022-02-07 20:06:56 -05:00
// Serialize the record to a package and remove attachments that will be stored externally.
2021-11-10 14:44:18 -05:00
FCbPackage Package = Record . Save ( ) ;
TArray < FCompressedBuffer , TInlineAllocator < 8 > > ExternalContent ;
2022-04-14 10:32:14 -04:00
if ( ExistingPackage & & ! bSavePackage )
2021-11-10 14:44:18 -05:00
{
// Mirror the existing internal/external attachment storage.
TArray < FCompressedBuffer , TInlineAllocator < 8 > > AllContent ;
Algo : : Transform ( Package . GetAttachments ( ) , AllContent , & FCbAttachment : : AsCompressedBinary ) ;
for ( FCompressedBuffer & Content : AllContent )
{
const FIoHash RawHash = Content . GetRawHash ( ) ;
if ( ! ExistingPackage . FindAttachment ( RawHash ) )
{
Package . RemoveAttachment ( RawHash ) ;
ExternalContent . Add ( MoveTemp ( Content ) ) ;
}
}
}
else
{
2022-02-07 20:06:56 -05:00
// Attempt to copy missing attachments from the existing package.
if ( ExistingPackage )
{
for ( const FValue & Value : Record . GetValues ( ) )
{
if ( ! Value . HasData ( ) )
{
if ( const FCbAttachment * Attachment = ExistingPackage . FindAttachment ( Value . GetRawHash ( ) ) )
{
Package . AddAttachment ( * Attachment ) ;
}
}
}
}
2021-11-10 14:44:18 -05:00
// Remove the largest attachments from the package until it fits within the size limits.
TArray < FCompressedBuffer , TInlineAllocator < 8 > > AllContent ;
Algo : : Transform ( Package . GetAttachments ( ) , AllContent , & FCbAttachment : : AsCompressedBinary ) ;
uint64 TotalSize = Algo : : TransformAccumulate ( AllContent , & FCompressedBuffer : : GetCompressedSize , uint64 ( 0 ) ) ;
2022-02-07 20:06:56 -05:00
const uint64 MaxSize = ( Record . GetValues ( ) . Num ( ) = = 1 ? MaxValueSizeKB : MaxRecordSizeKB ) * 1024 ;
2021-11-10 14:44:18 -05:00
if ( TotalSize > MaxSize )
{
Algo : : StableSortBy ( AllContent , & FCompressedBuffer : : GetCompressedSize , TGreater < > ( ) ) ;
for ( FCompressedBuffer & Content : AllContent )
{
const uint64 CompressedSize = Content . GetCompressedSize ( ) ;
Package . RemoveAttachment ( Content . GetRawHash ( ) ) ;
ExternalContent . Add ( MoveTemp ( Content ) ) ;
TotalSize - = CompressedSize ;
if ( TotalSize < = MaxSize )
{
break ;
}
}
}
}
// Save the external content to storage.
for ( FCompressedBuffer & Content : ExternalContent )
{
2021-12-16 19:57:53 -05:00
uint64 WriteSize = 0 ;
2022-01-10 12:13:18 -05:00
if ( ! PutCacheContent ( Name , Content , OutWriteSize ) )
{
return false ;
}
2021-12-16 19:57:53 -05:00
OutWriteSize + = WriteSize ;
2021-11-10 14:44:18 -05:00
}
// Save the record package to storage.
2022-04-14 10:32:14 -04:00
const auto WritePackage = [ & Package , & OutWriteSize ] ( FArchive & Ar )
{
Package . Save ( Ar ) ;
OutWriteSize + = uint64 ( Ar . TotalSize ( ) ) ;
} ;
if ( bSavePackage & & ! SaveFileWithHash ( Path , Name , WritePackage , bReplaceExisting ) )
2021-11-10 14:44:18 -05:00
{
return false ;
}
if ( AccessLogWriter )
{
AccessLogWriter - > Append ( Key , Path ) ;
}
return true ;
}
FOptionalCacheRecord FFileSystemCacheStore : : GetCacheRecordOnly (
2022-01-07 23:22:29 -05:00
const FStringView Name ,
2021-11-10 14:44:18 -05:00
const FCacheKey & Key ,
const FCacheRecordPolicy & Policy )
{
// Skip the request if querying the cache is disabled.
2022-06-07 11:17:10 -04:00
const ECachePolicy QueryFlag = SpeedClass = = EBackendSpeedClass : : Local ? ECachePolicy : : QueryLocal : ECachePolicy : : QueryRemote ;
2022-01-10 12:13:18 -05:00
if ( ! EnumHasAnyFlags ( Policy . GetRecordPolicy ( ) , QueryFlag ) )
2021-11-10 14:44:18 -05:00
{
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " %s: Skipped get of %s from '%.*s' due to cache policy " ) ,
2022-01-07 23:22:29 -05:00
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
2021-11-10 14:44:18 -05:00
return FOptionalCacheRecord ( ) ;
}
2022-02-23 17:04:41 -05:00
if ( DebugOptions . ShouldSimulateGetMiss ( Key ) )
2021-11-10 14:44:18 -05:00
{
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Simulated miss for get of %s from '%.*s' " ) ,
2022-01-07 23:22:29 -05:00
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
2021-11-10 14:44:18 -05:00
return FOptionalCacheRecord ( ) ;
}
TStringBuilder < 256 > Path ;
2022-01-10 12:13:18 -05:00
BuildCachePackagePath ( Key , Path ) ;
2021-11-10 14:44:18 -05:00
2022-01-10 12:13:18 -05:00
bool bDeletePackage = true ;
2021-11-10 14:44:18 -05:00
ON_SCOPE_EXIT
{
2022-01-10 12:13:18 -05:00
if ( bDeletePackage & & ! bReadOnly )
2021-11-10 14:44:18 -05:00
{
IFileManager : : Get ( ) . Delete ( * Path , /*bRequireExists*/ false , /*bEvenReadOnly*/ false , /*bQuiet*/ true ) ;
}
} ;
FOptionalCacheRecord Record ;
{
FCbPackage Package ;
2022-01-07 23:22:29 -05:00
if ( ! LoadFileWithHash ( Path , Name , [ & Package ] ( FArchive & Ar ) { Package . TryLoad ( Ar ) ; } ) )
2021-11-10 14:44:18 -05:00
{
2022-01-10 12:13:18 -05:00
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache miss with missing package for %s from '%.*s' " ) ,
2022-01-07 23:22:29 -05:00
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
2021-12-08 17:15:36 -05:00
return Record ;
}
if ( ValidateCompactBinary ( Package , ECbValidateMode : : Default | ECbValidateMode : : Package ) ! = ECbValidateError : : None )
{
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: Cache miss with invalid package for %s from '%.*s' " ) ,
2022-01-07 23:22:29 -05:00
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
2022-01-10 12:13:18 -05:00
return Record ;
2021-11-10 14:44:18 -05:00
}
2021-12-08 17:15:36 -05:00
2021-11-10 14:44:18 -05:00
Record = FCacheRecord : : Load ( Package ) ;
if ( Record . IsNull ( ) )
{
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: Cache miss with record load failure for %s from '%.*s' " ) ,
2022-01-07 23:22:29 -05:00
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
2021-12-08 17:15:36 -05:00
return Record ;
2021-11-10 14:44:18 -05:00
}
}
if ( AccessLogWriter )
{
AccessLogWriter - > Append ( Key , Path ) ;
}
2022-01-10 12:13:18 -05:00
bDeletePackage = false ;
2021-12-08 17:15:36 -05:00
return Record ;
2021-11-10 14:44:18 -05:00
}
FOptionalCacheRecord FFileSystemCacheStore : : GetCacheRecord (
2022-01-07 23:22:29 -05:00
const FStringView Name ,
2021-11-10 14:44:18 -05:00
const FCacheKey & Key ,
const FCacheRecordPolicy & Policy ,
EStatus & OutStatus )
{
2022-01-07 23:22:29 -05:00
FOptionalCacheRecord Record = GetCacheRecordOnly ( Name , Key , Policy ) ;
2021-11-10 14:44:18 -05:00
if ( Record . IsNull ( ) )
{
OutStatus = EStatus : : Error ;
return Record ;
}
OutStatus = EStatus : : Ok ;
FCacheRecordBuilder RecordBuilder ( Key ) ;
2022-01-31 11:52:13 -05:00
const ECachePolicy RecordPolicy = Policy . GetRecordPolicy ( ) ;
if ( ! EnumHasAnyFlags ( RecordPolicy , ECachePolicy : : SkipMeta ) )
2021-11-10 14:44:18 -05:00
{
RecordBuilder . SetMeta ( FCbObject ( Record . Get ( ) . GetMeta ( ) ) ) ;
}
2022-01-07 23:22:29 -05:00
for ( const FValueWithId & Value : Record . Get ( ) . GetValues ( ) )
2021-11-10 14:44:18 -05:00
{
2022-01-10 12:13:18 -05:00
const FValueId & Id = Value . GetId ( ) ;
const ECachePolicy ValuePolicy = Policy . GetValuePolicy ( Id ) ;
FValue Content ;
if ( GetCacheContent ( Name , Key , Id , Value , ValuePolicy , Content ) )
2021-11-10 14:44:18 -05:00
{
2022-01-10 12:13:18 -05:00
RecordBuilder . AddValue ( Id , MoveTemp ( Content ) ) ;
2021-12-08 17:15:36 -05:00
}
2022-01-31 11:52:13 -05:00
else if ( EnumHasAnyFlags ( RecordPolicy , ECachePolicy : : PartialRecord ) )
2021-12-08 17:15:36 -05:00
{
OutStatus = EStatus : : Error ;
2022-01-07 23:22:29 -05:00
RecordBuilder . AddValue ( Value ) ;
2021-12-08 17:15:36 -05:00
}
else
{
OutStatus = EStatus : : Error ;
2021-11-10 14:44:18 -05:00
return FOptionalCacheRecord ( ) ;
}
}
return RecordBuilder . Build ( ) ;
}
2022-01-10 12:13:18 -05:00
bool FFileSystemCacheStore : : PutCacheValue (
const FStringView Name ,
const FCacheKey & Key ,
const FValue & Value ,
const ECachePolicy Policy ,
uint64 & OutWriteSize )
{
2022-06-07 11:17:10 -04:00
if ( bReadOnly )
2022-01-10 12:13:18 -05:00
{
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
TEXT ( " %s: Skipped put of %s from '%.*s' because this cache store is read-only " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
return false ;
}
// Skip the request if storing to the cache is disabled.
2022-06-07 11:17:10 -04:00
const ECachePolicy StoreFlag = SpeedClass = = EBackendSpeedClass : : Local ? ECachePolicy : : StoreLocal : ECachePolicy : : StoreRemote ;
2022-01-10 12:13:18 -05:00
if ( ! EnumHasAnyFlags ( Policy , StoreFlag ) )
{
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " %s: Skipped put of %s from '%.*s' due to cache policy " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
return false ;
}
2022-02-23 17:04:41 -05:00
if ( DebugOptions . ShouldSimulatePutMiss ( Key ) )
2022-01-10 12:13:18 -05:00
{
2022-02-23 17:04:41 -05:00
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Simulated miss for put of %s from '%.*s' " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
return false ;
2022-01-10 12:13:18 -05:00
}
// Check if there is an existing value package.
FCbPackage ExistingPackage ;
TStringBuilder < 256 > Path ;
BuildCachePackagePath ( Key , Path ) ;
2022-06-07 11:17:10 -04:00
const ECachePolicy QueryFlag = SpeedClass = = EBackendSpeedClass : : Local ? ECachePolicy : : QueryLocal : ECachePolicy : : QueryRemote ;
2022-04-14 10:32:14 -04:00
bool bReplaceExisting = ! EnumHasAnyFlags ( Policy , QueryFlag ) ;
bool bSavePackage = bReplaceExisting ;
if ( const bool bLoadPackage = ! bReplaceExisting | | ! Value . HasData ( ) )
2022-01-10 12:13:18 -05:00
{
2022-04-14 10:32:14 -04:00
// Load the existing package to take its attachments into account.
// Save the new package if there is no existing package or it fails to load.
bSavePackage | = ! LoadFileWithHash ( Path , Name , [ & ExistingPackage ] ( FArchive & Ar ) { ExistingPackage . TryLoad ( Ar ) ; } ) ;
if ( ! bSavePackage )
{
const FCbObjectView Object = ExistingPackage . GetObject ( ) ;
const FIoHash RawHash = Object [ " RawHash " ] . AsHash ( ) ;
const uint64 RawSize = Object [ " RawSize " ] . AsUInt64 ( MAX_uint64 ) ;
if ( RawHash . IsZero ( ) | | RawSize = = MAX_uint64 )
{
// Save the new package because the existing package is invalid.
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: Cache put found invalid existing value for %s from '%.*s' " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
bSavePackage = true ;
}
else if ( ! ( RawHash = = Value . GetRawHash ( ) & & RawSize = = Value . GetRawSize ( ) ) )
{
// Content differs between the existing value and the new value.
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: Cache put found non-deterministic value "
" with new hash %s and existing hash %s for %s from '%.*s' " ) ,
* CachePath , * WriteToString < 48 > ( Value . GetRawHash ( ) ) , * WriteToString < 48 > ( RawHash ) ,
* WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
if ( ! ExistingPackage . FindAttachment ( RawHash ) & & ! GetCacheContentExists ( Key , RawHash ) )
{
// Save the new package because the existing package differs and is missing content.
UE_LOG ( LogDerivedDataCache , Log ,
TEXT ( " %s: Cache put of non-deterministic value will overwrite existing value due to "
" missing value with hash %s for %s from '%.*s' " ) ,
* CachePath , * WriteToString < 48 > ( RawHash ) , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
bSavePackage = true ;
}
}
bReplaceExisting | = bSavePackage ;
}
2022-01-10 12:13:18 -05:00
}
// Save the value to a package and save the data to external content depending on its size.
2022-04-14 10:32:14 -04:00
FCbPackage Package ;
TArray < FCompressedBuffer , TInlineAllocator < 1 > > ExternalContent ;
if ( ExistingPackage & & ! bSavePackage )
{
if ( Value . HasData ( ) & & ! ExistingPackage . FindAttachment ( Value . GetRawHash ( ) ) )
{
ExternalContent . Add ( Value . GetData ( ) ) ;
}
}
else
2022-01-10 12:13:18 -05:00
{
FCbWriter Writer ;
Writer . BeginObject ( ) ;
Writer . AddBinaryAttachment ( " RawHash " , Value . GetRawHash ( ) ) ;
Writer . AddInteger ( " RawSize " , Value . GetRawSize ( ) ) ;
Writer . EndObject ( ) ;
2022-04-14 10:32:14 -04:00
Package . SetObject ( Writer . Save ( ) . AsObject ( ) ) ;
2022-01-10 12:13:18 -05:00
if ( ! Value . HasData ( ) )
{
// Verify that the content exists in storage.
if ( ! GetCacheContentExists ( Key , Value . GetRawHash ( ) ) )
{
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Failed due to missing data for put of %s from '%.*s' " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
return false ;
}
}
2022-02-14 14:43:39 -05:00
else if ( Value . GetData ( ) . GetCompressedSize ( ) < = MaxValueSizeKB * 1024 )
2022-01-10 12:13:18 -05:00
{
// Store the content in the package.
Package . AddAttachment ( FCbAttachment ( Value . GetData ( ) ) ) ;
}
else
{
2022-04-14 10:32:14 -04:00
ExternalContent . Add ( Value . GetData ( ) ) ;
2022-01-10 12:13:18 -05:00
}
2022-04-14 10:32:14 -04:00
}
2022-01-10 12:13:18 -05:00
2022-04-14 10:32:14 -04:00
// Save the external content to storage.
for ( FCompressedBuffer & Content : ExternalContent )
{
uint64 WriteSize = 0 ;
if ( ! PutCacheContent ( Name , Content , OutWriteSize ) )
2022-01-10 12:13:18 -05:00
{
return false ;
}
2022-04-14 10:32:14 -04:00
OutWriteSize + = WriteSize ;
}
// Save the value package to storage.
const auto WritePackage = [ & Package , & OutWriteSize ] ( FArchive & Ar )
{
Package . Save ( Ar ) ;
OutWriteSize + = uint64 ( Ar . TotalSize ( ) ) ;
} ;
if ( bSavePackage & & ! SaveFileWithHash ( Path , Name , WritePackage , bReplaceExisting ) )
{
return false ;
2022-01-10 12:13:18 -05:00
}
if ( AccessLogWriter )
{
AccessLogWriter - > Append ( Key , Path ) ;
}
return true ;
}
bool FFileSystemCacheStore : : GetCacheValueOnly (
const FStringView Name ,
const FCacheKey & Key ,
const ECachePolicy Policy ,
FValue & OutValue )
{
// Skip the request if querying the cache is disabled.
2022-06-07 11:17:10 -04:00
const ECachePolicy QueryFlag = SpeedClass = = EBackendSpeedClass : : Local ? ECachePolicy : : QueryLocal : ECachePolicy : : QueryRemote ;
2022-01-10 12:13:18 -05:00
if ( ! EnumHasAnyFlags ( Policy , QueryFlag ) )
{
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " %s: Skipped get of %s from '%.*s' due to cache policy " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
return false ;
}
2022-02-23 17:04:41 -05:00
if ( DebugOptions . ShouldSimulateGetMiss ( Key ) )
2022-01-10 12:13:18 -05:00
{
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Simulated miss for get of %s from '%.*s' " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
return false ;
}
TStringBuilder < 256 > Path ;
BuildCachePackagePath ( Key , Path ) ;
bool bDeletePackage = true ;
ON_SCOPE_EXIT
{
if ( bDeletePackage & & ! bReadOnly )
{
IFileManager : : Get ( ) . Delete ( * Path , /*bRequireExists*/ false , /*bEvenReadOnly*/ false , /*bQuiet*/ true ) ;
}
} ;
FCbPackage Package ;
if ( ! LoadFileWithHash ( Path , Name , [ & Package ] ( FArchive & Ar ) { Package . TryLoad ( Ar ) ; } ) )
{
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache miss with missing package for %s from '%.*s' " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
return false ;
}
if ( ValidateCompactBinary ( Package , ECbValidateMode : : Default | ECbValidateMode : : Package ) ! = ECbValidateError : : None )
{
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: Cache miss with invalid package for %s from '%.*s' " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
return false ;
}
const FCbObjectView Object = Package . GetObject ( ) ;
const FIoHash RawHash = Object [ " RawHash " ] . AsHash ( ) ;
const uint64 RawSize = Object [ " RawSize " ] . AsUInt64 ( MAX_uint64 ) ;
if ( RawHash . IsZero ( ) | | RawSize = = MAX_uint64 )
{
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: Cache miss with invalid value for %s from '%.*s' " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
return false ;
}
2022-02-08 16:07:34 -05:00
if ( const FCbAttachment * const Attachment = Package . FindAttachment ( RawHash ) )
2022-01-10 12:13:18 -05:00
{
const FCompressedBuffer & Data = Attachment - > AsCompressedBinary ( ) ;
if ( Data . GetRawHash ( ) ! = RawHash | | Data . GetRawSize ( ) ! = RawSize )
{
UE_LOG ( LogDerivedDataCache , Display ,
TEXT ( " %s: Cache miss with invalid value attachment for %s from '%.*s' " ) ,
* CachePath , * WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
return false ;
}
OutValue = FValue ( Data ) ;
}
else
{
OutValue = FValue ( RawHash , RawSize ) ;
}
if ( AccessLogWriter )
{
AccessLogWriter - > Append ( Key , Path ) ;
}
bDeletePackage = false ;
return true ;
}
bool FFileSystemCacheStore : : GetCacheValue (
const FStringView Name ,
const FCacheKey & Key ,
const ECachePolicy Policy ,
FValue & OutValue )
{
return GetCacheValueOnly ( Name , Key , Policy , OutValue ) & & GetCacheContent ( Name , Key , { } , OutValue , Policy , OutValue ) ;
}
2021-12-16 19:57:53 -05:00
bool FFileSystemCacheStore : : PutCacheContent (
2022-01-07 23:22:29 -05:00
const FStringView Name ,
2021-12-16 19:57:53 -05:00
const FCompressedBuffer & Content ,
uint64 & OutWriteSize ) const
2021-11-10 14:44:18 -05:00
{
const FIoHash & RawHash = Content . GetRawHash ( ) ;
TStringBuilder < 256 > Path ;
BuildCacheContentPath ( RawHash , Path ) ;
2021-12-16 19:57:53 -05:00
const auto WriteContent = [ & ] ( FArchive & Ar ) { Content . Save ( Ar ) ; OutWriteSize + = uint64 ( Ar . TotalSize ( ) ) ; } ;
2022-01-07 23:22:29 -05:00
if ( ! FileExists ( Path ) & & ! SaveFileWithHash ( Path , Name , WriteContent ) )
2021-11-10 14:44:18 -05:00
{
2021-12-08 17:15:36 -05:00
return false ;
2021-11-10 14:44:18 -05:00
}
if ( AccessLogWriter )
{
AccessLogWriter - > Append ( RawHash , Path ) ;
}
return true ;
}
2022-01-10 12:13:18 -05:00
bool FFileSystemCacheStore : : GetCacheContentExists ( const FCacheKey & Key , const FIoHash & RawHash ) const
{
TStringBuilder < 256 > Path ;
BuildCacheContentPath ( RawHash , Path ) ;
return FileExists ( Path ) ;
}
bool FFileSystemCacheStore : : GetCacheContent (
2022-01-07 23:22:29 -05:00
const FStringView Name ,
2021-12-08 17:15:36 -05:00
const FCacheKey & Key ,
2022-01-10 12:13:18 -05:00
const FValueId & Id ,
const FValue & Value ,
const ECachePolicy Policy ,
FValue & OutValue ) const
2021-12-08 17:15:36 -05:00
{
if ( ! EnumHasAnyFlags ( Policy , ECachePolicy : : Query ) )
{
2022-01-10 12:13:18 -05:00
OutValue = Value . RemoveData ( ) ;
return true ;
2021-12-08 17:15:36 -05:00
}
2022-01-06 11:05:57 -05:00
if ( Value . HasData ( ) )
2021-12-08 17:15:36 -05:00
{
2022-01-10 12:13:18 -05:00
OutValue = EnumHasAnyFlags ( Policy , ECachePolicy : : SkipData ) ? Value . RemoveData ( ) : Value ;
return true ;
2021-12-08 17:15:36 -05:00
}
2022-01-06 11:05:57 -05:00
const FIoHash & RawHash = Value . GetRawHash ( ) ;
2021-12-08 17:15:36 -05:00
TStringBuilder < 256 > Path ;
BuildCacheContentPath ( RawHash , Path ) ;
2022-01-07 23:22:29 -05:00
if ( EnumHasAnyFlags ( Policy , ECachePolicy : : SkipData ) )
2021-12-08 17:15:36 -05:00
{
if ( FileExists ( Path ) )
{
if ( AccessLogWriter )
{
AccessLogWriter - > Append ( RawHash , Path ) ;
}
2022-01-10 12:13:18 -05:00
OutValue = Value ;
return true ;
2021-12-08 17:15:36 -05:00
}
}
else
{
FCompressedBuffer CompressedBuffer ;
2022-01-07 23:22:29 -05:00
if ( LoadFileWithHash ( Path , Name , [ & CompressedBuffer ] ( FArchive & Ar ) { CompressedBuffer = FCompressedBuffer : : Load ( Ar ) ; } ) )
2021-12-08 17:15:36 -05:00
{
if ( CompressedBuffer . GetRawHash ( ) = = RawHash )
{
if ( AccessLogWriter )
{
AccessLogWriter - > Append ( RawHash , Path ) ;
}
2022-01-10 12:13:18 -05:00
OutValue = FValue ( MoveTemp ( CompressedBuffer ) ) ;
return true ;
2021-12-08 17:15:36 -05:00
}
UE_LOG ( LogDerivedDataCache , Display ,
2022-01-06 11:05:57 -05:00
TEXT ( " %s: Cache miss with corrupted value %s with hash %s for %s from '%.*s' " ) ,
2022-01-10 12:13:18 -05:00
* CachePath , * WriteToString < 16 > ( Id ) , * WriteToString < 48 > ( RawHash ) ,
2022-01-07 23:22:29 -05:00
* WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
2022-01-10 12:13:18 -05:00
return false ;
2021-12-08 17:15:36 -05:00
}
}
UE_LOG ( LogDerivedDataCache , Verbose ,
2022-01-06 11:05:57 -05:00
TEXT ( " %s: Cache miss with missing value %s with hash %s for %s from '%.*s' " ) ,
2022-01-10 12:13:18 -05:00
* CachePath , * WriteToString < 16 > ( Id ) , * WriteToString < 48 > ( RawHash ) , * WriteToString < 96 > ( Key ) ,
2022-01-07 23:22:29 -05:00
Name . Len ( ) , Name . GetData ( ) ) ;
2022-01-10 12:13:18 -05:00
return false ;
2021-12-08 17:15:36 -05:00
}
2021-11-10 14:44:18 -05:00
void FFileSystemCacheStore : : GetCacheContent (
2022-01-07 23:22:29 -05:00
const FStringView Name ,
2021-11-10 14:44:18 -05:00
const FCacheKey & Key ,
2022-01-10 12:13:18 -05:00
const FValueId & Id ,
const FValue & Value ,
2021-11-10 14:44:18 -05:00
const ECachePolicy Policy ,
2021-12-08 17:15:36 -05:00
FCompressedBufferReader & Reader ,
TUniquePtr < FArchive > & OutArchive ) const
2021-11-10 14:44:18 -05:00
{
2021-12-08 17:15:36 -05:00
if ( ! EnumHasAnyFlags ( Policy , ECachePolicy : : Query ) )
2021-11-10 14:44:18 -05:00
{
return ;
}
2022-01-06 11:05:57 -05:00
if ( Value . HasData ( ) )
2021-12-08 17:15:36 -05:00
{
2022-01-07 23:22:29 -05:00
if ( ! EnumHasAnyFlags ( Policy , ECachePolicy : : SkipData ) )
2021-12-08 17:15:36 -05:00
{
2022-01-06 11:05:57 -05:00
Reader . SetSource ( Value . GetData ( ) ) ;
2021-12-08 17:15:36 -05:00
}
OutArchive . Reset ( ) ;
return ;
}
2022-01-06 11:05:57 -05:00
const FIoHash & RawHash = Value . GetRawHash ( ) ;
2021-12-08 17:15:36 -05:00
2021-11-10 14:44:18 -05:00
TStringBuilder < 256 > Path ;
BuildCacheContentPath ( RawHash , Path ) ;
2022-01-07 23:22:29 -05:00
if ( EnumHasAllFlags ( Policy , ECachePolicy : : SkipData ) )
2021-11-10 14:44:18 -05:00
{
if ( FileExists ( Path ) )
{
if ( AccessLogWriter )
{
AccessLogWriter - > Append ( RawHash , Path ) ;
}
return ;
}
}
else
{
2022-04-29 16:18:09 -04:00
OutArchive = OpenFileRead ( Path , Name ) ;
2021-12-08 17:15:36 -05:00
if ( OutArchive )
2021-11-10 14:44:18 -05:00
{
2021-12-08 17:15:36 -05:00
Reader . SetSource ( * OutArchive ) ;
if ( Reader . GetRawHash ( ) = = RawHash )
2021-11-10 14:44:18 -05:00
{
if ( AccessLogWriter )
{
AccessLogWriter - > Append ( RawHash , Path ) ;
}
return ;
}
UE_LOG ( LogDerivedDataCache , Display ,
2022-01-06 11:05:57 -05:00
TEXT ( " %s: Cache miss with corrupted value %s with hash %s for %s from '%.*s' " ) ,
2022-01-10 12:13:18 -05:00
* CachePath , * WriteToString < 16 > ( Id ) , * WriteToString < 48 > ( RawHash ) ,
2022-01-07 23:22:29 -05:00
* WriteToString < 96 > ( Key ) , Name . Len ( ) , Name . GetData ( ) ) ;
2021-12-08 17:15:36 -05:00
Reader . ResetSource ( ) ;
OutArchive . Reset ( ) ;
2021-11-10 14:44:18 -05:00
return ;
}
}
UE_LOG ( LogDerivedDataCache , Verbose ,
2022-01-06 11:05:57 -05:00
TEXT ( " %s: Cache miss with missing value %s with hash %s for %s from '%.*s' " ) ,
2022-01-10 12:13:18 -05:00
* CachePath , * WriteToString < 16 > ( Id ) , * WriteToString < 48 > ( RawHash ) , * WriteToString < 96 > ( Key ) ,
2022-01-07 23:22:29 -05:00
Name . Len ( ) , Name . GetData ( ) ) ;
2021-11-10 14:44:18 -05:00
}
2022-01-10 12:13:18 -05:00
void FFileSystemCacheStore : : BuildCachePackagePath ( const FCacheKey & CacheKey , FStringBuilderBase & Path ) const
2021-11-10 14:44:18 -05:00
{
Path < < CachePath < < TEXT ( ' / ' ) ;
2022-01-10 12:13:18 -05:00
BuildPathForCachePackage ( CacheKey , Path ) ;
2021-11-10 14:44:18 -05:00
}
void FFileSystemCacheStore : : BuildCacheContentPath ( const FIoHash & RawHash , FStringBuilderBase & Path ) const
{
Path < < CachePath < < TEXT ( ' / ' ) ;
BuildPathForCacheContent ( RawHash , Path ) ;
}
2021-12-15 09:30:12 -05:00
bool FFileSystemCacheStore : : SaveFileWithHash (
FStringBuilderBase & Path ,
2022-01-04 09:26:33 -05:00
const FStringView DebugName ,
2022-01-31 11:52:13 -05:00
const TFunctionRef < void ( FArchive & ) > WriteFunction ,
const bool bReplaceExisting ) const
2021-12-15 09:30:12 -05:00
{
2022-01-04 09:26:33 -05:00
return SaveFile ( Path , DebugName , [ & WriteFunction ] ( FArchive & Ar )
2021-12-15 09:30:12 -05:00
{
THashingArchiveProxy < FBlake3 > HashAr ( Ar ) ;
WriteFunction ( HashAr ) ;
FBlake3Hash Hash = HashAr . GetHash ( ) ;
Ar < < Hash ;
2022-01-31 11:52:13 -05:00
} , bReplaceExisting ) ;
2021-12-15 09:30:12 -05:00
}
bool FFileSystemCacheStore : : LoadFileWithHash (
FStringBuilderBase & Path ,
2022-01-04 09:26:33 -05:00
const FStringView DebugName ,
2021-12-15 09:30:12 -05:00
const TFunctionRef < void ( FArchive & Ar ) > ReadFunction ) const
{
2022-01-04 09:26:33 -05:00
return LoadFile ( Path , DebugName , [ this , & Path , & DebugName , & ReadFunction ] ( FArchive & Ar )
2021-12-15 09:30:12 -05:00
{
THashingArchiveProxy < FBlake3 > HashAr ( Ar ) ;
ReadFunction ( HashAr ) ;
const FBlake3Hash Hash = HashAr . GetHash ( ) ;
FBlake3Hash SavedHash ;
Ar < < SavedHash ;
if ( Hash ! = SavedHash & & ! Ar . IsError ( ) )
{
Ar . SetError ( ) ;
UE_LOG ( LogDerivedDataCache , Display ,
TEXT ( " %s: File %s from '%.*s' is corrupted and has hash %s when %s is expected. " ) ,
2022-01-04 09:26:33 -05:00
* CachePath , * Path , DebugName . Len ( ) , DebugName . GetData ( ) ,
2021-12-15 09:30:12 -05:00
* WriteToString < 80 > ( Hash ) , * WriteToString < 80 > ( SavedHash ) ) ;
}
} ) ;
}
2021-11-10 14:44:18 -05:00
bool FFileSystemCacheStore : : SaveFile (
FStringBuilderBase & Path ,
2022-01-04 09:26:33 -05:00
const FStringView DebugName ,
2022-01-31 11:52:13 -05:00
const TFunctionRef < void ( FArchive & ) > WriteFunction ,
const bool bReplaceExisting ) const
2021-11-10 14:44:18 -05:00
{
2022-02-07 20:06:56 -05:00
const double StartTime = FPlatformTime : : Seconds ( ) ;
2021-11-10 14:44:18 -05:00
TStringBuilder < 256 > TempPath ;
TempPath < < FPathViews : : GetPath ( Path ) < < TEXT ( " /Temp. " ) < < FGuid : : NewGuid ( ) ;
2022-05-04 16:23:17 -04:00
TUniquePtr < FArchive > Ar = OpenFileWrite ( TempPath , DebugName ) ;
if ( ! Ar )
2021-11-10 14:44:18 -05:00
{
UE_LOG ( LogDerivedDataCache , Warning ,
2022-05-04 16:23:17 -04:00
TEXT ( " %s: Failed to open temp file %s for writing when saving %s from '%.*s'. Error 0x%08x. " ) ,
2022-01-04 09:26:33 -05:00
* CachePath , * TempPath , * Path , DebugName . Len ( ) , DebugName . GetData ( ) , FPlatformMisc : : GetLastError ( ) ) ;
2021-11-10 14:44:18 -05:00
return false ;
}
2022-05-04 16:23:17 -04:00
WriteFunction ( * Ar ) ;
const int64 WriteSize = Ar - > Tell ( ) ;
if ( ! Ar - > Close ( ) | | WriteSize = = 0 | | WriteSize ! = IFileManager : : Get ( ) . FileSize ( * TempPath ) )
2021-11-10 14:44:18 -05:00
{
2021-12-08 17:15:36 -05:00
UE_LOG ( LogDerivedDataCache , Warning ,
2022-05-04 16:23:17 -04:00
TEXT ( " %s: Failed to write to temp file %s when saving %s from '%.*s'. Error 0x%08x. "
" File is % " INT64_FMT " bytes when % " INT64_FMT " bytes are expected. " ) ,
* CachePath , * TempPath , * Path , DebugName . Len ( ) , DebugName . GetData ( ) , FPlatformMisc : : GetLastError ( ) ,
2021-12-08 17:15:36 -05:00
IFileManager : : Get ( ) . FileSize ( * TempPath ) , WriteSize ) ;
2022-05-04 16:23:17 -04:00
IFileManager : : Get ( ) . Delete ( * TempPath , /*bRequireExists*/ false , /*bEvenReadOnly*/ false , /*bQuiet*/ true ) ;
2021-12-08 17:15:36 -05:00
return false ;
2021-11-10 14:44:18 -05:00
}
2021-12-08 17:15:36 -05:00
2022-05-04 16:23:17 -04:00
if ( ! IFileManager : : Get ( ) . Move ( * Path , * TempPath , bReplaceExisting , /*bEvenIfReadOnly*/ false , /*bAttributes*/ false , /*bDoNotRetryOrError*/ true ) )
2021-12-08 17:15:36 -05:00
{
UE_LOG ( LogDerivedDataCache , Log ,
TEXT ( " %s: Move collision when writing file %s from '%.*s'. " ) ,
2022-01-04 09:26:33 -05:00
* CachePath , * Path , DebugName . Len ( ) , DebugName . GetData ( ) ) ;
2022-05-04 16:23:17 -04:00
IFileManager : : Get ( ) . Delete ( * TempPath , /*bRequireExists*/ false , /*bEvenReadOnly*/ false , /*bQuiet*/ true ) ;
2021-12-08 17:15:36 -05:00
}
2022-02-07 20:06:56 -05:00
const double WriteDuration = FPlatformTime : : Seconds ( ) - StartTime ;
const double WriteSpeed = WriteDuration > 0.001 ? ( WriteSize / WriteDuration ) / ( 1024.0 * 1024.0 ) : 0.0 ;
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
TEXT ( " %s: Saved %s from '%.*s' (% " INT64_FMT " bytes, %.02f secs, %.2f MiB/s) " ) ,
* CachePath , * Path , DebugName . Len ( ) , DebugName . GetData ( ) , WriteSize , WriteDuration , WriteSpeed ) ;
2021-12-08 17:15:36 -05:00
return true ;
2021-11-10 14:44:18 -05:00
}
2021-12-08 17:15:36 -05:00
bool FFileSystemCacheStore : : LoadFile (
FStringBuilderBase & Path ,
2022-01-04 09:26:33 -05:00
const FStringView DebugName ,
2021-12-08 17:15:36 -05:00
const TFunctionRef < void ( FArchive & Ar ) > ReadFunction ) const
2021-11-10 14:44:18 -05:00
{
const double StartTime = FPlatformTime : : Seconds ( ) ;
2022-05-04 16:23:17 -04:00
TUniquePtr < FArchive > Ar = OpenFileRead ( Path , DebugName ) ;
if ( ! Ar )
2021-11-10 14:44:18 -05:00
{
2022-05-04 16:23:17 -04:00
return false ;
}
2021-11-10 14:44:18 -05:00
2022-05-04 16:23:17 -04:00
ReadFunction ( * Ar ) ;
const int64 ReadSize = Ar - > Tell ( ) ;
const bool bError = ! Ar - > Close ( ) ;
if ( bError )
{
UE_LOG ( LogDerivedDataCache , Display ,
TEXT ( " %s: Failed to load file %s from '%.*s'. " ) ,
* CachePath , * Path , DebugName . Len ( ) , DebugName . GetData ( ) ) ;
if ( ! bReadOnly )
2021-11-10 14:44:18 -05:00
{
2022-05-04 16:23:17 -04:00
IFileManager : : Get ( ) . Delete ( * Path , /*bRequireExists*/ false , /*bEvenReadOnly*/ false , /*bQuiet*/ true ) ;
2021-12-08 17:15:36 -05:00
}
2021-11-10 14:44:18 -05:00
}
2022-05-04 16:23:17 -04:00
else
2021-11-10 14:44:18 -05:00
{
2022-05-04 16:23:17 -04:00
const double ReadDuration = FPlatformTime : : Seconds ( ) - StartTime ;
const double ReadSpeed = ReadDuration > 0.001 ? ( ReadSize / ReadDuration ) / ( 1024.0 * 1024.0 ) : 0.0 ;
2021-11-10 14:44:18 -05:00
2022-05-04 16:23:17 -04:00
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
TEXT ( " %s: Loaded %s from '%.*s' (% " INT64_FMT " bytes, %.02f secs, %.2f MiB/s) " ) ,
* CachePath , * Path , DebugName . Len ( ) , DebugName . GetData ( ) , ReadSize , ReadDuration , ReadSpeed ) ;
2021-12-08 17:15:36 -05:00
2022-05-04 16:23:17 -04:00
if ( ! GIsBuildMachine & & ReadDuration > 5.0 )
{
// Slower than 0.5 MiB/s?
UE_CLOG ( ReadSpeed < 0.5 , LogDerivedDataCache , Warning ,
TEXT ( " %s: Loading %s from '%.*s' is very slow (%.2f MiB/s); consider disabling this cache store. " ) ,
* CachePath , * Path , DebugName . Len ( ) , DebugName . GetData ( ) , ReadSpeed ) ;
}
2021-11-10 14:44:18 -05:00
}
2021-12-08 17:15:36 -05:00
return ! bError & & ReadSize > 0 ;
}
2022-04-29 16:18:09 -04:00
TUniquePtr < FArchive > FFileSystemCacheStore : : OpenFileWrite ( FStringBuilderBase & Path , const FStringView DebugName ) const
{
// Retry to handle a race where the directory is deleted while the file is being created.
constexpr int32 MaxAttemptCount = 3 ;
for ( int32 AttemptCount = 0 ; AttemptCount < MaxAttemptCount ; + + AttemptCount )
{
if ( TUniquePtr < FArchive > Ar { IFileManager : : Get ( ) . CreateFileWriter ( * Path , FILEWRITE_Silent ) } )
{
return Ar ;
}
}
return nullptr ;
}
TUniquePtr < FArchive > FFileSystemCacheStore : : OpenFileRead ( FStringBuilderBase & Path , const FStringView DebugName ) const
2021-12-08 17:15:36 -05:00
{
2022-05-04 16:23:17 -04:00
// Checking for existence may update the modification time to avoid the file being evicted from the cache.
// Reduce Game Thread overhead by executing the update on a worker thread if the path implies higher latency.
if ( IsInGameThread ( ) & & FStringView ( CachePath ) . StartsWith ( TEXTVIEW ( " // " ) , ESearchCase : : CaseSensitive ) )
2021-12-08 17:15:36 -05:00
{
2022-05-04 16:23:17 -04:00
FRequestOwner AsyncOwner ( EPriority : : Normal ) ;
Private : : LaunchTaskInCacheThreadPool ( AsyncOwner , [ this , Path = WriteToString < 256 > ( Path ) ] ( ) mutable
{
( void ) FileExists ( Path ) ;
} ) ;
AsyncOwner . KeepAlive ( ) ;
}
else
{
if ( ! FileExists ( Path ) )
{
return nullptr ;
}
2021-12-08 17:15:36 -05:00
}
return TUniquePtr < FArchive > ( IFileManager : : Get ( ) . CreateFileReader ( * Path , FILEREAD_Silent ) ) ;
2021-11-10 14:44:18 -05:00
}
bool FFileSystemCacheStore : : FileExists ( FStringBuilderBase & Path ) const
{
const FDateTime TimeStamp = IFileManager : : Get ( ) . GetTimeStamp ( * Path ) ;
if ( TimeStamp = = FDateTime : : MinValue ( ) )
{
return false ;
}
if ( bTouch | | ( ! bReadOnly & & ( FDateTime : : UtcNow ( ) - TimeStamp ) . GetTotalDays ( ) > ( DaysToDeleteUnusedFiles / 4 ) ) )
{
IFileManager : : Get ( ) . SetTimeStamp ( * Path , FDateTime : : UtcNow ( ) ) ;
}
return true ;
}
2022-02-14 14:43:39 -05:00
ILegacyCacheStore * CreateFileSystemCacheStore (
2021-11-10 14:44:18 -05:00
const TCHAR * CachePath ,
const TCHAR * Params ,
2022-02-14 14:43:39 -05:00
const TCHAR * AccessLogPath ,
ECacheStoreFlags & OutFlags )
2021-11-10 14:44:18 -05:00
{
2022-06-07 11:17:10 -04:00
TUniquePtr < FFileSystemCacheStore > Store = MakeUnique < FFileSystemCacheStore > ( CachePath , Params , AccessLogPath , OutFlags ) ;
if ( OutFlags = = ECacheStoreFlags : : None )
2022-02-14 14:43:39 -05:00
{
2022-06-07 11:17:10 -04:00
Store . Reset ( ) ;
2022-02-14 14:43:39 -05:00
}
2022-06-07 11:17:10 -04:00
return Store . Release ( ) ;
2015-08-10 08:14:45 -04:00
}
2021-04-28 16:22:18 -04:00
2022-02-14 14:43:39 -05:00
} // UE::DerivedData