Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessTonemap.cpp
Jack Porter 1fdfd523ae Copying //UE4/Dev-Mobile to //UE4/Dev-Main (Source: //UE4/Dev-Mobile @ 3228013)
#lockdown Nick.Penwarden
#rb None

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

Change 3156770 on 2016/10/10 by Allan.Bentham

	Fix VULKAN_ENABLE_API_DUMP build on android.

Change 3157838 on 2016/10/11 by Allan.Bentham

	Fix Vulkan API tracing on android.

Change 3170713 on 2016/10/21 by Steve.Cano

	Adding the "Optional Mobile Features BP Library" plugin to explose Blueprint functions for Sound Volume, Battery level, system temperature (only available on android), and Headphones being plugged in

	#jira UE-30896
	#android
	#ios
	#ue4

Change 3171064 on 2016/10/21 by Steve.Cano

	Plugins to provide a common interface and Android implemenation for providing access to GPS / Location data from Blueprints

	#ue4
	#android
	#jira UE-35917

Change 3171065 on 2016/10/21 by Steve.Cano

	IOS implementation of LocationServices plugin to provide access to location data in Blueprints

	#ue4
	#ios
	#jira UE-35917

Change 3181802 on 2016/11/01 by Steve.Cano

	Fix an issue introduced in CL 3170713 - Adding new config section to RequiredSections list so that we can generate projects successfully.

	#jira UE-30896
	#ios

Change 3181807 on 2016/11/01 by Steve.Cano

	Make sure to clear out references to our eglSurface when we destroy it, else we will try to set our context with an invalid surface.

	#jira UE-35004
	#android
	#ue4

Change 3184827 on 2016/11/03 by Allan.Bentham

	Do not HDR32 encode for views without view family.
	TranslucentAlphaOnly blend mode now works with encoding.
	#jira UE-37951

Change 3186684 on 2016/11/04 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3188693 on 2016/11/07 by Chris.Babcock

	Corrections to memory stats for Android
	#jira WEX-3760
	#ue4
	#android

Change 3191538 on 2016/11/09 by Allan.Bentham

	Mobile tonemapper can use CVarTonemapperOverride

Change 3192575 on 2016/11/09 by Chris.Babcock

	Support placing UE4Game files in ExternalFilesDir for automatic cleanup on uninstall for distribution packages
	#jira UEPLAT-1422
	#ue4
	#android

Change 3196231 on 2016/11/13 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3196538 on 2016/11/14 by Jack.Porter

	Fix issue where lighting would not build for the first editor session of projects defaulting to MobileHDR=false as HardwareTargetingModule didn't set cvars
	#jira UE-38507

Change 3198068 on 2016/11/15 by Dmitriy.Dyomin

	Fixed android console command sender to work with ADB version 1.0.36
	#jira UE-35171

Change 3200230 on 2016/11/16 by Jack.Porter

	Remove mosaic resolution limitation on ES3 devices
	#jira WEX-3119

Change 3201251 on 2016/11/16 by Chris.Babcock

	Fix Android compile error

Change 3203542 on 2016/11/18 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3205598 on 2016/11/21 by Dmitriy.Dyomin

	Fixed: Missing selection outline in editor ES2 preview
	#jira UE-6458

Change 3205622 on 2016/11/21 by Dmitriy.Dyomin

	Fix ES2 build error from CL# 3205598

Change 3207232 on 2016/11/22 by Dmitriy.Dyomin

	ES2 GLSL generated shader simplifications

Change 3208608 on 2016/11/23 by Dmitriy.Dyomin

	Fixed: Landscape Grasstype warning causes log spam when landscape is streamed in or out
	#jira UE-38694

Change 3209135 on 2016/11/23 by Alicia.Cano

	Packaging for iOS fails due to several invalid object type errors
	#jira UE-38431
	#ios

Change 3209822 on 2016/11/24 by Jack.Porter

	IWYU changes for LocationServicesBPLibrary

Change 3209824 on 2016/11/24 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3210264 on 2016/11/25 by Jack.Porter

	Fix unicode compile issue on Korean windows

Change 3210268 on 2016/11/25 by Jack.Porter

	IWYU and unicode fixes to Dev-Mobile

Change 3211800 on 2016/11/28 by Allan.Bentham

	Create and set PrimitiveSceneProxy->PrimitiveSceneInfo before SetTransform render thread command is enqueued. Avoids race condition with FPrimitiveSceneInfo's constructor which can occur on out-of-order CPUs.

