Files
UnrealEngineUWP/Engine/Source/Developer/FunctionalTesting/Private/FunctionalTest.cpp
Bob Tellez 33f0f0a6e6 Copying //UE4/Fortnite-Staging to //UE4/Dev-Main (Source: //Fortnite/Main @ 3212531)
#lockdown Nick.Penwarden
#rb none

==========================
MAJOR FEATURES + CHANGES
==========================

Change 3212485 on 2016/11/28 by Dmitry.Rekman

	Update libwebsockets to use -fPIC.

Change 3212280 on 2016/11/28 by Guillaume.Abadie

	Fixes static lighting regression caused by selective outputs fix.

Change 3211095 on 2016/11/28 by Ian.Fox

	#UE4 - Add nullptr check to cookonthefly server ini check

Change 3211042 on 2016/11/28 by Bob.Tellez

	#UE4 Add an option to reset a particle system comp on a camera lens emitter when it is retriggered

Change 3209336 on 2016/11/23 by Rob.Cannaday

	Fix shutdown crash trying to cancel an HTTP request after the HTTP module has been unloaded
	Move the cancel call to the PreUnload step
	#jira FORT-33515

Change 3208350 on 2016/11/22 by Jeff.Campeau

	Added bVirtualKeyboardDisplayOnFocus to Slate settings defaulted to true (old behavior)
	Always open a virtual keyboard when the facebutton bottom is pressed on an active text field
	Do not open a virtual keyboard on focus gained by any method other than mouse if bVirtualKeyboardDisplayOnFocus is set to false

	#jira FORT-30722

Change 3207430 on 2016/11/22 by James.Hopkin

	#fortnite Applied changes from CL#3161737 (UE4/Main) to stage and package SSL certificate bundles.

