Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessTonemap.cpp
Marcus Wassmer f52bdcc738 Copying //UE4/Dev-Rendering to //UE4/Dev-Main (Source: //UE4/Dev-Rendering @ 3185985)
#lockdown Nick.Penwarden
#rb none

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

Change 3170391 on 2016/10/21 by Ben.Woodhouse

	Remove the wait on end of frame ensure, because we can't rely on all the the underlying codepaths to never miss a call to flush RHI resources. The consequences of missing a flush on a given frame are not serious now, since we enforce the synchronisation with a fence, preventing the rendering thread from getting too far ahead. We will simply accumulate resources for an additional frame when this happens.

	#jira UE-37437
	#fyi rolando.caloca, marcus.wassmer

Change 3170659 on 2016/10/21 by Rolando.Caloca

	DR - vk - Prep work for state key changes

Change 3170676 on 2016/10/21 by Rolando.Caloca

	DR - vk - Reworked blend state keys
	- Added depth/stencil to pipeline key

Change 3170848 on 2016/10/21 by Daniel.Wright

	Level viewport 'show stats' option is now enabled by default, which avoids confusion with artists thinking lighting is built, when really the message is hidden.

Change 3170849 on 2016/10/21 by Daniel.Wright

	Split FProjectedShadowInfo::RenderProjection into smaller functions which make the algorithm structure clear

Change 3170995 on 2016/10/21 by Rolando.Caloca

	DR - vk - Show object on vulkan validation msgs

Change 3171085 on 2016/10/21 by Rolando.Caloca

	DR - vk - Fix pipelines being used with incompatible renderpasses

Change 3171159 on 2016/10/21 by Rolando.Caloca

	DR - vk - Fix layout when reading textures on CPU

Change 3171167 on 2016/10/21 by Rolando.Caloca

	DR - vk - compile fix

Change 3172462 on 2016/10/24 by Daniel.Wright

	Added a warning about shader compile times to the material tooltip

Change 3172463 on 2016/10/24 by Daniel.Wright

	Reduced MinUnoccludedFraction to avoid artitfacts when a stationary light touches only a tiny part of a mesh

Change 3172716 on 2016/10/24 by Brian.Karis

	Fix for crash UE-37369 when reimporting over a generated LOD.

Change 3172967 on 2016/10/24 by Rolando.Caloca

	DR - vk - Fix writing buffers while GPU was using them

Change 3174187 on 2016/10/25 by Olaf.Piesche

	UE-37020

Change 3174718 on 2016/10/26 by Rolando.Caloca

	DR - vk - Remove old timestamp queries, increase occlusion queries per pool to 4k

Change 3175960 on 2016/10/26 by Rolando.Caloca

	DR - Added support for hlslcc header to have custom parsing

Change 3176611 on 2016/10/27 by David.Hill

	DrawWireCone  confusion:

	In response to a UDN, I'm updating confusing parameter names and comments for
	DrawWireCone() and DrawWireSphereCappedCone()

Change 3177111 on 2016/10/27 by Rolando.Caloca

	DR - vk - Fix timestamps for frame

Change 3177192 on 2016/10/27 by Arne.Schober

	DR - DitherLOD refactor - moved computation of the DepthStencil state out of SetMeshRenderState into GetDitheredLODTransitionState this is a prerequisite of further PSO work where we want to move up State setting in a similar war and reuse FMeshDrawingRenderState

Change 3177278 on 2016/10/27 by Olaf.Piesche

	UE-37484

Change 3177297 on 2016/10/27 by Rolando.Caloca

	DR - vk - Enable GRHISupportsBaseVertexIndex

Change 3177607 on 2016/10/27 by Rolando.Caloca

	DR - vk - SM4 UB prep

Change 3178052 on 2016/10/28 by Arne.Schober

	DR - fix WebGL - the WebGL compiler is very picky on double underscores and does want the presission to be defined before any function definition.

Change 3178156 on 2016/10/28 by Rolando.Caloca

	DR - vk - Added query timer
	- Fixed inline issues

Change 3178158 on 2016/10/28 by Rolando.Caloca

	DR - vk - Fixes for out of stencil bits

Change 3178462 on 2016/10/28 by Rolando.Caloca

	DR - vk - Fixes for Elemental

Change 3179131 on 2016/10/28 by Rolando.Caloca

	DR - vk - Fix for r.Vulkan.UseRealUBs

Change 3179139 on 2016/10/28 by Rolando.Caloca

	DR - vk - Move UB ring buffer to context

Change 3179145 on 2016/10/28 by Rolando.Caloca

	DR - vk - Fix buffer barriers

Change 3179888 on 2016/10/31 by Rolando.Caloca

	DR - vk - Align buffers to 16 bytes as we sometimes write to them with SIMD

Change 3179923 on 2016/10/31 by Rolando.Caloca

	DR - vk - Wait for swapchain counter

Change 3180430 on 2016/10/31 by Rolando.Caloca

	DR - vk - Properly wait for occlusion queries/cmd buffer
	- Actual log error if trying to use occlusion queries out of order

Change 3180746 on 2016/10/31 by Rolando.Caloca

	DR - vk - Undo some waiting as it was on the wrong thread

Change 3182115 on 2016/11/01 by Rolando.Caloca

	DR - hlslcc Linux path fix

Change 3182118 on 2016/11/01 by Daniel.Wright

	Fixed global distance field seam artifacts from landscapes with no subsections

Change 3182368 on 2016/11/01 by Daniel.Wright

	Dynamic Indirect Shadows for static meshes using distance fields
	* These Distance Field indirect shadows use the same tile culled and downsampled framework that capsule shadows use, with similar GPU cost
	* Individual StaticMesh assets can enable bGenerateMeshDistanceField to compute a distance field, without the memory cost of enabling for the whole project
	* New StaticMeshComponent properties bCastDynamicIndirectShadow and DynamicIndirectShadowMinVisibility
	* New WorldSettings property DynamicIndirectShadowsSelfShadowingIntensity which replaces the cvar
	* The GBuffer now stores HasDynamicIndirectShadowCasterRepresentation instead of HasHeightfieldRepresentation
	* DFAO from landscape is now done through the global distance field entirely.  Landscape contribution to the global distance field is deferred to attempt to workaround texture streaming issues.

Change 3182408 on 2016/11/01 by Rolando.Caloca

	DR - vk - Reworked occlusion queries, fixes flickering on AMD

Change 3182585 on 2016/11/01 by Daniel.Wright

	PS4 compile fix

Change 3183151 on 2016/11/02 by Rolando.Caloca

	DR - vk - Fix issue when processing super quick cmd buffers

Change 3183160 on 2016/11/02 by Rolando.Caloca

	Dr - vk - Call reset queries outside render pass

Change 3183182 on 2016/11/02 by Rolando.Caloca

	DR - Switch clear

Change 3183194 on 2016/11/02 by Rolando.Caloca

	DR - Try to catch crash ahead of time

Change 3183268 on 2016/11/02 by Rolando.Caloca

	DR - vk - Rename RenderPassState to TransitionState

Change 3183440 on 2016/11/02 by Daniel.Wright

	Renamed 'Dynamic Indirect Shadow' to 'Distance Field Indirect Shadow'

Change 3183793 on 2016/11/02 by Daniel.Wright

	Added ShadowResolutionScale to lightcomponent

Change 3183796 on 2016/11/02 by Daniel.Wright

	Improved bSimulatePhysics comment, with info on why it might be greyed out

Change 3183797 on 2016/11/02 by Daniel.Wright

	Precomputed shadowmaps no longer enable Force2To1Aspect, which is only needed for lightmaps.  Improves shadowmap utilization.

Change 3183915 on 2016/11/02 by Rolando.Caloca

	DR - vk - Remove redundant renderpasses

Change 3183991 on 2016/11/02 by Daniel.Wright

	Added r.ReflectionEnvironmentLightmapMixLargestWeight, useful for restricting lightmap mixing to darkening only

Change 3184001 on 2016/11/02 by Daniel.Wright

	Better draw event for IndirectCapsuleShadows in stereo

Change 3184096 on 2016/11/02 by Chris.Bunner

	HDR for D3D11 - NVAPI toggle and encoding, UI compositing.
	Removed some outdated tonemamping cvars and modes.

Change 3184399 on 2016/11/02 by Daniel.Wright

	Static analysis workaround

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

	Fix missing log10 from FCompositePS on hlslcc shader platforms so that QA can continue their integration.
	#jira UE-38164

Change 3184953 on 2016/11/03 by Chris.Bunner

	Fixing CIS warnings.

[CL 3186011 by Marcus Wassmer in Main branch]
2016-11-03 16:55:27 -04:00