Change 3212621 on 2016/11/28 by Jack.Porter

	More IWYU fixes.

Change 3213080 on 2016/11/29 by Allan.Bentham

	enable UseSingleSampleShadowFromStationaryLights on mobile.

	PR #2990: Fixes single sample shadows for mobile. (Contributed by kallehamalainen)

Change 3213164 on 2016/11/29 by Jack.Porter

	Win32 IWYU fix

Change 3213932 on 2016/11/29 by Chris.Babcock

	Fix handling of ETC2 filtering by Google Play Store (if only ETC2, require ES3.0+ but don't use texture filtering in manifest)
	#jira UE-39120
	#ue4
	#android

Change 3214563 on 2016/11/29 by Dmitriy.Dyomin

	Disable dynamic buffer discarding on Adreno330 (was casuing 10ms stalls on slate buffers update)

Change 3214571 on 2016/11/29 by Dmitriy.Dyomin

	Custom stencil on Mobile
	#jira UEMOB-183

Change 3214641 on 2016/11/30 by Dmitriy.Dyomin

	Disable distance culling when rendering image for world composition
	#jira UE-37754

Change 3214656 on 2016/11/30 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3218479 on 2016/12/01 by Jack.Porter

	Fix for Vulkan build error when using AutoSDK

Change 3218596 on 2016/12/02 by Dmitriy.Dyomin

	Fixed: Android split screen multiplayer does not function or render correctly
	#jira UE-35204

Change 3218874 on 2016/12/02 by Allan.Bentham

	Fix CSM shadow bug when ES3.1 + mobileHDR == false.
	Fix inverted culling when rendering shadow depths with mobilehdr == false
	#jira UE-39111

Change 3220911 on 2016/12/04 by Jack.Porter

	Fixed landscape duplicated GUID detection and crash when one of the landscapes is in a hidden streaming level

Change 3220935 on 2016/12/04 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3222831 on 2016/12/05 by Dmitriy.Dyomin

	Added support for MGD and Andreno profilers
	#jira UEMOB-184

Change 3222843 on 2016/12/05 by Jack.Porter

	Fixed bug caused by CL 3196538 causing r.MobileHDR to toggle and crash when opening render settings
	#jira UE-38507

Change 3222934 on 2016/12/06 by Jack.Porter

	Android graphics debugger text changes

Change 3223042 on 2016/12/06 by Dmitriy.Dyomin

	Fixed: missing GPU particles in LDR mode on device
	#jira UE-39363

Change 3223043 on 2016/12/06 by Jack.Porter

	Fixed logspam warnings from TcpMessaging launching on Android
	#jira UE-37895

Change 3223046 on 2016/12/06 by Allan.Bentham

	Move ES3.1 preview mode out of experimental.
	Add per device quality level preview to ES3.1 preview sub menu when project settings allow.

	#jira UEMOB-178

Change 3223113 on 2016/12/06 by Allan.Bentham

	changed MOBILE_EMULATION define for METAL_PROFILE on mac. fixes issues with editor primitive rendering with ES3.1 feature level preview.
	added -featureleveles2/31 commandline support to metalrhi.

Change 3223117 on 2016/12/06 by Allan.Bentham

	Initialise FPrimitiveSceneInfo::CachedReflectionCaptureProxies

Change 3223131 on 2016/12/06 by Allan.Bentham

	Fix translucency rendering when mobilehdr == false. (Disable mod shadows when mobilehdr == false.)
	#jira UE-38343

Change 3223162 on 2016/12/06 by Jack.Porter

	Disallow Absolute World Position with "excluding shader offset" on ES2
	#jra UE-25555

Change 3223204 on 2016/12/06 by Jack.Porter

	Fix for unable to save levels when landscape components with tessellation moved to another level
	#jira UE-39372

Change 3223677 on 2016/12/06 by Chris.Babcock

	Fix tabs

Change 3224139 on 2016/12/06 by Chris.Babcock

	Support modification of build.xml and add post import additions to GameActivity
	#jira UE-31372
	#PR #2440
	#ue4
	#android

Change 3224152 on 2016/12/06 by Chris.Babcock

	Add support for MadCatz C.T.R.L.R Android controller models
	#ue4
	#android

Change 3224162 on 2016/12/06 by Chris.Babcock

	Driver bug fix and using separate context for MediaPlayer14 (contributed by BrianHarris)
	#jira UE-35825
	#PR #2787
	#ue4
	#android

Change 3224581 on 2016/12/07 by Dmitriy.Dyomin

	Fixed black screen regression on iOS OpenGL
	Also now reporting shader compiler errors in iOS

Change 3224589 on 2016/12/07 by Allan.Bentham

	Increase mobile renderer's maximum CSM cascade limit to 4 and introduce max shadow cascade cvar for mobile which defaults to 2 retaining existing behaviour.
	#jira UEMOB-187

Change 3224774 on 2016/12/07 by Jack.Porter

	Added flags for mobile shader permutation reduction
	Renamed mobile lighting policies to better describe them
	Set flags to remove policies not used by SunTemple - saves 22MB package size on Android

	#jira: UEMOB-179

Change 3224782 on 2016/12/07 by Allan.Bentham

	Add project option to set maximum supported CSM cascades to mobile renderer.
	#jira UEMOB-187

Change 3224943 on 2016/12/07 by Dmitriy.Dyomin

	Metal iOS will use sRGB textures
	#jira UEMOB-189

Change 3225098 on 2016/12/07 by Dmitriy.Dyomin

	fix for compile error from 3224943

Change 3225188 on 2016/12/07 by Chris.Babcock

	Fix #includes in deferred Android OpenGL
	#jira UE-39440
	#ue4
	#android

Change 3226402 on 2016/12/07 by Dmitriy.Dyomin

	Fixed: wrong condition for r8 srgb on mac
	#jira UE-39471

Change 3226485 on 2016/12/08 by Dmitriy.Dyomin

	Fixed: Rendering Artifacts and invisible objects in Fortnite
	regression from nobile custom stencil changes
	#jira UE-39452, UE-39455

Change 3226637 on 2016/12/08 by Dmitriy.Dyomin

	Fixed: Odin Ensure Texture passed to ClearDepthStencil is not what is currently set as RenderTarget
	#jira UE-39482

Change 3226922 on 2016/12/08 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3227353 on 2016/12/08 by Chris.Babcock

	Remove CopyVisualizers batch call again

Change 3228013 on 2016/12/08 By Chris.Babcock

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

[CL 3228374 by Jack Porter in Main branch]
2016-12-08 22:53:00 -05:00

1832 lines
64 KiB
C++

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PostProcessTonemap.cpp: Post processing tone mapping implementation.
=============================================================================*/
#include "PostProcess/PostProcessTonemap.h"
#include "EngineGlobals.h"
#include "ScenePrivate.h"
#include "PostProcess/SceneFilterRendering.h"
#include "PostProcess/PostProcessCombineLUTs.h"
#include "PostProcess/PostProcessMobile.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 HDROutputEncodingValue = (CVarHDROutputEnabled.GetValueOnRenderThread() != 0 && IsRHIDeviceNVIDIA()) ? 1 : 0; // Nvidia-specific scRGB encoding
SetShaderValue(Context.RHICmdList, ShaderRHI, EncodeHDROutput, HDROutputEncodingValue);
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);
}
// Use a provided tonemaping LUT (provided by a CombineLUTs pass).
{
FRenderingCompositeOutputRef* OutputRef = Context.Pass->GetInput(ePId_Input3);
// Indicates the Tonemapper combined LUT node was nominally in the network.
const bool bUseLUT = (OutputRef != NULL);
if (bUseLUT)
{
const FTextureRHIRef* SrcTexture = Context.View.GetTonemappingLUTTexture();
if (SrcTexture)
{
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);
}
}
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 ? GRHIHDRDisplayOutputFormat : 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)
{
const uint32 ConfigBitmask = TonemapperConfBitmaskMobile[ConfigIndex];
// Only cache for ES2/3.1 shader platforms, and only compile 32bpp shaders for Android or PC emulation
return IsMobilePlatform(Platform) &&
(!TonemapperIsDefined(ConfigBitmask, Tonemapper32BPPHDR) || Platform == SP_OPENGL_ES2_ANDROID || (IsES2Platform(Platform) && IsPCPlatform(Platform)));
}
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
const 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());
// clear target when processing first view in case of splitscreen
const bool bFirstView = (&View == View.Family->Views[0]);
// Full clear to avoid restore
if ((View.StereoPass == eSSP_FULL && bFirstView) || 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());
const int32 ConfigOverride = CVarTonemapperOverride->GetInt();
const uint32 FinalConfigIndex = ConfigOverride == -1 ? ConfigIndexMobile : (int32)ConfigOverride;
switch(FinalConfigIndex)
{
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());
}
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;
}