Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp
Andrew Grant 2000999e23 Copying //UE4/Orion-Staging to //UE4/Main (Source: //Orion/Dev-General @ 2997507)
#lockdown Nick.Penwarden

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

Change 2997066 on 2016/06/01 by Michael.Noland

	Engine: Marked engine performance target cvars ECVF_Scalability so they can be adjusted via scalability buckets at runtime for games that target different framerates on different levels of hardware
	#rb none
	#tests Ran Paragon and changed video settings and tested t.TargetFrameTimeThreshold

Change 2996816 on 2016/06/01 by Dan.Youhon

	Add FixedWorldDirection option for Root Motion Radial Forces; code/BP hook-up for allowing Price's reworked RMB to send all targets in the same (correct) direction
	- FixedWorldDirection added to both root motion system and corresponding ability tasks
	- Exposed AddHitResult for EffectContexts for modifying EffectContext hit results from BP
	- Hooked up to Price RMB - we (somewhat dirtily) route Price's location and facing through the HitResult of the EffectContext for his displacement GE

	#rb Dave.Ratti
	#tests MultiPIE
	#codereview Billy.Rivers
	#lockdown Billy.Rivers

Change 2996526 on 2016/06/01 by Brian.Karis

	Fixed tube light typo

	JB made this robomerge up.  Shader recompiling in our future.

	#RB:none
	#Tests:none

	#ROBOMERGE: MAIN, 27, 26.2

Change 2996428 on 2016/06/01 by Rolando.Caloca

	O - Made r.D3D.RemoveUnusedInterpolators a system setting which now also alters ddc key; fix r.Shaders.FastMath actually affecting compilation when =0
	#rb Chris.Bunner
	#codereview Michael.Noland, Marcus.Wassmer
	#jira OR-22573
	#tests Run with and without r.D3D.RemoveUnusedInterpolators=1 on DefaultEngine.ini

Change 2996090 on 2016/06/01 by Jason.Bestimt

	#ORION_MAIN - Merge 26.2 @ CL 2995754

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2995816 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

	#CodeReview: jason.bestimt

Change 2995785 on 2016/06/01 by Robert.Manuszewski

	Don't delete non-backup log files when cleaning up the logs folder.

	#rb none
	#tests Tested in the editor with multiple old log files

Change 2995556 on 2016/05/31 by Dmitry.Rekman

	More info about timers on crash (OR-21986).

	- Somewhat desperate attempt to get more clue about timer crash. Intended to be removed later.

	#rb Michael.Noland
	#codereview Marc.Audy, Michael.Noland
	#tests Compiled the Linux server, ran it, crashed a few times.

Change 2995397 on 2016/05/31 by Michael.Noland

	Rendering: Made the optimization to combine upscaling/downscaling and tonemapping optional based on the amount of upscaling that will occur
	- r.Tonemapper.ScreenPercentage has been renamed to r.Tonemapper.MergeWithUpscale.Mode
	- r.Tonemapper.MergeWithUpscale.Threshold is a new setting used when r.Tonemapper.MergeWithUpscale.Mode is set to 2, which indcates to only try to merge the passes if the ratio of the area before upscale/downscale to the area afterwards is greater than the threshold
	This prevents running the tonemapper on all of the target res pixels when the source res is far smaller, as that can cause it to be a loss to merge the passes

	Upgrade Notes: r.Tonemapper.ScreenPercentage has been renamed to r.Tonemapper.MergeWithUpscale.Mode

	#rb marcus.wassmer
	#tests Ran Paragon at various resolutions on Intel and NV cards
	#rn

Change 2995118 on 2016/05/31 by David.Decker

	- Fix for build failure

	#rb none
	#tests golden path game

Change 2994929 on 2016/05/31 by David.Decker

	#Orion_Analytics
	-Added PacketRecievedHistogram event that fires every minute in game the rate is configurable in DefaultGame.ini
	-Moved FHistogram from PerfCountersModule to ProfilingHelpers
	-Re-enabled Location event

	#rb Dmitry.Rekman
	#codereview Dmitry.Rekman, John.Pollard, Christopher.Wright
	#tests golden path game

Change 2994920 on 2016/05/31 by Daniel.Lamb

	Added some more cooking stats to save package.
	#rb Wes.Hunt
	#test Cook Orion.

Change 2994622 on 2016/05/31 by Zak.Middleton

	#orion - Pickup and Coin filter collision optimizations.

	- Added coin collision profile preset and made it ignore everything but Pawn (importantly, now ignores triggers).
	- Pickups filter out collision with AI earlier. Profile already did this but this avoids more branches and cache misses in PreFilter.
	- Coins now additionally filter out more efficiently Heroes that can't pick them (no overlap events generated at all).

	#rb Jon.Lietz, Frank.Gigliotti
	#tests PlayGo MultiPIE

Change 2994305 on 2016/05/31 by Andrew.Grant

	Restoring prompt/exit on signed archive issue to help identify causes
	#rb none
	#tests compiled

Change 2994226 on 2016/05/31 by Jason.Bestimt

	#ROBOMERGE-AUTHOR: jason.bestimt
	#ORION_MAIN - Merge 27 @ CL 2993946

	#RB:none
	#Tests:none

	[CodeReviewed]: graeme.thornton

	#ROBOMERGE-SOURCE: CL 2994225 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2994204 on 2016/05/31 by bruce.nesbit

	More banner changes
	-Fixed an issue with InterpToComponent with very short times
	-revisions to test banner
	-added fade to banner/charms
	-tries to set team color on decativate FX

	#rb none

	#tests Game+PIE

	#codereview Jason.Bestimt

Change 2993973 on 2016/05/30 by Robert.Manuszewski

	Updated protection handling

	#rb none
	#tests Compiled and applied protection

Change 2993588 on 2016/05/27 by Michael.Noland

	Engine: Removed a bogus autocomplete for ShowMaterialDrawEvents, which was previously renamed r.ShowMaterialDrawEvents
	#rb none
	#tests Typed in ShowMat in the console and verified that no autocomplete appeared
	#rn

Change 2993510 on 2016/05/27 by John.Pollard

	Fix issue with root motion sources and replays, fixes TwinBlast RMB ability animation issue, and other artifacts

	#rb RyanG
	#tests Replays

Change 2993484 on 2016/05/27 by Uriel.Doyon

	New logic for computing the skel mesh streaming scales
	#rb marcus.wassmer
	#tests loaded editor, played with streaming scale

Change 2993211 on 2016/05/27 by Uriel.Doyon

	Workaround for lightmap streaming flags not being correctly set.
	#codereview marcus.wassmer
	#rb marcus.wassmer
	#tests building lighting and investigating streaming

Change 2993068 on 2016/05/27 by Marcus.Wassmer

	Duplicate 2989729
	Fix for lightmaps and shadowmaps having low resolutions after building lightings
	#rb none
	#test PC at various scalability
	#codereview Uriel.Doyon

Change 2993066 on 2016/05/27 by Lukasz.Furman

	fixed behavior tree getting stuck on ResumeLogic call
	copied from //UE4/Dev-Framework, CL# 2993058
	#jira OR-22498
	#rb none
	#tests none

Change 2992706 on 2016/05/27 by Marcus.Wassmer

	Duplicate 2991726
	Fix for grey skin in simple lighting model (shadows off)

	#rb none
	#test lowest settings on PC

Change 2992705 on 2016/05/27 by Marcus.Wassmer

	Duplicate 2991727
	Fix emissive decals in simple forward renderer
	#rb none
	#test PC lowest settings

Change 2992658 on 2016/05/27 by David.Ratti

	Remove all occurrences of Ability.PersistPastDeath from granted tags. Fix code to *only* check asset tags for this tag, instead of both.

	#rb none
	#test pie

Change 2992646 on 2016/05/27 by Ben.Marsh

	BuildGraph: Add a BuildGraph task to run a UE4 commandlet. Syntax is <Commandlet Name="..." Project="..." Arguments="...">.

	#rb none
	#tests none

Change 2992252 on 2016/05/26 by Jason.Bestimt

	#ORION_DG - Unclog ROBO Merge in DG

	#RB:none
	#Tests:none

Change 2992180 on 2016/05/26 by John.Pollard

	Fix issue where external data wasn't saving out properly

	#rb RyanG
	#tests Replays