1856 lines
64 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PostProcessTonemap.cpp: Post processing tone mapping implementation.
=============================================================================*/
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "SceneFilterRendering.h"
#include "PostProcessEyeAdaptation.h"
#include "PostProcessUpscale.h"
#include "PostProcessTonemap.h"
#include "PostProcessing.h"
#include "PostProcessCombineLUTs.h"
#include "PostProcessMobile.h"
#include "SceneUtils.h"
static TAutoConsoleVariable<float> CVarTonemapperSharpen(
TEXT("r.Tonemapper.Sharpen"),
0,
TEXT("Sharpening in the tonemapper (not for ES2), actual implementation is work in progress, clamped at 10\n")
TEXT(" 0: off(default)\n")
TEXT(" 0.5: half strength\n")
TEXT(" 1: full strength"),
ECVF_Scalability | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarDisplayColorGamut(
TEXT("r.HDR.Display.ColorGamut"),
0,
TEXT("Color gamut of the output display:\n")
TEXT("0: Rec709 / sRGB, D65 (default)\n")
TEXT("1: DCI-P3, D65\n")
TEXT("2: Rec2020 / BT2020, D65\n")
TEXT("3: ACES, D60\n")
TEXT("4: ACEScg, D60\n"),
ECVF_Scalability | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarDisplayOutputDevice(
TEXT("r.HDR.Display.OutputDevice"),
0,
TEXT("Device format of the output display:\n")
TEXT("0: sRGB\n")
TEXT("1: Rec709\n")
TEXT("2: Explicit gamma mapping\n")
TEXT("3: ACES 1000 nit ST-2084 (Dolby PQ) for HDR displays\n")
TEXT("4: ACES 2000 nit ST-2084 (Dolby PQ) for HDR displays\n"),
ECVF_Scalability | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarHDROutputEnabled(
TEXT("r.HDR.EnableHDROutput"),
0,
TEXT("Creates an HDR compatible swap-chain and enables HDR display output.")
TEXT("0: Disabled (default)\n")
TEXT("1: Enable hardware-specific implementation\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarTonemapperGamma(
TEXT("r.TonemapperGamma"),
0.0f,
TEXT("0: Default behavior\n")
TEXT("#: Use fixed gamma # instead of sRGB or Rec709 transform"),
ECVF_Scalability | ECVF_RenderThreadSafe);
//
// TONEMAPPER PERMUTATION CONTROL
//
// Tonemapper option bitmask.
// Adjusting this requires adjusting TonemapperCostTab[].
typedef enum {
TonemapperGammaOnly = (1<<0),
TonemapperColorMatrix = (1<<1),
TonemapperShadowTint = (1<<2),
TonemapperContrast = (1<<3),
TonemapperGrainJitter = (1<<4),
TonemapperGrainIntensity = (1<<5),
TonemapperGrainQuantization = (1<<6),
TonemapperBloom = (1<<7),
TonemapperDOF = (1<<8),
TonemapperVignette = (1<<9),
TonemapperLightShafts = (1<<10),
Tonemapper32BPPHDR = (1<<11),
TonemapperColorFringe = (1<<12),
TonemapperMsaa = (1<<13),
TonemapperSharpen = (1<<14),
TonemapperInverseTonemapping = (1 << 15),
} TonemapperOption;
// Tonemapper option cost (0 = no cost, 255 = max cost).
// Suggested cost should be
// These need a 1:1 mapping with TonemapperOption enum,
static uint8 TonemapperCostTab[] = {
1, //TonemapperGammaOnly
1, //TonemapperColorMatrix
1, //TonemapperShadowTint
1, //TonemapperContrast
1, //TonemapperGrainJitter
1, //TonemapperGrainIntensity
1, //TonemapperGrainQuantization
1, //TonemapperBloom
1, //TonemapperDOF
1, //TonemapperVignette
1, //TonemapperLightShafts
1, //TonemapperMosaic
1, //TonemapperColorFringe
1, //TonemapperMsaa
1, //TonemapperSharpen
1, //TonemapperInverseTonemapping
};
// Edit the following to add and remove configurations.
// This is a white list of the combinations which are compiled.
// Place most common first (faster when searching in TonemapperFindLeastExpensive()).
// List of configurations compiled for PC.
static uint32 TonemapperConfBitmaskPC[15] = {
TonemapperBloom +
TonemapperGrainJitter +
TonemapperGrainIntensity +
TonemapperGrainQuantization +
TonemapperVignette +
TonemapperColorFringe +
TonemapperSharpen +
0,
TonemapperBloom +
TonemapperGrainJitter +
TonemapperGrainIntensity +
TonemapperGrainQuantization +
TonemapperVignette +
TonemapperSharpen +
0,
TonemapperBloom +
TonemapperGrainJitter +
TonemapperGrainIntensity +
TonemapperGrainQuantization +
TonemapperVignette +
TonemapperColorFringe +
0,
TonemapperBloom +
TonemapperVignette +
TonemapperGrainQuantization +
TonemapperColorFringe +
0,
TonemapperBloom +
TonemapperVignette +
TonemapperGrainQuantization +
0,
TonemapperBloom +
TonemapperSharpen +
0,
// same without TonemapperGrainQuantization
TonemapperBloom +
TonemapperGrainJitter +
TonemapperGrainIntensity +
TonemapperVignette +
TonemapperColorFringe +
0,
TonemapperBloom +
TonemapperVignette +
TonemapperColorFringe +
0,
TonemapperBloom +
TonemapperVignette +
0,
// with TonemapperInverseTonemapping
TonemapperBloom +
TonemapperGrainJitter +
TonemapperGrainIntensity +
TonemapperGrainQuantization +
TonemapperVignette +
TonemapperColorFringe +
TonemapperSharpen +
TonemapperInverseTonemapping +
0,
TonemapperBloom +
TonemapperGrainJitter +
TonemapperGrainIntensity +
TonemapperGrainQuantization +
TonemapperVignette +
TonemapperColorFringe +
TonemapperInverseTonemapping +
0,
TonemapperBloom +
TonemapperVignette +
TonemapperGrainQuantization +
TonemapperColorFringe +
TonemapperInverseTonemapping +
0,
TonemapperBloom +
TonemapperVignette +
TonemapperGrainQuantization +
TonemapperInverseTonemapping +
0,
TonemapperBloom +
TonemapperSharpen +
TonemapperInverseTonemapping +
0,
//
TonemapperGammaOnly +
0,
};
// List of configurations compiled for Mobile.
static uint32 TonemapperConfBitmaskMobile[39] = {
//
// 15 for NON-MOSAIC
//
TonemapperGammaOnly +
0,
// Not supporting grain jitter or grain quantization on mobile.
// Bloom, LightShafts, Vignette all off.
TonemapperContrast +
0,
TonemapperContrast +
TonemapperColorMatrix +
0,
// Bloom, LightShafts, Vignette, and Vignette Color all use the same shader code in the tonemapper.
TonemapperContrast +
TonemapperBloom +
TonemapperLightShafts +
TonemapperVignette +
0,
TonemapperContrast +
TonemapperColorMatrix +
TonemapperBloom +
TonemapperLightShafts +
TonemapperVignette +
0,
TonemapperContrast +
TonemapperColorMatrix +
TonemapperShadowTint +
TonemapperBloom +
TonemapperLightShafts +
TonemapperVignette +
0,
// DOF enabled.
TonemapperContrast +
TonemapperColorMatrix +
TonemapperBloom +
TonemapperLightShafts +
TonemapperVignette +
TonemapperDOF +
0,
TonemapperContrast +
TonemapperColorMatrix +
TonemapperShadowTint +
TonemapperBloom +
TonemapperLightShafts +
TonemapperVignette +
TonemapperDOF +
0,
// Same with grain.
TonemapperContrast +
TonemapperGrainIntensity +
0,
TonemapperContrast +
TonemapperColorMatrix +
TonemapperGrainIntensity +
0,
TonemapperContrast +
TonemapperBloom +
TonemapperLightShafts +
TonemapperVignette +
TonemapperGrainIntensity +
0,
TonemapperContrast +
TonemapperColorMatrix +
TonemapperBloom +
TonemapperLightShafts +
TonemapperVignette +
TonemapperGrainIntensity +
0,
TonemapperContrast +
TonemapperColorMatrix +
TonemapperShadowTint +
TonemapperBloom +
TonemapperLightShafts +
TonemapperVignette +
TonemapperGrainIntensity +
0,
// DOF enabled.
TonemapperContrast +
TonemapperColorMatrix +
TonemapperBloom +
TonemapperLightShafts +
TonemapperVignette +
TonemapperDOF +
TonemapperGrainIntensity +
0,
TonemapperContrast +
TonemapperColorMatrix +
TonemapperShadowTint +
TonemapperBloom +
TonemapperLightShafts +
TonemapperVignette +
TonemapperDOF +
TonemapperGrainIntensity +
0,
//
// 14 for 32 bit HDR PATH
//
// This is 32bpp hdr without film post.
Tonemapper32BPPHDR +
TonemapperGammaOnly +
0,
Tonemapper32BPPHDR +
0,
Tonemapper32BPPHDR +
TonemapperContrast +
0,
Tonemapper32BPPHDR +
TonemapperContrast +
TonemapperColorMatrix +
0,
Tonemapper32BPPHDR +
TonemapperContrast +
TonemapperColorMatrix +
TonemapperShadowTint +
TonemapperBloom +
0,
Tonemapper32BPPHDR +
TonemapperContrast +
TonemapperVignette +
TonemapperBloom +
0,
Tonemapper32BPPHDR +
TonemapperContrast +
TonemapperColorMatrix +
TonemapperVignette +
TonemapperBloom +
0,
Tonemapper32BPPHDR +
TonemapperContrast +
TonemapperColorMatrix +
TonemapperShadowTint +
TonemapperVignette +
TonemapperBloom +
0,
// With grain
Tonemapper32BPPHDR +
TonemapperContrast +
TonemapperGrainIntensity +
0,
Tonemapper32BPPHDR +
TonemapperContrast +
TonemapperColorMatrix +
TonemapperGrainIntensity +
0,
Tonemapper32BPPHDR +
TonemapperContrast +
TonemapperColorMatrix +
TonemapperShadowTint +
TonemapperGrainIntensity +
TonemapperBloom +
0,
Tonemapper32BPPHDR +
TonemapperContrast +
TonemapperVignette +
TonemapperGrainIntensity +
TonemapperBloom +
0,
Tonemapper32BPPHDR +
TonemapperContrast +
TonemapperColorMatrix +
TonemapperVignette +
TonemapperGrainIntensity +
TonemapperBloom +
0,
Tonemapper32BPPHDR +
TonemapperContrast +
TonemapperColorMatrix +
TonemapperShadowTint +
TonemapperVignette +
TonemapperGrainIntensity +
TonemapperBloom +
0,
//
// 10 for MSAA
//
TonemapperMsaa +
TonemapperContrast +
0,
TonemapperMsaa +
TonemapperContrast +
TonemapperColorMatrix +
0,
TonemapperMsaa +
TonemapperContrast +
TonemapperBloom +
TonemapperVignette +
0,
TonemapperMsaa +
TonemapperContrast +
TonemapperColorMatrix +
TonemapperBloom +
TonemapperVignette +
0,
TonemapperMsaa +
TonemapperContrast +
TonemapperColorMatrix +
TonemapperShadowTint +
TonemapperBloom +
TonemapperVignette +
0,
// Same with grain.
TonemapperMsaa +
TonemapperContrast +
TonemapperGrainIntensity +
0,
TonemapperMsaa +
TonemapperContrast +
TonemapperColorMatrix +
TonemapperGrainIntensity +
0,
TonemapperMsaa +
TonemapperContrast +
TonemapperBloom +
TonemapperVignette +
TonemapperGrainIntensity +
0,
TonemapperMsaa +
TonemapperContrast +
TonemapperColorMatrix +
TonemapperBloom +
TonemapperVignette +
TonemapperGrainIntensity +
0,
TonemapperMsaa +
TonemapperContrast +
TonemapperColorMatrix +
TonemapperShadowTint +
TonemapperBloom +
TonemapperVignette +
TonemapperGrainIntensity +
0,
};
// Returns 1 if option is defined otherwise 0.
static uint32 TonemapperIsDefined(uint32 ConfigBitmask, TonemapperOption Option)
{
return (ConfigBitmask & Option) ? 1 : 0;
}
// This finds the least expensive configuration which supports all selected options in bitmask.
static uint32 TonemapperFindLeastExpensive(uint32* RESTRICT Table, uint32 TableEntries, uint8* RESTRICT CostTable, uint32 RequiredOptionsBitmask)
{
// Custom logic to insure fail cases do not happen.
uint32 MustNotHaveBitmask = 0;
MustNotHaveBitmask += ((RequiredOptionsBitmask & TonemapperDOF) == 0) ? TonemapperDOF : 0;
MustNotHaveBitmask += ((RequiredOptionsBitmask & Tonemapper32BPPHDR) == 0) ? Tonemapper32BPPHDR : 0;
MustNotHaveBitmask += ((RequiredOptionsBitmask & TonemapperMsaa) == 0) ? TonemapperMsaa : 0;
// Search for exact match first.
uint32 Index;
for(Index = 0; Index < TableEntries; ++Index)
{
if(Table[Index] == RequiredOptionsBitmask)
{
return Index;
}
}
// Search through list for best entry.
uint32 BestIndex = TableEntries;
uint32 BestCost = ~0;
uint32 NotRequiredOptionsBitmask = ~RequiredOptionsBitmask;
for(Index = 0; Index < TableEntries; ++Index)
{
uint32 Bitmask = Table[Index];
if((Bitmask & MustNotHaveBitmask) != 0)
{
continue;
}
if((Bitmask & RequiredOptionsBitmask) != RequiredOptionsBitmask)
{
// A match requires a minimum set of bits set.
continue;
}
uint32 BitExtra = Bitmask & NotRequiredOptionsBitmask;
uint32 Cost = 0;
while(BitExtra)
{
uint32 Bit = FMath::FloorLog2(BitExtra);
Cost += CostTable[Bit];
if(Cost > BestCost)
{
// Poor match.
goto PoorMatch;
}
BitExtra &= ~(1<<Bit);
}
// Better match.
BestCost = Cost;
BestIndex = Index;
PoorMatch:
;
}
// Fail returns 0, the gamma only shader.
if(BestIndex == TableEntries) BestIndex = 0;
return BestIndex;
}
// Common conversion of engine settings into a bitmask which describes the shader options required.
static uint32 TonemapperGenerateBitmask(const FViewInfo* RESTRICT View, bool bGammaOnly, bool bMobile)
{
check(View);
const FSceneViewFamily* RESTRICT Family = View->Family;
if(
bGammaOnly ||
(Family->EngineShowFlags.Tonemapper == 0) ||
(Family->EngineShowFlags.PostProcessing == 0))
{
return TonemapperGammaOnly;
}
uint32 Bitmask = 0;
const FPostProcessSettings* RESTRICT Settings = &(View->FinalPostProcessSettings);
FVector MixerR(Settings->FilmChannelMixerRed);
FVector MixerG(Settings->FilmChannelMixerGreen);
FVector MixerB(Settings->FilmChannelMixerBlue);
uint32 useColorMatrix = 0;
if(
(Settings->FilmSaturation != 1.0f) ||
((MixerR - FVector(1.0f,0.0f,0.0f)).GetAbsMax() != 0.0f) ||
((MixerG - FVector(0.0f,1.0f,0.0f)).GetAbsMax() != 0.0f) ||
((MixerB - FVector(0.0f,0.0f,1.0f)).GetAbsMax() != 0.0f))
{
Bitmask += TonemapperColorMatrix;
}
FVector Tint(Settings->FilmWhitePoint);
FVector TintShadow(Settings->FilmShadowTint);
float Sharpen = CVarTonemapperSharpen.GetValueOnRenderThread();
Bitmask += (Settings->FilmShadowTintAmount > 0.0f) ? TonemapperShadowTint : 0;
Bitmask += (Settings->FilmContrast > 0.0f) ? TonemapperContrast : 0;
Bitmask += (Settings->GrainIntensity > 0.0f) ? TonemapperGrainIntensity : 0;
Bitmask += (Settings->VignetteIntensity > 0.0f) ? TonemapperVignette : 0;
Bitmask += (Sharpen > 0.0f) ? TonemapperSharpen : 0;
return Bitmask;
}
// Common post.
// These are separated because mosiac mode doesn't support them.
static uint32 TonemapperGenerateBitmaskPost(const FViewInfo* RESTRICT View)
{
const FPostProcessSettings* RESTRICT Settings = &(View->FinalPostProcessSettings);
const FSceneViewFamily* RESTRICT Family = View->Family;
uint32 Bitmask = (Settings->GrainJitter > 0.0f) ? TonemapperGrainJitter : 0;
Bitmask += (Settings->BloomIntensity > 0.0f) ? TonemapperBloom : 0;
return Bitmask;
}
// PC only.
static uint32 TonemapperGenerateBitmaskPC(const FViewInfo* RESTRICT View, bool bGammaOnly)
{
uint32 Bitmask = TonemapperGenerateBitmask(View, bGammaOnly, false);
// PC doesn't support these
Bitmask &= ~TonemapperContrast;
Bitmask &= ~TonemapperColorMatrix;
Bitmask &= ~TonemapperShadowTint;
// Must early exit if gamma only.
if(Bitmask == TonemapperGammaOnly)
{
return Bitmask;
}
// Grain Quantization
{
static TConsoleVariableData<int32>* CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Tonemapper.GrainQuantization"));
int32 Value = CVar->GetValueOnRenderThread();
if(Value > 0)
{
Bitmask |= TonemapperGrainQuantization;
}
}
// Inverse Tonemapping
{
static TConsoleVariableData<int32>* CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.BufferVisualizationDumpFramesAsHDR"));
int32 Value = CVar->GetValueOnRenderThread();
if (Value > 0)
{
Bitmask |= TonemapperInverseTonemapping;
}
}
if( View->FinalPostProcessSettings.SceneFringeIntensity > 0.01f)
{
Bitmask |= TonemapperColorFringe;
}
return Bitmask + TonemapperGenerateBitmaskPost(View);
}
// Mobile only.
static uint32 TonemapperGenerateBitmaskMobile(const FViewInfo* RESTRICT View, bool bGammaOnly)
{
check(View);
uint32 Bitmask = TonemapperGenerateBitmask(View, bGammaOnly, true);
bool bUse32BPPHDR = IsMobileHDR32bpp();
bool bUseMosaic = IsMobileHDRMosaic();
// Must early exit if gamma only.
if(Bitmask == TonemapperGammaOnly)
{
return Bitmask + (bUse32BPPHDR ? Tonemapper32BPPHDR : 0);
}
if (bUseMosaic)
{
return Bitmask + Tonemapper32BPPHDR;
}
static const auto CVarMobileMSAA = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.MobileMSAA"));
const EShaderPlatform ShaderPlatform = GShaderPlatformForFeatureLevel[View->GetFeatureLevel()];
if ((GSupportsShaderFramebufferFetch && (ShaderPlatform == SP_METAL || ShaderPlatform == SP_VULKAN_PCES3_1)) && (CVarMobileMSAA ? CVarMobileMSAA->GetValueOnAnyThread() > 1 : false))
{
Bitmask += TonemapperMsaa;
}
if (bUse32BPPHDR)
{
// add limited post for 32 bit encoded hdr.
Bitmask += Tonemapper32BPPHDR;
Bitmask += TonemapperGenerateBitmaskPost(View);
}
else if (GSupportsRenderTargetFormat_PF_FloatRGBA)
{
// add full mobile post if FP16 is supported.
Bitmask += TonemapperGenerateBitmaskPost(View);
bool bUseDof = GetMobileDepthOfFieldScale(*View) > 0.0f && (!View->FinalPostProcessSettings.bMobileHQGaussian || (View->GetFeatureLevel() < ERHIFeatureLevel::ES3_1));
Bitmask += (bUseDof) ? TonemapperDOF : 0;
Bitmask += (View->bLightShaftUse) ? TonemapperLightShafts : 0;
}
// Mobile is not supporting grain quantization and grain jitter currently.
Bitmask &= ~(TonemapperGrainQuantization | TonemapperGrainJitter);
return Bitmask;
}
void GrainPostSettings(FVector* RESTRICT const Constant, const FPostProcessSettings* RESTRICT const Settings)
{
float GrainJitter = Settings->GrainJitter;
float GrainIntensity = Settings->GrainIntensity;
Constant->X = GrainIntensity;
Constant->Y = 1.0f + (-0.5f * GrainIntensity);
Constant->Z = GrainJitter;
}
// This code is shared by PostProcessTonemap and VisualizeHDR.
void FilmPostSetConstants(FVector4* RESTRICT const Constants, const uint32 ConfigBitmask, const FPostProcessSettings* RESTRICT const FinalPostProcessSettings, bool bMobile)
{
uint32 UseColorMatrix = TonemapperIsDefined(ConfigBitmask, TonemapperColorMatrix);
uint32 UseShadowTint = TonemapperIsDefined(ConfigBitmask, TonemapperShadowTint);
uint32 UseContrast = TonemapperIsDefined(ConfigBitmask, TonemapperContrast);
// Must insure inputs are in correct range (else possible generation of NaNs).
float InExposure = 1.0f;
FVector InWhitePoint(FinalPostProcessSettings->FilmWhitePoint);
float InSaturation = FMath::Clamp(FinalPostProcessSettings->FilmSaturation, 0.0f, 2.0f);
FVector InLuma = FVector(1.0f/3.0f, 1.0f/3.0f, 1.0f/3.0f);
FVector InMatrixR(FinalPostProcessSettings->FilmChannelMixerRed);
FVector InMatrixG(FinalPostProcessSettings->FilmChannelMixerGreen);
FVector InMatrixB(FinalPostProcessSettings->FilmChannelMixerBlue);
float InContrast = FMath::Clamp(FinalPostProcessSettings->FilmContrast, 0.0f, 1.0f) + 1.0f;
float InDynamicRange = powf(2.0f, FMath::Clamp(FinalPostProcessSettings->FilmDynamicRange, 1.0f, 4.0f));
float InToe = (1.0f - FMath::Clamp(FinalPostProcessSettings->FilmToeAmount, 0.0f, 1.0f)) * 0.18f;
InToe = FMath::Clamp(InToe, 0.18f/8.0f, 0.18f * (15.0f/16.0f));
float InHeal = 1.0f - (FMath::Max(1.0f/32.0f, 1.0f - FMath::Clamp(FinalPostProcessSettings->FilmHealAmount, 0.0f, 1.0f)) * (1.0f - 0.18f));
FVector InShadowTint(FinalPostProcessSettings->FilmShadowTint);
float InShadowTintBlend = FMath::Clamp(FinalPostProcessSettings->FilmShadowTintBlend, 0.0f, 1.0f) * 64.0f;
// Shadow tint amount enables turning off shadow tinting.
float InShadowTintAmount = FMath::Clamp(FinalPostProcessSettings->FilmShadowTintAmount, 0.0f, 1.0f);
InShadowTint = InWhitePoint + (InShadowTint - InWhitePoint) * InShadowTintAmount;
// Make sure channel mixer inputs sum to 1 (+ smart dealing with all zeros).
InMatrixR.X += 1.0f / (256.0f*256.0f*32.0f);
InMatrixG.Y += 1.0f / (256.0f*256.0f*32.0f);
InMatrixB.Z += 1.0f / (256.0f*256.0f*32.0f);
InMatrixR *= 1.0f / FVector::DotProduct(InMatrixR, FVector(1.0f));
InMatrixG *= 1.0f / FVector::DotProduct(InMatrixG, FVector(1.0f));
InMatrixB *= 1.0f / FVector::DotProduct(InMatrixB, FVector(1.0f));
// Conversion from linear rgb to luma (using HDTV coef).
FVector LumaWeights = FVector(0.2126f, 0.7152f, 0.0722f);
// Make sure white point has 1.0 as luma (so adjusting white point doesn't change exposure).
// Make sure {0.0,0.0,0.0} inputs do something sane (default to white).
InWhitePoint += FVector(1.0f / (256.0f*256.0f*32.0f));
InWhitePoint *= 1.0f / FVector::DotProduct(InWhitePoint, LumaWeights);
InShadowTint += FVector(1.0f / (256.0f*256.0f*32.0f));
InShadowTint *= 1.0f / FVector::DotProduct(InShadowTint, LumaWeights);
// Grey after color matrix is applied.
FVector ColorMatrixLuma = FVector(
FVector::DotProduct(InLuma.X * FVector(InMatrixR.X, InMatrixG.X, InMatrixB.X), FVector(1.0f)),
FVector::DotProduct(InLuma.Y * FVector(InMatrixR.Y, InMatrixG.Y, InMatrixB.Y), FVector(1.0f)),
FVector::DotProduct(InLuma.Z * FVector(InMatrixR.Z, InMatrixG.Z, InMatrixB.Z), FVector(1.0f)));
FVector OutMatrixR = FVector(0.0f);
FVector OutMatrixG = FVector(0.0f);
FVector OutMatrixB = FVector(0.0f);
FVector OutColorShadow_Luma = LumaWeights * InShadowTintBlend;
FVector OutColorShadow_Tint1 = InWhitePoint;
FVector OutColorShadow_Tint2 = InShadowTint - InWhitePoint;
if(UseColorMatrix)
{
// Final color matrix effected by saturation and exposure.
OutMatrixR = (ColorMatrixLuma + ((InMatrixR - ColorMatrixLuma) * InSaturation)) * InExposure;
OutMatrixG = (ColorMatrixLuma + ((InMatrixG - ColorMatrixLuma) * InSaturation)) * InExposure;
OutMatrixB = (ColorMatrixLuma + ((InMatrixB - ColorMatrixLuma) * InSaturation)) * InExposure;
if(UseShadowTint == 0)
{
OutMatrixR = OutMatrixR * InWhitePoint.X;
OutMatrixG = OutMatrixG * InWhitePoint.Y;
OutMatrixB = OutMatrixB * InWhitePoint.Z;
}
}
else
{
// No color matrix fast path.
if(UseShadowTint == 0)
{
OutMatrixB = InExposure * InWhitePoint;
}
else
{
// Need to drop exposure in.
OutColorShadow_Luma *= InExposure;
OutColorShadow_Tint1 *= InExposure;
OutColorShadow_Tint2 *= InExposure;
}
}
// Curve constants.
float OutColorCurveCh3;
float OutColorCurveCh0Cm1;
float OutColorCurveCd2;
float OutColorCurveCm0Cd0;
float OutColorCurveCh1;
float OutColorCurveCh2;
float OutColorCurveCd1;
float OutColorCurveCd3Cm3;
float OutColorCurveCm2;
// Line for linear section.
float FilmLineOffset = 0.18f - 0.18f*InContrast;
float FilmXAtY0 = -FilmLineOffset/InContrast;
float FilmXAtY1 = (1.0f - FilmLineOffset) / InContrast;
float FilmXS = FilmXAtY1 - FilmXAtY0;
// Coordinates of linear section.
float FilmHiX = FilmXAtY0 + InHeal*FilmXS;
float FilmHiY = FilmHiX*InContrast + FilmLineOffset;
float FilmLoX = FilmXAtY0 + InToe*FilmXS;
float FilmLoY = FilmLoX*InContrast + FilmLineOffset;
// Supported exposure range before clipping.
float FilmHeal = InDynamicRange - FilmHiX;
// Intermediates.
float FilmMidXS = FilmHiX - FilmLoX;
float FilmMidYS = FilmHiY - FilmLoY;
float FilmSlope = FilmMidYS / (FilmMidXS);
float FilmHiYS = 1.0f - FilmHiY;
float FilmLoYS = FilmLoY;
float FilmToe = FilmLoX;
float FilmHiG = (-FilmHiYS + (FilmSlope*FilmHeal)) / (FilmSlope*FilmHeal);
float FilmLoG = (-FilmLoYS + (FilmSlope*FilmToe)) / (FilmSlope*FilmToe);
if(UseContrast)
{
// Constants.
OutColorCurveCh1 = FilmHiYS/FilmHiG;
OutColorCurveCh2 = -FilmHiX*(FilmHiYS/FilmHiG);
OutColorCurveCh3 = FilmHiYS/(FilmSlope*FilmHiG) - FilmHiX;
OutColorCurveCh0Cm1 = FilmHiX;
OutColorCurveCm2 = FilmSlope;
OutColorCurveCm0Cd0 = FilmLoX;
OutColorCurveCd3Cm3 = FilmLoY - FilmLoX*FilmSlope;
// Handle these separate in case of FilmLoG being 0.
if(FilmLoG != 0.0f)
{
OutColorCurveCd1 = -FilmLoYS/FilmLoG;
OutColorCurveCd2 = FilmLoYS/(FilmSlope*FilmLoG);
}
else
{
// FilmLoG being zero means dark region is a linear segment (so just continue the middle section).
OutColorCurveCd1 = 0.0f;
OutColorCurveCd2 = 1.0f;
OutColorCurveCm0Cd0 = 0.0f;
OutColorCurveCd3Cm3 = 0.0f;
}
}
else
{
// Simplified for no dark segment.
OutColorCurveCh1 = FilmHiYS/FilmHiG;
OutColorCurveCh2 = -FilmHiX*(FilmHiYS/FilmHiG);
OutColorCurveCh3 = FilmHiYS/(FilmSlope*FilmHiG) - FilmHiX;
OutColorCurveCh0Cm1 = FilmHiX;
// Not used.
OutColorCurveCm2 = 0.0f;
OutColorCurveCm0Cd0 = 0.0f;
OutColorCurveCd3Cm3 = 0.0f;
OutColorCurveCd1 = 0.0f;
OutColorCurveCd2 = 0.0f;
}
Constants[0] = FVector4(OutMatrixR, OutColorCurveCd1);
Constants[1] = FVector4(OutMatrixG, OutColorCurveCd3Cm3);
Constants[2] = FVector4(OutMatrixB, OutColorCurveCm2);
Constants[3] = FVector4(OutColorCurveCm0Cd0, OutColorCurveCd2, OutColorCurveCh0Cm1, OutColorCurveCh3);
Constants[4] = FVector4(OutColorCurveCh1, OutColorCurveCh2, 0.0f, 0.0f);
Constants[5] = FVector4(OutColorShadow_Luma, 0.0f);
Constants[6] = FVector4(OutColorShadow_Tint1, 0.0f);
Constants[7] = FVector4(OutColorShadow_Tint2, 0.0f);
}
BEGIN_UNIFORM_BUFFER_STRUCT(FBloomDirtMaskParameters,)
DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(FVector4,Tint)
DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_TEXTURE(Texture2D,Mask)
DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_SAMPLER(SamplerState,MaskSampler)
END_UNIFORM_BUFFER_STRUCT(FBloomDirtMaskParameters)
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FBloomDirtMaskParameters,TEXT("BloomDirtMask"));
static TAutoConsoleVariable<float> CVarGamma(
TEXT("r.Gamma"),
1.0f,
TEXT("Gamma on output"),
ECVF_RenderThreadSafe);
/**
* Encapsulates the post processing tonemapper pixel shader.
*/
template<uint32 ConfigIndex>
class FPostProcessTonemapPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FPostProcessTonemapPS, Global);
static bool ShouldCache(EShaderPlatform Platform)
{
return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::ES2);
}
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
uint32 ConfigBitmask = TonemapperConfBitmaskPC[ConfigIndex];
OutEnvironment.SetDefine(TEXT("USE_GAMMA_ONLY"), TonemapperIsDefined(ConfigBitmask, TonemapperGammaOnly));
OutEnvironment.SetDefine(TEXT("USE_COLOR_MATRIX"), TonemapperIsDefined(ConfigBitmask, TonemapperColorMatrix));
OutEnvironment.SetDefine(TEXT("USE_SHADOW_TINT"), TonemapperIsDefined(ConfigBitmask, TonemapperShadowTint));
OutEnvironment.SetDefine(TEXT("USE_CONTRAST"), TonemapperIsDefined(ConfigBitmask, TonemapperContrast));
OutEnvironment.SetDefine(TEXT("USE_BLOOM"), TonemapperIsDefined(ConfigBitmask, TonemapperBloom));
OutEnvironment.SetDefine(TEXT("USE_GRAIN_JITTER"), TonemapperIsDefined(ConfigBitmask, TonemapperGrainJitter));
OutEnvironment.SetDefine(TEXT("USE_GRAIN_INTENSITY"), TonemapperIsDefined(ConfigBitmask, TonemapperGrainIntensity));
OutEnvironment.SetDefine(TEXT("USE_GRAIN_QUANTIZATION"), TonemapperIsDefined(ConfigBitmask, TonemapperGrainQuantization));
OutEnvironment.SetDefine(TEXT("USE_VIGNETTE"), TonemapperIsDefined(ConfigBitmask, TonemapperVignette));
OutEnvironment.SetDefine(TEXT("USE_COLOR_FRINGE"), TonemapperIsDefined(ConfigBitmask, TonemapperColorFringe));
OutEnvironment.SetDefine(TEXT("USE_SHARPEN"), TonemapperIsDefined(ConfigBitmask, TonemapperSharpen));
OutEnvironment.SetDefine(TEXT("USE_INVERSE_TONEMAPPING"), TonemapperIsDefined(ConfigBitmask, TonemapperInverseTonemapping));
OutEnvironment.SetDefine(TEXT("USE_VOLUME_LUT"), UseVolumeTextureLUT(Platform));
}
/** Default constructor. */
FPostProcessTonemapPS() {}
public:
FPostProcessPassParameters PostprocessParameter;
FShaderParameter ColorScale0;
FShaderParameter ColorScale1;
FShaderResourceParameter NoiseTexture;
FShaderResourceParameter NoiseTextureSampler;
FShaderParameter TexScale;
FShaderParameter TonemapperParams;
FShaderParameter GrainScaleBiasJitter;
FShaderResourceParameter ColorGradingLUT;
FShaderResourceParameter ColorGradingLUTSampler;
FShaderParameter InverseGamma;
FShaderParameter ColorMatrixR_ColorCurveCd1;
FShaderParameter ColorMatrixG_ColorCurveCd3Cm3;
FShaderParameter ColorMatrixB_ColorCurveCm2;
FShaderParameter ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3;
FShaderParameter ColorCurve_Ch1_Ch2;
FShaderParameter ColorShadow_Luma;
FShaderParameter ColorShadow_Tint1;
FShaderParameter ColorShadow_Tint2;
//@HACK
FShaderParameter OverlayColor;
FShaderParameter OutputDevice;
FShaderParameter OutputGamut;
FShaderParameter EncodeHDROutput;
/** Initialization constructor. */
FPostProcessTonemapPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
PostprocessParameter.Bind(Initializer.ParameterMap);
ColorScale0.Bind(Initializer.ParameterMap, TEXT("ColorScale0"));
ColorScale1.Bind(Initializer.ParameterMap, TEXT("ColorScale1"));
NoiseTexture.Bind(Initializer.ParameterMap,TEXT("NoiseTexture"));
NoiseTextureSampler.Bind(Initializer.ParameterMap,TEXT("NoiseTextureSampler"));
TexScale.Bind(Initializer.ParameterMap, TEXT("TexScale"));
TonemapperParams.Bind(Initializer.ParameterMap, TEXT("TonemapperParams"));
GrainScaleBiasJitter.Bind(Initializer.ParameterMap, TEXT("GrainScaleBiasJitter"));
ColorGradingLUT.Bind(Initializer.ParameterMap, TEXT("ColorGradingLUT"));
ColorGradingLUTSampler.Bind(Initializer.ParameterMap, TEXT("ColorGradingLUTSampler"));
InverseGamma.Bind(Initializer.ParameterMap,TEXT("InverseGamma"));
ColorMatrixR_ColorCurveCd1.Bind(Initializer.ParameterMap, TEXT("ColorMatrixR_ColorCurveCd1"));
ColorMatrixG_ColorCurveCd3Cm3.Bind(Initializer.ParameterMap, TEXT("ColorMatrixG_ColorCurveCd3Cm3"));
ColorMatrixB_ColorCurveCm2.Bind(Initializer.ParameterMap, TEXT("ColorMatrixB_ColorCurveCm2"));
ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.Bind(Initializer.ParameterMap, TEXT("ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3"));
ColorCurve_Ch1_Ch2.Bind(Initializer.ParameterMap, TEXT("ColorCurve_Ch1_Ch2"));
ColorShadow_Luma.Bind(Initializer.ParameterMap, TEXT("ColorShadow_Luma"));
ColorShadow_Tint1.Bind(Initializer.ParameterMap, TEXT("ColorShadow_Tint1"));
ColorShadow_Tint2.Bind(Initializer.ParameterMap, TEXT("ColorShadow_Tint2"));
OverlayColor.Bind(Initializer.ParameterMap, TEXT("OverlayColor"));
OutputDevice.Bind(Initializer.ParameterMap, TEXT("OutputDevice"));
OutputGamut.Bind(Initializer.ParameterMap, TEXT("OutputGamut"));
EncodeHDROutput.Bind(Initializer.ParameterMap, TEXT("EncodeHDROutput"));
}
// FShader interface.
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << PostprocessParameter << ColorScale0 << ColorScale1 << InverseGamma << NoiseTexture << NoiseTextureSampler
<< TexScale << TonemapperParams << GrainScaleBiasJitter
<< ColorGradingLUT << ColorGradingLUTSampler
<< ColorMatrixR_ColorCurveCd1 << ColorMatrixG_ColorCurveCd3Cm3 << ColorMatrixB_ColorCurveCm2 << ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3 << ColorCurve_Ch1_Ch2 << ColorShadow_Luma << ColorShadow_Tint1 << ColorShadow_Tint2
<< OverlayColor
<< OutputDevice << OutputGamut << EncodeHDROutput;
return bShaderHasOutdatedParameters;
}
void SetPS(const FRenderingCompositePassContext& Context)
{
const FPostProcessSettings& Settings = Context.View.FinalPostProcessSettings;
const FSceneViewFamily& ViewFamily = *(Context.View.Family);
const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
FGlobalShader::SetParameters(Context.RHICmdList, ShaderRHI, Context.View);
{
// filtering can cost performance so we use point where possible, we don't want anisotropic sampling
FSamplerStateRHIParamRef Filters[] =
{
TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp, 0, 1>::GetRHI(), // todo: could be SF_Point if fringe is disabled
TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp, 0, 1>::GetRHI(),
TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp, 0, 1>::GetRHI(),
TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp, 0, 1>::GetRHI(),
};
PostprocessParameter.SetPS(ShaderRHI, Context, 0, eFC_0000, Filters);
}
// Display format
int32 OutputGamutValue = CVarDisplayColorGamut.GetValueOnRenderThread();
SetShaderValue(Context.RHICmdList, ShaderRHI, OutputGamut, OutputGamutValue);
int32 HDROutputEnabledValue = CVarHDROutputEnabled.GetValueOnRenderThread();
SetShaderValue(Context.RHICmdList, ShaderRHI, EncodeHDROutput, HDROutputEnabledValue);
SetShaderValue(Context.RHICmdList, ShaderRHI, OverlayColor, Context.View.OverlayColor);
{
FLinearColor Col = Settings.SceneColorTint;
FVector4 ColorScale(Col.R, Col.G, Col.B, 0);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorScale0, ColorScale);
}
{
FLinearColor Col = FLinearColor::White * Settings.BloomIntensity;
FVector4 ColorScale(Col.R, Col.G, Col.B, 0);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorScale1, ColorScale);
}
{
UTexture2D* NoiseTextureValue = GEngine->HighFrequencyNoiseTexture;
SetTextureParameter(Context.RHICmdList, ShaderRHI, NoiseTexture, NoiseTextureSampler, TStaticSamplerState<SF_Point, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI(), NoiseTextureValue->Resource->TextureRHI);
}
{
const FPooledRenderTargetDesc* InputDesc = Context.Pass->GetInputDesc(ePId_Input0);
// we assume the this pass runs in 1:1 pixel
FVector2D TexScaleValue = FVector2D(InputDesc->Extent) / FVector2D(Context.View.ViewRect.Size());
SetShaderValue(Context.RHICmdList, ShaderRHI, TexScale, TexScaleValue);
}
{
float Sharpen = FMath::Clamp(CVarTonemapperSharpen.GetValueOnRenderThread(), 0.0f, 10.0f);
// /6.0 is to save one shader instruction
FVector2D Value(Settings.VignetteIntensity, Sharpen / 6.0f);
SetShaderValue(Context.RHICmdList, ShaderRHI, TonemapperParams, Value);
}
{
static TConsoleVariableData<int32>* CVarOutputDevice = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.HDR.Display.OutputDevice"));
static TConsoleVariableData<float>* CVarOutputGamma = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.TonemapperGamma"));
int32 OutputDeviceValue = CVarOutputDevice->GetValueOnRenderThread();
float Gamma = CVarOutputGamma->GetValueOnRenderThread();
if (PLATFORM_APPLE && Gamma == 0.0f)
{
Gamma = 2.2f;
}
if (Gamma > 0.0f)
{
// Enforce user-controlled ramp over sRGB or Rec709
OutputDeviceValue = FMath::Max(OutputDeviceValue, 2);
}
SetShaderValue(Context.RHICmdList, ShaderRHI, OutputDevice, OutputDeviceValue);
}
FVector GrainValue;
GrainPostSettings(&GrainValue, &Settings);
SetShaderValue(Context.RHICmdList, ShaderRHI, GrainScaleBiasJitter, GrainValue);
const TShaderUniformBufferParameter<FBloomDirtMaskParameters>& BloomDirtMaskParam = GetUniformBufferParameter<FBloomDirtMaskParameters>();
if (BloomDirtMaskParam.IsBound())
{
FBloomDirtMaskParameters BloomDirtMaskParams;
FLinearColor Col = Settings.BloomDirtMaskTint * Settings.BloomDirtMaskIntensity;
BloomDirtMaskParams.Tint = FVector4(Col.R, Col.G, Col.B, 0.f /*unused*/);
BloomDirtMaskParams.Mask = GSystemTextures.BlackDummy->GetRenderTargetItem().TargetableTexture;
if(Settings.BloomDirtMask && Settings.BloomDirtMask->Resource)
{
BloomDirtMaskParams.Mask = Settings.BloomDirtMask->Resource->TextureRHI;
}
BloomDirtMaskParams.MaskSampler = TStaticSamplerState<SF_Bilinear,AM_Wrap,AM_Wrap,AM_Wrap>::GetRHI();
FUniformBufferRHIRef BloomDirtMaskUB = TUniformBufferRef<FBloomDirtMaskParameters>::CreateUniformBufferImmediate(BloomDirtMaskParams, UniformBuffer_SingleDraw);
SetUniformBufferParameter(Context.RHICmdList, ShaderRHI, BloomDirtMaskParam, BloomDirtMaskUB);
}
// volume texture LUT
{
FRenderingCompositeOutputRef* OutputRef = Context.Pass->GetInput(ePId_Input3);
if(OutputRef)
{
FRenderingCompositeOutput* Input = OutputRef->GetOutput();
if(Input)
{
TRefCountPtr<IPooledRenderTarget> InputPooledElement = Input->RequestInput();
if(InputPooledElement)
{
check(!InputPooledElement->IsFree());
const FTextureRHIRef& SrcTexture = InputPooledElement->GetRenderTargetItem().ShaderResourceTexture;
SetTextureParameter(Context.RHICmdList, ShaderRHI, ColorGradingLUT, ColorGradingLUTSampler, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(), SrcTexture);
}
}
}
}
{
FVector InvDisplayGammaValue;
InvDisplayGammaValue.X = 1.0f / ViewFamily.RenderTarget->GetDisplayGamma();
InvDisplayGammaValue.Y = 2.2f / ViewFamily.RenderTarget->GetDisplayGamma();
{
static TConsoleVariableData<float>* CVar = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.TonemapperGamma"));
float Value = CVar->GetValueOnRenderThread();
if(Value < 1.0f)
{
Value = 1.0f;
}
InvDisplayGammaValue.Z = 1.0f / Value;
}
SetShaderValue(Context.RHICmdList, ShaderRHI, InverseGamma, InvDisplayGammaValue);
}
{
FVector4 Constants[8];
FilmPostSetConstants(Constants, TonemapperConfBitmaskPC[ConfigIndex], &Context.View.FinalPostProcessSettings, false);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorMatrixR_ColorCurveCd1, Constants[0]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorMatrixG_ColorCurveCd3Cm3, Constants[1]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorMatrixB_ColorCurveCm2, Constants[2]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3, Constants[3]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorCurve_Ch1_Ch2, Constants[4]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorShadow_Luma, Constants[5]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorShadow_Tint1, Constants[6]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorShadow_Tint2, Constants[7]);
}
}
static const TCHAR* GetSourceFilename()
{
return TEXT("PostProcessTonemap");
}
static const TCHAR* GetFunctionName()
{
return TEXT("MainPS");
}
};
// #define avoids a lot of code duplication
#define VARIATION1(A) typedef FPostProcessTonemapPS<A> FPostProcessTonemapPS##A; \
IMPLEMENT_SHADER_TYPE2(FPostProcessTonemapPS##A, SF_Pixel);
VARIATION1(0) VARIATION1(1) VARIATION1(2) VARIATION1(3) VARIATION1(4) VARIATION1(5) VARIATION1(6) VARIATION1(7) VARIATION1(8)
VARIATION1(9) VARIATION1(10) VARIATION1(11) VARIATION1(12) VARIATION1(13) VARIATION1(14)
#undef VARIATION1
// Vertex Shader permutations based on bool AutoExposure.
IMPLEMENT_SHADER_TYPE(template<>, TPostProcessTonemapVS<true>, TEXT("PostProcessTonemap"), TEXT("MainVS"), SF_Vertex);
IMPLEMENT_SHADER_TYPE(template<>, TPostProcessTonemapVS<false>, TEXT("PostProcessTonemap"), TEXT("MainVS"), SF_Vertex);
FRCPassPostProcessTonemap::FRCPassPostProcessTonemap(const FViewInfo& InView, bool bInDoGammaOnly, bool bInDoEyeAdaptation, bool bInHDROutput)
: bDoGammaOnly(bInDoGammaOnly)
, bDoScreenPercentageInTonemapper(false)
, bDoEyeAdaptation(bInDoEyeAdaptation)
, bHDROutput(bInHDROutput)
, View(InView)
{
uint32 ConfigBitmask = TonemapperGenerateBitmaskPC(&InView, bDoGammaOnly);
ConfigIndexPC = TonemapperFindLeastExpensive(TonemapperConfBitmaskPC, sizeof(TonemapperConfBitmaskPC)/4, TonemapperCostTab, ConfigBitmask);;
}
namespace PostProcessTonemapUtil
{
// Template implementation supports unique static BoundShaderState for each permutation of Vertex/Pixel Shaders
template <uint32 ConfigIndex, bool bDoEyeAdaptation>
static inline void SetShaderTempl(const FRenderingCompositePassContext& Context)
{
typedef TPostProcessTonemapVS<bDoEyeAdaptation> VertexShaderType;
typedef FPostProcessTonemapPS<ConfigIndex> PixelShaderType;
TShaderMapRef<PixelShaderType> PixelShader(Context.GetShaderMap());
TShaderMapRef<VertexShaderType> VertexShader(Context.GetShaderMap());
static FGlobalBoundShaderState BoundShaderState;
SetGlobalBoundShaderState(Context.RHICmdList, Context.GetFeatureLevel(), BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader);
VertexShader->SetVS(Context);
PixelShader->SetPS(Context);
}
template <uint32 ConfigIndex>
static inline void SetShaderTempl(const FRenderingCompositePassContext& Context, bool bDoEyeAdaptation)
{
if (bDoEyeAdaptation)
{
SetShaderTempl<ConfigIndex, true>(Context);
}
else
{
SetShaderTempl<ConfigIndex, false>(Context);
}
}
}
static TAutoConsoleVariable<int32> CVarTonemapperOverride(
TEXT("r.Tonemapper.ConfigIndexOverride"),
-1,
TEXT("direct configindex override. Ignores all other tonemapper configuration cvars"),
ECVF_RenderThreadSafe);
void FRCPassPostProcessTonemap::Process(FRenderingCompositePassContext& Context)
{
const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
if(!InputDesc)
{
// input is not hooked up correctly
return;
}
const FSceneViewFamily& ViewFamily = *(View.Family);
FIntRect SrcRect = View.ViewRect;
FIntRect DestRect = bDoScreenPercentageInTonemapper ? View.UnscaledViewRect : View.ViewRect;
FIntPoint SrcSize = InputDesc->Extent;
SCOPED_DRAW_EVENTF(Context.RHICmdList, PostProcessTonemap, TEXT("Tonemapper#%d GammaOnly=%d HandleScreenPercentage=%d %dx%d"),
ConfigIndexPC, bDoGammaOnly, bDoScreenPercentageInTonemapper, DestRect.Width(), DestRect.Height());
const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);
const EShaderPlatform ShaderPlatform = GShaderPlatformForFeatureLevel[Context.GetFeatureLevel()];
if (IsVulkanPlatform(ShaderPlatform))
{
//@HACK: needs to set the framebuffer to clear/ignore in vulkan (doesn't support RHIClear)
// Clearing for letterbox mode. We could ENoAction if View.ViewRect == RT dims.
FRHIRenderTargetView ColorView(DestRenderTarget.TargetableTexture, 0, -1, ERenderTargetLoadAction::EClear, ERenderTargetStoreAction::EStore);
FRHISetRenderTargetsInfo Info(1, &ColorView, FRHIDepthRenderTargetView());
Context.RHICmdList.SetRenderTargetsAndClear(Info);
}
else
{
// Set the view family's render target/viewport.
SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIParamRef(), ESimpleRenderTargetMode::EUninitializedColorAndDepth);
if (Context.HasHmdMesh() && View.StereoPass == eSSP_LEFT_EYE)
{
// needed when using an hmd mesh instead of a full screen quad because we don't touch all of the pixels in the render target
Context.RHICmdList.ClearColorTexture(DestRenderTarget.TargetableTexture, FLinearColor::Black, FIntRect());
}
else if (ViewFamily.RenderTarget->GetRenderTargetTexture() != DestRenderTarget.TargetableTexture)
{
// needed to not have PostProcessAA leaking in content (e.g. Matinee black borders), is optimized away if possible (RT size=view size, )
Context.RHICmdList.ClearColorTexture(DestRenderTarget.TargetableTexture, FLinearColor::Black, DestRect);
}
}
Context.SetViewportAndCallRHI(DestRect, 0.0f, 1.0f );
// set the state
Context.RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI());
Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
const int32 ConfigOverride = CVarTonemapperOverride->GetInt();
const uint32 FinalConfigIndex = ConfigOverride == -1 ? ConfigIndexPC : (int32)ConfigOverride;
switch (FinalConfigIndex)
{
using namespace PostProcessTonemapUtil;
case 0: SetShaderTempl<0>(Context, bDoEyeAdaptation); break;
case 1: SetShaderTempl<1>(Context, bDoEyeAdaptation); break;
case 2: SetShaderTempl<2>(Context, bDoEyeAdaptation); break;
case 3: SetShaderTempl<3>(Context, bDoEyeAdaptation); break;
case 4: SetShaderTempl<4>(Context, bDoEyeAdaptation); break;
case 5: SetShaderTempl<5>(Context, bDoEyeAdaptation); break;
case 6: SetShaderTempl<6>(Context, bDoEyeAdaptation); break;
case 7: SetShaderTempl<7>(Context, bDoEyeAdaptation); break;
case 8: SetShaderTempl<8>(Context, bDoEyeAdaptation); break;
case 9: SetShaderTempl<9>(Context, bDoEyeAdaptation); break;
case 10: SetShaderTempl<10>(Context, bDoEyeAdaptation); break;
case 11: SetShaderTempl<11>(Context, bDoEyeAdaptation); break;
case 12: SetShaderTempl<12>(Context, bDoEyeAdaptation); break;
case 13: SetShaderTempl<13>(Context, bDoEyeAdaptation); break;
case 14: SetShaderTempl<14>(Context, bDoEyeAdaptation); break;
default:
check(0);
}
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(Context.RHICmdList);
FShader* VertexShader;
if (bDoEyeAdaptation)
{
// Use the vertex shader that passes on eye-adaptation values to the pixel shader
TShaderMapRef<TPostProcessTonemapVS<true>> VertexShaderMapRef(Context.GetShaderMap());
VertexShader = *VertexShaderMapRef;
}
else
{
TShaderMapRef<TPostProcessTonemapVS<false>> VertexShaderMapRef(Context.GetShaderMap());
VertexShader = *VertexShaderMapRef;
}
DrawPostProcessPass(
Context.RHICmdList,
0, 0,
DestRect.Width(), DestRect.Height(),
View.ViewRect.Min.X, View.ViewRect.Min.Y,
View.ViewRect.Width(), View.ViewRect.Height(),
DestRect.Size(),
SceneContext.GetBufferSizeXY(),
VertexShader,
View.StereoPass,
Context.HasHmdMesh(),
EDRF_UseTriangleOptimization);
Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());
// We only release the SceneColor after the last view was processed (SplitScreen)
if(Context.View.Family->Views[Context.View.Family->Views.Num() - 1] == &Context.View && !GIsEditor)
{
// The RT should be released as early as possible to allow sharing of that memory for other purposes.
// This becomes even more important with some limited VRam (XBoxOne).
SceneContext.SetSceneColor(0);
}
if (ViewFamily.Scene && ViewFamily.Scene->GetShadingPath() == EShadingPath::Mobile)
{
// Double buffer tonemapper output for temporal AA.
if(View.AntiAliasingMethod == AAM_TemporalAA)
{
FSceneViewState* ViewState = (FSceneViewState*)View.State;
if(ViewState)
{
ViewState->MobileAaColor0 = PassOutputs[0].PooledRenderTarget;
}
}
}
}
FPooledRenderTargetDesc FRCPassPostProcessTonemap::ComputeOutputDesc(EPassOutputId InPassOutputId) const
{
FPooledRenderTargetDesc Ret = GetInput(ePId_Input0)->GetOutput()->RenderTargetDesc;
Ret.Reset();
// RGB is the color in LDR, A is the luminance for PostprocessAA
Ret.Format = bHDROutput ? PF_FloatRGBA : PF_B8G8R8A8;
Ret.DebugName = TEXT("Tonemap");
Ret.ClearValue = FClearValueBinding(FLinearColor(0, 0, 0, 0));
return Ret;
}
// ES2 version
/**
* Encapsulates the post processing tonemapper pixel shader.
*/
template<uint32 ConfigIndex>
class FPostProcessTonemapPS_ES2 : public FGlobalShader
{
DECLARE_SHADER_TYPE(FPostProcessTonemapPS_ES2, Global);
static bool ShouldCache(EShaderPlatform Platform)
{
// This is only used on ES2.
// TODO: Make this only compile on PC/Mobile (and not console).
return !IsConsolePlatform(Platform);
}
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
uint32 ConfigBitmask = TonemapperConfBitmaskMobile[ConfigIndex];
OutEnvironment.SetDefine(TEXT("USE_GAMMA_ONLY"), TonemapperIsDefined(ConfigBitmask, TonemapperGammaOnly));
OutEnvironment.SetDefine(TEXT("USE_COLOR_MATRIX"), TonemapperIsDefined(ConfigBitmask, TonemapperColorMatrix));
OutEnvironment.SetDefine(TEXT("USE_SHADOW_TINT"), TonemapperIsDefined(ConfigBitmask, TonemapperShadowTint));
OutEnvironment.SetDefine(TEXT("USE_CONTRAST"), TonemapperIsDefined(ConfigBitmask, TonemapperContrast));
OutEnvironment.SetDefine(TEXT("USE_32BPP_HDR"), TonemapperIsDefined(ConfigBitmask, Tonemapper32BPPHDR));
OutEnvironment.SetDefine(TEXT("USE_BLOOM"), TonemapperIsDefined(ConfigBitmask, TonemapperBloom));
OutEnvironment.SetDefine(TEXT("USE_GRAIN_JITTER"), TonemapperIsDefined(ConfigBitmask, TonemapperGrainJitter));
OutEnvironment.SetDefine(TEXT("USE_GRAIN_INTENSITY"), TonemapperIsDefined(ConfigBitmask, TonemapperGrainIntensity));
OutEnvironment.SetDefine(TEXT("USE_GRAIN_QUANTIZATION"), TonemapperIsDefined(ConfigBitmask, TonemapperGrainQuantization));
OutEnvironment.SetDefine(TEXT("USE_VIGNETTE"), TonemapperIsDefined(ConfigBitmask, TonemapperVignette));
OutEnvironment.SetDefine(TEXT("USE_LIGHT_SHAFTS"), TonemapperIsDefined(ConfigBitmask, TonemapperLightShafts));
OutEnvironment.SetDefine(TEXT("USE_DOF"), TonemapperIsDefined(ConfigBitmask, TonemapperDOF));
OutEnvironment.SetDefine(TEXT("USE_MSAA"), TonemapperIsDefined(ConfigBitmask, TonemapperMsaa));
//Need to hack in exposure scale for < SM5
OutEnvironment.SetDefine(TEXT("NO_EYEADAPTATION_EXPOSURE_FIX"), 1);
}
/** Default constructor. */
FPostProcessTonemapPS_ES2() {}
public:
FPostProcessPassParameters PostprocessParameter;
FShaderParameter ColorScale0;
FShaderParameter ColorScale1;
FShaderParameter TexScale;
FShaderParameter GrainScaleBiasJitter;
FShaderParameter InverseGamma;
FShaderParameter TonemapperParams;
FShaderParameter ColorMatrixR_ColorCurveCd1;
FShaderParameter ColorMatrixG_ColorCurveCd3Cm3;
FShaderParameter ColorMatrixB_ColorCurveCm2;
FShaderParameter ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3;
FShaderParameter ColorCurve_Ch1_Ch2;
FShaderParameter ColorShadow_Luma;
FShaderParameter ColorShadow_Tint1;
FShaderParameter ColorShadow_Tint2;
FShaderParameter OverlayColor;
FShaderParameter FringeIntensity;
/** Initialization constructor. */
FPostProcessTonemapPS_ES2(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
PostprocessParameter.Bind(Initializer.ParameterMap);
ColorScale0.Bind(Initializer.ParameterMap, TEXT("ColorScale0"));
ColorScale1.Bind(Initializer.ParameterMap, TEXT("ColorScale1"));
TexScale.Bind(Initializer.ParameterMap, TEXT("TexScale"));
TonemapperParams.Bind(Initializer.ParameterMap, TEXT("TonemapperParams"));
GrainScaleBiasJitter.Bind(Initializer.ParameterMap, TEXT("GrainScaleBiasJitter"));
InverseGamma.Bind(Initializer.ParameterMap,TEXT("InverseGamma"));
ColorMatrixR_ColorCurveCd1.Bind(Initializer.ParameterMap, TEXT("ColorMatrixR_ColorCurveCd1"));
ColorMatrixG_ColorCurveCd3Cm3.Bind(Initializer.ParameterMap, TEXT("ColorMatrixG_ColorCurveCd3Cm3"));
ColorMatrixB_ColorCurveCm2.Bind(Initializer.ParameterMap, TEXT("ColorMatrixB_ColorCurveCm2"));
ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.Bind(Initializer.ParameterMap, TEXT("ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3"));
ColorCurve_Ch1_Ch2.Bind(Initializer.ParameterMap, TEXT("ColorCurve_Ch1_Ch2"));
ColorShadow_Luma.Bind(Initializer.ParameterMap, TEXT("ColorShadow_Luma"));
ColorShadow_Tint1.Bind(Initializer.ParameterMap, TEXT("ColorShadow_Tint1"));
ColorShadow_Tint2.Bind(Initializer.ParameterMap, TEXT("ColorShadow_Tint2"));
OverlayColor.Bind(Initializer.ParameterMap, TEXT("OverlayColor"));
FringeIntensity.Bind(Initializer.ParameterMap, TEXT("FringeIntensity"));
}
// FShader interface.
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << PostprocessParameter << ColorScale0 << ColorScale1 << InverseGamma
<< TexScale << GrainScaleBiasJitter << TonemapperParams
<< ColorMatrixR_ColorCurveCd1 << ColorMatrixG_ColorCurveCd3Cm3 << ColorMatrixB_ColorCurveCm2 << ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3 << ColorCurve_Ch1_Ch2 << ColorShadow_Luma << ColorShadow_Tint1 << ColorShadow_Tint2
<< OverlayColor
<< FringeIntensity;
return bShaderHasOutdatedParameters;
}
void SetPS(const FRenderingCompositePassContext& Context)
{
const FPostProcessSettings& Settings = Context.View.FinalPostProcessSettings;
const FSceneViewFamily& ViewFamily = *(Context.View.Family);
const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
FGlobalShader::SetParameters(Context.RHICmdList, ShaderRHI, Context.View);
const uint32 ConfigBitmask = TonemapperConfBitmaskMobile[ConfigIndex];
if (TonemapperIsDefined(ConfigBitmask, Tonemapper32BPPHDR) && IsMobileHDRMosaic())
{
PostprocessParameter.SetPS(ShaderRHI, Context, TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
}
else
{
PostprocessParameter.SetPS(ShaderRHI, Context, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
}
SetShaderValue(Context.RHICmdList, ShaderRHI, OverlayColor, Context.View.OverlayColor);
SetShaderValue(Context.RHICmdList, ShaderRHI, FringeIntensity, fabsf(Settings.SceneFringeIntensity) * 0.01f); // Interpreted as [0-1] percentage
{
FLinearColor Col = Settings.SceneColorTint;
FVector4 ColorScale(Col.R, Col.G, Col.B, 0);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorScale0, ColorScale);
}
{
FLinearColor Col = FLinearColor::White * Settings.BloomIntensity;
FVector4 ColorScale(Col.R, Col.G, Col.B, 0);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorScale1, ColorScale);
}
{
const FPooledRenderTargetDesc* InputDesc = Context.Pass->GetInputDesc(ePId_Input0);
// we assume the this pass runs in 1:1 pixel
FVector2D TexScaleValue = FVector2D(InputDesc->Extent) / FVector2D(Context.View.ViewRect.Size());
SetShaderValue(Context.RHICmdList, ShaderRHI, TexScale, TexScaleValue);
}
{
float Sharpen = FMath::Clamp(CVarTonemapperSharpen.GetValueOnRenderThread(), 0.0f, 10.0f);
FVector2D Value(Settings.VignetteIntensity, Sharpen);
SetShaderValue(Context.RHICmdList, ShaderRHI, TonemapperParams, Value);
}
FVector GrainValue;
GrainPostSettings(&GrainValue, &Settings);
SetShaderValue(Context.RHICmdList, ShaderRHI, GrainScaleBiasJitter, GrainValue);
{
FVector InvDisplayGammaValue;
InvDisplayGammaValue.X = 1.0f / ViewFamily.RenderTarget->GetDisplayGamma();
InvDisplayGammaValue.Y = 2.2f / ViewFamily.RenderTarget->GetDisplayGamma();
InvDisplayGammaValue.Z = 1.0; // Unused on mobile.
SetShaderValue(Context.RHICmdList, ShaderRHI, InverseGamma, InvDisplayGammaValue);
}
{
FVector4 Constants[8];
FilmPostSetConstants(Constants, TonemapperConfBitmaskMobile[ConfigIndex], &Context.View.FinalPostProcessSettings, true);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorMatrixR_ColorCurveCd1, Constants[0]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorMatrixG_ColorCurveCd3Cm3, Constants[1]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorMatrixB_ColorCurveCm2, Constants[2]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3, Constants[3]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorCurve_Ch1_Ch2, Constants[4]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorShadow_Luma, Constants[5]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorShadow_Tint1, Constants[6]);
SetShaderValue(Context.RHICmdList, ShaderRHI, ColorShadow_Tint2, Constants[7]);
}
}
static const TCHAR* GetSourceFilename()
{
return TEXT("PostProcessTonemap");
}
static const TCHAR* GetFunctionName()
{
return TEXT("MainPS_ES2");
}
};
// #define avoids a lot of code duplication
#define VARIATION2(A) typedef FPostProcessTonemapPS_ES2<A> FPostProcessTonemapPS_ES2##A; \
IMPLEMENT_SHADER_TYPE2(FPostProcessTonemapPS_ES2##A, SF_Pixel);
VARIATION2(0) VARIATION2(1) VARIATION2(2) VARIATION2(3) VARIATION2(4) VARIATION2(5) VARIATION2(6) VARIATION2(7) VARIATION2(8) VARIATION2(9)
VARIATION2(10) VARIATION2(11) VARIATION2(12) VARIATION2(13) VARIATION2(14) VARIATION2(15) VARIATION2(16) VARIATION2(17) VARIATION2(18) VARIATION2(19)
VARIATION2(20) VARIATION2(21) VARIATION2(22) VARIATION2(23) VARIATION2(24) VARIATION2(25) VARIATION2(26) VARIATION2(27) VARIATION2(28) VARIATION2(29)
VARIATION2(30) VARIATION2(31) VARIATION2(32) VARIATION2(33) VARIATION2(34) VARIATION2(35) VARIATION2(36) VARIATION2(37) VARIATION2(38)
#undef VARIATION2
class FPostProcessTonemapVS_ES2 : public FGlobalShader
{
DECLARE_SHADER_TYPE(FPostProcessTonemapVS_ES2,Global);
static bool ShouldCache(EShaderPlatform Platform)
{
return !IsConsolePlatform(Platform);
}
FPostProcessTonemapVS_ES2() { }
public:
FPostProcessPassParameters PostprocessParameter;
FShaderResourceParameter EyeAdaptation;
FShaderParameter GrainRandomFull;
FShaderParameter FringeIntensity;
bool bUsedFramebufferFetch;
FPostProcessTonemapVS_ES2(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
PostprocessParameter.Bind(Initializer.ParameterMap);
GrainRandomFull.Bind(Initializer.ParameterMap, TEXT("GrainRandomFull"));
FringeIntensity.Bind(Initializer.ParameterMap, TEXT("FringeIntensity"));
}
void SetVS(const FRenderingCompositePassContext& Context)
{
const FVertexShaderRHIParamRef ShaderRHI = GetVertexShader();
FGlobalShader::SetParameters(Context.RHICmdList, ShaderRHI, Context.View);
PostprocessParameter.SetVS(ShaderRHI, Context, TStaticSamplerState<SF_Bilinear,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI());
FVector GrainRandomFullValue;
{
uint8 FrameIndexMod8 = 0;
if (Context.View.State)
{
FrameIndexMod8 = Context.View.State->GetFrameIndexMod8();
}
GrainRandomFromFrame(&GrainRandomFullValue, FrameIndexMod8);
}
// TODO: Don't use full on mobile with framebuffer fetch.
GrainRandomFullValue.Z = bUsedFramebufferFetch ? 0.0f : 1.0f;
SetShaderValue(Context.RHICmdList, ShaderRHI, GrainRandomFull, GrainRandomFullValue);
const FPostProcessSettings& Settings = Context.View.FinalPostProcessSettings;
SetShaderValue(Context.RHICmdList, ShaderRHI, FringeIntensity, fabsf(Settings.SceneFringeIntensity) * 0.01f); // Interpreted as [0-1] percentage
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << PostprocessParameter << GrainRandomFull << FringeIntensity;
return bShaderHasOutdatedParameters;
}
};
IMPLEMENT_SHADER_TYPE(,FPostProcessTonemapVS_ES2,TEXT("PostProcessTonemap"),TEXT("MainVS_ES2"),SF_Vertex);
namespace PostProcessTonemap_ES2Util
{
// Template implementation supports unique static BoundShaderState for each permutation of Pixel Shaders
template <uint32 ConfigIndex>
static inline void SetShaderTemplES2(const FRenderingCompositePassContext& Context, bool bUsedFramebufferFetch)
{
TShaderMapRef<FPostProcessTonemapVS_ES2> VertexShader(Context.GetShaderMap());
TShaderMapRef<FPostProcessTonemapPS_ES2<ConfigIndex> > PixelShader(Context.GetShaderMap());
VertexShader->bUsedFramebufferFetch = bUsedFramebufferFetch;
static FGlobalBoundShaderState BoundShaderState;
SetGlobalBoundShaderState(Context.RHICmdList, Context.GetFeatureLevel(), BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader);
VertexShader->SetVS(Context);
PixelShader->SetPS(Context);
}
}
FRCPassPostProcessTonemapES2::FRCPassPostProcessTonemapES2(const FViewInfo& View, FIntRect InViewRect, FIntPoint InDestSize, bool bInUsedFramebufferFetch)
:
ViewRect(InViewRect),
DestSize(InDestSize),
bUsedFramebufferFetch(bInUsedFramebufferFetch)
{
uint32 ConfigBitmask = TonemapperGenerateBitmaskMobile(&View, false);
ConfigIndexMobile = TonemapperFindLeastExpensive(TonemapperConfBitmaskMobile, sizeof(TonemapperConfBitmaskMobile)/4, TonemapperCostTab, ConfigBitmask);
}
void FRCPassPostProcessTonemapES2::Process(FRenderingCompositePassContext& Context)
{
SCOPED_DRAW_EVENTF(Context.RHICmdList, PostProcessTonemap, TEXT("Tonemapper#%d%s"), ConfigIndexMobile, bUsedFramebufferFetch ? TEXT(" FramebufferFetch=0") : TEXT("FramebufferFetch=1"));
const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
if(!InputDesc)
{
// input is not hooked up correctly
return;
}
const FSceneView& View = Context.View;
const FSceneViewFamily& ViewFamily = *(View.Family);
const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);
const FPooledRenderTargetDesc& OutputDesc = PassOutputs[0].RenderTargetDesc;
FIntRect SrcRect = ViewRect;
// no upscale if separate ren target is used.
FIntRect DestRect = (ViewFamily.bUseSeparateRenderTarget) ? ViewRect : View.UnscaledViewRect; // Simple upscaling, ES2 post process does not currently have a specific upscaling pass.
FIntPoint SrcSize = InputDesc->Extent;
FIntPoint DstSize = OutputDesc.Extent;
// Set the view family's render target/viewport.
//@todo Ronin find a way to use the same codepath for all platforms.
const EShaderPlatform ShaderPlatform = GShaderPlatformForFeatureLevel[Context.GetFeatureLevel()];
if (IsVulkanMobilePlatform(ShaderPlatform))
{
//@HACK: gets around an uneccessary load in Vulkan. NOT FOR MAIN as it'll probably kill GearVR
//@HACK: needs to set the framebuffer to clear/ignore in vulkan (doesn't support RHIClear)
FRHIRenderTargetView ColorView(DestRenderTarget.TargetableTexture, 0, -1, ERenderTargetLoadAction::EClear, ERenderTargetStoreAction::EStore);
FRHISetRenderTargetsInfo Info(1, &ColorView, FRHIDepthRenderTargetView());
Context.RHICmdList.SetRenderTargetsAndClear(Info);
}
else
{
SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIParamRef());
// Full clear to avoid restore
if (View.StereoPass == eSSP_FULL || View.StereoPass == eSSP_LEFT_EYE)
{
Context.RHICmdList.ClearColorTexture(DestRenderTarget.TargetableTexture, FLinearColor::Black, FIntRect());
}
}
Context.SetViewportAndCallRHI(DestRect);
// set the state
Context.RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI());
Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
switch(ConfigIndexMobile)
{
using namespace PostProcessTonemap_ES2Util;
case 0: SetShaderTemplES2<0>(Context, bUsedFramebufferFetch); break;
case 1: SetShaderTemplES2<1>(Context, bUsedFramebufferFetch); break;
case 2: SetShaderTemplES2<2>(Context, bUsedFramebufferFetch); break;
case 3: SetShaderTemplES2<3>(Context, bUsedFramebufferFetch); break;
case 4: SetShaderTemplES2<4>(Context, bUsedFramebufferFetch); break;
case 5: SetShaderTemplES2<5>(Context, bUsedFramebufferFetch); break;
case 6: SetShaderTemplES2<6>(Context, bUsedFramebufferFetch); break;
case 7: SetShaderTemplES2<7>(Context, bUsedFramebufferFetch); break;
case 8: SetShaderTemplES2<8>(Context, bUsedFramebufferFetch); break;
case 9: SetShaderTemplES2<9>(Context, bUsedFramebufferFetch); break;
case 10: SetShaderTemplES2<10>(Context, bUsedFramebufferFetch); break;
case 11: SetShaderTemplES2<11>(Context, bUsedFramebufferFetch); break;
case 12: SetShaderTemplES2<12>(Context, bUsedFramebufferFetch); break;
case 13: SetShaderTemplES2<13>(Context, bUsedFramebufferFetch); break;
case 14: SetShaderTemplES2<14>(Context, bUsedFramebufferFetch); break;
case 15: SetShaderTemplES2<15>(Context, bUsedFramebufferFetch); break;
case 16: SetShaderTemplES2<16>(Context, bUsedFramebufferFetch); break;
case 17: SetShaderTemplES2<17>(Context, bUsedFramebufferFetch); break;
case 18: SetShaderTemplES2<18>(Context, bUsedFramebufferFetch); break;
case 19: SetShaderTemplES2<19>(Context, bUsedFramebufferFetch); break;
case 20: SetShaderTemplES2<20>(Context, bUsedFramebufferFetch); break;
case 21: SetShaderTemplES2<21>(Context, bUsedFramebufferFetch); break;
case 22: SetShaderTemplES2<22>(Context, bUsedFramebufferFetch); break;
case 23: SetShaderTemplES2<23>(Context, bUsedFramebufferFetch); break;
case 24: SetShaderTemplES2<24>(Context, bUsedFramebufferFetch); break;
case 25: SetShaderTemplES2<25>(Context, bUsedFramebufferFetch); break;
case 26: SetShaderTemplES2<26>(Context, bUsedFramebufferFetch); break;
case 27: SetShaderTemplES2<27>(Context, bUsedFramebufferFetch); break;
case 28: SetShaderTemplES2<28>(Context, bUsedFramebufferFetch); break;
case 29: SetShaderTemplES2<29>(Context, bUsedFramebufferFetch); break;
case 30: SetShaderTemplES2<30>(Context, bUsedFramebufferFetch); break;
case 31: SetShaderTemplES2<31>(Context, bUsedFramebufferFetch); break;
case 32: SetShaderTemplES2<32>(Context, bUsedFramebufferFetch); break;
case 33: SetShaderTemplES2<33>(Context, bUsedFramebufferFetch); break;
case 34: SetShaderTemplES2<34>(Context, bUsedFramebufferFetch); break;
case 35: SetShaderTemplES2<35>(Context, bUsedFramebufferFetch); break;
case 36: SetShaderTemplES2<36>(Context, bUsedFramebufferFetch); break;
case 37: SetShaderTemplES2<37>(Context, bUsedFramebufferFetch); break;
case 38: SetShaderTemplES2<38>(Context, bUsedFramebufferFetch); break;
default:
check(0);
}
// Draw a quad mapping scene color to the view's render target
TShaderMapRef<FPostProcessTonemapVS_ES2> VertexShader(Context.GetShaderMap());
DrawRectangle(
Context.RHICmdList,
0, 0,
DstSize.X, DstSize.Y,
SrcRect.Min.X, SrcRect.Min.Y,
SrcRect.Width(), SrcRect.Height(),
DstSize,
SrcSize,
*VertexShader,
EDRF_UseTriangleOptimization);
Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());
// Double buffer tonemapper output for temporal AA.
if(Context.View.AntiAliasingMethod == AAM_TemporalAA)
{
FSceneViewState* ViewState = (FSceneViewState*)Context.View.State;
if(ViewState)
{
ViewState->MobileAaColor0 = PassOutputs[0].PooledRenderTarget;
}
}
}
FPooledRenderTargetDesc FRCPassPostProcessTonemapES2::ComputeOutputDesc(EPassOutputId InPassOutputId) const
{
FPooledRenderTargetDesc Ret = GetInput(ePId_Input0)->GetOutput()->RenderTargetDesc;
Ret.Reset();
Ret.Format = PF_B8G8R8A8;
Ret.DebugName = TEXT("Tonemap");
Ret.Extent = DestSize;
Ret.ClearValue = FClearValueBinding(FLinearColor(0, 0, 0, 0));
return Ret;
}