Change 3207422 on 2016/11/22 by Ben.Woodhouse

	* Fix UpdateTexture3D to create a staging texture of the region to update rather than the whole texture. This prevents distance fields crashing during update (allocating 18GB per frame in some cases)
	* Put UpdateTexture2D DMA support onto a cvar, disabled by default (corruption issues reported by licensees, plus not sure it's actually faster - could be slower due to reduced bandwidth; issues reported by licensees)
	* Fix UpdateTexture2D to only create a staging texture of the region to update, saving memory
	#jira UE-38609

Change 3206301 on 2016/11/21 by Ben.Woodhouse

	Fixed GPU hang in Zone Map view. Was an issue with RenderThread using the device context without appropriate RHIThread flushes.
	#jira FORT-31616
	#code_review keith.judge

Change 3206144 on 2016/11/21 by Lukasz.Furman

	improved path following sticking to tether bounds
	#jira FORT-32097

Change 3206142 on 2016/11/21 by Lukasz.Furman

	added post processing to navigation filters for making filter-bound paths (feedback iteration)
	#fortnite

Change 3206053 on 2016/11/21 by Lukasz.Furman

	added post processing to navigation filters for making filter-bound paths
	#fortnite

Change 3205790 on 2016/11/21 by Lukasz.Furman

	pass on flow field usage by EQS

Change 3205764 on 2016/11/21 by Lukasz.Furman

	seeding AIModule's random stream from world manager, using random stream in EQS
	#fortnite

Change 3205763 on 2016/11/21 by Lukasz.Furman

	added random stream to AIModule
	copy of CL# 3150031

Change 3205162 on 2016/11/19 by James.Hopkin

	Added missiing depending on SSL to Linux HTTP. Fixes CrashReportClient linker errors.

Change 3205124 on 2016/11/19 by James.Hopkin

	Enabled websockets and Stomp for Linux

Change 3205121 on 2016/11/19 by James.Hopkin

	From Nick Shin's Dev-Platform shelf: upgrade/rebuild of libcrypto, libcurl, libssl, libwebsockets and zlib for Linux

	#fyi Nick.Shin,Dmitry.Rekman,Bob.Tellez

Change 3205119 on 2016/11/19 by James.Hopkin

	Added OpenSSL version 1.0.2h headers for x86_64-unknown-linux-gnu

	#fyi Nick.Shin,Dmitry.Rekman,Bob.Tellez

Change 3204994 on 2016/11/18 by Billy.Bramer

	- Sort the function results that show up in the blueprint "Copy signature from:" combo box

Change 3203688 on 2016/11/18 by James.Hopkin

	#stomp Lower-cased FName strings before encoding to prevent random case at runtime.

Change 3201533 on 2016/11/16 by Mark.Satterthwaite

	More auto-release pool/memory-handling fixes for Metal's debug layer, which depends upon ARC:
	- Better handling of parallel context creation & pooling in MetalRHI.
	- Metal queries return the actual value so that we can use local autorelease pools to capture ARC retain/autorelease calls in the debug layer.
	- Similarly EndEncoding needs a local autorelease pool to handle the debug layer's ARC retain/autorelease calls.
	#jira FORT-32706

Change 3201077 on 2016/11/16 by Mark.Satterthwaite

	Trivial command-buffer fencing to avoid render-queries keeping MTLCommandBuffer's alive after they are completed, reducing total memory use.
	#jira FORT-32706

Change 3200269 on 2016/11/16 by John.Abercrombie

	Made GetPredictionData_Client_Character and GetPredictionData_Server_Character public
	- Removed unnecessary code duplication in FortIndicator as a result

Change 3198230 on 2016/11/15 by James.Hopkin

	#stomp Added dedicated server support to Stomp connection manager. Also fixed heartbeats and change retry strategy to retry forever, first retry after 5 seconds, doubling up to max interval of every minute.

Change 3197273 on 2016/11/14 by Mark.Satterthwaite

	Fix Metal related memory leaks.
	#jira FORT-32706

Change 3196974 on 2016/11/14 by Lukasz.Furman

	increased distance to focal point for path following
	copy of CL# 3196971
	#jira FORT-32048

Change 3196885 on 2016/11/14 by John.Pollard

	FORT-33019 - Fix crash when updating unmapped properties on replicator that was dormant

Change 3196772 on 2016/11/14 by John.Pollard

	Speculative fix for assert when shutting down replicators

Change 3196617 on 2016/11/14 by Lukasz.Furman

	improved readability of EQS results in gameplay debugger's table view
	#fortnite

Change 3195394 on 2016/11/11 by John.Pollard

	UE-37866 - Fix replication issue where unmapped properties wouldl fail to map if the replicator goes away due to dormancy

Change 3195272 on 2016/11/11 by Bob.Tellez

	#Fortnite Fix warning output in UDataTable for missing row

Change 3195152 on 2016/11/11 by Lukasz.Furman

	fixed target selection in gameplay debugger's spectator
	#fortnite

Change 3195071 on 2016/11/11 by Lukasz.Furman

	pass on EQS category of gameplay debugger
	#fortnite

Change 3194111 on 2016/11/10 by Bob.Tellez

	#UE4 if you have a checked out or out of date file in your rename list it is now properly skipped and reported after the rename.

Change 3193547 on 2016/11/10 by Bob.Tellez

	#UE4 LODGroup is now AssetRegistrySearchable

Change 3193545 on 2016/11/10 by Bob.Tellez

	#UE4 Allow setting the default LODGroup when importing a mesh

Change 3193541 on 2016/11/10 by Bob.Tellez

	#UE4 LODGroup settings application on load. Enable this behavior by setting r.StaticMesh.UpdateMeshLODGroupSettingsAtLoad=1

Change 3192035 on 2016/11/09 by Saad.Nader

	#engine Updated Migration of properties to handle static arrays properly from previous check-in.

Change 3191062 on 2016/11/08 by Saul.Abreu

	Added accessor for all items in list views.

Change 3190998 on 2016/11/08 by Chris.Gagnon

	Partially fixes a problem with the scale bax ignore inherited scale isn't working properly.

	There is more to fix by adding float InScale or similar to the GetRelativeLayoutScale() call chain.
	This portion will be handled by the tools team.

Change 3190812 on 2016/11/08 by Lukasz.Furman

	fixed crash on path string pulling when path corridor is empty
	#jira FORT-32811

Change 3190800 on 2016/11/08 by Saad.Nader

	#engine Fixed a case where a static array uproperty wasn't being migrated properly since it was being treated as a single value.

Change 3189573 on 2016/11/07 by Bob.Tellez

	#UE4 Since LightComponents now respect hiddeningame, I changed ALight to default to not be hidden in game. All components in the class that should not be seen are already bHiddenInGame=true on the component.

Change 3189268 on 2016/11/07 by Michael.Trepka

	Check is MacApplication is still valid when making a deferred call to OnApplicationActivationChanged

Change 3189179 on 2016/11/07 by Michael.Trepka

	Don't skip Mac windowDidResize: when switching between window modes. Fixes issues with screen not resizing properly when changing from windowed to windowed fullscreen

Change 3189154 on 2016/11/07 by Lukasz.Furman

	added unbound exploration mode to A* solver
	#ue4

Change 3189072 on 2016/11/07 by Saad.Nader

	#commonui Added ability to skip the stack of activatable panels so that global input handling can handle input for dynamically created buttons on a modal. Updated name of base button style as its name was conflicting with legacy ui base button style.

Change 3188769 on 2016/11/07 by Guillaume.Abadie

	Fixes r.SelectiveBasePassOutput and use it in Fortnite.

	This CL adds a selective base pass optimization not drawing scene color when  r.SelectiveBasePassOutput=1 on materials that  doesn't emit color.
	Use r.SelectiveBasePassOutput in Fortnite and avoid computing the fog in base pass to actually avoid drawing scene color.

	#review-3187180 @brian.karis

Change 3187864 on 2016/11/04 by Bob.Tellez

	#UE4 Better handling for setting return values in error cases where a function cannot be executed.

Change 3187815 on 2016/11/04 by Bob.Tellez

	#UE4 Fix for SetLODGroup to trim LODs that are not needed.

Change 3187309 on 2016/11/04 by Lukasz.Furman

	added projection and pathfinding to navigation graph
	#fortnite

Change 3186304 on 2016/11/03 by Saul.Abreu

	Made a pass on Common UI widgets, setting their widget palette category property or overriding the relevant virtual method in order to have a consistent value across all Common UI widgets.

Change 3186301 on 2016/11/03 by Saul.Abreu

	Exposed ability to compare Slate brushes in Blueprints. Helpful for Icon Text Button to be able to hide its icon image if the icon brush is identical to the default (which is intentionally 0-sized/draw-type none).

Change 3185979 on 2016/11/03 by David.Hamm

	Conditional gameplay effects with required tags were considering target tags in code, rather than source tags as presented in the editor.  Updating the code allows the Bearricade tag to be seen, triggering the desired slow effect.

	#jira FORT-32141

Change 3185534 on 2016/11/03 by Daniel.Broder

	Made GameplayDebuggerCategory_EQS log the description of filtered items rather than just their index (which doesn't tell much).

	#UE4 #NoReleaseNotes

Change 3185386 on 2016/11/03 by Daniel.Broder

	"Actors of Class" EQS Generator now supports returning all actors matching the class rather than only actors within the radius based on a new checkbox "Generate Only Actors In Radius".  For backwards compatibility, it defaults to true.

	#UE4 #ReleaseNoteAbove

Change 3185370 on 2016/11/03 by Mark.Satterthwaite

	Revert the only change to Metal texture uploads made in the merge leading up to 16/09/16 and disable more recent changes to reuse texture objects in the hope that this cures FORT-30180. If not then this will need to be handled by Apple/Nvidia as we're not doing anything obviously wrong on our side.
	#jira FORT-30180

Change 3185249 on 2016/11/03 by Lukasz.Furman

	added caching for neighbor count in template A* solver
	#fortnite

Change 3184403 on 2016/11/02 by Daniel.Broder

	Updated EnvQueryTest_GameplayTags to support Gameplay Tag Queries.

	^^ReleaseNoteAbove

	Data is automatically converted to the query from the old data format.

	Gameplay Tag Queries give much more flexibility for how to mach the queries, since they can include entire expressions of what must match and/or not match.

	#UE4 #ReleaseNoteAbove

Change 3184311 on 2016/11/02 by Daniel.Broder

	Removed unnecessary if/else that was calling identical code in both parts!  (Now it just calls the code directly).

	Fixed spelling of ReturnValueAddress (from ReturnValueAdress).

	#UE #NoReleaseNotes

Change 3183823 on 2016/11/02 by Mark.Satterthwaite

	Record Metal resource & state objects used in a command-buffer when rhi.Metal.RuntimeDebugLevel is set to 3 or higher. The object labels, types & descriptions will be printed on failure - if the object is deleted prior to this then we have a lifetime error and it will crash at this point and can be debugged further using our -metalretainrefs command-line option or Xcode's zombie-objects.

	Used to verify that FORT-31649 is not a simple resource lifetime error and thereby speed up Apple/vendor investigations.
	#jira FORT-31649

Change 3183807 on 2016/11/02 by Mark.Satterthwaite

	Change the way we access the Metal viewport's backbuffer, to reduce possible causes of FORT-31649:
	- Added console variable "rhi.Metal.SupportsIntermediateBackBuffer" to control whether to use an extra render-target so we can support screenshots & movie capture, or render directly to the back-buffer to save memory & GPU performance. Still defaults to ON for Mac & OFF for iOS/tvOS.
	- Change the way we handle updates to the back-buffer size to ensure that the different threads access their intended version.
	#jira FORT-31649

Change 3183470 on 2016/11/02 by Bob.Tellez

	#UE4 Lights with 0 intensity are now removed from the scene

Change 3183230 on 2016/11/02 by Bob.Tellez

	#UE4 Console history no longer keeps duplicate entries

Change 3182547 on 2016/11/01 by Bob.Tellez

	#UE4 Fixed an old bug which was causing thumbnail scenes to have incorrect lighting.

Change 3182498 on 2016/11/01 by Chris.Gagnon

	Added ItemIcon widget and ItemCountTextBlock widget.
	EpicCMSScreen derives from COmmonActivatable Panel.
	Added CommonUIUtils with function to get a owning userwidget or contexts.

	Begining of the new Topbar, and a number of supporting widgets.

Change 3182497 on 2016/11/01 by Chris.Gagnon

	Engine:
	GameViewportClient now has a global toggle to turn software cursor mapping on and off.

	Fortnite:
	Added software cursor, when using the gamepad we turn on the software cursor mapping. The asset is invisible.
	This allows us to hide the cursor without all the baggage and undesired behavior that comes with that.

Change 3181853 on 2016/11/01 by Saad.Nader

	#commonui
	Added uproperty annotations to prevent garbage collection.
	Updated code to cleanup internal caches to happen earlier.

Change 3181782 on 2016/11/01 by Bob.Tellez

	#UE4 LightComponents now respect bHiddenInGame (and other visibility flags) when determining whether they should be added to the scene.

Change 3181516 on 2016/11/01 by Saad.Nader

	#commonui

	Added an action handler interface that I have been wanting for awhile.
	Updated action widget to ignore design time changes since it relies on a common ui context instance.
	Cleaned up activatable panel interface and commited events on a input action registered to be handled. Our activatable handle automatically handles things for now without asking blueprint if we should.
	Cleanedup up miscellaneous activatable panel internals
	Activatable panels can now choose to expose input actions registered to that panel.
	Replaced activatable panel reflector with common input reflector
	Added a common global input handler that implements the action handler interface
	Updated common button and common tablist widgets appropriately to register with global input handler for appropriate actions.
	Buttons now have separate triggering actions vs. triggered actions. Triggering actions can only be set during creation of the button whereas triggered actions can be set anytime.
	Moved a lot of the boilerplate code for action button into common button to trigger and listen for actions, or register with the global input handler for triggering actions.
	Fixed typos in common ui types.
	Updated CommonUITestBed with new changes.

Change 3179753 on 2016/10/31 by Lukasz.Furman

	replaced ensure with vlog warning in GameplayTask processing
	#jira FORT-32324

Change 3178028 on 2016/10/28 by Lukasz.Furman

	attempt to fix rare crash in crowd simulation
	#jira FORT-27847

Change 3177966 on 2016/10/28 by James.Hopkin

	Removed some redundant text/string copies and conversions in 'Find in Blueprints'

Change 3176795 on 2016/10/27 by Fred.Kimberley

	Fixed the code path that grabs tooltip data for ability system components to respect the flag that shows buffs in the front end instead of final values.

	#jira FORT-30491

Change 3175818 on 2016/10/26 by Bob.Tellez

	#UE4 Protecting against a nullptr access in FVisibilityPropertySection::GenerateSectionLayout. More investigation is needed to determine if this should be allowed to be null.

Change 3175615 on 2016/10/26 by Michael.Trepka

	Check if MacApplication is valid in FMacApplication::OnCursorLock() block that's called asynchronously and can be executed after MacApplication was destroyed. Fixes FORT-32075

Change 3175369 on 2016/10/26 by Saul.Abreu

	Refactored CreateWidget functions to share UserWidgetClass validation logic and fixed a missing early-out return statement.

Change 3175233 on 2016/10/26 by Saul.Abreu

	#fortnite
	Common Button now properly handles its interactibility changing when it's toggleability has changed - previously, being selected when toggling is turned on would leave the button non-interactible and thus not practically toggleable.

Change 3174285 on 2016/10/25 by Mark.Satterthwaite

	Fix command-buffer failures when resizing windows on Mac - we have to capture windowWillResize: events in our window delegate and then forward on a call to Slate's OnResizingWindow event handler, that internally causes rendering to flush. If we wait to do this in windowDidResize then the actual device back-buffer resource will have been reallocated and we presumably end up trying to render into garbage memory on the GPU, causing the intermittent command-buffer failures.
	#jira FORT-31649

Change 3173872 on 2016/10/25 by Bob.Tellez

	#UE4 Fixed an issue where if you have a map with actors  that produce a ZeroVector bounds size, SetActorTransform complains.

Change 3172828 on 2016/10/24 by Saul.Abreu

	Added useful contextual information to the message log errors provided when attempting to create widgets but failing.

Change 3172649 on 2016/10/24 by Michael.Trepka

	Call setMinSize and setMaxSize in FMacApplication::OnCursorLock() on the main thread

	#jira FORT-30177

Change 3172568 on 2016/10/24 by Saad.Nader

	#commonui Exposed a flag to reflector to not show actions for an activtable panel if we don't want them exposed.

Change 3172341 on 2016/10/24 by Mark.Satterthwaite

	Fix FORT-31526 by setting appropriate defaults for FEditorCompositingParameters  when the feature isn't being used, as Metal still requires something be bound for the values. This all stems from Fortnite using GizmoMaterial somehow when whacking Llamas to reveal the cards contained within - I suspect the 'real' fix is not to use an Editor material in the game client...
	#jira FORT-31526

Change 3172304 on 2016/10/24 by James.Longstreet

	#fortnite #jira FORT-31090 Add setting to configure whether the virtual keyboard sends TextChanged or TextCommitted when complete.

	Add SlateSettings to project settings, for settings that need to be accessed from Slate -- the Slate module doesn't depend on Engine, so it can't access UserInterfaceSettings or InputSettings.

	Default to TextChanged in Fortnite.

Change 3171630 on 2016/10/24 by Saul.Abreu

	#fortnite
	Added API export to Common List View.
	Added support to Common List View for changing selection modes.
	Added delegate to Common List View to support hook-ups on creation of new list item widgets.
	Improved Common List View handling of item widgets that are buttons - no need to handle manually hooking up the list item clicked callback to the button.

Change 3171474 on 2016/10/22 by Saul.Abreu

	#fortnite
	New numeric text block.

Change 3171463 on 2016/10/22 by Saad.Nader

	#commonui
	Added the common action widget which can visualize the input of an activatable panel or button.
	Added the common activatable panel reflector widget so we can build a bar widget which can visualize the actions an activatable panel have registered to handle.
	Cleaned up the input manager's handling of pushing and poping activatable panels
	Updated widget switcher to completely push or pop tabs on or off the stack so that the stack is clean of any items not in the current tab.
	Updated common ui context to expose API blueprint.
	Updated input action data to make better sense in common ui types
	Added a viewport client to redirect input for the common ui test bed.
	Added a completion delegate for listeners such as a button in a activatable panel reflector widget.
	Added test harness for activatable panel, activatable panel reflector, action widget

Change 3170868 on 2016/10/21 by Jeff.Campeau

	AutoSDK props included earlier

Change 3170663 on 2016/10/21 by Mark.Satterthwaite

	Further changes to finally fix the underlying cause of FORT-25473 and all future potential instances: SetStreamSource overrides the stride from the vertex declaration and MetalRHI wasn't properly considering what to do with Stride=0, which should disabling vertex attribute stepping. This also requires fixing some gotcha's in the StateCache.
	#jira FORT-25473

Change 3170020 on 2016/10/20 by Bob.Tellez

	#UE4 Render scale was off by one when setting via buckets in the editor widget.

Change 3169764 on 2016/10/20 by Mark.Satterthwaite

	Fixed automatic conversion of G8_sRGB into RGBA8_sRGB required for Mac Metal, which fixes FORT-27627.
	#jira FORT-27627

Change 3169631 on 2016/10/20 by Mark.Satterthwaite

	Fix a potential crash due to unnecessary reinitialisation of the MetalRenderPipelineDesc mutex.

Change 3169614 on 2016/10/20 by Mark.Satterthwaite

	Fix FORT-25473 caused by incorrect handling of vertex attributes in Metal: FParticleSpriteVertexFactory specifies the dynamic particle parameter attribute (VA 5) with a non-zero stride, which implies vertex or instance stepping - but for the P_Rocket_ColdMist_FXV effect only a single float4 is provided with the intent that this be constant for all instances. Other APIs may implicitly wrap the VA read back around but Metal does not and simply reads garbage off the end of the buffer - potentially this could even cause a GPU crash. MetalRHI now detects when the buffer bound to an attribute can't support more than one instance and if needed updates the vertex declaration to make such attributes constant.
	#jira FORT-25473

Change 3169163 on 2016/10/20 by Fred.Kimberley

	Added UIProxyActor. This is intended as a single proxy actor to replace the existing, class specific, proxy actors.

Change 3168732 on 2016/10/20 by Saul.Abreu

	Exposed style references in UCommonTextBlock. Allows widgets to look at the styles on the CDO.

Change 3168713 on 2016/10/20 by Saul.Abreu

	Fixed unconditional inclusion of Developer module headers (settings module) in client builds from Common UI module.

Change 3168659 on 2016/10/20 by Saul.Abreu

	Created and exposed SetMinDesiredWidth on UTextBlock, following the example set by other setters in the class.

Change 3168658 on 2016/10/20 by Saul.Abreu

	The Common UI plugin now has a settings object which will appear in the project settings window. It exposes setting default styles for both CommonTextBlock and CommonButton in the Game config file.

Change 3167632 on 2016/10/19 by John.Pollard

	Fix FN replay scrubbing issues

	* Solution for net startup actors that need to be "rolled back" during scrubbing if they've been modified
	* Solution for when net startup actors should be deleted past checkpoints
	* Added version support to load older replays that don't save out deleted net startup actors in checkpoints

Change 3166065 on 2016/10/18 by Saad.Nader

	#commonui renaming UCommonActivatableManager to UCommonInputManager, added ability to change input method for desktop and console.

Change 3166049 on 2016/10/18 by Lukasz.Furman

	added navmesh exploration helpers in FortNavMesh
	#fortnite

Change 3165085 on 2016/10/17 by Saad.Nader

	#blueprintcontext fixed log output for created blueprint context

Change 3163115 on 2016/10/14 by James.Hopkin

	Prevented variable combo box clipping long type names in blueprint details panel

	[UE-19710]

Change 3162629 on 2016/10/13 by Saul.Abreu

	#fortnite
	#jira FORT-31489
	Ported Paragon's tile view widget over to the Common UI Plugin as Common Tile View. Added exemplar/test case in Common UI testbed.

Change 3162624 on 2016/10/13 by Saul.Abreu

	Improved "Create Event" node with text showing the function signature in a friendly manner.

Change 3162114 on 2016/10/13 by Guillaume.Abadie

	Implements r.EarlyZPassOnlyMaterialMasking.

	Fortnite grass/trees is using masked material. However masked materials are doing clip in early z pass and base pass, both preventing the pixel shader from using the early depth test. This CL execute material's mask opacity only in the early z pass to keep early depth test on expensive mask material's base pass pixel shader.

Change 3161479 on 2016/10/13 by Saad.Nader

	#commonui Updated Common button to be able to handle a bound common input action by causing the button to get clicked.
	Updated Activatable panel to ignore input if it is not activated
	Added helper functions to common widget switcher for activating/deactivating the active widget if it is a activatable panel.

Change 3161092 on 2016/10/13 by Saul.Abreu

	#fortnite
	Common Tab List widget now exposes access to its linked switcher as well as overridable events before and after the linked switcher is set. OnCreateNewTab can now be implemented in native code or blueprints. Buttons added as tabs in the tab list will now have their selectabilty and toggleability set as necessary.

Change 3160762 on 2016/10/12 by Billy.Bramer

	- Make UAbilitySystemComponent::AreAbilityTagsBlocked virtual so games can provide a custom implementation

Change 3160736 on 2016/10/12 by Lukasz.Furman

	fixed some gameplay debugger's categories not rendering correctly in simulate mode
	#fortnite

Change 3160417 on 2016/10/12 by Mark.Satterthwaite

	Disable DistanceField AO & Shadowing support on Intel GPUs under Metal - there are driver bugs that prevent them from working currently.
	#jira FORT-31268

Change 3160314 on 2016/10/12 by Michael.Trepka

	Fixed incorrect rect initialization in Mac GetDisplayMetrics

Change 3160309 on 2016/10/12 by Lukasz.Furman

	pass on gameplay debugger in Simulate in Editor mode
	copy of CL 3160014
	#ue4

Change 3159892 on 2016/10/12 by John.Abercrombie

	Fixed the Blackboard component pausing but never being unpaused if we ended up restarting the Behavior Tree instead of continuing

	#ue4

Change 3159630 on 2016/10/12 by Jamie.Dale

	Fixed an issue where async and non-async loading could result in the package being given a different name

	Async loading would always use the non-localized name (which is correct), but non-async loading would sometimes use the localized name (which is incorrect); now they both do the same thing.

Change 3159249 on 2016/10/11 by Jonathan.Lindquist

	fixing a potential uv bug related to their names

Change 3159145 on 2016/10/11 by Lukasz.Furman

	fixed behavior tree task restart conditions
	#ue4

Change 3158846 on 2016/10/11 by John.Pollard

	Add ability to override network async loading for replays

Change 3158551 on 2016/10/11 by Saad.Nader

	#commonui remove checks for common tab list widget when set listening for input occurs.

Change 3157727 on 2016/10/10 by Saul.Abreu

	#fortnite
	Common button style now has minimum width and minimum height properties and common button will use the maximum of its own and the style's minimums.

Change 3157364 on 2016/10/10 by Jamie.Dale

	Split localized package redirection out of FCoreDelegates::PackageNameResolvers

	They're different enough in behavior that the delegate resolution was breaking the localized package resolution by resolving in too many places and causing the localized package to be loaded with its real localized name as well as the fake non-localized name.

	#jira FORT-31207

Change 3156616 on 2016/10/10 by Lukasz.Furman

	added more failsafes to crowd simulation crash
	#jira FORT-27847

Change 3155092 on 2016/10/07 by Chris.Gagnon

	SlateApplication
	- Added more control over where navigation originates from with the ENavigationSource enumeration piped in through the FReply
	- Added custom handling support for the navigation responce using the FCustomNavigationHandler

	Fortnite
	- Added Input Preprocessor for generating navigation events and handling the "virtual cursor" position
	- Added the Input mode switching support for gamepad <-> keyboard (Currently disabled)

Change 3154721 on 2016/10/07 by Lukasz.Furman

	automation fix for AI tests with multiple spawn sets
	copy of CL# 3154035
	#jira FORT-31106

Change 3154466 on 2016/10/07 by Saul.Abreu

	#fortnite
	Additional logging and checking to help diagnose cause of current build breakage, possibly related to blueprint context OR unrelated but coincidental and related to game data or homebase manager.

Change 3154349 on 2016/10/06 by Saul.Abreu

	#fortnite
	Relocate BP context and common UI plugins to Engine (NotForLicensees).

Change 3152396 on 2016/10/05 by Lukasz.Furman

	fixed RECAST_ASYNC_REBUILDING define being ignored by navmesh generator
	#ue4

Change 3152390 on 2016/10/05 by Lukasz.Furman

	including AgentRadius in area modifier bounds in layer's intersection test
	fixes modifier cuts at tile boundary
	#jira FORT-31051

Change 3151999 on 2016/10/05 by Lukasz.Furman

	added vlogs for applying and removing gameplay effects
	#jira FORT-30982

Change 3150947 on 2016/10/04 by Bob.Tellez

	#UE4 Fix to find the title.json file in the correct game folder.

Change 3149775 on 2016/10/03 by Bob.Tellez

	#UE4 Added property editor code support for doubles.

Change 3148729 on 2016/10/03 by Lukasz.Furman

	fixed memory corruption in DemoNetDriver
	#fortnite

Change 3146148 on 2016/09/29 by Bob.Tellez

	#UE4 Fixed a case where the LastRecordedHittestIndex would remain zero, causing the widget path to get truncated and result in the mainframe window when determining if you should spawn a tooltip, causing us to try to create a tooltip outside of our tooltip presenter widget, causing a new window to be created and a crash to happen on consoles.

	#JIRA FORT-30378

Change 3146016 on 2016/09/29 by Daniel.Broder

	Added BlueprintGameplayTagLibrary function "Get All Actors of Class Matching Query".

	It uses TActorIterator to find only all actors derived from the specified class and then further winnows them by whether they match a GameplayTagQuery.  If any actor does NOT implement IGameplayTagAssetInterface, the function will log ONCE a warning that the class in question doesn't implement the required interface to be able to check for matching tags.  (NOTE: This function can be extremely expensive if there are a large number of actors of the class requested, so be cautious using it.

	It can be used at initialization time to find a specific subset of actors to act on (for example).

	#UE4 #ReleaseNote

Change 3145827 on 2016/09/29 by Lukasz.Furman

	added sanity checks to EQS tick
	#jira FORT-30755

Change 3145520 on 2016/09/29 by Chad.Garyet

	changing notifications to require there be a type to verify the user exists
	#jira FORT-30754

Change 3145428 on 2016/09/29 by Bob.Tellez

	#UE4 Made plugin loaded log statements verbose.

Change 3145229 on 2016/09/29 by Bob.Tellez

	#UE4 Fix for only running the first test on commandline

Change 3142730 on 2016/09/27 by Bob.Tellez

	#UE4 Removing needless scope on a virtual function call that made it seem static and made UpdateResolutionQuality protected so it can be called from subclasses that may be procedurally determining DesiredScreenWidth and DesiredScreenHeight

Change 3142632 on 2016/09/27 by Saul.Abreu

	#fortnite
	Improved data table row struct post-data-import method with more context provided through parameters. Used to fixup homebase node display names to have stable keys generated from the row name.

Change 3140907 on 2016/09/26 by Bob.Tellez

	#UE4 Allowing movie files to be renamed to match platform requirements

Change 3140399 on 2016/09/26 by Lukasz.Furman

	fixed uninitialized configs of gameplay debugger
	#jira FORT-30439

Change 3138880 on 2016/09/23 by Fred.Kimberley

	Added source tag requirements to conditional gameplay effects.

	#jira FORT-29772

Change 3138262 on 2016/09/23 by Chad.Garyet

	Integrating codesign fix into Fortnite/Main

Change 3137164 on 2016/09/22 by Mark.Satterthwaite

	Add stats to track exactly how many command buffers are allocated and committed each frame to work out why Fortnite on AMD is hanging, which turns out to be because each texture update/reallocation ends up in its own command-buffer. This needs to be rethought to pack these into fewer command buffers with the same synchronisation requirements to minimise command-buffer splits but for now we'll just make the default sufficiently large that we shouldn't see the hang until the work is done. Also ensure that command-buffer failure is always fatal - there is no way to recover or continue if a command-buffer fails.
	#jira FORT-30377

Change 3136720 on 2016/09/22 by Rob.Cannaday

	Fix crash in FCurlHttpRequest::DebugCallback
	+ Specify the string length to FString's constructor as the result from StringCast is not null terminated if the string's length is specified (instead of assuming null termination).
	#jira OGS-428

Change 3136391 on 2016/09/22 by Lukasz.Furman

	fixed crowd path section switch rejecting navlinks at end of path
	#jira FORT-30400, FORT-30402

Change 3136295 on 2016/09/22 by Lukasz.Furman

	fixed navlinks not connecting to navmesh correctly in "snap to cheapest area" mode,
	adjusted scoring in navmesh projection - findNearestPoly2D
	#jira FORT-30358

Change 3136033 on 2016/09/22 by Mark.Satterthwaite

	To fix the Fortnite login screen force Nvidia Macs to use the set*Bytes API for small buffer updates even on El Capitan. We can't do this globally as Intel didn't implement these functions until macOS Sierra.
	Fix GPU selection code in MetalRHI to confirm everything is working.
	#jira FORT-30385

Change 3135237 on 2016/09/21 by Mark.Satterthwaite

	Metal validation layer fix: under Metal if there are no reads from the vertex stage-in buffers we should use the Empty vertex declaration, not the filter declaration, otherwise we have to bind a redundant vertex stream buffer to silence the validation layer.

Change 3135177 on 2016/09/21 by Rob.Cannaday

	Demote "Missing party state during exit" log from warning to display, as order of operations cause this to always be triggered when voluntarily leaving a party
	#jira FORT-22575

Change 3135176 on 2016/09/21 by Rob.Cannaday

	When returning to front-end, re-evaluate pending party joins that were in the waiting for beacon reservation state.
	#jira FORT-27737

Change 3135174 on 2016/09/21 by Mark.Satterthwaite

	- Copy MetalRHI & MetalShaderFormat from Dev-Rendering CL #3132772
	Provides significant performance improvements on CPU due to improved vertex declaration handling & much reduced GPU heap fragmentation + more stats.
	Definitely fixes:
	#jira FORT-29430

Change 3135169 on 2016/09/21 by Mark.Satterthwaite

	Correct Metal texture creation for AVF media framework - we can't provide a render-targetable version of the texture without blitting. The native texture we get is a GPU copy that can be made CPU accessible (i.e. it is not tiled).

Change 3135157 on 2016/09/21 by Mark.Satterthwaite

	Fix one cause of Metal crashes loading into a zone - the PlanarReflection shader code needs to always set the IsStereoParameter so that the shader can perform the if-test without causing an invalid GPU access.
	#jira FORT-30061

Change 3135136 on 2016/09/21 by Bob.Tellez

	#UE4 Added GetPackageDependenciesForManifestGenerator delegate for games to be able to determine package dependencies however they deem fit.

Change 3135132 on 2016/09/21 by Bob.Tellez

	#UE4 Better final cook platform path creation. WindowsClient was incorrectly forming a path to WindowsNoEditor when looking for chunk manifests

Change 3134313 on 2016/09/21 by Lukasz.Furman

	attempt to fix crash in navmesh generation
	#jira FORT-30340

Change 3134091 on 2016/09/21 by Rob.Cannaday

	Fix crash in lib curl debug callback because the string parameter provided by libcurl is not null terminated
	#jira OGS-428

Change 3133949 on 2016/09/21 by Lukasz.Furman

	crowd agents will use shorter path corridor when one of two last polys are navlink
	(corridor part switch happens with 2 or less polys left, we don't want to switch while on navlink)
	#jira FORT-29880

Change 3133219 on 2016/09/20 by Lukasz.Furman

	fixed broken navlink's "snap to cheapest area" mode
	#fortnite

Change 3133087 on 2016/09/20 by Saul.Abreu

	Updated comments on FARFilter to more explicitly express interactions between class filtering settings.

Change 3132990 on 2016/09/20 by Saul.Abreu

	#fortnite
	Overhaul of asset crawling localizable text gathering commandlet. Added feature for filtering processed assets based on membership in a collection.

Change 3132627 on 2016/09/20 by Bob.Tellez

	#Fortnite Added XLoc language ID for for zh-CN

Change 3132616 on 2016/09/20 by Lukasz.Furman

	added tolerance to navmesh project point 2D query
	added overrides for accessing projection with tolerance during navwalking height checks, should be replaced with navdata flags later on
	#jira FORT-29474

Change 3130819 on 2016/09/19 by Ben.Marsh

	UBT: Read additional configuration settings for BuildConfiguration and UEBuildConfiguration from the engine config settings. Allows setting project-specific config values.

Change 3130639 on 2016/09/19 by Lukasz.Furman

	pass on crowd simulation
	- husks should move faster through funnels now
	- husks can clip each other a bit more often :(

	#fortnite

Change 3130625 on 2016/09/19 by Bob.Tellez

	#UE4 Added an ensure to further track down invalid usage of playerinput

	#JIRA FORT-30183

Change 3128884 on 2016/09/16 by Ben.Salem

	Repair nightly FTest runs. FTests are now namespaced differently as of new main merge, and had to uncomment a load-bearing wait that exists to enable the way we run our nightlies (nullrhi w/ execcmds) to start the test properly.

Change 3128874 on 2016/09/16 by Daniel.Lamb

	Testing to see if memory changes have injured fortnite cook times.

Change 3127175 on 2016/09/15 by John.Abercrombie

	GameplayCueInterface's TagToFunctionMap is now keyed by FObjectKey of a UClass, rather than using the UClass as the key
	- Since UClass-es can be unloaded at run-time, and then loaded again later in a different spot in memory, this is a better solution

	Clear out the TagToFunctionMap whenever we cleanup a world

	Move the TagToFunctionMap into a namespace

	#jira FORT-29194 - Crash during Fight the Storm Defense

Change 3126840 on 2016/09/15 by Bob.Tellez

	#UE4 Added a hack to aid in the conversion from the "USA" and "Poland" region names to "NA" and "EU"

Change 3125944 on 2016/09/14 by Billy.Bramer

	- Fix for FJsonObjectWrapper incorrectly exporting to JSON in a string representation instead of an object representation now that it has an implementation of export text

Change 3125764 on 2016/09/14 by Saul.Abreu

	Change to enum and struct registration so that their packages are all created before either set gets to run their registration logic.

Change 3125719 on 2016/09/14 by Bob.Tellez

	#UE4 Windows in nullrhi do not have OS handles and not initializing you parent window causes a crash when you start PIE (needed for headless automation testing)

Change 3125504 on 2016/09/14 by jonathan.lindquist

	adding a comment to the exclude wpo offsets input

Change 3124203 on 2016/09/13 by Bob.Tellez

	Temporarily removing IOS.Automation.csproj dependency on MobileDeviceInterface since it is causing warnings in UGS right now.

Change 3124192 on 2016/09/13 by Tim.Tillotson

	Fix bad format string in FLinkerLoad::VerifyImport

	Warning:
	[2016.09.13-18.49.05:928][927]LogText:Warning: Failed to parse argument "ImportClass" as a number (using "0" as a fallback). Please check your format string for
	 errors: ": Failed import for {ImportClass}".

Change 3124083 on 2016/09/13 by Bob.Tellez

	#UE4 Re-disabling EQFilter for all machines. This is a temporary solution until a more efficient method is found that does not cause machines to lag.

Change 3123783 on 2016/09/13 by Jonathan.Lindquist

	Subtacting 1 from the VAT tools output texture file name uv number to match unreals 0-based system.

Change 3122223 on 2016/09/12 by Jonathan.Lindquist

	Adding optional uv controls for the texture based animations

Change 3122220 on 2016/09/12 by jonathan.lindquist

	adding an optional uv input for the Vertex animation toolset

Change 3122070 on 2016/09/12 by John.Abercrombie

	Added nav links to corner walls, rather than depending on a nav area to traverse the low edge of the corner

	Made crowd folowing component use the velocity while traversing a link, except if we're falling

	AIs will not update their paths while following a nav link

	Lowered the step height of all AIs from 90 to 72

	#jira FORT-29786 - Husks can move over the balcony wall on floor structures.

Change 3121098 on 2016/09/12 by Chris.Wood

	Increased Linux timeout when waiting for CRC to complete.
	[UE-30259] - Some server crashes are missing from crashreporter database

	#jira UE-30259

Change 3120694 on 2016/09/12 by Saul.Abreu

	#fortnite
	Refactored CMS reader to support URLs with protocols (http, https, and file). URIs (URL sans protocol) will no longer work, but we can add in smart fallback logic later, as this is only in Fortnite currently and the only CMS data available currently is via local file. Console command will handle URLs using double quotes, since the colon trips up existing console command parsing logic (it seems).

Change 3120686 on 2016/09/11 by Saul.Abreu

	#fortnite
	Deleting erroneous config files in EpicCMS plugin.

Change 3120659 on 2016/09/11 by Saul.Abreu

	Added support to widget carousel for getting a callback when the active widget changes. (Not sure who the original author was, but the oldest tracked revision codereview'd Justin Sargent.)

Change 3120658 on 2016/09/11 by Saul.Abreu

	Fixed UMG grid panel to properly set the padding on the slots it creates.

Change 3118466 on 2016/09/08 by Bob.Tellez

	#UE4 There is now an option to exclude all UMG widgets and slots from dedicated server builds. Set bLoadWidgetsOnDedicatedServer=false for this behavior

Change 3118149 on 2016/09/08 by Bob.Tellez

	#UE4 Dont cook non-native CDO references that are excluded for your target

Change 3117604 on 2016/09/08 by John.Abercrombie

	FortGameModeFTesting no longer spawns a pawn

	Added automated test setting to FortGameMode so we can avoid waiting for a pawn before removing the loading screen

	Made the FunctionalTest set the view target to the Observation Point if we don't have a pawn, note that this only works on Player Controllers that aren't Debug Camera Controllers so we don't annoy any user who's moving around

Change 3116964 on 2016/09/07 by Bob.Tellez

	#Fortnite We are now building crashreportclient for linux instead of using the stale binary in P4

Change 3116284 on 2016/09/07 by Tim.Tillotson

	#fortnite Add support for quest objectives that track player ability activation.

	As part of this also:
	+Added a bWasCancelled parameter to GameplayAbility::EndAbility. This allows us to determine if an ability was ended prematurely.
	+Added a OnAbilitySucceeded delegate for determining when an ability was successfully ended.

	Some additional improvements thanks to code review feedback from Fred.Kimberley.

	After discussing with Matt Hancy we decided to keep the OnAbilityCompleted delegate for now. We may be able to deprecate and remove it in the future if we rewrite all the existing abilities that use it.

Change 3116039 on 2016/09/07 by John.Abercrombie

	Fix crash when you change the blueprint of a class referenced by a gameplay cue between PIE runs

[CL 3215544 by Bob Tellez in Main branch]
2016-11-30 14:12:57 -05:00

1177 lines
34 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "FunctionalTest.h"
#include "Misc/Paths.h"
#include "Engine/GameViewportClient.h"
#include "Engine/LatentActionManager.h"
#include "Components/BillboardComponent.h"
#include "HAL/FileManager.h"
#include "Misc/FileHelper.h"
#include "UObject/ConstructorHelpers.h"
#include "ProfilingDebugging/ProfilingHelpers.h"
#include "Misc/AutomationTest.h"
#include "GameFramework/PlayerController.h"
#include "Components/TextRenderComponent.h"
#include "Engine/Selection.h"
#include "FuncTestManager.h"
#include "FuncTestRenderingComponent.h"
#include "ObjectEditorUtils.h"
#include "VisualLogger/VisualLogger.h"
#include "EngineGlobals.h"
#include "Engine/Engine.h"
#include "Engine/Texture2D.h"
#include "DelayForFramesLatentAction.h"
#include "Engine/DebugCameraController.h"
namespace
{
template <typename T>
bool PerformComparison(const T& lhs, const T& rhs, EComparisonMethod comparison)
{
switch (comparison)
{
case EComparisonMethod::Equal_To:
return lhs == rhs;
case EComparisonMethod::Not_Equal_To:
return lhs != rhs;
case EComparisonMethod::Greater_Than_Or_Equal_To:
return lhs >= rhs;
case EComparisonMethod::Less_Than_Or_Equal_To:
return lhs <= rhs;
case EComparisonMethod::Greater_Than:
return lhs > rhs;
case EComparisonMethod::Less_Than:
return lhs < rhs;
}
UE_LOG(LogFunctionalTest, Error, TEXT("Invalid comparison method"));
return false;
}
FString GetComparisonAsString(EComparisonMethod comparison)
{
UEnum* Enum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EComparisonMethod"), true);
return Enum->GetEnumName((uint8)comparison).ToLower().Replace(TEXT("_"), TEXT(" "));
}
FString TransformToString(const FTransform &transform)
{
const FRotator R(transform.Rotator());
FVector T(transform.GetTranslation());
FVector S(transform.GetScale3D());
return FString::Printf(TEXT("Translation: %f, %f, %f | Rotation: %f, %f, %f | Scale: %f, %f, %f"), T.X, T.Y, T.Z, R.Pitch, R.Yaw, R.Roll, S.X, S.Y, S.Z);
}
void DelayForFramesCommon(UObject* WorldContextObject, FLatentActionInfo LatentInfo, int32 NumFrames)
{
if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject))
{
FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
if (LatentActionManager.FindExistingAction<FDelayForFramesLatentAction>(LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr)
{
LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FDelayForFramesLatentAction(LatentInfo, NumFrames));
}
}
}
}
AFunctionalTest::AFunctionalTest( const FObjectInitializer& ObjectInitializer )
: Super(ObjectInitializer)
, bIsEnabled(true)
, bWarningsAsErrors(false)
, Result(EFunctionalTestResult::Invalid)
, PreparationTimeLimit(15.0f)
, TimeLimit(60.0f)
, TimesUpMessage( NSLOCTEXT("FunctionalTest", "DefaultTimesUpMessage", "Time's up!") )
, TimesUpResult(EFunctionalTestResult::Failed)
, bIsRunning(false)
, TotalTime(0.f)
, RunFrame(0)
, StartFrame(0)
, bIsReady(false)
{
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bStartWithTickEnabled = false;
bCanBeDamaged = false;
SpriteComponent = CreateDefaultSubobject<UBillboardComponent>(TEXT("Sprite"));
if (SpriteComponent)
{
SpriteComponent->bHiddenInGame = true;
#if WITH_EDITORONLY_DATA
if (!IsRunningCommandlet())
{
struct FConstructorStatics
{
ConstructorHelpers::FObjectFinderOptional<UTexture2D> Texture;
FName ID_FTests;
FText NAME_FTests;
FConstructorStatics()
: Texture(TEXT("/Engine/EditorResources/S_FTest"))
, ID_FTests(TEXT("FTests"))
, NAME_FTests(NSLOCTEXT( "SpriteCategory", "FTests", "FTests" ))
{
}
};
static FConstructorStatics ConstructorStatics;
SpriteComponent->Sprite = ConstructorStatics.Texture.Get();
SpriteComponent->SpriteInfo.Category = ConstructorStatics.ID_FTests;
SpriteComponent->SpriteInfo.DisplayName = ConstructorStatics.NAME_FTests;
}
#endif
RootComponent = SpriteComponent;
}
#if WITH_EDITORONLY_DATA
RenderComp = CreateDefaultSubobject<UFuncTestRenderingComponent>(TEXT("RenderComp"));
RenderComp->PostPhysicsComponentTick.bCanEverTick = false;
RenderComp->SetupAttachment(RootComponent);
#endif // WITH_EDITORONLY_DATA
#if WITH_EDITOR
static bool bSelectionHandlerSetUp = false;
if (HasAnyFlags(RF_ClassDefaultObject) && !HasAnyFlags(RF_TagGarbageTemp) && bSelectionHandlerSetUp == false)
{
USelection::SelectObjectEvent.AddStatic(&AFunctionalTest::OnSelectObject);
bSelectionHandlerSetUp = true;
}
#endif // WITH_EDITOR
#if WITH_EDITORONLY_DATA
TestName = CreateEditorOnlyDefaultSubobject<UTextRenderComponent>(TEXT("TestName"));
if ( TestName )
{
TestName->bHiddenInGame = true;
TestName->SetHorizontalAlignment(EHTA_Center);
TestName->SetTextRenderColor(FColor(11, 255, 0));
TestName->SetRelativeLocation(FVector(0, 0, 80));
TestName->SetRelativeRotation(FRotator(0, 0, 0));
TestName->PostPhysicsComponentTick.bCanEverTick = false;
TestName->SetupAttachment(RootComponent);
}
#endif
}
void AFunctionalTest::OnConstruction(const FTransform& Transform)
{
Super::OnConstruction(Transform);
#if WITH_EDITOR
if ( TestName )
{
TestName->SetText(FText::FromString(GetActorLabel()));
}
#endif
}
bool AFunctionalTest::RunTest(const TArray<FString>& Params)
{
FAutomationTestFramework::Get().SetTreatWarningsAsErrors(bWarningsAsErrors);
//Scalability::FQualityLevels Quality;
//Quality.SetDefaults();
//Scalability::SetQualityLevels(Quality);
FailureMessage = TEXT("");
//Do not collect garbage during the test. We force GC at the end.
GetWorld()->DelayGarbageCollection();
RunFrame = GFrameNumber;
TotalTime = 0.f;
if (TimeLimit >= 0)
{
SetActorTickEnabled(true);
}
bIsReady = false;
bIsRunning = true;
GoToObservationPoint();
PrepareTest();
return true;
}
void AFunctionalTest::PrepareTest()
{
ReceivePrepareTest();
}
void AFunctionalTest::StartTest()
{
TotalTime = 0.f;
StartFrame = GFrameNumber;
ReceiveStartTest();
OnTestStart.Broadcast();
}
void AFunctionalTest::Tick(float DeltaSeconds)
{
// already requested not to tick.
if ( bIsRunning == false )
{
return;
}
//Do not collect garbage during the test. We force GC at the end.
GetWorld()->DelayGarbageCollection();
if ( !bIsReady )
{
bIsReady = IsReady();
// Once we're finally ready to begin the test, then execute the Start event.
if ( bIsReady )
{
StartTest();
}
}
if ( bIsReady )
{
TotalTime += DeltaSeconds;
if ( TimeLimit > 0.f && TotalTime > TimeLimit )
{
FinishTest(TimesUpResult, TimesUpMessage.ToString());
}
else
{
Super::Tick(DeltaSeconds);
}
}
else
{
TotalTime += DeltaSeconds;
if ( PreparationTimeLimit > 0.f && TotalTime > PreparationTimeLimit )
{
FinishTest(TimesUpResult, TimesUpMessage.ToString());
}
}
}
bool AFunctionalTest::IsReady_Implementation()
{
return true;
}
void AFunctionalTest::FinishTest(EFunctionalTestResult TestResult, const FString& Message)
{
const static UEnum* FTestResultTypeEnum = FindObject<UEnum>( nullptr, TEXT("FunctionalTesting.EFunctionalTestResult") );
if (bIsRunning == false)
{
// ignore
return;
}
//Force GC at the end of every test.
GetWorld()->ForceGarbageCollection();
Result = TestResult;
bIsRunning = false;
SetActorTickEnabled(false);
OnTestFinished.Broadcast();
AActor** ActorToDestroy = AutoDestroyActors.GetData();
for (int32 ActorIndex = 0; ActorIndex < AutoDestroyActors.Num(); ++ActorIndex, ++ActorToDestroy)
{
if (*ActorToDestroy != NULL)
{
// will be removed next frame
(*ActorToDestroy)->SetLifeSpan( 0.01f );
}
}
const FText ResultText = FTestResultTypeEnum->GetEnumText( (int32)TestResult );
const FString OutMessage = FString::Printf(TEXT("%s %s: \"%s\"")
, *GetName()
, *ResultText.ToString()
, Message.IsEmpty() == false ? *Message : TEXT("Test finished") );
const FString AdditionalDetails = FString::Printf(TEXT("%s %s, time %.2fs"), *GetAdditionalTestFinishedMessage(TestResult), *OnAdditionalTestFinishedMessageRequest(TestResult), TotalTime);
AutoDestroyActors.Reset();
switch (TestResult)
{
case EFunctionalTestResult::Invalid:
case EFunctionalTestResult::Error:
case EFunctionalTestResult::Failed:
UE_VLOG(this, LogFunctionalTest, Error, TEXT("%s"), *OutMessage);
UE_LOG(LogFunctionalTest, Error, TEXT("%s"), *OutMessage);
break;
case EFunctionalTestResult::Running:
UE_VLOG(this, LogFunctionalTest, Warning, TEXT("%s"), *OutMessage);
UE_LOG(LogFunctionalTest, Warning, TEXT("%s"), *OutMessage);
break;
default:
UE_VLOG(this, LogFunctionalTest, Log, TEXT("%s"), *OutMessage);
UE_LOG(LogFunctionalTest, Log, TEXT("%s"), *OutMessage);
break;
}
if (AdditionalDetails.IsEmpty() == false)
{
UE_LOG(LogFunctionalTest, Log, TEXT("%s"), *AdditionalDetails);
}
TestFinishedObserver.ExecuteIfBound(this);
FAutomationTestFramework::Get().SetTreatWarningsAsErrors(TOptional<bool>());
}
void AFunctionalTest::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
TestFinishedObserver.Unbind();
Super::EndPlay(EndPlayReason);
}
void AFunctionalTest::CleanUp()
{
FailureMessage = TEXT("");
}
bool AFunctionalTest::IsRunning() const
{
return bIsRunning;
}
bool AFunctionalTest::IsEnabled() const
{
return bIsEnabled;
}
//@todo add "warning" level here
void AFunctionalTest::LogMessage(const FString& Message)
{
UE_LOG(LogFunctionalTest, Log, TEXT("%s"), *Message);
UE_VLOG(this, LogFunctionalTest, Log
, TEXT("%s> %s")
, *GetName(), *Message);
}
void AFunctionalTest::SetTimeLimit(float InTimeLimit, EFunctionalTestResult InResult)
{
if (InTimeLimit < 0.f)
{
UE_VLOG(this, LogFunctionalTest, Warning
, TEXT("%s> Trying to set TimeLimit to less than 0. Falling back to 0 (infinite).")
, *GetName());
InTimeLimit = 0.f;
}
TimeLimit = InTimeLimit;
if (InResult == EFunctionalTestResult::Invalid)
{
UE_VLOG(this, LogFunctionalTest, Warning
, TEXT("%s> Trying to set test Result to \'Invalid\'. Falling back to \'Failed\'")
, *GetName());
InResult = EFunctionalTestResult::Failed;
}
TimesUpResult = InResult;
}
void AFunctionalTest::GatherRelevantActors(TArray<AActor*>& OutActors) const
{
if (ObservationPoint)
{
OutActors.AddUnique(ObservationPoint);
}
for (auto Actor : AutoDestroyActors)
{
if (Actor)
{
OutActors.AddUnique(Actor);
}
}
OutActors.Append(DebugGatherRelevantActors());
}
void AFunctionalTest::AddRerun(FName Reason)
{
RerunCauses.Add(Reason);
}
FName AFunctionalTest::GetCurrentRerunReason()const
{
return CurrentRerunCause;
}
void AFunctionalTest::RegisterAutoDestroyActor(AActor* ActorToAutoDestroy)
{
AutoDestroyActors.AddUnique(ActorToAutoDestroy);
}
#if WITH_EDITOR
void AFunctionalTest::PostEditChangeProperty( struct FPropertyChangedEvent& PropertyChangedEvent)
{
static const FName NAME_FunctionalTesting = FName(TEXT("FunctionalTesting"));
static const FName NAME_TimeLimit = FName(TEXT("TimeLimit"));
static const FName NAME_TimesUpResult = FName(TEXT("TimesUpResult"));
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property != NULL)
{
if (FObjectEditorUtils::GetCategoryFName(PropertyChangedEvent.Property) == NAME_FunctionalTesting)
{
// first validate new data since there are some dependencies
if (PropertyChangedEvent.Property->GetFName() == NAME_TimeLimit)
{
if (TimeLimit < 0.f)
{
TimeLimit = 0.f;
}
}
else if (PropertyChangedEvent.Property->GetFName() == NAME_TimesUpResult)
{
if (TimesUpResult == EFunctionalTestResult::Invalid)
{
TimesUpResult = EFunctionalTestResult::Failed;
}
}
}
}
}
void AFunctionalTest::OnSelectObject(UObject* NewSelection)
{
AFunctionalTest* AsFTest = Cast<AFunctionalTest>(NewSelection);
if (AsFTest)
{
AsFTest->MarkComponentsRenderStateDirty();
}
}
#endif // WITH_EDITOR
void AFunctionalTest::GoToObservationPoint()
{
if (ObservationPoint == nullptr)
{
return;
}
UWorld* World = GetWorld();
if (World && World->GetGameInstance())
{
APlayerController* TargetPC = nullptr;
for (FConstPlayerControllerIterator PCIterator = World->GetPlayerControllerIterator(); PCIterator; ++PCIterator)
{
APlayerController* PC = PCIterator->Get();
// Don't use debug camera player controllers.
// While it's tempting to teleport the camera if the user is debugging something then moving the camera around will them.
if (PC && !PC->IsA(ADebugCameraController::StaticClass()))
{
TargetPC = PC;
break;
}
}
if (TargetPC)
{
if (TargetPC->GetPawn())
{
TargetPC->GetPawn()->TeleportTo(ObservationPoint->GetActorLocation(), ObservationPoint->GetActorRotation(), /*bIsATest=*/false, /*bNoCheck=*/true);
TargetPC->SetControlRotation(ObservationPoint->GetActorRotation());
}
else
{
TargetPC->SetViewTarget(ObservationPoint);
}
}
}
}
/** Returns SpriteComponent subobject **/
UBillboardComponent* AFunctionalTest::GetSpriteComponent()
{
return SpriteComponent;
}
//////////////////////////////////////////////////////////////////////////
void AFunctionalTest::AssertTrue(bool Condition, FString Message, const UObject* ContextObject)
{
if ( !Condition )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Assertion failed: '%s' for context '%s'"), *Message, ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("Assertion passed (%s)"), *Message));
}
}
void AFunctionalTest::AssertFalse(bool Condition, FString Message, const UObject* ContextObject)
{
AssertTrue(!Condition, Message, ContextObject);
}
void AFunctionalTest::AssertIsValid(UObject* Object, FString Message, const UObject* ContextObject)
{
if ( !IsValid(Object) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Invalid object: '%s' for context '%s'"), *Message, ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("Valid object: (%s)"), *Message));
}
}
void AFunctionalTest::AssertValue_Int(int32 Actual, EComparisonMethod ShouldBe, int32 Expected, const FString& What, const UObject* ContextObject)
{
if ( !PerformComparison(Actual, Expected, ShouldBe) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("%s: expected {%d} to be %s {%d} for context '%s'"), *What, Actual, *GetComparisonAsString(ShouldBe), Expected, ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("Int assertion passed (%s)"), *What));
}
}
void AFunctionalTest::AssertValue_Float(float Actual, EComparisonMethod ShouldBe, float Expected, const FString& What, const UObject* ContextObject)
{
if ( !PerformComparison(Actual, Expected, ShouldBe) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("%s: expected {%f} to be %s {%f} for context '%s'"), *What, Actual, *GetComparisonAsString(ShouldBe), Expected, ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("Float assertion passed (%s)"), *What));
}
}
void AFunctionalTest::AssertValue_DateTime(FDateTime Actual, EComparisonMethod ShouldBe, FDateTime Expected, const FString& What, const UObject* ContextObject)
{
if ( !PerformComparison(Actual, Expected, ShouldBe) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("%s: expected {%s} to be %s {%s} for context '%s'"), *What, *Actual.ToString(), *GetComparisonAsString(ShouldBe), *Expected.ToString(), ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("DateTime assertion passed (%s)"), *What));
}
}
void AFunctionalTest::AssertEqual_Float(const float Actual, const float Expected, const FString& What, const float Tolerance, const UObject* ContextObject)
{
if ( !FMath::IsNearlyEqual(Actual, Expected, Tolerance) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Expected '%s' to be {%f}, but it was {%f} within tolerance {%f} for context '%s'"), *What, Expected, Actual, Tolerance, ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("Float assertion passed (%s)"), *What));
}
}
void AFunctionalTest::AssertEqual_Transform(const FTransform Actual, const FTransform Expected, const FString& What, const UObject* ContextObject)
{
if ( !Expected.Equals(Actual) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Expected '%s' to be {%s}, but it was {%s} for context '%s'"), *What, *TransformToString(Expected), *TransformToString(Actual), ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("Transform assertion passed (%s)"), *What));
}
}
void AFunctionalTest::AssertNotEqual_Transform(const FTransform Actual, const FTransform NotExpected, const FString& What, const UObject* ContextObject)
{
if ( NotExpected.Equals(Actual) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Expected '%s' not to be {%s} for context '%s'"), *What, *TransformToString(NotExpected), ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("Transform assertion passed (%s)"), *What));
}
}
void AFunctionalTest::AssertEqual_Rotator(const FRotator Actual, const FRotator Expected, const FString& What, const UObject* ContextObject)
{
if ( !Expected.Equals(Actual) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Expected '%s' to be {%s} but it was {%s} for context '%s'"), *What, *Expected.ToString(), *Actual.ToString(), ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("Rotator assertion passed (%s)"), *What));
}
}
void AFunctionalTest::AssertNotEqual_Rotator(const FRotator Actual, const FRotator NotExpected, const FString& What, const UObject* ContextObject)
{
if ( NotExpected.Equals(Actual) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Expected '%s' not to be {%s} for context '%s'"), *What, *NotExpected.ToString(), ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("Rotator assertion passed (%s)"), *What));
}
}
void AFunctionalTest::AssertEqual_Vector(const FVector Actual, const FVector Expected, const FString& What, const float Tolerance, const UObject* ContextObject)
{
if ( !Expected.Equals(Actual, Tolerance) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Expected '%s' to be {%s} but it was {%s} within tolerance {%f} for context '%s'"), *What, *Expected.ToString(), *Actual.ToString(), Tolerance, ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("Vector assertion passed (%s)"), *What));
}
}
void AFunctionalTest::AssertNotEqual_Vector(const FVector Actual, const FVector NotExpected, const FString& What, const UObject* ContextObject)
{
if ( NotExpected.Equals(Actual) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Expected '%s' not to be {%s} for context '%s'"), *What, *NotExpected.ToString(), ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("Vector assertion passed (%s)"), *What));
}
}
void AFunctionalTest::AssertEqual_String(const FString Actual, const FString Expected, const FString& What, const UObject* ContextObject)
{
if ( !Expected.Equals(Actual) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Expected '%s' to be {%s} but it was {%s} for context '%s'"), *What, *Expected, *Actual, ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("String assertion passed (%s)"), *What));
}
}
void AFunctionalTest::AssertNotEqual_String(const FString Actual, const FString NotExpected, const FString& What, const UObject* ContextObject)
{
if ( NotExpected.Equals(Actual) )
{
LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Expected '%s' not to be {%s} for context '%s'"), *What, *NotExpected, ContextObject ? *ContextObject->GetName() : TEXT("")));
}
else
{
LogStep(ELogVerbosity::Log, FString::Printf(TEXT("String assertion passed (%s)"), *What));
}
}
void AFunctionalTest::AddWarning(const FString Message)
{
LogStep(ELogVerbosity::Warning, Message);
}
void AFunctionalTest::AddError(const FString Message)
{
LogStep(ELogVerbosity::Error, Message);
}
void AFunctionalTest::LogStep(ELogVerbosity::Type Verbosity, const FString& Message)
{
FString FullMessage(Message);
if ( IsInStep() )
{
FullMessage.Append(TEXT(" in step: "));
FString StepName = TEXT("");
if ( StepName.IsEmpty() )
{
StepName = TEXT("<UN-NAMED STEP>");
}
FullMessage.Append(StepName);
}
switch ( Verbosity )
{
case ELogVerbosity::Log:
UE_VLOG(this, LogFunctionalTest, Log, TEXT("%s"), *FullMessage);
UE_LOG(LogFunctionalTest, Log, TEXT("%s"), *FullMessage);
break;
case ELogVerbosity::Warning:
UE_VLOG(this, LogFunctionalTest, Warning, TEXT("%s"), *FullMessage);
UE_LOG(LogFunctionalTest, Warning, TEXT("%s"), *FullMessage);
break;
case ELogVerbosity::Error:
UE_VLOG(this, LogFunctionalTest, Error, TEXT("%s"), *FullMessage);
UE_LOG(LogFunctionalTest, Error, TEXT("%s"), *FullMessage);
break;
}
}
FString AFunctionalTest::GetCurrentStepName() const
{
return IsInStep() ? Steps.Top() : FString();
}
void AFunctionalTest::StartStep(const FString& StepName)
{
Steps.Push(StepName);
}
void AFunctionalTest::FinishStep()
{
if ( Steps.Num() > 0 )
{
Steps.Pop();
}
else
{
AddWarning(TEXT("FinishStep was called when no steps were currently in progress."));
}
}
bool AFunctionalTest::IsInStep() const
{
return Steps.Num() > 0;
}
//////////////////////////////////////////////////////////////////////////
FPerfStatsRecord::FPerfStatsRecord(FString InName)
: Name(InName)
, GPUBudget(0.0f)
, RenderThreadBudget(0.0f)
, GameThreadBudget(0.0f)
{
}
void FPerfStatsRecord::SetBudgets(float InGPUBudget, float InRenderThreadBudget, float InGameThreadBudget)
{
GPUBudget = InGPUBudget;
RenderThreadBudget = InRenderThreadBudget;
GameThreadBudget = InGameThreadBudget;
}
FString FPerfStatsRecord::GetReportString() const
{
return FString::Printf(TEXT("%s,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f"),
*Name,
Record.FrameTimeTracker.GetMinValue() - Baseline.FrameTimeTracker.GetMinValue(),
Record.FrameTimeTracker.GetAvgValue() - Baseline.FrameTimeTracker.GetAvgValue(),
Record.FrameTimeTracker.GetMaxValue() - Baseline.FrameTimeTracker.GetMaxValue(),
Record.RenderThreadTimeTracker.GetMinValue() - Baseline.RenderThreadTimeTracker.GetMinValue(),
Record.RenderThreadTimeTracker.GetAvgValue() - Baseline.RenderThreadTimeTracker.GetAvgValue(),
Record.RenderThreadTimeTracker.GetMaxValue() - Baseline.RenderThreadTimeTracker.GetMaxValue(),
Record.GameThreadTimeTracker.GetMinValue() - Baseline.GameThreadTimeTracker.GetMinValue(),
Record.GameThreadTimeTracker.GetAvgValue() - Baseline.GameThreadTimeTracker.GetAvgValue(),
Record.GameThreadTimeTracker.GetMaxValue() - Baseline.GameThreadTimeTracker.GetMaxValue(),
Record.GPUTimeTracker.GetMinValue() - Baseline.GPUTimeTracker.GetMinValue(),
Record.GPUTimeTracker.GetAvgValue() - Baseline.GPUTimeTracker.GetAvgValue(),
Record.GPUTimeTracker.GetMaxValue() - Baseline.GPUTimeTracker.GetMaxValue());
}
FString FPerfStatsRecord::GetBaselineString() const
{
return FString::Printf(TEXT("%s,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f"),
*Name,
Baseline.FrameTimeTracker.GetMinValue(),
Baseline.FrameTimeTracker.GetAvgValue(),
Baseline.FrameTimeTracker.GetMaxValue(),
Baseline.RenderThreadTimeTracker.GetMinValue(),
Baseline.RenderThreadTimeTracker.GetAvgValue(),
Baseline.RenderThreadTimeTracker.GetMaxValue(),
Baseline.GameThreadTimeTracker.GetMinValue(),
Baseline.GameThreadTimeTracker.GetAvgValue(),
Baseline.GameThreadTimeTracker.GetMaxValue(),
Baseline.GPUTimeTracker.GetMinValue(),
Baseline.GPUTimeTracker.GetAvgValue(),
Baseline.GPUTimeTracker.GetMaxValue());
}
FString FPerfStatsRecord::GetRecordString() const
{
return FString::Printf(TEXT("%s,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f"),
*Name,
Record.FrameTimeTracker.GetMinValue(),
Record.FrameTimeTracker.GetAvgValue(),
Record.FrameTimeTracker.GetMaxValue(),
Record.RenderThreadTimeTracker.GetMinValue(),
Record.RenderThreadTimeTracker.GetAvgValue(),
Record.RenderThreadTimeTracker.GetMaxValue(),
Record.GameThreadTimeTracker.GetMinValue(),
Record.GameThreadTimeTracker.GetAvgValue(),
Record.GameThreadTimeTracker.GetMaxValue(),
Record.GPUTimeTracker.GetMinValue(),
Record.GPUTimeTracker.GetAvgValue(),
Record.GPUTimeTracker.GetMaxValue());
}
FString FPerfStatsRecord::GetOverBudgetString() const
{
double Min, Max, Avg;
GetRenderThreadTimes(Min, Max, Avg);
float RTMax = Max;
float RTBudgetFrac = Max / RenderThreadBudget;
GetGameThreadTimes(Min, Max, Avg);
float GTMax = Max;
float GTBudgetFrac = Max / GameThreadBudget;
GetGPUTimes(Min, Max, Avg);
float GPUMax = Max;
float GPUBudgetFrac = Max / GPUBudget;
return FString::Printf(TEXT("%s,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f"),
*Name,
RTMax,
RenderThreadBudget,
RTBudgetFrac,
GTMax,
GameThreadBudget,
GTBudgetFrac,
GPUMax,
GPUBudget,
GPUBudgetFrac
);
}
bool FPerfStatsRecord::IsWithinGPUBudget()const
{
double Min, Max, Avg;
GetGPUTimes(Min, Max, Avg);
return Max <= GPUBudget;
}
bool FPerfStatsRecord::IsWithinGameThreadBudget()const
{
double Min, Max, Avg;
GetGameThreadTimes(Min, Max, Avg);
return Max <= GameThreadBudget;
}
bool FPerfStatsRecord::IsWithinRenderThreadBudget()const
{
double Min, Max, Avg;
GetRenderThreadTimes(Min, Max, Avg);
return Max <= RenderThreadBudget;
}
void FPerfStatsRecord::GetGPUTimes(double& OutMin, double& OutMax, double& OutAvg)const
{
OutMin = Record.GPUTimeTracker.GetMinValue() - Baseline.GPUTimeTracker.GetMinValue();
OutMax = Record.GPUTimeTracker.GetMaxValue() - Baseline.GPUTimeTracker.GetMaxValue();
OutAvg = Record.GPUTimeTracker.GetAvgValue() - Baseline.GPUTimeTracker.GetAvgValue();
}
void FPerfStatsRecord::GetGameThreadTimes(double& OutMin, double& OutMax, double& OutAvg)const
{
OutMin = Record.GameThreadTimeTracker.GetMinValue() - Baseline.GameThreadTimeTracker.GetMinValue();
OutMax = Record.GameThreadTimeTracker.GetMaxValue() - Baseline.GameThreadTimeTracker.GetMaxValue();
OutAvg = Record.GameThreadTimeTracker.GetAvgValue() - Baseline.GameThreadTimeTracker.GetAvgValue();
}
void FPerfStatsRecord::GetRenderThreadTimes(double& OutMin, double& OutMax, double& OutAvg)const
{
OutMin = Record.RenderThreadTimeTracker.GetMinValue() - Baseline.RenderThreadTimeTracker.GetMinValue();
OutMax = Record.RenderThreadTimeTracker.GetMaxValue() - Baseline.RenderThreadTimeTracker.GetMaxValue();
OutAvg = Record.RenderThreadTimeTracker.GetAvgValue() - Baseline.RenderThreadTimeTracker.GetAvgValue();
}
void FPerfStatsRecord::Sample(UWorld* World, float DeltaSeconds, bool bBaseline)
{
check(World);
const FStatUnitData* StatUnitData = World->GetGameViewport()->GetStatUnitData();
check(StatUnitData);
if (bBaseline)
{
Baseline.FrameTimeTracker.AddSample(StatUnitData->RawFrameTime);
Baseline.GameThreadTimeTracker.AddSample(FPlatformTime::ToMilliseconds(GGameThreadTime));
Baseline.RenderThreadTimeTracker.AddSample(FPlatformTime::ToMilliseconds(GRenderThreadTime));
Baseline.GPUTimeTracker.AddSample(FPlatformTime::ToMilliseconds(GGPUFrameTime));
Baseline.NumFrames++;
Baseline.SumTimeSeconds += DeltaSeconds;
}
else
{
Record.FrameTimeTracker.AddSample(StatUnitData->RawFrameTime);
Record.GameThreadTimeTracker.AddSample(FPlatformTime::ToMilliseconds(GGameThreadTime));
Record.RenderThreadTimeTracker.AddSample(FPlatformTime::ToMilliseconds(GRenderThreadTime));
Record.GPUTimeTracker.AddSample(FPlatformTime::ToMilliseconds(GGPUFrameTime));
Record.NumFrames++;
Record.SumTimeSeconds += DeltaSeconds;
}
}
UAutomationPerformaceHelper::UAutomationPerformaceHelper()
: bRecordingBasicStats(false)
, bRecordingBaselineBasicStats(false)
, bRecordingCPUCapture(false)
, bRecordingStatsFile(false)
, bGPUTraceIfBelowBudget(false)
{
}
void UAutomationPerformaceHelper::BeginRecordingBaseline(FString RecordName)
{
bRecordingBasicStats = true;
bRecordingBaselineBasicStats = true;
bGPUTraceIfBelowBudget = false;
Records.Add(FPerfStatsRecord(RecordName));
GEngine->SetEngineStat(GetOuter()->GetWorld(), GetOuter()->GetWorld()->GetGameViewport(), TEXT("Unit"), true);
}
void UAutomationPerformaceHelper::EndRecordingBaseline()
{
bRecordingBaselineBasicStats = false;
bRecordingBasicStats = false;
}
void UAutomationPerformaceHelper::BeginRecording(FString RecordName, float InGPUBudget, float InRenderThreadBudget, float InGameThreadBudget)
{
//Ensure we're recording engine stats.
GEngine->SetEngineStat(GetOuter()->GetWorld(), GetOuter()->GetWorld()->GetGameViewport(), TEXT("Unit"), true);
bRecordingBasicStats = true;
bRecordingBaselineBasicStats = false;
bGPUTraceIfBelowBudget = false;
FPerfStatsRecord* CurrRecord = GetCurrentRecord();
if (!CurrRecord || CurrRecord->Name != RecordName)
{
Records.Add(FPerfStatsRecord(RecordName));
CurrRecord = GetCurrentRecord();
}
check(CurrRecord);
CurrRecord->SetBudgets(InGPUBudget, InRenderThreadBudget, InGameThreadBudget);
}
void UAutomationPerformaceHelper::EndRecording()
{
if (const FPerfStatsRecord* Record = GetCurrentRecord())
{
UE_LOG(LogFunctionalTest, Log, TEXT("Finished Perf Stats Record:\n%s"), *Record->GetReportString());
}
bRecordingBasicStats = false;
}
void UAutomationPerformaceHelper::Tick(float DeltaSeconds)
{
if (bRecordingBasicStats)
{
Sample(DeltaSeconds);
}
if (bGPUTraceIfBelowBudget)
{
if (!IsCurrentRecordWithinGPUBudget())
{
FString PathName = FPaths::ProfilingDir();
GGPUTraceFileName = PathName / CreateProfileFilename(GetCurrentRecord()->Name, TEXT(".rtt"), true);
UE_LOG(LogFunctionalTest, Log, TEXT("Functional Test has fallen below GPU budget. Performing GPU trace."));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Performed GPU Thred Trace!"));
//Only perform one trace per test.
bGPUTraceIfBelowBudget = false;
}
}
//Other stats need ticking?
}
void UAutomationPerformaceHelper::Sample(float DeltaSeconds)
{
int32 Index = Records.Num() - 1;
if (Index >= 0 && bRecordingBasicStats)
{
Records[Index].Sample(GetOuter()->GetWorld(), DeltaSeconds, bRecordingBaselineBasicStats);
}
}
void UAutomationPerformaceHelper::WriteLogFile(const FString& CaptureDir, const FString& CaptureExtension)
{
FString PathName = FPaths::ProfilingDir();
if (!CaptureDir.IsEmpty())
{
PathName = PathName + (CaptureDir + TEXT("/"));
IFileManager::Get().MakeDirectory(*PathName);
}
FString Extension = CaptureExtension;
if (Extension.IsEmpty())
{
Extension = TEXT("perf.csv");
}
const FString Filename = CreateProfileFilename(CaptureExtension, true);
const FString FilenameFull = PathName + Filename;
const FString OverBudgetTableHeader = TEXT("TestName, MaxRT, RT Budget, RT Frac, MaxGT, GT Budget, GT Frac, MaxGPU, GPU Budget, GPU Frac\n");
FString OverbudgetTable;
const FString DataTableHeader = TEXT("TestName,MinFrameTime,AvgFrameTime,MaxFrameTime,MinRT,AvgRT,MaxRT,MinGT,AvgGT,MaxGT,MinGPU,AvgGPU,MaxGPU\n");
FString AdjustedTable;
FString RecordTable;
FString BaselineTable;
for (FPerfStatsRecord& Record : Records)
{
AdjustedTable += Record.GetReportString() + FString(TEXT("\n"));
RecordTable += Record.GetRecordString() + FString(TEXT("\n"));
BaselineTable += Record.GetBaselineString() + FString(TEXT("\n"));
if (!Record.IsWithinGPUBudget() || !Record.IsWithinRenderThreadBudget() || !Record.IsWithinGameThreadBudget())
{
OverbudgetTable += Record.GetOverBudgetString() + FString(TEXT("\n"));
}
}
FString FileContents = FString::Printf(TEXT("Over Budget Tests\n%s%s\nAdjusted Results\n%s%s\nRaw Results\n%s%s\nBaseline Results\n%s%s\n"),
*OverBudgetTableHeader, *OverbudgetTable, *DataTableHeader, *AdjustedTable, *DataTableHeader, *RecordTable, *DataTableHeader, *BaselineTable);
FFileHelper::SaveStringToFile(FileContents, *FilenameFull);
UE_LOG(LogTemp, Display, TEXT("Finished test, wrote file to %s"), *FilenameFull);
Records.Empty();
bRecordingBasicStats = false;
bRecordingBaselineBasicStats = false;
}
bool UAutomationPerformaceHelper::IsRecording()const
{
return bRecordingBasicStats;
}
void UAutomationPerformaceHelper::OnBeginTests()
{
OutputFileBase = CreateProfileFilename(TEXT(""), true);
StartOfTestingTime = FDateTime::Now().ToString();
}
void UAutomationPerformaceHelper::OnAllTestsComplete()
{
if (bRecordingBaselineBasicStats)
{
EndRecordingBaseline();
}
if (bRecordingBasicStats)
{
EndRecording();
}
if (bRecordingCPUCapture)
{
StopCPUProfiling();
}
if (bRecordingStatsFile)
{
EndStatsFile();
}
bGPUTraceIfBelowBudget = false;
if (Records.Num() > 0)
{
WriteLogFile(TEXT(""), TEXT("perf.csv"));
}
}
bool UAutomationPerformaceHelper::IsCurrentRecordWithinGPUBudget()const
{
if (const FPerfStatsRecord* Curr = GetCurrentRecord())
{
return Curr->IsWithinGPUBudget();
}
return true;
}
bool UAutomationPerformaceHelper::IsCurrentRecordWithinGameThreadBudget()const
{
if (const FPerfStatsRecord* Curr = GetCurrentRecord())
{
return Curr->IsWithinGameThreadBudget();
}
return true;
}
bool UAutomationPerformaceHelper::IsCurrentRecordWithinRenderThreadBudget()const
{
if (const FPerfStatsRecord* Curr = GetCurrentRecord())
{
return Curr->IsWithinRenderThreadBudget();
}
return true;
}
const FPerfStatsRecord* UAutomationPerformaceHelper::GetCurrentRecord()const
{
int32 Index = Records.Num() - 1;
if (Index >= 0)
{
return &Records[Index];
}
return nullptr;
}
FPerfStatsRecord* UAutomationPerformaceHelper::GetCurrentRecord()
{
int32 Index = Records.Num() - 1;
if (Index >= 0)
{
return &Records[Index];
}
return nullptr;
}
void UAutomationPerformaceHelper::StartCPUProfiling()
{
UE_LOG(LogFunctionalTest, Log, TEXT("START PROFILING..."));
ExternalProfiler.StartProfiler(false);
}
void UAutomationPerformaceHelper::StopCPUProfiling()
{
UE_LOG(LogFunctionalTest, Log, TEXT("STOP PROFILING..."));
ExternalProfiler.StopProfiler();
}
void UAutomationPerformaceHelper::TriggerGPUTraceIfRecordFallsBelowBudget()
{
bGPUTraceIfBelowBudget = true;
}
void UAutomationPerformaceHelper::BeginStatsFile(const FString& RecordName)
{
FString MapName = GetOuter()->GetWorld()->GetMapName();
FString Cmd = FString::Printf(TEXT("Stat StartFile %s-%s/%s.ue4stats"), *MapName, *StartOfTestingTime, *RecordName);
GEngine->Exec(GetOuter()->GetWorld(), *Cmd);
}
void UAutomationPerformaceHelper::EndStatsFile()
{
GEngine->Exec(GetOuter()->GetWorld(), TEXT("Stat StopFile"));
}