Change 2992159 on 2016/05/26 by Michael.Noland

	CVar to disable/freeze GPU particle simulation (r.GPUParticle.Simulate)
	[Replicated from Dev-Rendering checkin CL# 2989752 by Olaf]
	#rb olaf.piesche
	#tests Tested the command in Agora and verified that GPU particles were not being drawn

Change 2992158 on 2016/05/26 by Michael.Noland

	Rendering: Added a cvar that controls unbinding of all texture resources between materials changes in the DX11 renderer (r.UnbindResourcesBetweenDrawsInDX11) to improve the readability of GPA captures
	Note: This will probably be moved to be on when markers are on rather than an independent cvar, but it is currently separate for testing
	#codereview marcus.wassmer
	#rb none
	#tests Ran with the var off and on and verified in GPA captures

Change 2991645 on 2016/05/26 by Andrew.Grant

	Fix for filesize returning 0 if file not found
	#rb none
	#tests bugit now works
	#jira OR-20488

Change 2991290 on 2016/05/26 by Mieszko.Zielinski

	Added a static flag to NavigationSystem that can be used to short-circuit dynamic navigation related functions #UE4

	Will save some perf on PS4 in Orion, since clients do use navigation system there.

	#rb Lukasz.Furman
	#test golden path

Change 2991288 on 2016/05/26 by Mieszko.Zielinski

	CL#2990243 manually redone in for Orion #UE4

	Original description:
	> Fixed behavior tree observers not being applied correctly

	#rb Lukasz.Furman
	#test golden path

Change 2991271 on 2016/05/26 by Jason.Bestimt

	#ROBOMERGE-AUTHOR: jason.bestimt
	#ORION_MAIN - Merge 26.2 @ CL 2990688

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2991269 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2991185 on 2016/05/26 by Mieszko.Zielinski

	Fixed UAITask_MoveTo not releasing paths properly, or clearing path observing when task is being reused #UE4

	Also, fixed FNavigationPath::DoneUpdating not converting ENavPathUpdateType properly

	#rb Lukasz.Furman
	#test golden path

Change 2990788 on 2016/05/25 by Marcus.Wassmer

	Fix deprecation warning
	#rb none
	#test none

Change 2990582 on 2016/05/25 by Marcus.Wassmer

	Now that render commands are enqueued again on servers, we shouldn't outright crash if an allocation gets to nullrhi
	#codereview Dmitry.Rekman
	#rb none
	#test none

Change 2990450 on 2016/05/25 by Martin.Mittring

	OR-22233 GPU Sprites invisible unless solo'd
	#rb:David.Hill
	#jira:OR-22233
	#test:PC

Change 2990199 on 2016/05/25 by Marcus.Wassmer

	Remove experimental HDR support in tonemapper. Brings tonemapper cost back down into line
	#rb none
	#codereview Michael.Noland,Brian.Karis

Change 2989908 on 2016/05/25 by Andrew.Grant

	Changed warning about DDC cache full to Display
	#rb none
	#tests compiled

Change 2989903 on 2016/05/25 by Mieszko.Zielinski

	Made BT component ignore subtree injection request if relevant BT nodes already use indicated asset #UE4

	#rb Lukasz.Furman
	#test golden path

Change 2989795 on 2016/05/25 by Ryan.Gerleve

	Fix for storing the correct URL on the pending net game for replay playback.
	Re-implemented this fix from Dev-Networking CL 2981198, fixes deathcam after latest main integration.

	#tests played a reply, enabled deathcam
	#rb none

Change 2989483 on 2016/05/25 by David.Ratti

	ToggleJuggernaut cheat
	#rb danY
	#tests pie

Change 2989384 on 2016/05/25 by Graeme.Thornton

	Extra chunk decryption tests and logging to help diagnose the random failure we're seeing in the wild
	 - retry decrypt three times
	 - after the first attempt, re-decrypt original source, just incase the decrypt cache has been corrupted

	#tests cooked pc client + dedicated server
	#rb robert.manuszewski

Change 2989225 on 2016/05/24 by Dmitry.Rekman

	Fix rare crash in Linux threading code (OR-22193).

	- Sometimes, for some reason, freeing memory for an alternate thread from a thread in PostRun() can crash because the jemalloc apparently does not have an arena for this thread anymore.
	- This change works around the problem by allocating the said memory statically in LinuxThread class.

	#rb none
	#codereview Bob.Tellez, David.Vossel
	#tests Compiled Linux server, started it.

Change 2988768 on 2016/05/24 by Uriel.Doyon

	Added support for SkinnedMesh in the texture streaming MeshCoordSize accuracy viewmode.
	#RB marcus.wassmer
	#tests loaded game and editor

Change 2988462 on 2016/05/24 by Mieszko.Zielinski

	Added a piece of logging to both scenarios or movement aborting in UPathFollowingComponent::UpdatePathSegment to be able to tell them appart while reading the log #Orion

	#rb Lukasz.Furman
	#test golden path

Change 2988036 on 2016/05/24 by Jason.Bestimt

	#ROBOMERGE-AUTHOR: jason.bestimt
	#ORION_MAIN - Merge 26.2 @ CL 2987910

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2988035 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2987457 on 2016/05/23 by Mieszko.Zielinski

	Redone changes from CL#2981193 #UE4

	Original description: fixed missing observers in behavior tree when dynamic subtree is changed while waiting for full restart (out of nodes)

	#rb Lukasz.Furman
	#test golden path

Change 2987388 on 2016/05/23 by Olaf.Piesche

	Replicating CL 2985226; don't push mesh emitter transform to pixel shader unless used in the material graph

	#rb marcus.wassmer
	#tests editor game PC

Change 2986255 on 2016/05/22 by Mieszko.Zielinski

	Manually resolving conflict that stoped robomerge from Main to DG #Orion

	#rb none
	#test compile
	#codereview Jason.Bestimt

Change 2986209 on 2016/05/21 by Andrew.Grant

	Removed hitchunter logging from http thread
	#rb none
	#tests compiled

Change 2986202 on 2016/05/21 by Andrew.Grant

	Merging //UE4/Main @ 2981382 from //UE4/Orion-Staging
	#rb none
	#tests engine & game QA passed, built locally

Change 2985899 on 2016/05/20 by Rob.Cannaday

	Move PS4 HTTP processing to HTTP thread
	#tests golden path
	#rb dmitry.rekman

Change 2985884 on 2016/05/20 by Bart.Bressler

	Fix issue where oodle wasn't enabled in shipping correctly.

	#rb john.pollard
	#tests ran orion server in shipping and connecting with a client

Change 2985778 on 2016/05/20 by Jason.Bestimt

	#ROBOMERGE-AUTHOR: jason.bestimt
	#ORION_MAIN - Merge 26.2 @ CL 2985753

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2985774 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2985760 on 2016/05/20 by Rob.Cannaday

	Second pass on HTTP threading
	Move threaded objects into separate class, FHttpThread.  FCurlHttpThread derives from FHttpThread and the curl multi work is performed in FCurlHttpThread
	Removed code that limited number of curl easy requests that were added to the multi simultaneously / per frame as now that curl work is performed on a separate thread the performance no longer directly impacts the game thread
	Remove lock from CurlHttp and instead of use FThreadSafeCounter
	#rb dmitry.rekman
	#tests golden path (PC & PS4)

Change 2985658 on 2016/05/20 by John.Pollard

	Fixed issue with cached http replay results making time go backwards

	#rb none
	#tests replays

Change 2985640 on 2016/05/20 by Jason.Bestimt

	#ROBOMERGE-AUTHOR: david.ratti
	Ability System: call OnRemove event for gameplay cues that are mispredicted. Previously if a looping GC was predictively added, it would only get the OnRemove event if the replicated GC was removed. In the case of a mis prediction there is no replicated version, so the OnRemove was never called and cleanup was never happening.

	#rb FrankG
	#tests multi pie

	#ROBOMERGE-SOURCE: CL 2985638 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2985631 on 2016/05/20 by Jason.Bestimt

	#ROBOMERGE-AUTHOR: david.ratti
	Fast TArray serialization fixes:
	1. Fix case where Array ReplicationKey has changed no items between base and current state have changed. Previously server would early out and not send an update: but this needs be sent so that the client can potentially perform an implicit delete.

	This fixes the case where client TArray would have stale items hanging around until a new update was sent (which could potentially be never).

	2. Fix case where an array item would be deleted by both explicit delete and implicit delete: causing other items in the array to be deleted (!).

	#rb frankG, pollard
	#tests golden path
	[CodeReviewed] Bob.Tellez, Billy.Bramer, Ben.Zeigler

	#ROBOMERGE-SOURCE: CL 2985629 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2985542 on 2016/05/20 by Daniel.Lamb

	Added per package stats.
	Optimized cooker, moved FTextureSource::Compress from UTexture::Presave to UTexture::Serialize so we can avoid it in the cooker.
	#rb Robert.Manuszewski, Andrew.Grant, Marcus.Wassmer
	#test cook paragon, save packages paragon editor

Change 2985152 on 2016/05/20 by Jason.Bestimt

	#ROBOMERGE-AUTHOR: jason.bestimt
	#ORION_MAIN - Merge 26.2 @ CL 2985092

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2985150 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2985001 on 2016/05/20 by Chris.Wood

	Move fullcrashdump location for Paragon from QA deptartment storage to Paragon project storage. Changes CrashReportClient config only.

Change 2984839 on 2016/05/20 by Robert.Manuszewski

	Renaming some confusing function names and updating messages related to exception handling.

	#rb none
	#tests Cooked Win64 Client and Server, Tested crash reporting in cooked game

Change 2984517 on 2016/05/19 by Mike.Larson

	Adjusted 'PlatformHeadroom' audio volume settings to DB-3 on both Windows and PS4

Change 2983932 on 2016/05/19 by jason.bestimt

	#ORION_MAIN - Merge 26.2 @ CL 2983814

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2983921 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

	#ROBOMERGE-SAYS: Beep boop! I couldn't merge this change. Please do it yourself, human.
	#CodeReview: jason.bestimt

Change 2983864 on 2016/05/19 by Wes.Hunt

	Added missing assignment copy/move ops to FAnalyticsEventAttribute. Doh, should have looked at more usages of PLATFORM_COMPILER_HAS_DEFAULTED_FUNCTIONS...
	#codereview:steve.robb
	#rb none
	#tests compiled Orion Editor Win64

Change 2983780 on 2016/05/19 by Wes.Hunt

	Modernize FAnalyticsEventAttribute usage. #jira UE-30551.

	Replaced FAnalyticsEventAttribute various ctors with a perfect forwarding one that can convert them to strings.
	* The Name must be convertible to a string
	* The value must be convertible to a string via an AnalyticsConversion::ToString() overload.
	* Added/expanded the supported conversions to strings to analytics attribute values. See AnalyticsConversion.h which contains all the previously supported conversions and more.

	Added MakeAnalyticsEventAttributeArray(), which uses variadic templates to create an array of event attributes inline, which can be passed to RecordEvent[Json] and efficiently taken ownership of:
	    RecordEvent("EventName", MakeAnalyticsEventAttributeArray(
	        "Attr1", false,
	        "Attr2", 42.0,
	        "Attr3",  SomeMap,
	        "Attr4", SomeArray);

	#codereview:steve.robb,david.decker,sam.spiro
	SamS - you've been asking for better attribute conversion facilities for years. Finally got it, haha.
	SteveR - I only added copy/move ctors to FAnalyticsEventAttribute. Do I also need to explicitly add the copy/move assignment ops?
	DavidD - This will allow you to create attributes a lot more easily and efficiently now.
	#rb steve.robb,david.decker
	#tests orion editor runs

Change 2983702 on 2016/05/19 by Daniel.Lamb

	Renumbered cooking stats to be more correct
	#rb Wes.Hunt
	#test cook paragon.

Change 2983392 on 2016/05/19 by Jason.Bestimt

	#ROBOMERGE-AUTHOR: jason.bestimt
	#ORION_MAIN - Merge 26.2 @ CL 2983342

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2983391 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2982910 on 2016/05/18 by Marcus.Wassmer

	Don't fail entire deployments because obsolete manifest can't find the files to delete
	#rb none
	#codereview Peter.Sauerbrei
	#test none

Change 2982902 on 2016/05/18 by Marcus.Wassmer

	Disable HTTP networkfilesystem because it constantly crashes cookonthefly servers.  platform team is aware
	#rb none
	#test cookonthefly

Change 2982837 on 2016/05/18 by David.Ratti

	Spot merge safety check in ~FAgggregator. From BobT CL 2966255.

	#rb none
	#tests compile

Change 2982723 on 2016/05/18 by Wes.Hunt

	Analytics no longer adds IsEditor attribute to all events. Wasn't actually used by anyone anymore. #jira UE-30559
	#rb none
	#tests none

Change 2982716 on 2016/05/18 by Wes.Hunt

	Remove Analytics code to divert legacy code to source data collector. #jira UE-27794
	#rb none
	#tests run orion editor

Change 2982707 on 2016/05/18 by Wes.Hunt

	AnalyticsET support for arbitrary Json events. #jira UE-30375
	* AnalyticsET supports a new API, RecordEventJson.
	* API supports rvalue refs to avoid unnecessary copies of the attribute array.
		/**
		 * Sends an event where each attribute value is expected to be a string-ified Json value.
		 * Meaning, each attribute value can be an integer, float, bool, string,
		 * arbitrarily complex Json array, or arbitrarily complex Json object.
		 *
		 * The main thing to remember is that if you pass a Json string as an attribute value, it is up to you to
		 * quote the string, as the string you pass is expected to be able to be pasted directly into a Json value. ie:
		 *
		 * {
		 *     "EventName": "MyStringEvent",
		 *     "IntAttr": 42                 <--- You simply pass this in as "42"
		 *     "StringAttr": "SomeString"    <--- You must pass SomeString as "\"SomeString\""
		 * }
		 *
		 * @param EventName			The name of the event.
		 * @param AttributesJson	array of key/value attribute pairs where each value is a Json value (pure Json strings mustbe quoted by the caller).
		 */
		virtual void RecordEventJson(const FString& EventName, TArray<FAnalyticsEventAttribute>&& AttributesJson) = 0;
	#codereview:david.decker
	#rb david.decker
	#tests run orion editor

Change 2982057 on 2016/05/18 by David.Ratti

	GameplayCue loading - fix issue where GCM would invoke fully loaded when there were still UGameplayCueNotify_Statics to be loaded.

	#rb Ori.Cohen
	#tests golden path

Change 2981943 on 2016/05/18 by Jason.Bestimt

	#ROBOMERGE-AUTHOR: jason.bestimt
	#ORION_MAIN - Merge 26.2 @ CL 2981896

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2981942 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2981812 on 2016/05/18 by Robert.Manuszewski

	Suspending thread heartbeat when a message box is being displayed. Fixes false positives in hand detection.

	#rb Steve.Robb
	#tests Cooked and launched win64 client and server

Change 2981718 on 2016/05/18 by Robert.Manuszewski

	Changed how suspending/resuming thread heartbeat works: it will no longer create a heartbeat for a thread that hasn't sent any heartbeats yet.

	Reimplementing CL #2951209 from Dev-Core

	#rb Steve.Robb
	#tests None

Change 2981108 on 2016/05/17 by Wes.Hunt

	Fix perfect forwarding constructor in CookStats stuff.
	#rb none
	#tests investigating assembly output of cook stats code.
	#codereview:daniel.lamb

Change 2981028 on 2016/05/17 by Nick.Atamas

	Fixing hittest grid with virtual cursor.
	We now prefer any directly hit-test widgets with higher layer ids to those discovered through a distance search.

	#rb none
	#test Game menus
	#codereview Cody.Haskell,Matt.Kuhlenschmidt,Sammy.James,Dan.Hertzka

Change 2980963 on 2016/05/17 by Marc.Audy

	Fix shadowed variable
	#rb None
	#tests Compile

Change 2980917 on 2016/05/17 by Daniel.Lamb

	Removed script packages from unable to find packages warning.
	#rb Andrew.Grant
	#test cook paragon

Change 2980838 on 2016/05/17 by Marc.Audy

	Shave some time out of UPlayerInput::ProcessInputStack
	#rb Michael.Noland
	#tests Input works, performance improvement

Change 2980710 on 2016/05/17 by Michael.Noland

	Engine: Added helpful comments to the LOD visualization colors
	#rn
	#rb david.ratti
	#tests none

Change 2980706 on 2016/05/17 by Michael.Noland

	Engine: Removed unused setting bAllowDebugViewmodesOnConsoles (replaced some time ago by r.ForceDebugViewModes)
	#rn
	#rb david.ratti
	#tests Ran a cooked build with only r.ForceDebugViewModes=1 and confirmed that debug view modes still worked

Change 2980703 on 2016/05/17 by Michael.Noland

	Blueprints: Added support for emitting the Blueprint Description as tooltip metadata for the compiled Blueprint class (displayed in class pickers, etc...)
	#rb david.ratti
	#tests Tested on a Blueprint in Paragon
	#codereview mike.beach
	#rn

Change 2980702 on 2016/05/17 by Michael.Noland

	Rendering: Added ProfileGPU to the console autocomplete list
	#rb david.ratti
	#tests Tried typing Profile in the console and verified that the completion worked and tooltip was displayed
	#rn

Change 2980697 on 2016/05/17 by Michael.Noland

	Landscape: Added a 'resource' name for landscape to improve display in the mesh summary list of ProfileGPU
	#codereview jack.porter
	#rb david.ratti
	#tests Used ProfileGPU while standing on some terrain
	#rn

Change 2980692 on 2016/05/17 by Michael.Noland

	Landscape: Added a scalability CVar for landscape LOD biasing (r.LandscapeLODBias)
	#codereview jack.porter
	#rb david.ratti
	#tests Ran around in Paragon with various r.LandscapeLODBias values
	#rn

Change 2980630 on 2016/05/17 by Daniel.Lamb

	Added more warnings to help track down crash in paragon cook.
	#rb Andrew.Grant
	#test cook orion

Change 2980585 on 2016/05/17 by Jamie.Dale

	Fixed an issue where the editable text caret could become invisible when using UI scaling

	It's now clamped to a min draw size of 1px.

	#jira OR-18524
	#rb none
	#tests Built and ran the game. Verified that the caret now appears where it didn't before.

Change 2979908 on 2016/05/16 by jason.bestimt

	#ORION_MAIN - Merge 26.2 @ CL 2979859

	#RB:none
	#Tests:none

	#CodeReview: jaymee.stanford

Change 2979472 on 2016/05/16 by Nick.Atamas

	Added support for not clearing the render target when rendering a widget.

	#rb Nick.Darnell
	#test PIE w/ minimap

Change 2979434 on 2016/05/16 by Dmitry.Rekman

	Server: Add reporting of frame time without sleep.

	- Also add NumClients to each event so it's easy to filter events that didn't have 10 clients.

	#rb none
	#tests Built Linux server, ran match on a compatible content.

Change 2979267 on 2016/05/16 by Dmitry.Rekman

	Improvements in server hitch hunting / alerting.

	- Add an analytics event for unplayable conditions.
	- Send % of frames we hitched and time we spent hitching.
	- Send more detail about the machine.

	#rb none
	#tests Built Linux server and Windows client, played a match.

Change 2979030 on 2016/05/16 by Andrew.Grant

	Added quick way to reasign GUIDs (-AssignNewMapGuids) to map objects
	#rb none
	#tests used in editor

Change 2978914 on 2016/05/16 by David.Ratti

	Fix issue causing gameplay cue parameters not properly being passed through in cases where GA adds/removes gameplay cue.

	#rb DanY
	#tests multi pie
	#codereview Dave.Ewing

Change 2978681 on 2016/05/16 by Martin.Wilson

	Performance improvements for recalc required bones, removed a lot of unneeded array iterating. Reduces cost to roughly 30% of original code.

	#rb Thomas.Sarkanen
	#tests PS4 games, ded server

Change 2978098 on 2016/05/15 by Andrew.Grant

	Clearer error message
	#rb none
	#tests ran game

Change 2977597 on 2016/05/13 by Olaf.Piesche

	Merging using //UE4/Dev-Rendering->//Orion/Dev-General; fixes for beam particle selection code

	#rb martin.mittring
	#tests PC editor game

Change 2977531 on 2016/05/13 by Daniel.Lamb

	Added cooking stat for PreSave callback.
	#rb Wes.Hunt
	#test cook paragon

Change 2977340 on 2016/05/13 by jason.bestimt

	#ORION_MAIN - Merge 26@ CL 2977290

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2977329 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

	#ROBOMERGE-SAYS: Beep boop! I couldn't merge this change. Please do it yourself, human.
	#CodeReview: jason.bestimt

Change 2977139 on 2016/05/13 by Jason.Bestimt

	#ROBOMERGE-AUTHOR: jon.lietz
	OR-20830 only allow the periodic effects from a gameplay volume trigger first application triggers on BeginOverlap and Enable volume.

	#RB DaveR
	#test tracked when the poinson from an active card is applied and not applied

	#ROBOMERGE-SOURCE: CL 2977135 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2976741 on 2016/05/13 by David.Ratti

	GameplayCues that are triggered off animation notifies on the non-primary mesh will now properly attach to that non primary mesh.

	#rb lietz
	#test pie, golden path

Change 2976715 on 2016/05/13 by Jason.Bestimt

	#ROBOMERGE-AUTHOR: andrew.grant
	[NULL MERGE]
	Fix for bad merge
	#rb none
	#tests built automation

	#ROBOMERGE-SOURCE: CL 2976680 in //Orion/Release-0.26/... via CL 2976712 via CL 2976713 via CL 2976714
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2976679 on 2016/05/13 by Robert.Manuszewski

	Tweaks to DLL injection detection code

	#rb Steve.robb
	#tests cooked Win64 client

Change 2976670 on 2016/05/13 by Robert.Manuszewski

	UAT: Arxan upgrade to 3.9.1

	#rb Ben.Marsh
	#tests Win64 cooked client (test config)

Change 2976654 on 2016/05/13 by Graeme.Thornton

	Shadowed variable warning fix

	#rb none
	#tests compiled win64/ps4 client

Change 2976645 on 2016/05/13 by Graeme.Thornton

	Refactoring of resident mip calculations
	 - Cooker takes into account the same compression block thresholds that the runtime previously used
	 - Runtime doesn't attempt to calculate which mips to perma-load, but just looks at the ones whose bulk data is flagged as end-of-file or seperate-file

	#rb nick.penwarden
	#tests win64/ps4 client builds, golden path

[CL 3000872 by Andrew Grant in Main branch]
2016-06-04 01:20:53 -04:00

2425 lines
101 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PostProcessing.cpp: The center for all post processing activities.
=============================================================================*/
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "PostProcessing.h"
#include "PostProcessAA.h"
#if WITH_EDITOR
#include "PostProcessBufferInspector.h"
#endif
#include "PostProcessMaterial.h"
#include "PostProcessInput.h"
#include "PostProcessWeightedSampleSum.h"
#include "PostProcessBloomSetup.h"
#include "PostProcessMobile.h"
#include "PostProcessDownsample.h"
#include "PostProcessHistogram.h"
#include "PostProcessHistogramReduce.h"
#include "PostProcessVisualizeHDR.h"
#include "VisualizeShadingModels.h"
#include "PostProcessSelectionOutline.h"
#include "PostProcessGBufferHints.h"
#include "PostProcessVisualizeBuffer.h"
#include "PostProcessEyeAdaptation.h"
#include "PostProcessTonemap.h"
#include "PostProcessLensFlares.h"
#include "PostProcessLensBlur.h"
#include "PostProcessBokehDOF.h"
#include "PostProcessBokehDOFRecombine.h"
#include "PostProcessCombineLUTs.h"
#include "BatchedElements.h"
#include "ScreenRendering.h"
#include "PostProcessTemporalAA.h"
#include "PostProcessMotionBlur.h"
#include "PostProcessDOF.h"
#include "PostProcessCircleDOF.h"
#include "PostProcessUpscale.h"
#include "PostProcessHMD.h"
#include "PostProcessVisualizeComplexity.h"
#include "PostProcessCompositeEditorPrimitives.h"
#include "PostProcessPassThrough.h"
#include "PostProcessAmbientOcclusion.h"
#include "ScreenSpaceReflections.h"
#include "PostProcessTestImage.h"
#include "HighResScreenshot.h"
#include "PostProcessSubsurface.h"
#include "PostProcessMorpheus.h"
#include "IHeadMountedDisplay.h"
#include "BufferVisualizationData.h"
#include "PostProcessLpvIndirect.h"
/** The global center for all post processing activities. */
FPostProcessing GPostProcessing;
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
int32 GShaderModelDebug = 0;
static FAutoConsoleVariableRef CVarShaderModelDebug(
TEXT("r.ShaderModelDebug"),
GShaderModelDebug,
TEXT("Added logging to each frame that should allow to track down issues with View.ShadingModelMaskInView\n")
TEXT(" 0: off (default)\n")
TEXT(" 1: logging is enabled\n")
TEXT("-1: logging is enabled for one frame only"),
ECVF_Scalability | ECVF_RenderThreadSafe);
#endif
static TAutoConsoleVariable<int32> CVarUseMobileBloom(
TEXT("r.UseMobileBloom"),
0,
TEXT("HACK: Set to 1 to use mobile bloom."),
ECVF_Scalability | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarDepthOfFieldNearBlurSizeThreshold(
TEXT("r.DepthOfField.NearBlurSizeThreshold"),
0.01f,
TEXT("Sets the minimum near blur size before the effect is forcably disabled. Currently only affects Gaussian DOF.\n")
TEXT(" (default: 0.01)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarDepthOfFieldMaxSize(
TEXT("r.DepthOfField.MaxSize"),
100.0f,
TEXT("Allows to clamp the gaussian depth of field radius (for better performance), default: 100"),
ECVF_Scalability | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarRenderTargetSwitchWorkaround(
TEXT("r.RenderTargetSwitchWorkaround"),
0,
TEXT("Workaround needed on some mobile platforms to avoid a performance drop related to switching render targets.\n")
TEXT("Only enabled on some hardware. This affects the bloom quality a bit. It runs slower than the normal code path but\n")
TEXT("still faster as it avoids the many render target switches. (Default: 0)\n")
TEXT("We want this enabled (1) on all 32 bit iOS devices (implemented through DeviceProfiles)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarUpscaleQuality(
TEXT("r.Upscale.Quality"),
3,
TEXT("Defines the quality in which ScreenPercentage and WindowedFullscreen scales the 3d rendering.\n")
TEXT(" 0: Nearest filtering\n")
TEXT(" 1: Simple Bilinear\n")
TEXT(" 2: 4 tap bilinear\n")
TEXT(" 3: Directional blur with unsharp mask upsample. (default)"),
ECVF_Scalability | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CDownsampleQuality(
TEXT("r.Downsample.Quality"),
3,
TEXT("Defines the quality in which the Downsample passes. we might add more quality levels later.\n")
TEXT(" 0: low quality\n")
TEXT(">0: high quality (default: 3)\n"),
ECVF_Scalability | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarMotionBlurSoftEdgeSize(
TEXT("r.MotionBlurSoftEdgeSize"),
1.0f,
TEXT("Defines how wide the object motion blur is blurred (percent of screen width) to allow soft edge motion blur.\n")
TEXT("This scales linearly with the size (up to a maximum of 32 samples, 2.5 is about 18 samples) and with screen resolution\n")
TEXT("Smaller values are better for performance and provide more accurate motion vectors but the blurring outside the object is reduced.\n")
TEXT("If needed this can be exposed like the other motionblur settings.\n")
TEXT(" 0:off (not free and does never completely disable), >0, 1.0 (default)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarBloomCross(
TEXT("r.Bloom.Cross"),
0.0f,
TEXT("Experimental feature to give bloom kernel a more bright center sample (values between 1 and 3 work without causing aliasing)\n")
TEXT("Existing bloom get lowered to match the same brightness\n")
TEXT("<0 for a anisomorphic lens flare look (X only)\n")
TEXT(" 0 off (default)\n")
TEXT(">0 for a cross look (X and Y)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarTonemapperMergeMode(
TEXT("r.Tonemapper.MergeWithUpscale.Mode"),
0,
TEXT("ScreenPercentage upscale integrated into tonemapper pass (if certain conditions apply, e.g., no FXAA)\n")
TEXT(" if enabled both features are done in one pass (faster, affects post process passes after the tonemapper including material post process e.g. sharpen)\n")
TEXT(" 0: off, the features run in separate passes (default)\n")
TEXT(" 1: always enabled, try to merge the passes unless something makes it impossible\n")
TEXT(" 2: merge when the ratio of areas is above the r.Tonemapper.MergeWithUpscale.Threshold and it is otherwise possible"),
ECVF_Scalability | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarTonemapperMergeThreshold(
TEXT("r.Tonemapper.MergeWithUpscale.Threshold"),
0.49f,
TEXT("If r.Tonemapper.MergeWithUpscale.Mode is 2, the ratio of the area before upscale/downscale to the area afterwards\n")
TEXT("is compared to this threshold when deciding whether or not to merge the passes. The reasoning is that if the ratio\n")
TEXT("is too low, running the tonemapper on the higher number of pixels is more expensive than doing two passes\n")
TEXT("\n")
TEXT("Defauls to 0.49 (e.g., if r.ScreenPercentage is 70 or higher, try to merge)"),
ECVF_Scalability | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarMotionBlurScatter(
TEXT("r.MotionBlurScatter"),
0,
TEXT("Forces scatter based max velocity method (slower)."),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarMotionBlurSeparable(
TEXT("r.MotionBlurSeparable"),
0,
TEXT("Adds a second motion blur pass that smooths noise for a higher quality blur."),
ECVF_RenderThreadSafe
);
IMPLEMENT_SHADER_TYPE(,FPostProcessVS,TEXT("PostProcessBloom"),TEXT("MainPostprocessCommonVS"),SF_Vertex);
static bool HasPostProcessMaterial(FPostprocessContext& Context, EBlendableLocation InLocation);
// -------------------------------------------------------
FPostprocessContext::FPostprocessContext(FRHICommandListImmediate& InRHICmdList, FRenderingCompositionGraph& InGraph, const FViewInfo& InView)
: RHICmdList(InRHICmdList)
, Graph(InGraph)
, View(InView)
, SceneColor(0)
, SceneDepth(0)
{
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get_Todo_PassContext();
if(SceneContext.IsSceneColorAllocated())
{
SceneColor = Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(SceneContext.GetSceneColor()));
}
SceneDepth = Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(SceneContext.SceneDepthZ));
FinalOutput = FRenderingCompositeOutputRef(SceneColor);
}
// Array of downsampled color with optional log2 luminance stored in alpha
template <int32 DownSampleStages>
class TBloomDownSampleArray
{
public:
// Convenience typedefs
typedef FRenderingCompositeOutputRef FRenderingRefArray[DownSampleStages];
typedef TSharedPtr<TBloomDownSampleArray> Ptr;
// Constructor: Generates and registers the downsamples with the Context Graph.
TBloomDownSampleArray(FPostprocessContext& InContext, FRenderingCompositeOutputRef SourceDownsample, bool bGenerateLog2Alpha) :
bHasLog2Alpha(bGenerateLog2Alpha), Context(InContext)
{
static const TCHAR* PassLabels[] =
{ NULL, TEXT("BloomDownsample1"), TEXT("BloomDownsample2"), TEXT("BloomDownsample3"), TEXT("BloomDownsample4"), TEXT("BloomDownsample5") };
static_assert(ARRAY_COUNT(PassLabels) == DownSampleStages, "PassLabel count must be equal to DownSampleStages.");
// The first down sample is the input
PostProcessDownsamples[0] = SourceDownsample;
// Queue the down samples.
for (int i = 1; i < DownSampleStages; i++)
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessDownsample(PF_Unknown, 1, PassLabels[i]));
Pass->SetInput(ePId_Input0, PostProcessDownsamples[i - 1]);
PostProcessDownsamples[i] = FRenderingCompositeOutputRef(Pass);
// Add log2 data to the alpha channel after doing the 1st (i==1) down sample pass
if (bHasLog2Alpha && i == 1 ) {
FRenderingCompositePass* BasicEyeSetupPass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBasicEyeAdaptationSetUp());
BasicEyeSetupPass->SetInput(ePId_Input0, PostProcessDownsamples[i]);
PostProcessDownsamples[i] = FRenderingCompositeOutputRef(BasicEyeSetupPass);
}
}
}
// The number of elements in the array.
inline static int32 Num() { return DownSampleStages; }
// Member data kept public for simplicity
bool bHasLog2Alpha;
FPostprocessContext& Context;
FRenderingRefArray PostProcessDownsamples;
private:
// no default constructor.
TBloomDownSampleArray() {};
};
// Standard DownsampleArray shared by Bloom, Tint, and Eye-Adaptation.
typedef TBloomDownSampleArray<6/*DownSampleStages*/> FBloomDownSampleArray;
FBloomDownSampleArray::Ptr CreateDownSampleArray(FPostprocessContext& Context, FRenderingCompositeOutputRef SourceToDownSample, bool bAddLog2)
{
return FBloomDownSampleArray::Ptr(new FBloomDownSampleArray(Context, SourceToDownSample, bAddLog2));
}
static FRenderingCompositeOutputRef RenderHalfResBloomThreshold(FPostprocessContext& Context, FRenderingCompositeOutputRef SceneColorHalfRes, FRenderingCompositeOutputRef EyeAdaptation)
{
// with multiple view ports the Setup pass also isolates the view from the others which allows for simpler simpler/faster blur passes.
if(Context.View.FinalPostProcessSettings.BloomThreshold <= -1 && Context.View.Family->Views.Num() == 1)
{
// no need for threshold, we don't need this pass
return SceneColorHalfRes;
}
else
{
// todo: optimize later, the missing node causes some wrong behavior
// if(Context.View.FinalPostProcessSettings.BloomIntensity <= 0.0f)
// {
// // this pass is not required
// return FRenderingCompositeOutputRef();
// }
// bloom threshold
FRenderingCompositePass* PostProcessBloomSetup = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomSetup());
PostProcessBloomSetup->SetInput(ePId_Input0, SceneColorHalfRes);
PostProcessBloomSetup->SetInput(ePId_Input1, EyeAdaptation);
return FRenderingCompositeOutputRef(PostProcessBloomSetup);
}
}
// 2 pass Gaussian blur using uni-linear filtering
// @param CrossCenterWeight see r.Bloom.Cross (positive for X and Y, otherwise for X only)
static FRenderingCompositeOutputRef RenderGaussianBlur(
FPostprocessContext& Context,
const TCHAR* DebugNameX,
const TCHAR* DebugNameY,
const FRenderingCompositeOutputRef& Input,
float SizeScale,
FLinearColor Tint = FLinearColor::White,
const FRenderingCompositeOutputRef Additive = FRenderingCompositeOutputRef(),
float CrossCenterWeight = 0.0f)
{
// Gaussian blur in x
FRCPassPostProcessWeightedSampleSum* PostProcessBlurX = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessWeightedSampleSum(EFS_Horiz, EFCM_Weighted, SizeScale, DebugNameX));
PostProcessBlurX->SetInput(ePId_Input0, Input);
if(CrossCenterWeight > 0)
{
PostProcessBlurX->SetCrossCenterWeight(CrossCenterWeight);
}
// Gaussian blur in y
FRCPassPostProcessWeightedSampleSum* PostProcessBlurY = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessWeightedSampleSum(EFS_Vert, EFCM_Weighted, SizeScale, DebugNameY, Tint));
PostProcessBlurY->SetInput(ePId_Input0, FRenderingCompositeOutputRef(PostProcessBlurX));
PostProcessBlurY->SetInput(ePId_Input1, Additive);
PostProcessBlurY->SetCrossCenterWeight(FMath::Abs(CrossCenterWeight));
return FRenderingCompositeOutputRef(PostProcessBlurY);
}
// render one bloom pass and add another optional texture to it
static FRenderingCompositeOutputRef RenderBloom(
FPostprocessContext& Context,
const FRenderingCompositeOutputRef& PreviousBloom,
float Size,
FLinearColor Tint = FLinearColor::White,
const FRenderingCompositeOutputRef Additive = FRenderingCompositeOutputRef())
{
const float CrossBloom = CVarBloomCross.GetValueOnRenderThread();
return RenderGaussianBlur(Context, TEXT("BloomBlurX"), TEXT("BloomBlurY"), PreviousBloom, Size, Tint, Additive,CrossBloom);
}
static FRCPassPostProcessTonemap* AddTonemapper(
FPostprocessContext& Context,
const FRenderingCompositeOutputRef& BloomOutputCombined,
const FRenderingCompositeOutputRef& EyeAdaptation,
const EAutoExposureMethod& EyeAdapationMethodId,
const bool bDoGammaOnly)
{
const FEngineShowFlags& EngineShowFlags = Context.View.Family->EngineShowFlags;
FRenderingCompositePass* CombinedLUT = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessCombineLUTs(Context.View.GetShaderPlatform()));
const bool bDoEyeAdaptation = IsAutoExposureMethodSupported(Context.View.GetFeatureLevel(), EyeAdapationMethodId);
FRCPassPostProcessTonemap* PostProcessTonemap = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessTonemap(Context.View, bDoGammaOnly, bDoEyeAdaptation));
PostProcessTonemap->SetInput(ePId_Input0, Context.FinalOutput);
PostProcessTonemap->SetInput(ePId_Input1, BloomOutputCombined);
PostProcessTonemap->SetInput(ePId_Input2, EyeAdaptation);
PostProcessTonemap->SetInput(ePId_Input3, CombinedLUT);
Context.FinalOutput = FRenderingCompositeOutputRef(PostProcessTonemap);
return PostProcessTonemap;
}
#if WITH_EDITOR
static void AddSelectionOutline(FPostprocessContext& Context)
{
FRenderingCompositePass* SelectionColorPass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSelectionOutlineColor());
SelectionColorPass->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSelectionOutline());
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Node->SetInput(ePId_Input1, FRenderingCompositeOutputRef(FRenderingCompositeOutputRef(SelectionColorPass)));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
#endif
static void AddGammaOnlyTonemapper(FPostprocessContext& Context)
{
FRenderingCompositePass* PostProcessTonemap = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessTonemap(Context.View, true, false/*eye*/));
PostProcessTonemap->SetInput(ePId_Input0, Context.FinalOutput);
Context.FinalOutput = FRenderingCompositeOutputRef(PostProcessTonemap);
}
static void AddPostProcessAA(FPostprocessContext& Context)
{
// console variable override
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.PostProcessAAQuality"));
uint32 Quality = FMath::Clamp(CVar->GetValueOnRenderThread(), 1, 6);
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessAA(Quality));
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
static FRenderingCompositeOutputRef AddPostProcessBasicEyeAdaptation(const FViewInfo& View, FBloomDownSampleArray& BloomAndEyeDownSamples)
{
// Extract the context
FPostprocessContext& Context = BloomAndEyeDownSamples.Context;
// Extract the last (i.e. smallest) down sample
static const int32 FinalDSIdx = FBloomDownSampleArray::Num() - 1;
FRenderingCompositeOutputRef PostProcessPriorReduction = BloomAndEyeDownSamples.PostProcessDownsamples[FinalDSIdx];
// Compute the eye adaptation value based on average luminance from log2 luminance buffer, history, and specific shader parameters.
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBasicEyeAdaptation());
Node->SetInput(ePId_Input0, PostProcessPriorReduction);
return FRenderingCompositeOutputRef(Node);
}
static FRenderingCompositeOutputRef AddPostProcessHistogramEyeAdaptation(FPostprocessContext& Context, FRenderingCompositeOutputRef& Histogram)
{
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessEyeAdaptation());
Node->SetInput(ePId_Input0, Histogram);
return FRenderingCompositeOutputRef(Node);
}
static void AddVisualizeBloomSetup(FPostprocessContext& Context)
{
auto Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeBloomSetup());
Node->SetInput(ePId_Input0, Context.FinalOutput);
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
static void AddVisualizeBloomOverlay(FPostprocessContext& Context, FRenderingCompositeOutputRef& HDRColor, FRenderingCompositeOutputRef& BloomOutputCombined)
{
auto Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeBloomOverlay());
Node->SetInput(ePId_Input0, Context.FinalOutput);
Node->SetInput(ePId_Input1, HDRColor);
Node->SetInput(ePId_Input2, BloomOutputCombined);
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
static void AddPostProcessDepthOfFieldBokeh(FPostprocessContext& Context, FRenderingCompositeOutputRef& SeparateTranslucency, FRenderingCompositeOutputRef& VelocityInput)
{
// downsample, mask out the in focus part, depth in alpha
FRenderingCompositePass* DOFSetup = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOFSetup());
DOFSetup->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
DOFSetup->SetInput(ePId_Input1, FRenderingCompositeOutputRef(Context.SceneDepth));
FSceneViewState* ViewState = (FSceneViewState*)Context.View.State;
FRenderingCompositePass* DOFInputPass = DOFSetup;
if( Context.View.FinalPostProcessSettings.AntiAliasingMethod == AAM_TemporalAA && ViewState )
{
FRenderingCompositePass* HistoryInput;
if( ViewState->DOFHistoryRT && ViewState->bDOFHistory && !Context.View.bCameraCut )
{
HistoryInput = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessInput( ViewState->DOFHistoryRT ) );
}
else
{
// No history so use current as history
HistoryInput = DOFSetup;
}
FRenderingCompositePass* NodeTemporalAA = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessDOFTemporalAA );
NodeTemporalAA->SetInput( ePId_Input0, DOFSetup );
NodeTemporalAA->SetInput( ePId_Input1, FRenderingCompositeOutputRef( HistoryInput ) );
NodeTemporalAA->SetInput( ePId_Input2, FRenderingCompositeOutputRef( HistoryInput ) );
NodeTemporalAA->SetInput( ePId_Input3, VelocityInput );
DOFInputPass = NodeTemporalAA;
ViewState->bDOFHistory = true;
}
FRenderingCompositePass* NodeBlurred = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOF());
NodeBlurred->SetInput(ePId_Input0, DOFInputPass);
NodeBlurred->SetInput(ePId_Input1, Context.SceneColor);
NodeBlurred->SetInput(ePId_Input2, Context.SceneDepth);
FRenderingCompositePass* NodeRecombined = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOFRecombine());
NodeRecombined->SetInput(ePId_Input0, Context.FinalOutput);
NodeRecombined->SetInput(ePId_Input1, NodeBlurred);
NodeRecombined->SetInput(ePId_Input2, SeparateTranslucency);
Context.FinalOutput = FRenderingCompositeOutputRef(NodeRecombined);
}
static bool AddPostProcessDepthOfFieldGaussian(FPostprocessContext& Context, FDepthOfFieldStats& Out, FRenderingCompositeOutputRef& VelocityInput, FRenderingCompositeOutputRef& SeparateTranslucencyRef)
{
// GaussianDOFPass performs Gaussian setup, blur and recombine.
auto GaussianDOFPass = [&Context, &Out, &VelocityInput](FRenderingCompositeOutputRef& SeparateTranslucency, float FarSize, float NearSize)
{
// GenerateGaussianDOFBlur produces a blurred image from setup or potentially from taa result.
auto GenerateGaussianDOFBlur = [&Context, &VelocityInput](FRenderingCompositeOutputRef& DOFSetup, bool bFarPass, float BlurSize)
{
FSceneViewState* ViewState = (FSceneViewState*)Context.View.State;
FRenderingCompositeOutputRef DOFInputPass = DOFSetup;
const bool bMobileQuality = (Context.View.GetFeatureLevel() <= ERHIFeatureLevel::ES3_1);
if (Context.View.FinalPostProcessSettings.AntiAliasingMethod == AAM_TemporalAA && ViewState && !bMobileQuality)
{
// If no history use current as history
FRenderingCompositeOutputRef HistoryInput = DOFSetup;
TRefCountPtr<IPooledRenderTarget> DOFHistoryRT = bFarPass ? ViewState->DOFHistoryRT : ViewState->DOFHistoryRT2;
bool& bDOFHistory = bFarPass ? ViewState->bDOFHistory : ViewState->bDOFHistory2;
if (DOFHistoryRT && !bDOFHistory && !Context.View.bCameraCut)
{
HistoryInput = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(DOFHistoryRT));
}
FRenderingCompositePass* NodeTemporalAA = bFarPass ?
(FRenderingCompositePass*)Context.Graph.RegisterPass(new (FMemStack::Get()) FRCPassPostProcessDOFTemporalAA) :
(FRenderingCompositePass*)Context.Graph.RegisterPass(new (FMemStack::Get()) FRCPassPostProcessDOFTemporalAANear);
NodeTemporalAA->SetInput(ePId_Input0, DOFSetup);
NodeTemporalAA->SetInput(ePId_Input1, HistoryInput);
NodeTemporalAA->SetInput(ePId_Input2, HistoryInput);
NodeTemporalAA->SetInput(ePId_Input3, VelocityInput);
DOFInputPass = FRenderingCompositeOutputRef(NodeTemporalAA);
bDOFHistory = false;
}
const TCHAR* BlurDebugX = bFarPass ? TEXT("FarDOFBlurX") : TEXT("NearDOFBlurX");
const TCHAR* BlurDebugY = bFarPass ? TEXT("FarDOFBlurY") : TEXT("NearDOFBlurY");
return RenderGaussianBlur(Context, BlurDebugX, BlurDebugY, DOFInputPass, BlurSize);
};
const bool bFar = FarSize > 0.0f;
const bool bNear = NearSize > 0.0f;
const bool bCombinedNearFarPass = bFar && bNear;
const bool bMobileQuality = Context.View.FeatureLevel < ERHIFeatureLevel::SM4;
FRenderingCompositeOutputRef SetupInput(Context.FinalOutput);
if (bMobileQuality)
{
FRenderingCompositePass* HalfResFar = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessDownsample(PF_FloatRGBA, 1, TEXT("GausSetupHalfRes")));
HalfResFar->SetInput(ePId_Input0, FRenderingCompositeOutputRef(SetupInput));
SetupInput = HalfResFar;
}
FRenderingCompositePass* DOFSetupPass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessDOFSetup(bFar, bNear));
DOFSetupPass->SetInput(ePId_Input0, FRenderingCompositeOutputRef(SetupInput));
DOFSetupPass->SetInput(ePId_Input1, FRenderingCompositeOutputRef(Context.SceneDepth));
FRenderingCompositeOutputRef DOFSetupFar(DOFSetupPass);
FRenderingCompositeOutputRef DOFSetupNear(DOFSetupPass, bCombinedNearFarPass ? ePId_Output1 : ePId_Output0);
FRenderingCompositeOutputRef DOFFarBlur, DOFNearBlur;
if (bFar)
{
DOFFarBlur = GenerateGaussianDOFBlur(DOFSetupFar, true, FarSize);
}
if (bNear)
{
DOFNearBlur = GenerateGaussianDOFBlur(DOFSetupNear, false, NearSize);
}
FRenderingCompositePass* GaussianDOFRecombined = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessDOFRecombine());
GaussianDOFRecombined->SetInput(ePId_Input0, Context.FinalOutput);
GaussianDOFRecombined->SetInput(ePId_Input1, DOFFarBlur);
GaussianDOFRecombined->SetInput(ePId_Input2, DOFNearBlur);
GaussianDOFRecombined->SetInput(ePId_Input3, SeparateTranslucency);
Context.FinalOutput = FRenderingCompositeOutputRef(GaussianDOFRecombined);
};
float FarSize = Context.View.FinalPostProcessSettings.DepthOfFieldFarBlurSize;
float NearSize = Context.View.FinalPostProcessSettings.DepthOfFieldNearBlurSize;
const float MaxSize = CVarDepthOfFieldMaxSize.GetValueOnRenderThread();
FarSize = FMath::Min(FarSize, MaxSize);
NearSize = FMath::Min(NearSize, MaxSize);
Out.bFar = FarSize >= 0.01f;
{
const float CVarThreshold = CVarDepthOfFieldNearBlurSizeThreshold.GetValueOnRenderThread();
Out.bNear = (NearSize >= CVarThreshold);
}
if (Context.View.Family->EngineShowFlags.VisualizeDOF)
{
// no need for this pass
Out.bFar = false;
Out.bNear = false;
}
const bool bMobileQuality = Context.View.FeatureLevel < ERHIFeatureLevel::SM4;
const bool bShouldApplySepTrans = SeparateTranslucencyRef.IsValid() && !bMobileQuality;
const bool bCombineNearFarPass = !bShouldApplySepTrans && Out.bFar && Out.bNear;
if (bCombineNearFarPass)
{
GaussianDOFPass(SeparateTranslucencyRef, FarSize, NearSize);
}
else
{
FRenderingCompositeOutputRef SeparateTranslucency = SeparateTranslucencyRef;
if (Out.bFar)
{
GaussianDOFPass(SeparateTranslucency, FarSize, 0.0f);
SeparateTranslucency = FRenderingCompositeOutputRef();
}
if (Out.bNear)
{
GaussianDOFPass(SeparateTranslucency, 0.0f, NearSize);
}
}
return bShouldApplySepTrans && (Out.bFar || Out.bNear);
}
static void AddPostProcessDepthOfFieldCircle(FPostprocessContext& Context, FDepthOfFieldStats& Out, FRenderingCompositeOutputRef& VelocityInput)
{
if(Context.View.Family->EngineShowFlags.VisualizeDOF)
{
// no need for this pass
return;
}
FRenderingCompositePass* DOFSetup = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessCircleDOFSetup(false));
DOFSetup->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
DOFSetup->SetInput(ePId_Input1, FRenderingCompositeOutputRef(Context.SceneDepth));
FSceneViewState* ViewState = (FSceneViewState*)Context.View.State;
FRenderingCompositePass* DOFInputPass = DOFSetup;
if( Context.View.FinalPostProcessSettings.AntiAliasingMethod == AAM_TemporalAA && ViewState )
{
FRenderingCompositePass* HistoryInput;
if( ViewState->DOFHistoryRT && !ViewState->bDOFHistory && !Context.View.bCameraCut )
{
HistoryInput = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessInput( ViewState->DOFHistoryRT ) );
}
else
{
// No history so use current as history
HistoryInput = DOFSetup;
}
FRenderingCompositePass* NodeTemporalAA = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessDOFTemporalAA );
NodeTemporalAA->SetInput( ePId_Input0, DOFSetup );
NodeTemporalAA->SetInput( ePId_Input1, FRenderingCompositeOutputRef( HistoryInput ) );
NodeTemporalAA->SetInput( ePId_Input2, FRenderingCompositeOutputRef( HistoryInput ) );
NodeTemporalAA->SetInput( ePId_Input3, VelocityInput );
DOFInputPass = NodeTemporalAA;
ViewState->bDOFHistory = false;
}
FRenderingCompositeOutputRef Far;
FRenderingCompositeOutputRef Near;
FRenderingCompositePass* DOFNear = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessCircleDOFDilate());
DOFNear->SetInput(ePId_Input0, FRenderingCompositeOutputRef(DOFInputPass, ePId_Output0));
Near = FRenderingCompositeOutputRef(DOFNear, ePId_Output0);
FRenderingCompositePass* DOFApply = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessCircleDOF(false));
DOFApply->SetInput(ePId_Input0, FRenderingCompositeOutputRef(DOFInputPass, ePId_Output0));
DOFApply->SetInput(ePId_Input1, Near);
Far = FRenderingCompositeOutputRef(DOFApply, ePId_Output0);
FRenderingCompositePass* NodeRecombined = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessCircleDOFRecombine(false));
NodeRecombined->SetInput(ePId_Input0, Context.FinalOutput);
NodeRecombined->SetInput(ePId_Input1, Far);
Context.FinalOutput = FRenderingCompositeOutputRef(NodeRecombined);
}
static FRenderingCompositeOutputRef AddBloom(FBloomDownSampleArray& BloomDownSampleArray, bool bVisualizeBloom)
{
// Quality level to bloom stages table. Note: 0 is omitted, ensure element count tallys with the range documented with 'r.BloomQuality' definition.
const static uint32 BloomQualityStages[] =
{
3,// Q1
3,// Q2
4,// Q3
5,// Q4
6,// Q5
};
int32 BloomQuality;
{
// console variable override
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.BloomQuality"));
BloomQuality = FMath::Clamp(CVar->GetValueOnRenderThread(), 0, (int32)ARRAY_COUNT(BloomQualityStages));
}
// Extract the Context
FPostprocessContext& Context = BloomDownSampleArray.Context;
// Extract the downsample array.
FBloomDownSampleArray::FRenderingRefArray& PostProcessDownsamples = BloomDownSampleArray.PostProcessDownsamples;
FRenderingCompositeOutputRef BloomOutput;
if (BloomQuality == 0)
{
// No bloom, provide substitute source for lens flare.
BloomOutput = PostProcessDownsamples[0];
}
else
{
// Perform bloom blur + accumulate.
struct FBloomStage
{
float BloomSize;
const FLinearColor* Tint;
};
const FFinalPostProcessSettings& Settings = Context.View.FinalPostProcessSettings;
FBloomStage BloomStages[] =
{
{ Settings.Bloom6Size, &Settings.Bloom6Tint },
{ Settings.Bloom5Size, &Settings.Bloom5Tint },
{ Settings.Bloom4Size, &Settings.Bloom4Tint },
{ Settings.Bloom3Size, &Settings.Bloom3Tint },
{ Settings.Bloom2Size, &Settings.Bloom2Tint },
{ Settings.Bloom1Size, &Settings.Bloom1Tint },
};
static const uint32 NumBloomStages = ARRAY_COUNT(BloomStages);
const uint32 BloomStageCount = BloomQualityStages[BloomQuality - 1];
check(BloomStageCount <= NumBloomStages);
float TintScale = 1.0f / NumBloomStages;
for (uint32 i = 0, SourceIndex = NumBloomStages - 1; i < BloomStageCount; i++, SourceIndex--)
{
FBloomStage& Op = BloomStages[i];
FLinearColor Tint = (*Op.Tint) * TintScale;
if (bVisualizeBloom)
{
float LumScale = Tint.ComputeLuminance();
// R is used to pass down the reference, G is the emulated bloom
Tint.R = 0;
Tint.G = LumScale;
Tint.B = 0;
}
BloomOutput = RenderBloom(Context, PostProcessDownsamples[SourceIndex], Op.BloomSize * Settings.BloomSizeScale, Tint, BloomOutput);
}
}
// Lens Flares
FLinearColor LensFlareHDRColor = Context.View.FinalPostProcessSettings.LensFlareTint * Context.View.FinalPostProcessSettings.LensFlareIntensity;
static const int32 MaxLensFlareQuality = 3;
int32 LensFlareQuality;
{
// console variable override
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.LensFlareQuality"));
LensFlareQuality = FMath::Clamp(CVar->GetValueOnRenderThread(), 0, MaxLensFlareQuality);
}
if (!LensFlareHDRColor.IsAlmostBlack() && LensFlareQuality > 0 && !bVisualizeBloom)
{
float PercentKernelSize = Context.View.FinalPostProcessSettings.LensFlareBokehSize;
bool bLensBlur = PercentKernelSize > 0.3f;
FRenderingCompositePass* PostProcessFlares = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessLensFlares(bLensBlur ? 2.0f : 1.0f));
PostProcessFlares->SetInput(ePId_Input0, BloomOutput);
FRenderingCompositeOutputRef LensFlareInput = PostProcessDownsamples[MaxLensFlareQuality - LensFlareQuality];
if (bLensBlur)
{
float Threshold = Context.View.FinalPostProcessSettings.LensFlareThreshold;
FRenderingCompositePass* PostProcessLensBlur = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessLensBlur(PercentKernelSize, Threshold));
PostProcessLensBlur->SetInput(ePId_Input0, LensFlareInput);
PostProcessFlares->SetInput(ePId_Input1, FRenderingCompositeOutputRef(PostProcessLensBlur));
}
else
{
// fast: no blurring or blurring shared from bloom
PostProcessFlares->SetInput(ePId_Input1, LensFlareInput);
}
BloomOutput = FRenderingCompositeOutputRef(PostProcessFlares);
}
return BloomOutput;
}
static void AddTemporalAA( FPostprocessContext& Context, FRenderingCompositeOutputRef& VelocityInput )
{
check(VelocityInput.IsValid());
FSceneViewState* ViewState = (FSceneViewState*)Context.View.State;
FRenderingCompositePass* HistoryInput;
if( ViewState && ViewState->TemporalAAHistoryRT && !Context.View.bCameraCut )
{
HistoryInput = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessInput( ViewState->TemporalAAHistoryRT ) );
}
else
{
// No history so use current as history
HistoryInput = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessInput( FSceneRenderTargets::Get_Todo_PassContext().GetSceneColor() ) );
}
FRenderingCompositePass* TemporalAAPass = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessTemporalAA );
TemporalAAPass->SetInput( ePId_Input0, Context.FinalOutput );
TemporalAAPass->SetInput( ePId_Input1, FRenderingCompositeOutputRef( HistoryInput ) );
TemporalAAPass->SetInput( ePId_Input2, FRenderingCompositeOutputRef( HistoryInput ) );
TemporalAAPass->SetInput( ePId_Input3, VelocityInput );
Context.FinalOutput = FRenderingCompositeOutputRef( TemporalAAPass );
}
FPostProcessMaterialNode* IteratePostProcessMaterialNodes(const FFinalPostProcessSettings& Dest, EBlendableLocation InLocation, FBlendableEntry*& Iterator)
{
for(;;)
{
FPostProcessMaterialNode* DataPtr = Dest.BlendableManager.IterateBlendables<FPostProcessMaterialNode>(Iterator);
if(!DataPtr || DataPtr->GetLocation() == InLocation)
{
return DataPtr;
}
}
}
static FRenderingCompositePass* AddSinglePostProcessMaterial(FPostprocessContext& Context, EBlendableLocation InLocation)
{
if(!Context.View.Family->EngineShowFlags.PostProcessing || !Context.View.Family->EngineShowFlags.PostProcessMaterial)
{
return 0;
}
FBlendableEntry* Iterator = 0;
FPostProcessMaterialNode PPNode;
while(FPostProcessMaterialNode* Data = IteratePostProcessMaterialNodes(Context.View.FinalPostProcessSettings, InLocation, Iterator))
{
check(Data->GetMaterialInterface());
if(PPNode.IsValid())
{
FPostProcessMaterialNode::FCompare Dummy;
// take the one with the highest priority
if(!Dummy.operator()(PPNode, *Data))
{
continue;
}
}
PPNode = *Data;
}
if(UMaterialInterface* MaterialInterface = PPNode.GetMaterialInterface())
{
FMaterialRenderProxy* Proxy = MaterialInterface->GetRenderProxy(false);
check(Proxy);
const FMaterial* Material = Proxy->GetMaterial(Context.View.GetFeatureLevel());
check(Material);
if(Material->NeedsGBuffer())
{
// AdjustGBufferRefCount(-1) call is done when the pass gets executed
FSceneRenderTargets::Get_Todo_PassContext().AdjustGBufferRefCount(Context.RHICmdList, 1);
}
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessMaterial(MaterialInterface, Context.View.GetFeatureLevel()));
return Node;
}
return 0;
}
// simplied version of AddPostProcessMaterial(), side effect free
static bool HasPostProcessMaterial(FPostprocessContext& Context, EBlendableLocation InLocation)
{
if(!Context.View.Family->EngineShowFlags.PostProcessing || !Context.View.Family->EngineShowFlags.PostProcessMaterial)
{
return false;
}
if(Context.View.Family->EngineShowFlags.VisualizeBuffer)
{
// Apply requested material to the full screen
UMaterial* Material = GetBufferVisualizationData().GetMaterial(Context.View.CurrentBufferVisualizationMode);
if(Material && Material->BlendableLocation == InLocation)
{
return true;
}
}
FBlendableEntry* Iterator = 0;
FPostProcessMaterialNode* Data = IteratePostProcessMaterialNodes(Context.View.FinalPostProcessSettings, InLocation, Iterator);
if(Data)
{
return true;
}
return false;
}
static void AddPostProcessMaterial(FPostprocessContext& Context, EBlendableLocation InLocation, FRenderingCompositeOutputRef SeparateTranslucency, FRenderingCompositeOutputRef HDRColor = FRenderingCompositeOutputRef())
{
if( !Context.View.Family->EngineShowFlags.PostProcessing ||
!Context.View.Family->EngineShowFlags.PostProcessMaterial ||
Context.View.Family->EngineShowFlags.VisualizeShadingModels) // we should add more
{
return;
}
// hard coded - this should be a reasonable limit
const uint32 MAX_PPMATERIALNODES = 10;
FBlendableEntry* Iterator = 0;
FPostProcessMaterialNode PPNodes[MAX_PPMATERIALNODES];
uint32 PPNodeCount = 0;
bool bVisualizingBuffer = false;
if(Context.View.Family->EngineShowFlags.VisualizeBuffer)
{
// Apply requested material to the full screen
UMaterial* Material = GetBufferVisualizationData().GetMaterial(Context.View.CurrentBufferVisualizationMode);
if(Material && Material->BlendableLocation == InLocation)
{
PPNodes[0] = FPostProcessMaterialNode(Material, InLocation, Material->BlendablePriority);
++PPNodeCount;
bVisualizingBuffer = true;
}
}
for(;PPNodeCount < MAX_PPMATERIALNODES; ++PPNodeCount)
{
FPostProcessMaterialNode* Data = IteratePostProcessMaterialNodes(Context.View.FinalPostProcessSettings, InLocation, Iterator);
if(!Data)
{
break;
}
check(Data->GetMaterialInterface());
PPNodes[PPNodeCount] = *Data;
}
::Sort(PPNodes, PPNodeCount, FPostProcessMaterialNode::FCompare());
ERHIFeatureLevel::Type FeatureLevel = Context.View.GetFeatureLevel();
for(uint32 i = 0; i < PPNodeCount; ++i)
{
UMaterialInterface* MaterialInterface = PPNodes[i].GetMaterialInterface();
FMaterialRenderProxy* Proxy = MaterialInterface->GetRenderProxy(false);
check(Proxy);
const FMaterial* Material = Proxy->GetMaterial(Context.View.GetFeatureLevel());
check(Material);
if(Material->NeedsGBuffer())
{
// AdjustGBufferRefCount(-1) call is done when the pass gets executed
FSceneRenderTargets::Get_Todo_PassContext().AdjustGBufferRefCount(Context.RHICmdList, 1);
}
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessMaterial(MaterialInterface,FeatureLevel));
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
// We are binding separate translucency here because the post process SceneTexture node can reference
// the separate translucency buffers through ePId_Input1.
// TODO: Check if material actually uses this texture and only bind if needed.
Node->SetInput(ePId_Input1, SeparateTranslucency);
// This input is only needed for visualization and frame dumping
if (bVisualizingBuffer)
{
Node->SetInput(ePId_Input2, HDRColor);
}
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
}
static void AddHighResScreenshotMask(FPostprocessContext& Context, FRenderingCompositeOutputRef& SeparateTranslucencyInput)
{
if (Context.View.Family->EngineShowFlags.HighResScreenshotMask != 0)
{
check(Context.View.FinalPostProcessSettings.HighResScreenshotMaterial);
FRenderingCompositeOutputRef Input = Context.FinalOutput;
FRenderingCompositePass* CompositePass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessMaterial(Context.View.FinalPostProcessSettings.HighResScreenshotMaterial, Context.View.GetFeatureLevel()));
CompositePass->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Input));
Context.FinalOutput = FRenderingCompositeOutputRef(CompositePass);
if (GIsHighResScreenshot)
{
check(Context.View.FinalPostProcessSettings.HighResScreenshotMaskMaterial);
FRenderingCompositePass* MaskPass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessMaterial(Context.View.FinalPostProcessSettings.HighResScreenshotMaskMaterial, Context.View.GetFeatureLevel()));
MaskPass->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Input));
CompositePass->AddDependency(MaskPass);
FString BaseFilename = FString(Context.View.FinalPostProcessSettings.BufferVisualizationDumpBaseFilename);
MaskPass->SetOutputColorArray(ePId_Output0, FScreenshotRequest::GetHighresScreenshotMaskColorArray());
}
}
// Draw the capture region if a material was supplied
if (Context.View.FinalPostProcessSettings.HighResScreenshotCaptureRegionMaterial)
{
auto Material = Context.View.FinalPostProcessSettings.HighResScreenshotCaptureRegionMaterial;
FRenderingCompositePass* CaptureRegionVisualizationPass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessMaterial(Material, Context.View.GetFeatureLevel()));
CaptureRegionVisualizationPass->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(CaptureRegionVisualizationPass);
auto Proxy = Material->GetRenderProxy(false);
const FMaterial* RendererMaterial = Proxy->GetMaterial(Context.View.GetFeatureLevel());
if (RendererMaterial->NeedsGBuffer())
{
// AdjustGBufferRefCount(-1) call is done when the pass gets executed
FSceneRenderTargets::Get_Todo_PassContext().AdjustGBufferRefCount(Context.RHICmdList, 1);
}
}
}
static void AddGBufferVisualizationOverview(FPostprocessContext& Context, FRenderingCompositeOutputRef& SeparateTranslucencyInput, FRenderingCompositeOutputRef& HDRColorInput)
{
static const auto CVarDumpFrames = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.BufferVisualizationDumpFrames"));
static const auto CVarDumpFramesAsHDR = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.BufferVisualizationDumpFramesAsHDR"));
bool bVisualizationEnabled = Context.View.Family->EngineShowFlags.VisualizeBuffer;
bool bOverviewModeEnabled = bVisualizationEnabled && (Context.View.CurrentBufferVisualizationMode == NAME_None);
bool bHighResBufferVisualizationDumpRequried = GIsHighResScreenshot && GetHighResScreenshotConfig().bDumpBufferVisualizationTargets;
bool bDumpFrames = Context.View.FinalPostProcessSettings.bBufferVisualizationDumpRequired && (CVarDumpFrames->GetValueOnRenderThread() || bHighResBufferVisualizationDumpRequried);
bool bCaptureAsHDR = CVarDumpFramesAsHDR->GetValueOnRenderThread() || GetHighResScreenshotConfig().bCaptureHDR;
FString BaseFilename;
if (bDumpFrames)
{
BaseFilename = FString(Context.View.FinalPostProcessSettings.BufferVisualizationDumpBaseFilename);
}
if (bDumpFrames || bVisualizationEnabled)
{
FRenderingCompositeOutputRef IncomingStage = Context.FinalOutput;
if (bDumpFrames || bOverviewModeEnabled)
{
FRenderingCompositePass* CompositePass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeBuffer());
CompositePass->SetInput(ePId_Input0, FRenderingCompositeOutputRef(IncomingStage));
Context.FinalOutput = FRenderingCompositeOutputRef(CompositePass);
EPixelFormat OutputFormat = bCaptureAsHDR ? PF_FloatRGBA : PF_Unknown;
// Loop over materials, creating stages for generation and downsampling of the tiles.
for (TArray<UMaterialInterface*>::TConstIterator It = Context.View.FinalPostProcessSettings.BufferVisualizationOverviewMaterials.CreateConstIterator(); It; ++It)
{
auto MaterialInterface = *It;
if (MaterialInterface)
{
// Apply requested material
FRenderingCompositePass* MaterialPass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessMaterial(*It, Context.View.GetFeatureLevel(), OutputFormat));
MaterialPass->SetInput(ePId_Input0, FRenderingCompositeOutputRef(IncomingStage));
MaterialPass->SetInput(ePId_Input1, FRenderingCompositeOutputRef(SeparateTranslucencyInput));
MaterialPass->SetInput(ePId_Input2, FRenderingCompositeOutputRef(HDRColorInput));
auto Proxy = MaterialInterface->GetRenderProxy(false);
const FMaterial* Material = Proxy->GetMaterial(Context.View.GetFeatureLevel());
if (Material->NeedsGBuffer())
{
// AdjustGBufferRefCount(-1) call is done when the pass gets executed
FSceneRenderTargets::Get_Todo_PassContext().AdjustGBufferRefCount(Context.RHICmdList, 1);
}
if (BaseFilename.Len())
{
// First off, allow the user to specify the pass as a format arg (using {material})
TMap<FString, FStringFormatArg> FormatMappings;
FormatMappings.Add(TEXT("material"), (*It)->GetName());
FString MaterialFilename = FString::Format(*BaseFilename, FormatMappings);
// If the format made no change to the string, we add the name of the material to ensure uniqueness
if (MaterialFilename == BaseFilename)
{
MaterialFilename = BaseFilename + TEXT("_") + (*It)->GetName();
}
MaterialFilename.Append(TEXT(".png"));
MaterialPass->SetOutputDumpFilename(ePId_Output0, *MaterialFilename);
}
// If the overview mode is activated, downsample the material pass to quarter size
if (bOverviewModeEnabled)
{
// Down-sample to 1/2 size
FRenderingCompositePass* HalfSize = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessDownsample(PF_Unknown, 0, TEXT("MaterialHalfSize")));
HalfSize->SetInput(ePId_Input0, FRenderingCompositeOutputRef(MaterialPass));
// Down-sample to 1/4 size
FRenderingCompositePass* QuarterSize = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessDownsample(PF_Unknown, 0, TEXT("MaterialQuarterSize")));
QuarterSize->SetInput(ePId_Input0, FRenderingCompositeOutputRef(HalfSize));
// Mark the quarter size target as the dependency for the composite pass
((FRCPassPostProcessVisualizeBuffer*)CompositePass)->AddVisualizationBuffer(FRenderingCompositeOutputRef(QuarterSize), (*It)->GetName());
}
else
{
// We are just dumping the frames, so the material pass is the dependency of the composite
CompositePass->AddDependency(MaterialPass);
}
}
else
{
if (bOverviewModeEnabled)
{
((FRCPassPostProcessVisualizeBuffer*)CompositePass)->AddVisualizationBuffer(FRenderingCompositeOutputRef(), FString());
}
}
}
}
}
}
// could be moved into the graph
// allows for Framebuffer blending optimization with the composition graph
void OverrideRenderTarget(FRenderingCompositeOutputRef It, TRefCountPtr<IPooledRenderTarget>& RT, FPooledRenderTargetDesc& Desc)
{
for(;;)
{
It.GetOutput()->PooledRenderTarget = RT;
It.GetOutput()->RenderTargetDesc = Desc;
if(!It.GetPass()->FrameBufferBlendingWithInput0())
{
break;
}
It = *It.GetPass()->GetInput(ePId_Input0);
}
}
bool FPostProcessing::AllowFullPostProcessing(const FViewInfo& View, ERHIFeatureLevel::Type FeatureLevel)
{
return View.Family->EngineShowFlags.PostProcessing
&& FeatureLevel >= ERHIFeatureLevel::SM4
&& !View.Family->EngineShowFlags.VisualizeDistanceFieldAO
&& !View.Family->EngineShowFlags.VisualizeDistanceFieldGI
&& !View.Family->EngineShowFlags.VisualizeShadingModels
&& !View.Family->EngineShowFlags.VisualizeMeshDistanceFields;
}
void FPostProcessing::Process(FRHICommandListImmediate& RHICmdList, const FViewInfo& View, TRefCountPtr<IPooledRenderTarget>& VelocityRT)
{
QUICK_SCOPE_CYCLE_COUNTER( STAT_PostProcessing_Process );
check(IsInRenderingThread());
const auto FeatureLevel = View.GetFeatureLevel();
GRenderTargetPool.AddPhaseEvent(TEXT("PostProcessing"));
// This page: https://udn.epicgames.com/Three/RenderingOverview#Rendering%20state%20defaults
// describes what state a pass can expect and to what state it need to be set back.
// All post processing is happening on the render thread side. All passes can access FinalPostProcessSettings and all
// view settings. Those are copies for the RT then never get access by the main thread again.
// Pointers to other structures might be unsafe to touch.
// so that the passes can register themselves to the graph
{
FMemMark Mark(FMemStack::Get());
FRenderingCompositePassContext CompositeContext(RHICmdList, View);
FPostprocessContext Context(RHICmdList, CompositeContext.Graph, View);
// not always valid
FRenderingCompositeOutputRef HDRColor;
// not always valid
FRenderingCompositeOutputRef HistogramOverScreen;
// not always valid
FRenderingCompositeOutputRef Histogram;
class FAutoExposure
{
public:
FAutoExposure(const FViewInfo& InView) :
MethodId(GetAutoExposureMethod(InView))
{}
// distinguish between Basic and Histogram-based
EAutoExposureMethod MethodId;
// not always valid
FRenderingCompositeOutputRef EyeAdaptation;
} AutoExposure(View);
// not always valid
FRenderingCompositeOutputRef SeparateTranslucency;
// optional
FRenderingCompositeOutputRef BloomOutputCombined;
// not always valid
FRenderingCompositePass* VelocityFlattenPass = 0;
// in the following code some feature might set this to false
bool bAllowTonemapper = FeatureLevel >= ERHIFeatureLevel::SM4;
//
bool bStereoRenderingAndHMD = View.Family->EngineShowFlags.StereoRendering && View.Family->EngineShowFlags.HMDDistortion;
//
FRCPassPostProcessUpscale::PaniniParams PaniniConfig(View);
//
EStereoscopicPass StereoPass = View.StereoPass;
//
FSceneViewState* ViewState = (FSceneViewState*)Context.View.State;
//
bool bDoScreenPercentage;
{
//
bool bHMDWantsUpscale = bStereoRenderingAndHMD && GEngine->HMDDevice->NeedsUpscalePostProcessPass();
// Do not use upscale if SeparateRenderTarget is in use! (stereo rendering wants to control this)
bool bAllowScreenPercentage = bHMDWantsUpscale || !View.Family->EngineShowFlags.StereoRendering || (!View.Family->EngineShowFlags.HMDDistortion && !View.Family->bUseSeparateRenderTarget);
// is Upscale from a lower resolution needed and allowed
bDoScreenPercentage = bAllowScreenPercentage && (View.UnscaledViewRect != View.ViewRect);
}
{
if (FSceneRenderTargets::Get(RHICmdList).SeparateTranslucencyRT)
{
FRenderingCompositePass* NodeSeparateTranslucency = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(FSceneRenderTargets::Get(RHICmdList).SeparateTranslucencyRT));
SeparateTranslucency = FRenderingCompositeOutputRef(NodeSeparateTranslucency);
// make sure we only release if this is the last view we're rendering
int32 LastView = View.Family->Views.Num() - 1;
if (View.Family->Views[LastView] == &View)
{
// the node keeps another reference so the RT will not be release too early
FSceneRenderTargets::Get(RHICmdList).FreeSeparateTranslucency();
check(!FSceneRenderTargets::Get(RHICmdList).SeparateTranslucencyRT);
}
}
}
bool bVisualizeHDR = View.Family->EngineShowFlags.VisualizeHDR && FeatureLevel >= ERHIFeatureLevel::SM5;
bool bVisualizeBloom = View.Family->EngineShowFlags.VisualizeBloom && FeatureLevel >= ERHIFeatureLevel::SM4;
bool bVisualizeMotionBlur = View.Family->EngineShowFlags.VisualizeMotionBlur && FeatureLevel >= ERHIFeatureLevel::SM4;
if(bVisualizeHDR || bVisualizeBloom || bVisualizeMotionBlur)
{
bAllowTonemapper = false;
}
FRCPassPostProcessTonemap* Tonemapper = 0;
// add the passes we want to add to the graph (commenting a line means the pass is not inserted into the graph) ---------
if (AllowFullPostProcessing(View, FeatureLevel))
{
FRenderingCompositeOutputRef VelocityInput;
if(VelocityRT)
{
VelocityInput = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(VelocityRT));
}
AddPostProcessMaterial(Context, BL_BeforeTranslucency, SeparateTranslucency);
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.DepthOfFieldQuality"));
check(CVar)
bool bDepthOfField = View.Family->EngineShowFlags.DepthOfField && CVar->GetValueOnRenderThread() > 0;
FDepthOfFieldStats DepthOfFieldStat;
bool bSepTransWasApplied = false;
if(bDepthOfField && View.FinalPostProcessSettings.DepthOfFieldMethod != DOFM_BokehDOF)
{
bool bCircleDOF = View.FinalPostProcessSettings.DepthOfFieldMethod == DOFM_CircleDOF;
if(!bCircleDOF)
{
if(VelocityInput.IsValid())
{
bSepTransWasApplied = AddPostProcessDepthOfFieldGaussian(Context, DepthOfFieldStat, VelocityInput, SeparateTranslucency);
}
else
{
// todo: black/white default is a compositing graph feature, no need to hook up a node
// black is how we clear the velocity buffer so this means no velocity
FRenderingCompositePass* NoVelocity = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(GSystemTextures.BlackDummy));
FRenderingCompositeOutputRef NoVelocityRef(NoVelocity);
bSepTransWasApplied = AddPostProcessDepthOfFieldGaussian(Context, DepthOfFieldStat, NoVelocityRef, SeparateTranslucency);
}
}
else
{
if(VelocityInput.IsValid())
{
AddPostProcessDepthOfFieldCircle(Context, DepthOfFieldStat, VelocityInput);
}
else
{
// todo: black/white default is a compositing graph feature, no need to hook up a node
// black is how we clear the velocity buffer so this means no velocity
FRenderingCompositePass* NoVelocity = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(GSystemTextures.BlackDummy));
FRenderingCompositeOutputRef NoVelocityRef(NoVelocity);
AddPostProcessDepthOfFieldCircle(Context, DepthOfFieldStat, NoVelocityRef);
}
}
}
bool bBokehDOF = bDepthOfField
&& View.FinalPostProcessSettings.DepthOfFieldScale > 0
&& View.FinalPostProcessSettings.DepthOfFieldMethod == DOFM_BokehDOF
&& !Context.View.Family->EngineShowFlags.VisualizeDOF;
if(bBokehDOF)
{
if(VelocityInput.IsValid())
{
AddPostProcessDepthOfFieldBokeh(Context, SeparateTranslucency, VelocityInput);
}
else
{
// todo: black/white default is a compositing graph feature, no need to hook up a node
// black is how we clear the velocity buffer so this means no velocity
FRenderingCompositePass* NoVelocity = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(GSystemTextures.BlackDummy));
FRenderingCompositeOutputRef NoVelocityRef(NoVelocity);
AddPostProcessDepthOfFieldBokeh(Context, SeparateTranslucency, NoVelocityRef);
}
bSepTransWasApplied = true;
}
if(SeparateTranslucency.IsValid() && !bSepTransWasApplied)
{
// separate translucency is done here or in AddPostProcessDepthOfFieldBokeh()
FRenderingCompositePass* NodeRecombined = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOFRecombine());
NodeRecombined->SetInput(ePId_Input0, Context.FinalOutput);
NodeRecombined->SetInput(ePId_Input2, SeparateTranslucency);
Context.FinalOutput = FRenderingCompositeOutputRef(NodeRecombined);
}
AddPostProcessMaterial(Context, BL_BeforeTonemapping, SeparateTranslucency);
EAntiAliasingMethod AntiAliasingMethod = Context.View.FinalPostProcessSettings.AntiAliasingMethod;
if( AntiAliasingMethod == AAM_TemporalAA && ViewState)
{
if(VelocityInput.IsValid())
{
AddTemporalAA( Context, VelocityInput );
}
else
{
// black is how we clear the velocity buffer so this means no velocity
FRenderingCompositePass* NoVelocity = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(GSystemTextures.BlackDummy));
FRenderingCompositeOutputRef NoVelocityRef(NoVelocity);
AddTemporalAA( Context, NoVelocityRef );
}
}
if(IsMotionBlurEnabled(View) && VelocityInput.IsValid() && !bVisualizeMotionBlur)
{
// Motion blur
FRenderingCompositeOutputRef MaxTileVelocity;
{
check(!VelocityFlattenPass);
VelocityFlattenPass = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessVelocityFlatten() );
VelocityFlattenPass->SetInput( ePId_Input0, VelocityInput );
VelocityFlattenPass->SetInput( ePId_Input1, Context.SceneDepth );
VelocityInput = FRenderingCompositeOutputRef( VelocityFlattenPass, ePId_Output0 );
MaxTileVelocity = FRenderingCompositeOutputRef( VelocityFlattenPass, ePId_Output1 );
}
const float SizeX = View.ViewRect.Width();
// 0:no 1:full screen width, percent conversion
float MaxVelocity = View.FinalPostProcessSettings.MotionBlurMax / 100.0f;
float MaxVelocityTiles = MaxVelocity * SizeX * (0.5f / 16.0f);
float MaxTileDistGathered = 3.0f;
if( MaxVelocityTiles > MaxTileDistGathered || CVarMotionBlurScatter.GetValueOnRenderThread() || (ViewState && ViewState->bSequencerIsPaused) )
{
FRenderingCompositePass* VelocityScatterPass = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessVelocityScatter() );
VelocityScatterPass->SetInput( ePId_Input0, MaxTileVelocity );
MaxTileVelocity = FRenderingCompositeOutputRef( VelocityScatterPass );
}
else
{
FRenderingCompositePass* VelocityGatherPass = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessVelocityGather() );
VelocityGatherPass->SetInput( ePId_Input0, MaxTileVelocity );
MaxTileVelocity = FRenderingCompositeOutputRef( VelocityGatherPass );
}
bool bTwoPass = CVarMotionBlurSeparable.GetValueOnRenderThread() != 0;
{
FRenderingCompositePass* MotionBlurPass = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessMotionBlur( GetMotionBlurQualityFromCVar(), bTwoPass ? 0 : -1 ) );
MotionBlurPass->SetInput( ePId_Input0, Context.FinalOutput );
MotionBlurPass->SetInput( ePId_Input1, Context.SceneDepth );
MotionBlurPass->SetInput( ePId_Input2, VelocityInput );
MotionBlurPass->SetInput( ePId_Input3, MaxTileVelocity );
Context.FinalOutput = FRenderingCompositeOutputRef( MotionBlurPass );
}
if( bTwoPass )
{
FRenderingCompositePass* MotionBlurPass = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessMotionBlur( GetMotionBlurQualityFromCVar(), 1 ) );
MotionBlurPass->SetInput( ePId_Input0, Context.FinalOutput );
MotionBlurPass->SetInput( ePId_Input1, Context.SceneDepth );
MotionBlurPass->SetInput( ePId_Input2, VelocityInput );
MotionBlurPass->SetInput( ePId_Input3, MaxTileVelocity );
Context.FinalOutput = FRenderingCompositeOutputRef( MotionBlurPass );
}
}
if(VelocityInput.IsValid() && bVisualizeMotionBlur)
{
auto VisualizePass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeMotionBlur());
VisualizePass->SetInput(ePId_Input0, Context.FinalOutput);
VisualizePass->SetInput(ePId_Input1, Context.SceneDepth);
VisualizePass->SetInput(ePId_Input2, VelocityInput);
Context.FinalOutput = FRenderingCompositeOutputRef(VisualizePass);
}
if(bVisualizeBloom)
{
AddVisualizeBloomSetup(Context);
}
// down sample Scene color from full to half res
FRenderingCompositeOutputRef SceneColorHalfRes;
{
int32 DownsampleQuality = FMath::Clamp(CDownsampleQuality.GetValueOnRenderThread(), 0, 1);
// doesn't have to be as high quality as the Scene color
FRenderingCompositePass* HalfResPass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessDownsample(PF_FloatRGB, DownsampleQuality, TEXT("SceneColorHalfRes")));
HalfResPass->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
SceneColorHalfRes = FRenderingCompositeOutputRef(HalfResPass);
}
{
bool bHistogramNeeded = false;
if (View.Family->EngineShowFlags.EyeAdaptation && (AutoExposure.MethodId == EAutoExposureMethod::AEM_Histogram)
&& View.FinalPostProcessSettings.AutoExposureMinBrightness < View.FinalPostProcessSettings.AutoExposureMaxBrightness
&& !View.bIsSceneCapture // Eye adaption is not available for scene captures.
&& !bVisualizeBloom)
{
bHistogramNeeded = true;
}
if(!bAllowTonemapper)
{
bHistogramNeeded = false;
}
if(View.Family->EngineShowFlags.VisualizeHDR)
{
bHistogramNeeded = true;
}
if (!GIsHighResScreenshot && bHistogramNeeded && FeatureLevel >= ERHIFeatureLevel::SM5 && StereoPass != eSSP_RIGHT_EYE)
{
FRenderingCompositePass* NodeHistogram = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessHistogram());
NodeHistogram->SetInput(ePId_Input0, SceneColorHalfRes);
HistogramOverScreen = FRenderingCompositeOutputRef(NodeHistogram);
FRenderingCompositePass* NodeHistogramReduce = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessHistogramReduce());
NodeHistogramReduce->SetInput(ePId_Input0, NodeHistogram);
Histogram = FRenderingCompositeOutputRef(NodeHistogramReduce);
}
}
// Compute DownSamples passes used by bloom, tint and eye-adaptation if possible.
FBloomDownSampleArray::Ptr BloomAndEyeDownSamplesPtr;
if (View.FinalPostProcessSettings.BloomIntensity > 0.f) // do bloom
{
// No Threshold: We can share with Eye-Adaptation.
if (Context.View.FinalPostProcessSettings.BloomThreshold <= -1 && Context.View.Family->Views.Num() == 1)
{
if (!GIsHighResScreenshot && View.State &&
(StereoPass != eSSP_RIGHT_EYE) &&
(AutoExposure.MethodId == EAutoExposureMethod::AEM_Basic))
{
BloomAndEyeDownSamplesPtr = CreateDownSampleArray(Context, SceneColorHalfRes, true /*bGenerateLog2Alpha*/);
}
}
}
// some views don't have a state (thumbnail rendering)
if(!GIsHighResScreenshot && View.State && (StereoPass != eSSP_RIGHT_EYE))
{
const bool bUseBasicEyeAdaptation = (AutoExposure.MethodId == EAutoExposureMethod::AEM_Basic);
if (bUseBasicEyeAdaptation) // log average ps reduction ( non histogram )
{
if (!BloomAndEyeDownSamplesPtr.IsValid())
{
// need downsamples for eye-adaptation.
FBloomDownSampleArray::Ptr EyeDownSamplesPtr = CreateDownSampleArray(Context, SceneColorHalfRes, true /*bGenerateLog2Alpha*/);
AutoExposure.EyeAdaptation = AddPostProcessBasicEyeAdaptation(View, *EyeDownSamplesPtr);
}
else
{
// Use the alpha channel in the last downsample (smallest) to compute eye adaptations values.
AutoExposure.EyeAdaptation = AddPostProcessBasicEyeAdaptation(View, *BloomAndEyeDownSamplesPtr);
}
}
else // Use histogram version version
{
// we always add eye adaptation, if the engine show flag is disabled we set the ExposureScale in the texture to a fixed value
AutoExposure.EyeAdaptation = AddPostProcessHistogramEyeAdaptation(Context, Histogram);
}
}
if(View.FinalPostProcessSettings.BloomIntensity > 0.0f)
{
if (CVarUseMobileBloom.GetValueOnRenderThread() == 0)
{
if (!BloomAndEyeDownSamplesPtr.IsValid())
{
FRenderingCompositeOutputRef HalfResBloomThreshold = RenderHalfResBloomThreshold(Context, SceneColorHalfRes, AutoExposure.EyeAdaptation);
BloomAndEyeDownSamplesPtr = CreateDownSampleArray(Context, HalfResBloomThreshold, false /*bGenerateLog2Alpha*/);
}
BloomOutputCombined = AddBloom(*BloomAndEyeDownSamplesPtr, bVisualizeBloom);
}
else
{
FIntPoint PrePostSourceViewportSize = View.ViewRect.Size();
// Bloom.
FRenderingCompositeOutputRef PostProcessDownsample2;
FRenderingCompositeOutputRef PostProcessDownsample3;
FRenderingCompositeOutputRef PostProcessDownsample4;
FRenderingCompositeOutputRef PostProcessDownsample5;
FRenderingCompositeOutputRef PostProcessUpsample4;
FRenderingCompositeOutputRef PostProcessUpsample3;
FRenderingCompositeOutputRef PostProcessUpsample2;
FRenderingCompositeOutputRef PostProcessSunMerge;
float DownScale = 0.66f * 4.0f;
// Downsample by 2
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomDownES2(PrePostSourceViewportSize/4, DownScale));
Pass->SetInput(ePId_Input0, SceneColorHalfRes);
PostProcessDownsample2 = FRenderingCompositeOutputRef(Pass);
}
// Downsample by 2
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomDownES2(PrePostSourceViewportSize/8, DownScale));
Pass->SetInput(ePId_Input0, PostProcessDownsample2);
PostProcessDownsample3 = FRenderingCompositeOutputRef(Pass);
}
// Downsample by 2
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomDownES2(PrePostSourceViewportSize/16, DownScale));
Pass->SetInput(ePId_Input0, PostProcessDownsample3);
PostProcessDownsample4 = FRenderingCompositeOutputRef(Pass);
}
// Downsample by 2
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomDownES2(PrePostSourceViewportSize/32, DownScale));
Pass->SetInput(ePId_Input0, PostProcessDownsample4);
PostProcessDownsample5 = FRenderingCompositeOutputRef(Pass);
}
const FFinalPostProcessSettings& Settings = Context.View.FinalPostProcessSettings;
float UpScale = 0.66f * 2.0f;
// Upsample by 2
{
FVector4 TintA = FVector4(Settings.Bloom4Tint.R, Settings.Bloom4Tint.G, Settings.Bloom4Tint.B, 0.0f);
FVector4 TintB = FVector4(Settings.Bloom5Tint.R, Settings.Bloom5Tint.G, Settings.Bloom5Tint.B, 0.0f);
TintA *= View.FinalPostProcessSettings.BloomIntensity;
TintB *= View.FinalPostProcessSettings.BloomIntensity;
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomUpES2(PrePostSourceViewportSize/32, FVector2D(UpScale, UpScale), TintA, TintB));
Pass->SetInput(ePId_Input0, PostProcessDownsample4);
Pass->SetInput(ePId_Input1, PostProcessDownsample5);
PostProcessUpsample4 = FRenderingCompositeOutputRef(Pass);
}
// Upsample by 2
{
FVector4 TintA = FVector4(Settings.Bloom3Tint.R, Settings.Bloom3Tint.G, Settings.Bloom3Tint.B, 0.0f);
TintA *= View.FinalPostProcessSettings.BloomIntensity;
FVector4 TintB = FVector4(1.0f, 1.0f, 1.0f, 0.0f);
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomUpES2(PrePostSourceViewportSize/16, FVector2D(UpScale, UpScale), TintA, TintB));
Pass->SetInput(ePId_Input0, PostProcessDownsample3);
Pass->SetInput(ePId_Input1, PostProcessUpsample4);
PostProcessUpsample3 = FRenderingCompositeOutputRef(Pass);
}
// Upsample by 2
{
FVector4 TintA = FVector4(Settings.Bloom2Tint.R, Settings.Bloom2Tint.G, Settings.Bloom2Tint.B, 0.0f);
TintA *= View.FinalPostProcessSettings.BloomIntensity;
// Scaling Bloom2 by extra factor to match filter area difference between PC default and mobile.
TintA *= 0.5;
FVector4 TintB = FVector4(1.0f, 1.0f, 1.0f, 0.0f);
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomUpES2(PrePostSourceViewportSize/8, FVector2D(UpScale, UpScale), TintA, TintB));
Pass->SetInput(ePId_Input0, PostProcessDownsample2);
Pass->SetInput(ePId_Input1, PostProcessUpsample3);
PostProcessUpsample2 = FRenderingCompositeOutputRef(Pass);
}
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSunMergeES2(PrePostSourceViewportSize));
Pass->SetInput(ePId_Input1, SceneColorHalfRes);
Pass->SetInput(ePId_Input2, PostProcessUpsample2);
PostProcessSunMerge = FRenderingCompositeOutputRef(Pass);
BloomOutputCombined = PostProcessSunMerge;
}
}
}
HDRColor = Context.FinalOutput;
if(bAllowTonemapper)
{
auto Node = AddSinglePostProcessMaterial(Context, BL_ReplacingTonemapper);
if(Node)
{
// a custom tonemapper is provided
Node->SetInput(ePId_Input0, Context.FinalOutput);
// We are binding separate translucency here because the post process SceneTexture node can reference
// the separate translucency buffers through ePId_Input1.
// TODO: Check if material actually uses this texture and only bind if needed.
Node->SetInput(ePId_Input1, SeparateTranslucency);
Node->SetInput(ePId_Input2, BloomOutputCombined);
Context.FinalOutput = Node;
}
else
{
Tonemapper = AddTonemapper(Context, BloomOutputCombined, AutoExposure.EyeAdaptation, AutoExposure.MethodId, false);
}
}
if(AntiAliasingMethod == AAM_FXAA)
{
AddPostProcessAA(Context);
}
if(bDepthOfField && Context.View.Family->EngineShowFlags.VisualizeDOF)
{
FRenderingCompositePass* VisualizeNode = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeDOF(DepthOfFieldStat));
VisualizeNode->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(VisualizeNode);
bAllowTonemapper = false;
}
}
else
{
check(!FSceneRenderTargets::Get(RHICmdList).SeparateTranslucencyRT);
if (SeparateTranslucency.IsValid())
{
// separate translucency is done here or in AddPostProcessDepthOfFieldBokeh()
FRenderingCompositePass* NodeRecombined = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOFRecombine());
NodeRecombined->SetInput(ePId_Input0, Context.FinalOutput);
NodeRecombined->SetInput(ePId_Input2, SeparateTranslucency);
Context.FinalOutput = FRenderingCompositeOutputRef(NodeRecombined);
}
// Shader complexity does not actually output a color
if (!View.Family->EngineShowFlags.ShaderComplexity)
{
AddGammaOnlyTonemapper(Context);
}
}
bool bResultsUpsampled = false;
if(View.Family->EngineShowFlags.StationaryLightOverlap)
{
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeComplexity(GEngine->StationaryLightOverlapColors, FVisualizeComplexityApplyPS::CS_RAMP, 1.f, false));
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.SceneColor));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
bResultsUpsampled = true;
}
const EDebugViewShaderMode DebugViewShaderMode = View.Family->GetDebugViewShaderMode();
if(DebugViewShaderMode == DVSM_QuadComplexity)
{
float ComplexityScale = 1.f / (float)(GEngine->QuadComplexityColors.Num() - 1) / NormalizedQuadComplexityValue; // .1f comes from the values used in LightAccumulator_GetResult
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeComplexity(GEngine->QuadComplexityColors, FVisualizeComplexityApplyPS::CS_STAIR, ComplexityScale, true));
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
bResultsUpsampled = true;
}
if(DebugViewShaderMode == DVSM_ShaderComplexity || DebugViewShaderMode == DVSM_ShaderComplexityContainedQuadOverhead || DebugViewShaderMode == DVSM_ShaderComplexityBleedingQuadOverhead)
{
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeComplexity(GEngine->ShaderComplexityColors, FVisualizeComplexityApplyPS::CS_RAMP, 1.f, true));
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
bResultsUpsampled = true;
}
if(View.Family->EngineShowFlags.VisualizeLightCulling)
{
float ComplexityScale = 1.f / (float)(GEngine->LightComplexityColors.Num() - 1) / .1f; // .1f comes from the values used in LightAccumulator_GetResult
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeComplexity(GEngine->LightComplexityColors, FVisualizeComplexityApplyPS::CS_LINEAR, ComplexityScale, false));
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.SceneColor));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
bResultsUpsampled = true;
}
if(View.Family->EngineShowFlags.VisualizeLPV && !View.Family->EngineShowFlags.VisualizeHDR)
{
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeLPV());
Node->SetInput(ePId_Input0, Context.FinalOutput);
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
bResultsUpsampled = true;
}
#if WITH_EDITOR
// Show the selection outline if it is in the editor and we aren't in wireframe
// If the engine is in demo mode and game view is on we also do not show the selection outline
if ( GIsEditor
&& View.Family->EngineShowFlags.SelectionOutline
&& !(View.Family->EngineShowFlags.Wireframe)
&& ( !GIsDemoMode || ( GIsDemoMode && !View.Family->EngineShowFlags.Game ) )
&& !bVisualizeBloom
&& !View.Family->EngineShowFlags.VisualizeHDR)
{
// Selection outline is after bloom, but before AA
AddSelectionOutline(Context);
}
// Composite editor primitives if we had any to draw and compositing is enabled
if (FSceneRenderer::ShouldCompositeEditorPrimitives(View) && !bVisualizeBloom)
{
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessCompositeEditorPrimitives(true));
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
#endif
if(View.Family->EngineShowFlags.VisualizeShadingModels && FeatureLevel >= ERHIFeatureLevel::SM4)
{
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeShadingModels(RHICmdList));
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
if (View.Family->EngineShowFlags.GBufferHints && FeatureLevel >= ERHIFeatureLevel::SM4)
{
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessGBufferHints(RHICmdList));
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
// Ideally without lighting as we want the emissive, we should do that later.
Node->SetInput(ePId_Input1, FRenderingCompositeOutputRef(Context.SceneColor));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
AddPostProcessMaterial(Context, BL_AfterTonemapping, SeparateTranslucency, HDRColor);
#if WITH_EDITOR
//Inspect the Final color, GBuffer and HDR
//No more postprocess Final color should be the real one
//The HDR was save before the tonemapping
//GBuffer should not be change during post process
if (View.bUsePixelInspector && FeatureLevel >= ERHIFeatureLevel::SM4)
{
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBufferInspector(RHICmdList));
Node->SetInput(ePId_Input0, Context.FinalOutput);
Node->SetInput(ePId_Input1, HDRColor);
Node->SetInput(ePId_Input2, Context.SceneColor);
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
#endif //WITH_EDITOR
if(bVisualizeBloom)
{
AddVisualizeBloomOverlay(Context, HDRColor, BloomOutputCombined);
}
if (View.Family->EngineShowFlags.VisualizeSSS)
{
// the setup pass also does visualization, based on EngineShowFlags.VisualizeSSS
FRenderingCompositePass* PassVisualize = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSubsurfaceVisualize(RHICmdList));
PassVisualize->SetInput(ePId_Input0, Context.FinalOutput);
Context.FinalOutput = FRenderingCompositeOutputRef(PassVisualize);
}
AddGBufferVisualizationOverview(Context, SeparateTranslucency, HDRColor);
if (bStereoRenderingAndHMD)
{
FRenderingCompositePass* Node = NULL;
const EHMDDeviceType::Type DeviceType = GEngine->HMDDevice->GetHMDDeviceType();
if(DeviceType == EHMDDeviceType::DT_OculusRift)
{
Node = Context.Graph.RegisterPass(new FRCPassPostProcessHMD());
}
else if(DeviceType == EHMDDeviceType::DT_Morpheus)
{
#if MORPHEUS_ENGINE_DISTORTION
FRCPassPostProcessMorpheus* MorpheusPass = new FRCPassPostProcessMorpheus();
MorpheusPass->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Node = Context.Graph.RegisterPass(MorpheusPass);
#endif
}
if(Node)
{
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
}
if(bVisualizeHDR)
{
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeHDR());
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Node->SetInput(ePId_Input1, Histogram);
Node->SetInput(ePId_Input2, HDRColor);
Node->SetInput(ePId_Input3, HistogramOverScreen);
Node->AddDependency(AutoExposure.EyeAdaptation);
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
if(View.Family->EngineShowFlags.TestImage && FeatureLevel >= ERHIFeatureLevel::SM4)
{
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessTestImage());
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
AddHighResScreenshotMask(Context, SeparateTranslucency);
if(bDoScreenPercentage && !bResultsUpsampled)
{
// Check if we can save the Upscale pass and do it in the Tonemapper to save performance
if(Tonemapper && !PaniniConfig.IsEnabled() && !Tonemapper->bDoGammaOnly)
{
if (Context.FinalOutput.GetPass() == Tonemapper)
{
const int32 TonemapperMergeMode = CVarTonemapperMergeMode.GetValueOnRenderThread();
bool bCombineTonemapperAndUpsample = false;
if (TonemapperMergeMode == 1)
{
bCombineTonemapperAndUpsample = true;
}
else if (TonemapperMergeMode == 2)
{
const float TonemapperMergeThreshold = CVarTonemapperMergeThreshold.GetValueOnRenderThread();
const float AreaRatio = View.ViewRect.Area() / (float)View.UnscaledViewRect.Area();
bCombineTonemapperAndUpsample = AreaRatio > TonemapperMergeThreshold;
}
if (bCombineTonemapperAndUpsample)
{
Tonemapper->bDoScreenPercentageInTonemapper = true;
// the following pass is no longer needed
bDoScreenPercentage = false;
}
}
}
if (PaniniConfig.IsEnabled() || bDoScreenPercentage)
{
int32 UpscaleQuality = CVarUpscaleQuality.GetValueOnRenderThread();
UpscaleQuality = FMath::Clamp(UpscaleQuality, 0, 3);
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessUpscale(UpscaleQuality, PaniniConfig));
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput)); // Bilinear sampling.
Node->SetInput(ePId_Input1, FRenderingCompositeOutputRef(Context.FinalOutput)); // Point sampling.
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
}
// After the graph is built but before the graph is processed.
// If a postprocess material is using a GBuffer it adds the refcount int FRCPassPostProcessMaterial::Process()
// and when it gets processed it removes the refcount
// We only release the GBuffers after the last view was processed (SplitScreen)
if(View.Family->Views[View.Family->Views.Num() - 1] == &View)
{
// Generally we no longer need the GBuffers, anyone that wants to keep the GBuffers for longer should have called AdjustGBufferRefCount(1) to keep it for longer
// and call AdjustGBufferRefCount(-1) once it's consumed. This needs to happen each frame. PostProcessMaterial do that automatically
FSceneRenderTargets::Get_Todo_PassContext().AdjustGBufferRefCount(RHICmdList, -1);
}
// The graph setup should be finished before this line ----------------------------------------
{
// currently created on the heap each frame but View.Family->RenderTarget could keep this object and all would be cleaner
TRefCountPtr<IPooledRenderTarget> Temp;
FSceneRenderTargetItem Item;
Item.TargetableTexture = (FTextureRHIRef&)View.Family->RenderTarget->GetRenderTargetTexture();
Item.ShaderResourceTexture = (FTextureRHIRef&)View.Family->RenderTarget->GetRenderTargetTexture();
FPooledRenderTargetDesc Desc;
// Texture could be bigger than viewport
if (View.Family->RenderTarget->GetRenderTargetTexture())
{
Desc.Extent.X = View.Family->RenderTarget->GetRenderTargetTexture()->GetSizeX();
Desc.Extent.Y = View.Family->RenderTarget->GetRenderTargetTexture()->GetSizeY();
}
else
{
Desc.Extent = View.Family->RenderTarget->GetSizeXY();
}
// todo: this should come from View.Family->RenderTarget
Desc.Format = PF_B8G8R8A8;
Desc.NumMips = 1;
Desc.DebugName = TEXT("FinalPostProcessColor");
GRenderTargetPool.CreateUntrackedElement(Desc, Temp, Item);
OverrideRenderTarget(Context.FinalOutput, Temp, Desc);
// execute the graph/DAG
CompositeContext.Process(Context.FinalOutput.GetPass(), TEXT("PostProcessing"));
}
}
GRenderTargetPool.AddPhaseEvent(TEXT("AfterPostprocessing"));
// End of frame, we don't need it anymore
FSceneRenderTargets::Get(RHICmdList).FreeSeparateTranslucencyDepth();
}
static bool IsGaussianActive(FPostprocessContext& Context)
{
float FarSize = Context.View.FinalPostProcessSettings.DepthOfFieldFarBlurSize;
float NearSize = Context.View.FinalPostProcessSettings.DepthOfFieldNearBlurSize;
float MaxSize = CVarDepthOfFieldMaxSize.GetValueOnRenderThread();
FarSize = FMath::Min(FarSize, MaxSize);
NearSize = FMath::Min(NearSize, MaxSize);
const float CVarThreshold = CVarDepthOfFieldNearBlurSizeThreshold.GetValueOnRenderThread();
if ((FarSize < 0.01f) && (NearSize < CVarThreshold))
{
return false;
}
return true;
}
void FPostProcessing::ProcessES2(FRHICommandListImmediate& RHICmdList, const FViewInfo& View, bool bUsedFramebufferFetch)
{
check(IsInRenderingThread());
// This page: https://udn.epicgames.com/Three/RenderingOverview#Rendering%20state%20defaults
// describes what state a pass can expect and to what state it need to be set back.
// All post processing is happening on the render thread side. All passes can access FinalPostProcessSettings and all
// view settings. Those are copies for the RT then never get access by the main thread again.
// Pointers to other structures might be unsafe to touch.
// so that the passes can register themselves to the graph
{
FMemMark Mark(FMemStack::Get());
FRenderingCompositePassContext CompositeContext(RHICmdList, View);
FPostprocessContext Context(RHICmdList, CompositeContext.Graph, View);
FRenderingCompositeOutputRef BloomOutput;
FRenderingCompositeOutputRef DofOutput;
bool bUseAa = View.FinalPostProcessSettings.AntiAliasingMethod == AAM_TemporalAA;
// AA with Mobile32bpp mode requires this outside of bUsePost.
if(bUseAa)
{
// Handle pointer swap for double buffering.
FSceneViewState* ViewState = (FSceneViewState*)View.State;
if(ViewState)
{
// Note that this drops references to the render targets from two frames ago. This
// causes them to be added back to the pool where we can grab them again.
ViewState->MobileAaBloomSunVignette1 = ViewState->MobileAaBloomSunVignette0;
ViewState->MobileAaColor1 = ViewState->MobileAaColor0;
}
}
const FIntPoint FinalTargetSize = View.Family->RenderTarget->GetSizeXY();
FIntRect FinalOutputViewRect = View.ViewRect;
FIntPoint PrePostSourceViewportSize = View.ViewRect.Size();
// ES2 preview uses a subsection of the scene RT, bUsedFramebufferFetch == true deals with this case.
FIntPoint SceneColorSize = FSceneRenderTargets::Get(RHICmdList).GetBufferSizeXY();
bool bViewRectSource = bUsedFramebufferFetch || SceneColorSize != PrePostSourceViewportSize;
// add the passes we want to add to the graph (commenting a line means the pass is not inserted into the graph) ---------
if( View.Family->EngineShowFlags.PostProcessing )
{
bool bUseMosaic = IsMobileHDRMosaic();
bool bUseEncodedHDR = IsMobileHDR32bpp() && !bUseMosaic;
bool bUseSun = !bUseEncodedHDR && View.bLightShaftUse;
bool bUseDof = !bUseEncodedHDR && View.FinalPostProcessSettings.DepthOfFieldScale > 0.0f && !Context.View.Family->EngineShowFlags.VisualizeDOF;
bool bUseBloom = View.FinalPostProcessSettings.BloomIntensity > 0.0f;
bool bUseVignette = View.FinalPostProcessSettings.VignetteIntensity > 0.0f;
bool bWorkaround = CVarRenderTargetSwitchWorkaround.GetValueOnRenderThread() != 0;
// Use original mobile Dof on ES2 devices regardless of bMobileHQGaussian.
// HQ gaussian
bool bUseMobileDof = bUseDof && (!View.FinalPostProcessSettings.bMobileHQGaussian || (Context.View.GetFeatureLevel() < ERHIFeatureLevel::ES3_1));
// This is a workaround to avoid a performance cliff when using many render targets.
bool bUseBloomSmall = bUseBloom && !bUseSun && !bUseDof && bWorkaround;
bool bUsePost = bUseSun | bUseDof | bUseBloom | bUseVignette;
// Post is not supported on ES2 devices using mosaic.
bUsePost &= !bUseMosaic;
bUsePost &= IsMobileHDR();
if(bUsePost)
{
// Skip this pass if the pass was done prior before resolve.
if ((!bUsedFramebufferFetch) && (bUseSun || bUseDof))
{
// Convert depth to {circle of confusion, sun shaft intensity} before resolve.
// FRenderingCompositePass* PostProcessSunMask = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSunMaskES2(PrePostSourceViewportSize, false));
FRenderingCompositePass* PostProcessSunMask = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSunMaskES2(SceneColorSize, false));
PostProcessSunMask->SetInput(ePId_Input0, Context.FinalOutput);
Context.FinalOutput = FRenderingCompositeOutputRef(PostProcessSunMask);
//@todo Ronin sunmask pass isnt clipping to image only.
}
FRenderingCompositeOutputRef PostProcessBloomSetup;
if (bUseSun || bUseMobileDof || bUseBloom)
{
if(bUseBloomSmall)
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomSetupSmallES2(PrePostSourceViewportSize, bViewRectSource));
Pass->SetInput(ePId_Input0, Context.FinalOutput);
PostProcessBloomSetup = FRenderingCompositeOutputRef(Pass);
}
else
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomSetupES2(FinalOutputViewRect, bViewRectSource));
Pass->SetInput(ePId_Input0, Context.FinalOutput);
PostProcessBloomSetup = FRenderingCompositeOutputRef(Pass);
}
}
if (bUseDof)
{
if (bUseMobileDof)
{
// Near dilation circle of confusion size.
// Samples at 1/16 area, writes to 1/16 area.
FRenderingCompositeOutputRef PostProcessNear;
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessDofNearES2(FinalOutputViewRect.Size()));
Pass->SetInput(ePId_Input0, PostProcessBloomSetup);
PostProcessNear = FRenderingCompositeOutputRef(Pass);
}
// DOF downsample pass.
// Samples at full resolution, writes to 1/4 area.
FRenderingCompositeOutputRef PostProcessDofDown;
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessDofDownES2(FinalOutputViewRect, bViewRectSource));
Pass->SetInput(ePId_Input0, Context.FinalOutput);
Pass->SetInput(ePId_Input1, PostProcessNear);
PostProcessDofDown = FRenderingCompositeOutputRef(Pass);
}
// DOF blur pass.
// Samples at 1/4 area, writes to 1/4 area.
FRenderingCompositeOutputRef PostProcessDofBlur;
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessDofBlurES2(FinalOutputViewRect.Size()));
Pass->SetInput(ePId_Input0, PostProcessDofDown);
Pass->SetInput(ePId_Input1, PostProcessNear);
PostProcessDofBlur = FRenderingCompositeOutputRef(Pass);
DofOutput = PostProcessDofBlur;
}
}
else
{
// black is how we clear the velocity buffer so this means no velocity
FRenderingCompositePass* NoVelocity = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(GSystemTextures.BlackDummy));
FRenderingCompositeOutputRef NoVelocityRef(NoVelocity);
if(View.FinalPostProcessSettings.DepthOfFieldMethod == DOFM_Gaussian && IsGaussianActive(Context))
{
FDepthOfFieldStats DepthOfFieldStat;
FRenderingCompositeOutputRef DummySeparateTranslucency;
AddPostProcessDepthOfFieldGaussian(Context, DepthOfFieldStat, NoVelocityRef, DummySeparateTranslucency);
}
}
}
// Bloom.
FRenderingCompositeOutputRef PostProcessDownsample2;
FRenderingCompositeOutputRef PostProcessDownsample3;
FRenderingCompositeOutputRef PostProcessDownsample4;
FRenderingCompositeOutputRef PostProcessDownsample5;
FRenderingCompositeOutputRef PostProcessUpsample4;
FRenderingCompositeOutputRef PostProcessUpsample3;
FRenderingCompositeOutputRef PostProcessUpsample2;
if(bUseBloomSmall)
{
float DownScale = 0.66f * 4.0f;
// Downsample by 2
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomDownES2(PrePostSourceViewportSize/4, DownScale * 2.0f));
Pass->SetInput(ePId_Input0, PostProcessBloomSetup);
PostProcessDownsample2 = FRenderingCompositeOutputRef(Pass);
}
}
if(bUseBloom && (!bUseBloomSmall))
{
float DownScale = 0.66f * 4.0f;
// Downsample by 2
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomDownES2(PrePostSourceViewportSize/4, DownScale));
Pass->SetInput(ePId_Input0, PostProcessBloomSetup);
PostProcessDownsample2 = FRenderingCompositeOutputRef(Pass);
}
// Downsample by 2
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomDownES2(PrePostSourceViewportSize/8, DownScale));
Pass->SetInput(ePId_Input0, PostProcessDownsample2);
PostProcessDownsample3 = FRenderingCompositeOutputRef(Pass);
}
// Downsample by 2
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomDownES2(PrePostSourceViewportSize/16, DownScale));
Pass->SetInput(ePId_Input0, PostProcessDownsample3);
PostProcessDownsample4 = FRenderingCompositeOutputRef(Pass);
}
// Downsample by 2
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomDownES2(PrePostSourceViewportSize/32, DownScale));
Pass->SetInput(ePId_Input0, PostProcessDownsample4);
PostProcessDownsample5 = FRenderingCompositeOutputRef(Pass);
}
const FFinalPostProcessSettings& Settings = Context.View.FinalPostProcessSettings;
float UpScale = 0.66f * 2.0f;
// Upsample by 2
{
FVector4 TintA = FVector4(Settings.Bloom4Tint.R, Settings.Bloom4Tint.G, Settings.Bloom4Tint.B, 0.0f);
FVector4 TintB = FVector4(Settings.Bloom5Tint.R, Settings.Bloom5Tint.G, Settings.Bloom5Tint.B, 0.0f);
TintA *= View.FinalPostProcessSettings.BloomIntensity;
TintB *= View.FinalPostProcessSettings.BloomIntensity;
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomUpES2(PrePostSourceViewportSize/32, FVector2D(UpScale, UpScale), TintA, TintB));
Pass->SetInput(ePId_Input0, PostProcessDownsample4);
Pass->SetInput(ePId_Input1, PostProcessDownsample5);
PostProcessUpsample4 = FRenderingCompositeOutputRef(Pass);
}
// Upsample by 2
{
FVector4 TintA = FVector4(Settings.Bloom3Tint.R, Settings.Bloom3Tint.G, Settings.Bloom3Tint.B, 0.0f);
TintA *= View.FinalPostProcessSettings.BloomIntensity;
FVector4 TintB = FVector4(1.0f, 1.0f, 1.0f, 0.0f);
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomUpES2(PrePostSourceViewportSize/16, FVector2D(UpScale, UpScale), TintA, TintB));
Pass->SetInput(ePId_Input0, PostProcessDownsample3);
Pass->SetInput(ePId_Input1, PostProcessUpsample4);
PostProcessUpsample3 = FRenderingCompositeOutputRef(Pass);
}
// Upsample by 2
{
FVector4 TintA = FVector4(Settings.Bloom2Tint.R, Settings.Bloom2Tint.G, Settings.Bloom2Tint.B, 0.0f);
TintA *= View.FinalPostProcessSettings.BloomIntensity;
// Scaling Bloom2 by extra factor to match filter area difference between PC default and mobile.
TintA *= 0.5;
FVector4 TintB = FVector4(1.0f, 1.0f, 1.0f, 0.0f);
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomUpES2(PrePostSourceViewportSize/8, FVector2D(UpScale, UpScale), TintA, TintB));
Pass->SetInput(ePId_Input0, PostProcessDownsample2);
Pass->SetInput(ePId_Input1, PostProcessUpsample3);
PostProcessUpsample2 = FRenderingCompositeOutputRef(Pass);
}
}
FRenderingCompositeOutputRef PostProcessSunBlur;
if(bUseSun)
{
// Sunshaft depth blur using downsampled alpha.
FRenderingCompositeOutputRef PostProcessSunAlpha;
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSunAlphaES2(PrePostSourceViewportSize));
Pass->SetInput(ePId_Input0, PostProcessBloomSetup);
PostProcessSunAlpha = FRenderingCompositeOutputRef(Pass);
}
// Sunshaft blur number two.
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSunBlurES2(PrePostSourceViewportSize));
Pass->SetInput(ePId_Input0, PostProcessSunAlpha);
PostProcessSunBlur = FRenderingCompositeOutputRef(Pass);
}
}
if(bUseSun | bUseVignette | bUseBloom)
{
FRenderingCompositeOutputRef PostProcessSunMerge;
if(bUseBloomSmall)
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSunMergeSmallES2(PrePostSourceViewportSize));
Pass->SetInput(ePId_Input0, PostProcessBloomSetup);
Pass->SetInput(ePId_Input1, PostProcessDownsample2);
PostProcessSunMerge = FRenderingCompositeOutputRef(Pass);
BloomOutput = PostProcessSunMerge;
}
else
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSunMergeES2(PrePostSourceViewportSize));
if(bUseSun)
{
Pass->SetInput(ePId_Input0, PostProcessSunBlur);
}
if(bUseBloom)
{
Pass->SetInput(ePId_Input1, PostProcessBloomSetup);
Pass->SetInput(ePId_Input2, PostProcessUpsample2);
}
PostProcessSunMerge = FRenderingCompositeOutputRef(Pass);
BloomOutput = PostProcessSunMerge;
}
// Mobile temporal AA requires a composite of two of these frames.
if(bUseAa && (bUseBloom || bUseSun))
{
FSceneViewState* ViewState = (FSceneViewState*)View.State;
FRenderingCompositeOutputRef PostProcessSunMerge2;
if(ViewState && ViewState->MobileAaBloomSunVignette1)
{
FRenderingCompositePass* History;
History = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(ViewState->MobileAaBloomSunVignette1));
PostProcessSunMerge2 = FRenderingCompositeOutputRef(History);
}
else
{
PostProcessSunMerge2 = PostProcessSunMerge;
}
FRenderingCompositeOutputRef PostProcessSunAvg;
{
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSunAvgES2(PrePostSourceViewportSize));
Pass->SetInput(ePId_Input0, PostProcessSunMerge);
Pass->SetInput(ePId_Input1, PostProcessSunMerge2);
PostProcessSunAvg = FRenderingCompositeOutputRef(Pass);
}
BloomOutput = PostProcessSunAvg;
}
}
}
}
static const auto VarTonemapperFilm = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.TonemapperFilm"));
const bool bUseTonemapperFilm = IsMobileHDR() && GSupportsRenderTargetFormat_PF_FloatRGBA && (VarTonemapperFilm && VarTonemapperFilm->GetValueOnRenderThread());
if ( bUseTonemapperFilm)
{
//@todo Ronin Set to EAutoExposureMethod::AEM_Basic for PC vk crash.
AddTonemapper(Context, BloomOutput, nullptr, EAutoExposureMethod::AEM_Histogram, false);
}
else
{
// Must run to blit to back buffer even if post processing is off.
FRenderingCompositePass* PostProcessTonemap = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessTonemapES2(Context.View, FinalOutputViewRect, FinalTargetSize, bViewRectSource));
PostProcessTonemap->SetInput(ePId_Input0, Context.FinalOutput);
PostProcessTonemap->SetInput(ePId_Input1, BloomOutput);
PostProcessTonemap->SetInput(ePId_Input2, DofOutput);
Context.FinalOutput = FRenderingCompositeOutputRef(PostProcessTonemap);
}
// if Context.FinalOutput was the clipped result of sunmask stage then this stage also restores Context.FinalOutput back original target size.
FinalOutputViewRect = View.UnscaledViewRect;
if(bUseAa && View.Family->EngineShowFlags.PostProcessing)
{
// Double buffer post output.
FSceneViewState* ViewState = (FSceneViewState*)View.State;
FRenderingCompositeOutputRef PostProcessPrior = Context.FinalOutput;
if(ViewState && ViewState->MobileAaColor1)
{
FRenderingCompositePass* History;
History = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(ViewState->MobileAaColor1));
PostProcessPrior = FRenderingCompositeOutputRef(History);
}
// Mobile temporal AA is done after tonemapping.
FRenderingCompositePass* PostProcessAa = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessAaES2());
PostProcessAa->SetInput(ePId_Input0, Context.FinalOutput);
PostProcessAa->SetInput(ePId_Input1, PostProcessPrior);
Context.FinalOutput = FRenderingCompositeOutputRef(PostProcessAa);
}
#if WITH_EDITOR
if (FSceneRenderer::ShouldCompositeEditorPrimitives(View) )
{
FRenderingCompositePass* EditorCompNode = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessCompositeEditorPrimitives(false));
EditorCompNode->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(EditorCompNode);
}
#endif
const EDebugViewShaderMode DebugViewShaderMode = View.Family->GetDebugViewShaderMode();
if(DebugViewShaderMode == DVSM_QuadComplexity)
{
// Legend is costly so we don't do it for ES2, ideally we make a shader permutation
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeComplexity(GEngine->QuadComplexityColors, FVisualizeComplexityApplyPS::CS_STAIR, 1.f, false));
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
if(DebugViewShaderMode == DVSM_ShaderComplexity || DebugViewShaderMode == DVSM_ShaderComplexityContainedQuadOverhead || DebugViewShaderMode == DVSM_ShaderComplexityBleedingQuadOverhead)
{
// Legend is costly so we don't do it for ES2, ideally we make a shader permutation
FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessVisualizeComplexity(GEngine->ShaderComplexityColors, FVisualizeComplexityApplyPS::CS_RAMP, 1.f, false));
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
bool bStereoRenderingAndHMD = View.Family->EngineShowFlags.StereoRendering && View.Family->EngineShowFlags.HMDDistortion;
if (bStereoRenderingAndHMD)
{
FRenderingCompositePass* Node = NULL;
const EHMDDeviceType::Type DeviceType = GEngine->HMDDevice->GetHMDDeviceType();
if (DeviceType == EHMDDeviceType::DT_ES2GenericStereoMesh)
{
Node = Context.Graph.RegisterPass(new FRCPassPostProcessHMD());
}
if (Node)
{
Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
Context.FinalOutput = FRenderingCompositeOutputRef(Node);
}
}
// The graph setup should be finished before this line ----------------------------------------
{
// currently created on the heap each frame but View.Family->RenderTarget could keep this object and all would be cleaner
TRefCountPtr<IPooledRenderTarget> Temp;
FSceneRenderTargetItem Item;
Item.TargetableTexture = (FTextureRHIRef&)View.Family->RenderTarget->GetRenderTargetTexture();
Item.ShaderResourceTexture = (FTextureRHIRef&)View.Family->RenderTarget->GetRenderTargetTexture();
FPooledRenderTargetDesc Desc;
if (View.Family->RenderTarget->GetRenderTargetTexture())
{
Desc.Extent.X = View.Family->RenderTarget->GetRenderTargetTexture()->GetSizeX();
Desc.Extent.Y = View.Family->RenderTarget->GetRenderTargetTexture()->GetSizeY();
}
else
{
Desc.Extent = View.Family->RenderTarget->GetSizeXY();
}
// todo: this should come from View.Family->RenderTarget
Desc.Format = PF_B8G8R8A8;
Desc.NumMips = 1;
GRenderTargetPool.CreateUntrackedElement(Desc, Temp, Item);
OverrideRenderTarget(Context.FinalOutput, Temp, Desc);
CompositeContext.Process(Context.FinalOutput.GetPass(), TEXT("PostProcessingES2"));
}
}
}
void FPostProcessing::ProcessPlanarReflection(FRHICommandListImmediate& RHICmdList, FViewInfo& View, TRefCountPtr<IPooledRenderTarget>& VelocityRT, TRefCountPtr<IPooledRenderTarget>& OutFilteredSceneColor)
{
{
FMemMark Mark(FMemStack::Get());
FRenderingCompositePassContext CompositeContext(RHICmdList, View);
FPostprocessContext Context(RHICmdList, CompositeContext.Graph, View);
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get_Todo_PassContext();
FRenderingCompositeOutputRef VelocityInput;
if(VelocityRT)
{
VelocityInput = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(VelocityRT));
}
FSceneViewState* ViewState = Context.View.ViewState;
EAntiAliasingMethod AntiAliasingMethod = Context.View.FinalPostProcessSettings.AntiAliasingMethod;
if (AntiAliasingMethod == AAM_TemporalAA && ViewState)
{
if(VelocityInput.IsValid())
{
AddTemporalAA( Context, VelocityInput );
}
else
{
// black is how we clear the velocity buffer so this means no velocity
FRenderingCompositePass* NoVelocity = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(GSystemTextures.BlackDummy));
FRenderingCompositeOutputRef NoVelocityRef(NoVelocity);
AddTemporalAA( Context, NoVelocityRef );
}
}
CompositeContext.Process(Context.FinalOutput.GetPass(), TEXT("ProcessPlanarReflection"));
OutFilteredSceneColor = Context.FinalOutput.GetOutput()->PooledRenderTarget;
}
}