From 57e6235bd7e59309b4664c83a39cca907c0a3218 Mon Sep 17 00:00:00 2001 From: David Huynh Date: Thu, 1 Oct 2026 19:28:14 -0700 Subject: [PATCH 1/5] BO3: better whiteclip --- Source/Games/Call of Duty Black Ops 3/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Games/Call of Duty Black Ops 3/main.cpp b/Source/Games/Call of Duty Black Ops 3/main.cpp index 08322ff97..abcc765dc 100644 --- a/Source/Games/Call of Duty Black Ops 3/main.cpp +++ b/Source/Games/Call of Duty Black Ops 3/main.cpp @@ -2453,7 +2453,7 @@ class CallOfDutyBlackOps3 final : public Game //GameSettings default default_luma_global_game_settings.TonemapperRolloffStart = cb_luma_global_settings.GameSettings.TonemapperRolloffStart = 36.f; - default_luma_global_game_settings.TonemapperMaxExpected = cb_luma_global_settings.GameSettings.TonemapperMaxExpected = 50000.f; + default_luma_global_game_settings.TonemapperMaxExpected = cb_luma_global_settings.GameSettings.TonemapperMaxExpected = 40000.f; default_luma_global_game_settings.AmbientOcclusion = cb_luma_global_settings.GameSettings.AmbientOcclusion = 1.f; default_luma_global_game_settings.Bloom = cb_luma_global_settings.GameSettings.Bloom = 1.f; default_luma_global_game_settings.LensFlare = cb_luma_global_settings.GameSettings.LensFlare = 1.f; From 9b92d832c32b6eea226bbbd1272a4feda2f00fa2 Mon Sep 17 00:00:00 2001 From: David Huynh Date: Fri, 2 Oct 2026 22:50:43 -0700 Subject: [PATCH 2/5] BO3: fxfs_teleportvid --- .../fsfx_teleportvid_0xADBF1975.ps_5_0.hlsl | 254 ++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 Shaders/Call of Duty Black Ops 3/fsfx_teleportvid_0xADBF1975.ps_5_0.hlsl diff --git a/Shaders/Call of Duty Black Ops 3/fsfx_teleportvid_0xADBF1975.ps_5_0.hlsl b/Shaders/Call of Duty Black Ops 3/fsfx_teleportvid_0xADBF1975.ps_5_0.hlsl new file mode 100644 index 000000000..4a17b14da --- /dev/null +++ b/Shaders/Call of Duty Black Ops 3/fsfx_teleportvid_0xADBF1975.ps_5_0.hlsl @@ -0,0 +1,254 @@ +// ---- Created with 3Dmigoto v1.3.16 on Fri Oct 02 22:40:00 2026 + +cbuffer _Globals : register(b0) +{ + float4 flagParams : packoffset(c0); + float4 colorObjMin : packoffset(c1); + float4 colorObjMax : packoffset(c2); + float colorObjMinBaseBlend : packoffset(c3); + float colorObjMaxBaseBlend : packoffset(c3.y); + float2 uvScroll : packoffset(c3.z); + float4 detailScale : packoffset(c4); + float4 detailScale1 : packoffset(c5); + float4 detailScale2 : packoffset(c6); + float4 detailScale3 : packoffset(c7); + float4 alphaRevealParms : packoffset(c8); + float4 colorDetailScale : packoffset(c9); + float alphaRevealSoftEdge : packoffset(c10); + float alphaRevealRamp : packoffset(c10.y); + float warpPixels : packoffset(c10.z); + float aberrationPixels : packoffset(c10.w); + float3 aberrationFactors : packoffset(c11); + float2 aberrationScroll : packoffset(c12); + float2 colorFlipRowsCols : packoffset(c12.z); + float colorFlipTime : packoffset(c13); + bool useAlphaBlend : packoffset(c13.y); + float refractionAmount : packoffset(c13.z); + float2 normalScroll : packoffset(c14); + float2 normalTile : packoffset(c14.z); +} + +cbuffer GenericsCBuffer : register(b3) +{ + float4 scriptVector0 : packoffset(c0); + float4 scriptVector1 : packoffset(c1); + float4 scriptVector2 : packoffset(c2); + float4 scriptVector3 : packoffset(c3); + float4 scriptVector4 : packoffset(c4); + float4 scriptVector5 : packoffset(c5); + float4 scriptVector6 : packoffset(c6); + float4 scriptVector7 : packoffset(c7); + float4 weaponParam0 : packoffset(c8); + float4 weaponParam1 : packoffset(c9); + float4 weaponParam2 : packoffset(c10); + float4 weaponParam3 : packoffset(c11); + float4 weaponParam4 : packoffset(c12); + float4 weaponParam5 : packoffset(c13); + float4 weaponParam6 : packoffset(c14); + float4 weaponParam7 : packoffset(c15); +} + +cbuffer PerSceneConsts : register(b1) +{ + row_major float4x4 projectionMatrix : packoffset(c0); + row_major float4x4 viewMatrix : packoffset(c4); + row_major float4x4 viewProjectionMatrix : packoffset(c8); + row_major float4x4 inverseProjectionMatrix : packoffset(c12); + row_major float4x4 inverseViewMatrix : packoffset(c16); + row_major float4x4 inverseViewProjectionMatrix : packoffset(c20); + float4 eyeOffset : packoffset(c24); + float4 adsZScale : packoffset(c25); + float4 hdrControl0 : packoffset(c26); + float4 hdrControl1 : packoffset(c27); + float4 fogColor : packoffset(c28); + float4 fogConsts : packoffset(c29); + float4 fogConsts2 : packoffset(c30); + float4 fogConsts3 : packoffset(c31); + float4 fogConsts4 : packoffset(c32); + float4 fogConsts5 : packoffset(c33); + float4 fogConsts6 : packoffset(c34); + float4 fogConsts7 : packoffset(c35); + float4 fogConsts8 : packoffset(c36); + float4 fogConsts9 : packoffset(c37); + float3 sunFogDir : packoffset(c38); + float4 sunFogColor : packoffset(c39); + float2 sunFog : packoffset(c40); + float4 zNear : packoffset(c41); + float3 clothPrimaryTint : packoffset(c42); + float3 clothSecondaryTint : packoffset(c43); + float4 renderTargetSize : packoffset(c44); + float4 upscaledTargetSize : packoffset(c45); + float4 materialColor : packoffset(c46); + float4 cameraUp : packoffset(c47); + float4 cameraLook : packoffset(c48); + float4 cameraSide : packoffset(c49); + float4 cameraVelocity : packoffset(c50); + float4 skyMxR : packoffset(c51); + float4 skyMxG : packoffset(c52); + float4 skyMxB : packoffset(c53); + float4 sunMxR : packoffset(c54); + float4 sunMxG : packoffset(c55); + float4 sunMxB : packoffset(c56); + float4 skyRotationTransition : packoffset(c57); + float4 debugColorOverride : packoffset(c58); + float4 debugAlphaOverride : packoffset(c59); + float4 debugNormalOverride : packoffset(c60); + float4 debugSpecularOverride : packoffset(c61); + float4 debugGlossOverride : packoffset(c62); + float4 debugOcclusionOverride : packoffset(c63); + float4 debugStreamerControl : packoffset(c64); + float4 emblemLUTSelector : packoffset(c65); + float4 colorMatrixR : packoffset(c66); + float4 colorMatrixG : packoffset(c67); + float4 colorMatrixB : packoffset(c68); + float4 gameTime : packoffset(c69); + float4 gameTick : packoffset(c70); + float4 subpixelOffset : packoffset(c71); + float4 viewportDimensions : packoffset(c72); + float4 viewSpaceScaleBias : packoffset(c73); + float4 ui3dUVSetup0 : packoffset(c74); + float4 ui3dUVSetup1 : packoffset(c75); + float4 ui3dUVSetup2 : packoffset(c76); + float4 ui3dUVSetup3 : packoffset(c77); + float4 ui3dUVSetup4 : packoffset(c78); + float4 ui3dUVSetup5 : packoffset(c79); + float4 clipSpaceLookupScale : packoffset(c80); + float4 clipSpaceLookupOffset : packoffset(c81); + uint4 computeSpriteControl : packoffset(c82); + float4 invBcTexSizes : packoffset(c83); + float4 invMaskTexSizes : packoffset(c84); + float4 relHDRExposure : packoffset(c85); + uint4 triDensityFlags : packoffset(c86); + float4 triDensityParams : packoffset(c87); + float4 voldecalRevealTextureInfo : packoffset(c88); + float4 extraClipPlane0 : packoffset(c89); + float4 extraClipPlane1 : packoffset(c90); + float4 shaderDebug : packoffset(c91); + uint isDepthHack : packoffset(c92); +} + +SamplerState bilinearClampler_s : register(s1); +Texture2D frameBuffer : register(t0); +Texture2D revealMap : register(t6); +Texture2D aberrationMask : register(t7); +Texture2D colorMap : register(t9); +Texture2D normalMap : register(t10); + + +// 3Dmigoto declarations +#define cmp - +#include "./common1.hlsl" + +void main( + float4 v0 : SV_POSITION0, + float2 v1 : TEXCOORD0, + out float4 o0 : SV_TARGET0) +{ + float4 r0,r1,r2,r3,r4,r5; + uint4 bitmask, uiDest; + float4 fDest; + +#if CUSTOM_SDR > 0 + LET_THIS_BREAK; +#endif + + r0.x = colorFlipRowsCols.x * colorFlipRowsCols.y; + r0.y = colorFlipTime * r0.x; + r0.z = gameTime.w * r0.y; + r0.z = cmp(r0.z >= -r0.z); + r0.z = r0.z ? r0.y : -r0.y; + r0.w = 1 / r0.z; + r0.w = gameTime.w * r0.w; + r0.w = frac(r0.w); + r0.z = r0.z * r0.w; + r0.y = r0.z / r0.y; + r0.x = r0.y * r0.x; + r0.x = round(r0.x); + r0.y = r0.x / colorFlipRowsCols.y; + r0.z = colorFlipRowsCols.y * r0.x; + r0.z = cmp(r0.z >= -r0.z); + r0.z = r0.z ? colorFlipRowsCols.y : -colorFlipRowsCols.y; + r0.w = 1 / r0.z; + r0.x = r0.x * r0.w; + r0.x = frac(r0.x); + r0.x = r0.z * r0.x; + r0.x = trunc(r0.x); + r0.x = v1.x + r0.x; + r1.x = r0.x / colorFlipRowsCols.y; + r0.x = trunc(r0.y); + r0.x = v1.y + r0.x; + r1.y = r0.x / colorFlipRowsCols.x; + r0.xyzw = colorMap.Sample(bilinearClampler_s, r1.xy).xyzw; + r1.xy = saturate(scriptVector0.zy); + r0.w = r1.x * r0.w; + r1.xzw = log2(scriptVector1.xyz); + r1.xzw = float3(2.20000005,2.20000005,2.20000005) * r1.xzw; + r1.xzw = exp2(r1.xzw); + r1.xzw = float3(-1,-1,-1) + r1.xzw; + r1.xzw = scriptVector1.www * r1.xzw + float3(1,1,1); + r2.xyz = r1.xzw * r0.xyz; + r2.w = revealMap.Sample(bilinearClampler_s, v1.xy).x; + r2.w = 1 + -r2.w; + r3.x = min(1, abs(scriptVector0.x)); + r3.x = warpPixels * r3.x; + r3.x = renderTargetSize.z * r3.x; + r3.yz = v1.xy * float2(2,2) + float2(-1,-1); + r4.xy = cmp(float2(0,0) < scriptVector0.xw); + r3.w = r2.w * r3.x + 1; + r4.zw = r3.yz * r3.ww; + r4.zw = r4.zw * float2(0.5,0.5) + float2(0.5,0.5); + r2.w = -r2.w * r3.x + 1; + r3.xy = r3.yz * r2.ww; + r3.xy = r3.xy * float2(0.5,0.5) + float2(0.5,0.5); + r3.xy = r4.xx ? r4.zw : r3.xy; + if (r4.y != 0) { + r3.zw = gameTime.ww * normalScroll.xy; + r3.zw = v1.xy * normalTile.xy + r3.zw; + r3.zw = frac(r3.zw); + r4.xy = renderTargetSize.zw * refractionAmount; + r3.zw = normalMap.Sample(bilinearClampler_s, r3.zw).xy; + r3.zw = r3.zw * float2(2,2) + float2(-1,-1); + r3.zw = scriptVector0.ww * r3.zw; + r3.zw = r4.xy * r3.zw; + } else { + r3.zw = float2(0,0); + } + r3.xy = r3.xy + r3.zw; + r3.zw = gameTime.ww * aberrationScroll.xy + v1.xy; + r2.w = aberrationMask.Sample(bilinearClampler_s, r3.zw).x; + r1.y = r2.w * r1.y; + r1.y = aberrationPixels * r1.y; + r3.zw = renderTargetSize.zw * r1.yy; + r4.xyzw = r3.zwzw * aberrationFactors.xxyy + r3.xyxy; + r5.x = frameBuffer.Sample(bilinearClampler_s, r4.xy).x; + r5.y = frameBuffer.Sample(bilinearClampler_s, r4.zw).y; + r3.xy = r3.zw * aberrationFactors.zz + r3.xy; + r5.z = frameBuffer.Sample(bilinearClampler_s, r3.xy).z; + +#if CUSTOM_SDR == 0 + r3.xyz = Trade_Out(r5.xyz); + r3.xyz = max(0, r3.xyz); +#else + r3.xyz = float3(3.05175781e-005, 3.05175781e-005, 3.05175781e-005) * r5.xyz; +#endif + r0.xyz = r0.xyz * r1.xzw + -r3.xyz; + r0.xyz = r0.www * r0.xyz + r3.xyz; + +#if CUSTOM_SDR == 0 + r1.xyz = -Trade_Out(r5.xyz) + float3(1, 1, 1); + r1.xyz = max(0, r1.xyz); //TODO: or should we tonemap? +#else + r1.xyz = -r5.xyz * float3(3.05175781e-005, 3.05175781e-005, 3.05175781e-005) + float3(1, 1, 1); +#endif + + r2.xyz = -r2.xyz * r0.www + float3(1,1,1); + r1.xyz = -r1.xyz * r2.xyz + float3(1,1,1); + r0.xyz = useAlphaBlend ? r0.xyz : r1.xyz; +#if CUSTOM_SDR == 0 + o0.xyz = Trade_In(r0.xyz); +#else + o0.xyz = float3(32768, 32768, 32768) * r0.xyz; +#endif + o0.w = 1; + return; +} \ No newline at end of file From d08bb999190e37737d0a015c236dbe3b56a20f56 Mon Sep 17 00:00:00 2001 From: David Huynh Date: Fri, 2 Oct 2026 22:51:15 -0700 Subject: [PATCH 3/5] BO3: (not PR-able) OutputHandler (HDR10) --- .../Includes/Common.hlsl | 51 ++ .../Luma_CoDBO3_DisplayComposition.hlsl | 745 ++---------------- Shaders/Call of Duty Black Ops 3/common1.hlsl | 34 - Source/Core/core.hpp | 11 +- Source/Core/includes/game.h | 2 + .../Games/Call of Duty Black Ops 3/main.cpp | 663 ++++++++++------ 6 files changed, 545 insertions(+), 961 deletions(-) diff --git a/Shaders/Call of Duty Black Ops 3/Includes/Common.hlsl b/Shaders/Call of Duty Black Ops 3/Includes/Common.hlsl index f8fd54351..4a733ac37 100644 --- a/Shaders/Call of Duty Black Ops 3/Includes/Common.hlsl +++ b/Shaders/Call of Duty Black Ops 3/Includes/Common.hlsl @@ -5,6 +5,57 @@ #include "../../Includes/Common.hlsl" #include "Settings.hlsl" +// // \left\{x<0.081:\frac{x}{4.5},\left(\frac{x+0.099}{1.099}\right)^{\frac{1}{0.45}}\right\} +// #define DECODEREC709(T)\ +// T DecodeRec709(T x) {\ +// return x < 0.081 ? x / 4.5 : pow((x + 0.099) / 1.099, 1.0 / 0.45);\ +// } +// DECODEREC709(float) +// DECODEREC709(float3) +// DECODEREC709(float4) +// #undef DECODEREC709 +// +// // \left\{x<0.018:4.5x,1.099x^{0.45}-0.099\right\} +// #define ENCODEREC709(T)\ +// T EncodeRec709(T x) {\ +// return x < 0.018 ? 4.5 * x : pow(x * 1.099, 0.45) - 0.099;\ +// } +// ENCODEREC709(float) +// ENCODEREC709(float3) +// ENCODEREC709(float4) +// #undef ENCODEREC709 + +#define DECODEREC709(T)\ +T DecodeRec709(T x) {\ + T r0, r2, r3, r4;\ + r0 = x;\ + r2 = 0.0989999995 + r0; \ + r2 = 0.909918129 * r2;\ + r2 = pow(r2, 2.22222233);\ + r3 = -(0.0810000002 >= r0);\ + r4 = 0.222222224 * r0;\ + r2 = r3 ? r4 : r2;\ + return r2;\ +} +DECODEREC709(float3) +DECODEREC709(float4) +#undef DECODEREC709 + +#define ENCODEREC709(T)\ +T EncodeRec709(T x) {\ + T r0, r1, r2;\ + r1 = x;\ + r0 = pow(r1, 0.449999988);\ + r0 = r0 * 1.09899998 + -0.0989999995;\ + r2 = -(0.0179999992 >= r1);\ + r1 = 4.5 * r1;\ + r0 = r2 ? r1 : r0;\ + return r0;\ +} +ENCODEREC709(float3) +ENCODEREC709(float4) +#undef ENCODEREC709 + #if CUSTOM_LUMA == 1 #if CUSTOM_SDR == 1 #undef CUSTOM_LUTBUILDER_COLORSPACE diff --git a/Shaders/Call of Duty Black Ops 3/Luma_CoDBO3_DisplayComposition.hlsl b/Shaders/Call of Duty Black Ops 3/Luma_CoDBO3_DisplayComposition.hlsl index 282a08411..484ee0773 100644 --- a/Shaders/Call of Duty Black Ops 3/Luma_CoDBO3_DisplayComposition.hlsl +++ b/Shaders/Call of Duty Black Ops 3/Luma_CoDBO3_DisplayComposition.hlsl @@ -1,719 +1,80 @@ -#include "../Includes/Common.hlsl" -#include "../Includes/ColorGradingLUT.hlsl" // Use this as it has some gamma correction helpers -#include "../Includes/Reinhard.hlsl" +#include "./Includes/Common.hlsl" Texture2D sourceTexture : register(t0); -Texture2D uiTexture : register(t1); // Optional: Pre-multiplied UI -#if DEVELOPMENT -Texture2D debugTexture2D : register(t2); -Texture2DMS debugTexture2DMS : register(t3); -Texture2DArray debugTexture2DArray : register(t4); -Texture2DMSArray debugTexture2DMSArray : register(t5); -Texture3D debugTexture3D : register(t6); -Texture1D debugTexture1D : register(t7); -Texture1DArray debugTexture1DArray : register(t8); -// TODO: add TextureCube(s) support... Update "debug_draw_srv_slot_numbers" in c++ if you change this. And also for uint/sint textures. -#endif - -// Note: this might not always be set, only use in known cases -SamplerState linearSampler : register(s0); - -#if DEVELOPMENT -bool DrawDebugTexture(float3 pos, inout float4 outColor, float gamePaperWhite, float UIPaperWhite) -{ - bool _texture2D = (LumaData.CustomData2 & (1 << 13)) != 0; // "_" as the name is seemengly taken in hlsl - bool _texture3D = (LumaData.CustomData2 & (1 << 14)) != 0; - bool _texture1D = !_texture2D && !_texture3D; // We don't have a flag for "no texture", we simply unbind all of them - bool _textureCube = _texture2D && _texture3D; - bool _textureMS = (LumaData.CustomData2 & (1 << 11)) != 0; - bool _textureArray = (LumaData.CustomData2 & (1 << 12)) != 0; - float debugWidth = 0.0, debugHeightOrArraySize = 1.0, debugDepthOrArraySize = 1.0, sampleCount = 1.0; // Default to 0 to make sure they stay zero if the texture wasn't bound - if (_texture1D && _textureArray) - { - debugTexture1DArray.GetDimensions(debugWidth, debugHeightOrArraySize); - } - else if (_texture1D) - { - debugTexture1D.GetDimensions(debugWidth); - } - else if (_texture3D) - { - debugTexture3D.GetDimensions(debugWidth, debugHeightOrArraySize, debugDepthOrArraySize); - } - else if (_textureMS && _textureArray) - { - debugTexture2DMSArray.GetDimensions(debugWidth, debugHeightOrArraySize, debugDepthOrArraySize, sampleCount); - } - else if (_textureMS) - { - debugTexture2DMS.GetDimensions(debugWidth, debugHeightOrArraySize, sampleCount); - } - else if (_textureArray) - { - debugTexture2DArray.GetDimensions(debugWidth, debugHeightOrArraySize, debugDepthOrArraySize); - } - else // _texture2D - { - debugTexture2D.GetDimensions(debugWidth, debugHeightOrArraySize); - } - // TODO: add better support for depth stencil formats? uint? - // Skip if there's no texture. It might be undefined behaviour, but it seems to work on Nvidia - if (debugWidth != 0.0) - { - float2 resolutionScale = 1.0; - bool fullscreen = (LumaData.CustomData2 & (1 << 0)) != 0; - bool renderResolutionScale = (LumaData.CustomData2 & (1 << 1)) != 0; - bool showAlpha = (LumaData.CustomData2 & (1 << 2)) != 0; - bool premultiplyAlpha = (LumaData.CustomData2 & (1 << 3)) != 0; - bool invertColors = (LumaData.CustomData2 & (1 << 4)) != 0; - bool linearToGamma = (LumaData.CustomData2 & (1 << 5)) != 0; - bool gammaToLinear = (LumaData.CustomData2 & (1 << 6)) != 0; - bool flipY = (LumaData.CustomData2 & (1 << 7)) != 0; - bool doAbs = (LumaData.CustomData2 & (1 << 15)) != 0; - bool doSaturate = (LumaData.CustomData2 & (1 << 8)) != 0; - bool redOnly = (LumaData.CustomData2 & (1 << 9)) != 0; - bool backgroundPassthrough = (LumaData.CustomData2 & (1 << 10)) != 0; - bool zoom4x = (LumaData.CustomData2 & (1 << 16)) != 0; - bool bilinear = (LumaData.CustomData2 & (1 << 17)) != 0; - bool tonemap = (LumaData.CustomData2 & (1 << 18)) != 0; - bool sRGB = (LumaData.CustomData2 & (1 << 19)) != 0; // sRGB in/out (instead of gamma 2.2) - bool UVToPixelSpace = (LumaData.CustomData2 & (1 << 20)) != 0; - bool denormalize = (LumaData.CustomData2 & (1 << 21)) != 0; - int mipLevel = LumaData.CustomData3 + 0.5; // 10 bits for the mip level - - float3 debugPos = float3(pos.xy, 0.5); - int sliceWidth = debugWidth + 0.5; - debugPos.z = (uint(debugPos.x) / sliceWidth) + 0.5; // Basically: "depthOrArrayIndex" - - float targetWidth; - float targetHeight; - sourceTexture.GetDimensions(targetWidth, targetHeight); - - bool size2DMatches = debugWidth == targetWidth && debugHeightOrArraySize == targetHeight; - fullscreen = fullscreen || size2DMatches; - - if (fullscreen) // Stretch to fullscreen - { - resolutionScale = float2(debugWidth / targetWidth, debugHeightOrArraySize / targetHeight); - if (flipY) - { - debugPos.y = targetHeight - debugPos.y; - } - } - else - { - if (debugDepthOrArraySize != 1) // TODO: what is this for exactly? Some array textures? - { - debugPos.x = (uint(debugPos.x) % sliceWidth) + 0.5; - } - // TODO: handle if this works with "renderResolutionScale" (e.g. Prey) - if (flipY) - { - debugPos.y = debugHeightOrArraySize - debugPos.y; - } - } - if (renderResolutionScale) // Scale by rendering resolution (so to stretch the used part of the image to the full texture range) (note that this might not work so well if the game draws at a different aspect ratio and then adds black bars) - { - resolutionScale *= LumaData.RenderResolutionScale; - } - - // Zoom around the center - float zoom = zoom4x ? 4.0 : 1.0; - float3 debugSize = float3(debugWidth, debugHeightOrArraySize, debugDepthOrArraySize); // I don't think we need -1 on this - float3 center = fullscreen ? 0 : (-debugSize * 0.5); // Zoom around the center if non fullscreen mode // TODO: polish... not right - debugPos.xyz = ((debugPos.xyz - center) / zoom) + center; - // TODO: add a brightness scale to convert from UV space to pixel space (e.g. motion vectors) - - // TODO: should we also apply the "resolutionScale" around the center? - int3 debugPosInt; - debugPosInt.xy = round((debugPos.xy - 0.5) * resolutionScale) + 0.5; - debugPosInt.z = debugPos.z * resolutionScale.x; - - debugPos.xy *= resolutionScale; - debugPos.z *= resolutionScale.x; - - bool validTexel = debugPos.x < debugSize.x && debugPos.y < debugSize.y && debugPos.z < debugSize.z && debugPos.x >= 0.0 && debugPos.y >= 0.0 && debugPos.z >= 0.0; - float4 color = 0.0; - int sampleIndex = 0; // Expose for manual analysis if necessary - if (_texture1D && _textureArray) - { - color = debugTexture1DArray.Load(int3(debugPosInt.x, debugPosInt.y, mipLevel)); // The array elements are spread vertically - } - else if (_texture1D) - { - color = debugTexture1D.Load(int2(debugPosInt.x, mipLevel)); - } - else if (_texture3D) - { - color = debugTexture3D.Load(int4(debugPosInt.x, debugPosInt.y, debugPosInt.z, mipLevel)); // The array elements are spread horizontally - } - // All "_texture2D" from here - else if (_textureMS && _textureArray) - { - for (; sampleIndex < sampleCount; sampleIndex++) - { - color += debugTexture2DMSArray.Load(int3(debugPosInt.x, debugPosInt.y, debugPosInt.z), sampleIndex); // The array elements are spread horizontally - } - } - else if (_textureMS) - { - for (; sampleIndex < sampleCount; sampleIndex++) // Take all MS samples, to get a better overview of the texture - { - color += debugTexture2DMS.Load(int2(debugPosInt.xy), sampleIndex); // Ideally we'd average them in linear space, but whatever - } - } - else if (_textureArray) - { - color = debugTexture2DArray.Load(int4(debugPosInt.x, debugPosInt.y, debugPosInt.z, mipLevel)); // The array elements are spread horizontally - } - else - { - if (bilinear) // TODO: implement for other texture types - color = debugTexture2D.SampleLevel(linearSampler, debugPos.xy / debugSize.xy, mipLevel); - else - color = debugTexture2D.Load(int3(debugPosInt.x, debugPosInt.y, mipLevel)); // Approximate to the closest texel (sharp!) - } - color /= sampleCount; - - // Samples on invalid coordinates should already return 0, but we force it anyway - if (!validTexel) - { - color.rgb = 0; - } - - if (denormalize) - { - color.rgb = (color.rgb - 0.5) * 2.0; // Note: 0.5 wouldn't have been the exact center in UNORM textures, but it should do (otherwise we'd need to branch based on the format precision) - } - // Do it early so it also fixes nans - if (doAbs) - { - color = abs(color); - } - if (doSaturate) - { - color = saturate(color); - } - if (redOnly) - { - color.rgb = color.r; - } - if (showAlpha) - { - color.rgb = color.a; - } - if (premultiplyAlpha) - { - color.rgb *= color.a; - } - if (UVToPixelSpace) - { - color.rgb *= debugSize; - } - if (invertColors) // Only works on in SDR range - { - color.rgb = 1.0 - color.rgb; - } - if (gammaToLinear) // Linearize (output expects linear) (use if image appeared too bright (gamma space viewed as linear)) - { - color.rgb = sRGB ? gamma_sRGB_to_linear(color.rgb, GCT_MIRROR) : (pow(abs(color.rgb), DefaultGamma) * sign(color.rgb)); - } - if (linearToGamma) // Gammify (usually not necessary) (use if image appeared too dark (linear space viewed as gamma)) - { - color.rgb = sRGB ? linear_to_sRGB_gamma(color.rgb, GCT_MIRROR) : (pow(abs(color.rgb), 1.f / DefaultGamma) * sign(color.rgb)); - } - if (tonemap && !showAlpha) - { - const float peakWhite = LumaSettings.PeakWhiteNits / sRGB_WhiteLevelNits; -#if 1 // No hue shifts, better analysis - color.rgb = RestoreLuminance(color.rgb, Reinhard::ReinhardRange(GetLuminance(color.rgb), MidGray, -1.0, peakWhite / gamePaperWhite).x, true); - color.rgb = CorrectOutOfRangeColor(color.rgb, true, true, 0.5, peakWhite / gamePaperWhite); -#else - color.rgb = Reinhard::ReinhardRange(color.rgb, MidGray, -1.0, peakWhite / gamePaperWhite); -#endif - } - if (validTexel || !backgroundPassthrough) - { - outColor = color * gamePaperWhite; // Scale by user paper white brightness just to make it more visible - return true; - } - } - return false; -} -#endif - -float3 ComposeUI(float3 pos, float3 linearSceneColor, float gamePaperWhite, float UIPaperWhite) -{ -#if 0 - float3 sceneColorGamma = linear_to_game_gamma(linearSceneColor.rgb, GCT_MIRROR); - float3 UIRelativeColor = linearSceneColor.rgb * (gamePaperWhite / UIPaperWhite); - float3 sceneColorGammaTonemapped = linear_to_game_gamma((UIRelativeColor / (UIRelativeColor + 1.f)) / (gamePaperWhite / UIPaperWhite), GCT_MIRROR); // Tonemap the UI background based on the UI intensity to avoid bright backgrounds (e.g. sun) burning through the UI - float3 UIInverseInfluence = 1.0; - float4 UIColor = uiTexture.Load((int3)pos.xyz); - float UIIntensity = saturate(UIColor.a); - sceneColorGamma *= pow(gamePaperWhite, 1.0 / DefaultGamma); - sceneColorGammaTonemapped *= pow(gamePaperWhite, 1.0 / DefaultGamma); - UIColor.rgb *= pow(UIPaperWhite, 1.0 / DefaultGamma); - // Darken the scene background based on the UI intensity - float3 composedColor = lerp(sceneColorGamma, sceneColorGammaTonemapped, UIIntensity) * (1.0 - UIIntensity); - // Calculate how much the additive UI influenced the darkened scene color, so we can determine the intensity to blend the composed color with the scene paper white (it's better to calculate this in gamma space) - UIInverseInfluence = safeDivision(composedColor, composedColor + UIColor.rgb, 1); //TODO: handle negative colors? - // Add pre-multiplied UI - composedColor += UIColor.rgb; - - float3 compositionPaperWhite = lerp(pow(UIPaperWhite, 1.0 / DefaultGamma), pow(gamePaperWhite, 1.0 / DefaultGamma), UIInverseInfluence); - composedColor /= compositionPaperWhite; - - float3 linearComposedColor = game_gamma_to_linear(composedColor, GCT_MIRROR) * pow(compositionPaperWhite, DefaultGamma); // Note: this won't scale the paper white 100% correctly, slightly shifting colors -#else - linearSceneColor.rgb /= UIPaperWhite / gamePaperWhite; - //linearSceneColor.rgb /= UIPaperWhite; - float3 sceneColorGamma = linear_to_game_gamma(linearSceneColor.rgb, GCT_MIRROR); - float3 sceneColorGammaTonemapped = linear_to_game_gamma(linearSceneColor.rgb / (linearSceneColor.rgb + 1.f), GCT_MIRROR); // Tonemap the UI background based on the UI intensity to avoid bright backgrounds (e.g. sun) burning through the UI - float3 UIInverseInfluence = 1.0; - float4 UIColor = uiTexture.Load((int3)pos.xyz); - float UIIntensity = saturate(UIColor.a); - // Darken the scene background based on the UI intensity - float3 composedColor = lerp(sceneColorGamma, sceneColorGammaTonemapped, UIIntensity) * (1.0 - UIIntensity); - composedColor = sceneColorGamma * (1.0 - UIIntensity); // Disable TM for now - // Calculate how much the additive UI influenced the darkened scene color, so we can determine the intensity to blend the composed color with the scene paper white (it's better to calculate this in gamma space) - UIInverseInfluence = safeDivision(composedColor, composedColor + UIColor.rgb, 1); - // Add pre-multiplied UI - composedColor += UIColor.rgb; - float3 linearComposedColor; - linearComposedColor.rgb = game_gamma_to_linear(composedColor, GCT_MIRROR); - linearComposedColor.rgb *= UIPaperWhite / gamePaperWhite; - //linearComposedColor.rgb *= UIPaperWhite; - //linearComposedColor.rgb *= lerp(1.0, gamePaperWhite, saturate(UIInverseInfluence)); -#endif - return linearComposedColor; -} - -#define cmp - - -//REC709 -#define DECODEREC709(T)\ -T DecodeRec709(T x) {\ - T r0, r2, r3, r4;\ - r0 = x;\ - r2 = 0.0989999995 + r0; \ - r2 = 0.909918129 * r2;\ - r2 = pow(r2, 2.22222233);\ - r3 = cmp(0.0810000002 >= r0);\ - r4 = 0.222222224 * r0;\ - r2 = r3 ? r4 : r2;\ - return r2;\ -} -DECODEREC709(float3) -DECODEREC709(float4) -#undef DECODEREC709 - -#define ENCODEREC709(T)\ -T EncodeRec709(T x) {\ - T r0, r1, r2;\ - r1 = x;\ - r0 = pow(r1, 0.449999988);\ - r0 = r0 * 1.09899998 + -0.0989999995;\ - r2 = cmp(0.0179999992 >= r1);\ - r1 = 4.5 * r1;\ - r0 = r2 ? r1 : r0;\ - return r0;\ -} -ENCODEREC709(float3) -ENCODEREC709(float4) -#undef ENCODEREC709 +Texture2D debugTexture : register(t2); // DEVELOPMENT +Texture2D sourceTextureHDR10 : register(t10); // rect is top left (x,y), bottom right (x,y) -float3 DrawRect(float2 uv, float4 rect, float3 color, float3 rectColor) { +float3 DrawRect(float2 uv, float4 rect, float3 x, float3 rectColor) { float r = step(rect.x, uv.x) * step(uv.x, rect.z) * step(rect.y, uv.y) * step(uv.y, rect.w); - if (r == 0) return color; + if (r == 0) return x; return rectColor; } -// Custom Luma shader to apply the display (or output) transfer function from a linear input (or apply custom gamma correction) float4 main(float4 pos : SV_Position) : SV_Target0 { - // Game scene paper white and Generic paper white for when we can't account for the UI paper white. - // If "POST_PROCESS_SPACE_TYPE" or "EARLY_DISPLAY_ENCODING" are 1, this might have already been applied in. - // This essentially means that the SDR range we receive at this point is 0-1 in the buffers, with 1 matching "sRGB_WhiteLevelNits" as opposued to "ITU_WhiteLevelNits". - float gamePaperWhite = LumaSettings.GamePaperWhiteNits / sRGB_WhiteLevelNits; - float UIPaperWhite = LumaSettings.UIPaperWhiteNits / sRGB_WhiteLevelNits; - -#if DEVELOPMENT - float4 debugTextureColor = 0.0; - if (DrawDebugTexture(pos.xyz, debugTextureColor, gamePaperWhite, UIPaperWhite)) - { - return debugTextureColor; - } -#endif - - float sourceWidth; - float sourceHeight; - sourceTexture.GetDimensions(sourceWidth, sourceHeight); - -#if TEST_2X_ZOOM // TEST: zoom into the image to analyze it - const float scale = 2.0; - pos.xy = pos.xy / scale + float2(sourceWidth, sourceHeight) / (scale * 2.0); -#endif - - float2 uv = pos.xy / float2(sourceWidth, sourceHeight); - -#if 0 // TEST: draw gradient - return float4(uv.x, uv.x, uv.x, 1.0) * 2; -#endif - - float targetHeight1 = 1080.0 * DVS4; - float targetHeight2 = targetHeight1 * DVS5 * 0.5; - float mipLevel1 = log2(sourceHeight / targetHeight1); - float mipLevel2 = log2(sourceHeight / targetHeight2); - float4 color = sourceTexture.Load((int3)pos.xyz); - float4 mipColor1 = 0.0; - float4 mipColor2 = 0.0; -#if 0 - mipColor1 = sourceTexture.SampleLevel(linearSampler, uv, mipLevel1); - mipColor2 = sourceTexture.SampleLevel(linearSampler, uv, mipLevel2); - int offset = 5; - mipColor1 = sourceTexture.Load((int3)pos.xyz + int3( offset, -offset, 0)) + - sourceTexture.Load((int3)pos.xyz + int3(-offset, offset, 0)) + - sourceTexture.Load((int3)pos.xyz + int3(-offset, -offset, 0)) + - sourceTexture.Load((int3)pos.xyz + int3( offset, offset, 0)) + - color; - mipColor1 /= 5.0; - offset = 30; - mipColor2 = sourceTexture.Load((int3)pos.xyz + int3( offset, -offset, 0)) + - sourceTexture.Load((int3)pos.xyz + int3(-offset, offset, 0)) + - sourceTexture.Load((int3)pos.xyz + int3(-offset, -offset, 0)) + - sourceTexture.Load((int3)pos.xyz + int3( offset, offset, 0)) + - color; - mipColor2 /= 5.0; -#endif - //return mipColor2; //TODOFT - -#if SWAPCHAIN_SKIPALL > 0 - return color; -#endif - - //rec709 decode, doing 0x8324B585 - #if CUSTOM_HDTVREC709 > 0 - color.rgb = gamma_sRGB_to_linear(color.rgb, GCT_MIRROR); - color.rgb = sign(color.rgb) * EncodeRec709(abs(color.rgb)); - #endif - - // This case means the game currently doesn't have Luma custom shaders built in (fallback in case of problems), or has manually unloaded them, so the value of some macro defines do not matter - const bool modActive = LumaData.CustomData1 == 0; - if (!modActive) - { - // SDR was already linear, assuming we are outputting on scRGB HDR buffers (usually implies "POST_PROCESS_SPACE_TYPE" is 1) - const bool vanillaSwapchainWasLinear = LumaData.CustomData1 >= 2; - // "VANILLA_ENCODING_TYPE" is expected to be 0 for this branch - if (vanillaSwapchainWasLinear) - { - // SDR (on SDR) - if (LumaSettings.DisplayMode <= 0) - { - // Nothing to do, the game would have encoded with sRGB, the display will decode sRGB with gamma 2.2 as it would have in vanilla SDR, handling gamma correction for us. - // "GAMMA_CORRECTION_TYPE" is not implemented here as it's not a common case that we'd wanna handle, and we want to keep SDR looking like SDR always did. - } - // HDR - else - { - //TODOFT: wrap in a func, given it's duplicate below too? One of the ColorGradingLUTTransferFunctionIn... - float3 colorGammaCorrectedByChannel = gamma_to_linear(linear_to_sRGB_gamma(color.rgb, GCT_MIRROR), GCT_MIRROR); - float luminanceGammaCorrected = gamma_to_linear(linear_to_sRGB_gamma(GetLuminance(color.rgb), GCT_POSITIVE).x, GCT_POSITIVE).x; - float3 colorGammaCorrectedByLuminance = RestoreLuminance(color.rgb, luminanceGammaCorrected); -#if GAMMA_CORRECTION_TYPE == 1 - color.rgb = colorGammaCorrectedByChannel; -#elif GAMMA_CORRECTION_TYPE == 2 - color.rgb = RestoreLuminance(color.rgb, colorGammaCorrectedByChannel); - float3 colorGammaCorrectedByChannelMip1 = gamma_to_linear(linear_to_sRGB_gamma(mipColor1.rgb, GCT_MIRROR), GCT_MIRROR); - float3 colorGammaCorrectedByChannelMip2 = gamma_to_linear(linear_to_sRGB_gamma(mipColor2.rgb, GCT_MIRROR), GCT_MIRROR); - mipColor1.rgb = RestoreLuminance(mipColor1.rgb, colorGammaCorrectedByChannelMip1); - mipColor2.rgb = RestoreLuminance(mipColor2.rgb, colorGammaCorrectedByChannelMip2); -#elif GAMMA_CORRECTION_TYPE == 3 //TODOFT: probably doesn't look good? It'd treat green and blue massively different - color.rgb = colorGammaCorrectedByLuminance; -#elif GAMMA_CORRECTION_TYPE >= 4 - color.rgb = RestoreHueAndChrominance(colorGammaCorrectedByLuminance, colorGammaCorrectedByChannel, 0.0, 1.0); -#endif // GAMMA_CORRECTION_TYPE == 1 - } - } - // SDR was gamma space, but now we are outputting on scRGB HDR buffers - else - { - // SDR (on SDR) - if (LumaSettings.DisplayMode <= 0) - { - // The SDR display will (usually) linearize with gamma 2.2, hence applying the usual gamma mismatch, so we don't correct gamma here - color.rgb = gamma_sRGB_to_linear(color.rgb, GCT_NONE); - } - // HDR (we assume this is the default case for Luma users/devs, this isn't an officially supported case anyway) (we ignore "GAMMA_CORRECTION_RANGE_TYPE" and "VANILLA_ENCODING_TYPE" here, it doesn't matter) - else - { - color.rgb = ColorGradingLUTTransferFunctionOut(color.rgb, GAMMA_CORRECTION_TYPE); - - mipColor1.rgb = ColorGradingLUTTransferFunctionOut(mipColor1.rgb, GAMMA_CORRECTION_TYPE); - mipColor2.rgb = ColorGradingLUTTransferFunctionOut(mipColor2.rgb, GAMMA_CORRECTION_TYPE); - } - } - -#if 0 // AntiBloom AutoHDR - // TODO: add an "unclipping" mode that tries to detect the channel value before it clipped to 1 by querying smaller mips until one isn't 1, and then tries to increase the value in the center of the clipped highlight blob. Also try "CorrectPerChannelTonemapDesaturation" to resaturate per channel tonemaped highlights. - float shoulderPow = 2.75f; // Default value, can be changed in the settings - float maxShoulderPow = lerp(shoulderPow, 1.f, LumaSettings.DevSetting01); // Default value, can be changed in the settings - float mipRatio1 = GetLuminance(mipColor1.rgb) / GetLuminance(color.rgb); - float mipRatio2 = GetLuminance(mipColor2.rgb) / GetLuminance(color.rgb); - float peak = 500.f; - float maxPeak = lerp(peak, 800.f, LumaSettings.DevSetting02); - if (LumaSettings.DevSetting03 <= 0.0) - { - shoulderPow = lerp(shoulderPow, maxShoulderPow, saturate(mipRatio1 * mipRatio2 * GetLuminance(color.rgb))); - peak = lerp(peak, maxPeak, saturate(mipRatio1 * mipRatio2 * GetLuminance(color.rgb))); - } - if (LumaSettings.DevSetting03 > 0.75) - { - color.rgb = mipColor2.rgb; - } - else if (LumaSettings.DevSetting03 > 0.5) - { - color.rgb = mipColor1.rgb; - } - else if (LumaSettings.DevSetting03 > 0.25) - { - } - else - { - color.rgb = PumboAutoHDR(color.rgb, peak, LumaSettings.GamePaperWhiteNits, shoulderPow); // This won't multiply the paper white in, it just uses it as a modifier for the AutoHDR logic - } - if (LumaSettings.DevSetting03 <= 0.0) - { - color.rgb = Saturation(color.rgb, (saturate(mipRatio1 * mipRatio2 * GetLuminance(color.rgb))* 0.333 + 1.0)); - } +#if OUTPUT_MODE == 0 + float4 x = sourceTexture.Load((int3)pos.xyz); +#elif OUTPUT_MODE == 1 + float4 x = sourceTextureHDR10.Load((int3)pos.xyz); +#elif OUTPUT_MODE == 2 + float4 x = sourceTexture.Load((int3)pos.xyz); #endif -#if DEVELOPMENT // Optionally clamp SDR and SDR on HDR modes (dev only) - if (LumaSettings.DisplayMode != 1) - color.rgb = saturate(color.rgb); -#endif - color.rgb *= gamePaperWhite; - return float4(color.rgb, color.a); - } - - float postLinearizationScale = 1.0; + float p = HDR_PEAK; + float intScaling = HDR_INTSCALING; + float2 uv = pos.xy * LumaSettings.SwapchainInvSize; -//TODOFT: split this and other code behaviours into functions -#if UI_DRAW_TYPE == 2 // The scene color was scaled by "scene paper white / UI paper white" (in linear space) to make the UI blend in correctly in gamma space without modifying its shaders. sRGB/2.2 gamma mismatch correction would have already applied temporarily at native SDR range on the pre-UI scene color, before scaling it by the UI factors, to then be undone. So we need to re-apply the gamma mismatch fix on the UI SDR range color (and the rest of the scene, for which the fix was already undone at that scaling level, and thus will normalize itself out). - - postLinearizationScale *= UIPaperWhite; -#if !EARLY_DISPLAY_ENCODING - postLinearizationScale /= gamePaperWhite; -#endif - -#elif UI_DRAW_TYPE == 3 // Compose UI on top of "scene" and tonemap the scene background //TODOFT6: clean up all the defines that we don't need anymore - - // These should imply the scene is rendering (the UI might still be all zero) - if (LumaData.CustomData4 != 0.f) - { - color.rgb = ComposeUI(pos.xyz, color.rgb, gamePaperWhite, UIPaperWhite); - - //color.rgb = game_gamma_to_linear(uiTexture.Load((int3)pos.xyz, GCT_MIRROR).rgb); - //color.rgb = uiTexture.Load((int3)pos.xyz, GCT_MIRROR).a; - } - // There's no scene rendering, which imply the image is all UI, so scale the overall brightness with the UI brightness parameter instead of the game scene one - else - { - //color.rgb = game_gamma_to_linear(color.rgb, GCT_MIRROR); - gamePaperWhite = UIPaperWhite; - } - -#endif // UI_DRAW_TYPE != 0 - - // SDR: In this case, paper white (game and UI) would have been 1 (neutral for SDR), so we can ignore it if we want to - if (LumaSettings.DisplayMode <= 0) - { - color.rgb = saturate(color.rgb); // Optional, but saves performance on the gamma pows below (the vanilla SDR tonemapper might have retained some values beyond 1 so we want to clip them anyway, for a "reliable" SDR look) - -#if SWAPCHAIN_SKIP_DECODE == 0 -#if POST_PROCESS_SPACE_TYPE == 1 - // Revert whatever gamma adjustment "GAMMA_CORRECTION_TYPE" would have made, and get the color is sRGB gamma encoding (which would have been meant for 2.2 displays) - // This function does more stuff than we need (like handling colors beyond the 0-1 range, which we've clamped out above), but we use it anyway for simplicity - ColorGradingLUTTransferFunctionInOutCorrected(color.rgb, (EARLY_DISPLAY_ENCODING && GAMMA_CORRECTION_TYPE < 2) ? GAMMA_CORRECTION_TYPE : VANILLA_ENCODING_TYPE, LUT_EXTRAPOLATION_TRANSFER_FUNCTION_SRGB, true); - // No need for "postLinearizationScale", it should always be 1 in SDR. -#else // POST_PROCESS_SPACE_TYPE != 1 - // In SDR, we ignore "GAMMA_CORRECTION_TYPE" as they are not that relevant - // We are target the gamma 2.2 look here, which would likely match the average SDR screen, so - // we linearize with sRGB because scRGB HDR buffers (Luma) in SDR are re-encoded with sRGB and then (likely) linearized by the display with 2.2, which would then apply the gamma correction. - // For any user that wanted to play in sRGB, they'd need to have an sRGB monitor. - // We could theoretically add a mode that fakes sRGB output on scRGB->2.2 but it wouldn't really be useful as the game was likely designed for 2.2 displays (unconsciously). - color.rgb = ColorGradingLUTTransferFunctionOut(color.rgb, LUT_EXTRAPOLATION_TRANSFER_FUNCTION_SRGB, false); -#endif // POST_PROCESS_SPACE_TYPE == 1 -#endif //SWAPCHAIN_SKIP_DECODE - -#if 0 // For linux support (somehow scRGB is not interpreted as linear when in SDR) //TODOFT4: expose? - color.rgb = linear_to_sRGB_gamma(color.rgb, GCT_NONE); -#endif - } - // HDR and SDR in HDR: in this case the UI paper white would have already been mutliplied in, relatively to the game paper white, so we only apply the game paper white. - else if (LumaSettings.DisplayMode >= 1) - { -#if POST_PROCESS_SPACE_TYPE != 1 // Gamma->Linear space - - // At this point, in this case, the color would have been gamma space (sRGB or 2.2, depending on the game), normalized around SDR range (80 nits paper white). - // The gamma correction both acts as correction but also as "absolute" gamma curve selection (emulating either an sRGB or Gamma 2.2 display). - -#if SWAPCHAIN_SKIP_DECODE == 0 -#if GAMMA_CORRECTION_RANGE_TYPE == 1 // Apply gamma correction only in the 0-1 range - - color.rgb = ColorGradingLUTTransferFunctionOutCorrected(color.rgb, VANILLA_ENCODING_TYPE, GAMMA_CORRECTION_TYPE); - -#else // GAMMA_CORRECTION_RANGE_TYPE != 0 // Apply gamma correction around the whole range (alternative branch) (this doesn't acknowledge "VANILLA_ENCODING_TYPE", it doesn't need to) - - color.rgb = ColorGradingLUTTransferFunctionOut(color.rgb, GAMMA_CORRECTION_TYPE); - -#endif // GAMMA_CORRECTION_RANGE_TYPE == 1 -#endif //SWAPCHAIN_SKIP_DECODE - -#else // POST_PROCESS_SPACE_TYPE == 1 // Linear->Linear space - -#if EARLY_DISPLAY_ENCODING - // At this point, for this case, we expect the paper white to already have been multiplied in the color (earlier in the linear post processing pipeline) - color.rgb /= gamePaperWhite; -#endif - -#if SWAPCHAIN_SKIP_DECODE == 0 -#if !EARLY_DISPLAY_ENCODING && GAMMA_CORRECTION_TYPE <= 1 - - ColorGradingLUTTransferFunctionInOutCorrected(color.rgb, VANILLA_ENCODING_TYPE, GAMMA_CORRECTION_TYPE, true); // We enforce "GAMMA_CORRECTION_RANGE_TYPE" 1 as the other case it too complicated and unnecessary to implement - -// "GAMMA_CORRECTION_TYPE >= 2" is always delayed until the end and treated as sRGB gamma before (independently of "EARLY_DISPLAY_ENCODING"). -// We originally applied this gamma correction directly during tonemapping/grading and other later passes, -// but given that the formula is slow to execute and isn't easily revertible -// (mirroring back and forth is lossy, at least in the current lightweight implementation), -// we moved it to a single application here (it might not look as good but it's certainly good enough). -// Any linear->gamma->linear encoding (e.g. "PostAAComposites") or linear->gamma->luminance encoding (e.g. Anti Aliasing) -// should fall back on gamma 2.2 instead of sRGB for this gamma correction type, but we haven't bothered implementing that (it's not worth it). -#elif GAMMA_CORRECTION_TYPE >= 2 // Note: this might not work with "UI_DRAW_TYPE == 2" - - float3 colorInExcess = color.rgb - saturate(color.rgb); // Only correct in the 0-1 range - color.rgb = saturate(color.rgb); - - float3 colorGammaCorrectedByChannel = gamma_to_linear(linear_to_sRGB_gamma(color.rgb)); - float luminanceGammaCorrected = gamma_to_linear1(linear_to_sRGB_gamma1(GetLuminance(color.rgb))); - float3 colorGammaCorrectedByLuminance = RestoreLuminance(color.rgb, luminanceGammaCorrected); -#if GAMMA_CORRECTION_TYPE == 2 - color.rgb = RestoreLuminance(color.rgb, colorGammaCorrectedByChannel); -#elif GAMMA_CORRECTION_TYPE == 3 - color.rgb = colorGammaCorrectedByLuminance; -#elif GAMMA_CORRECTION_TYPE >= 4 - color.rgb = RestoreHueAndChrominance(colorGammaCorrectedByLuminance, colorGammaCorrectedByChannel, 0.0, 1.0); -#endif // GAMMA_CORRECTION_TYPE == 2 - - color.rgb += colorInExcess; - -#endif // !EARLY_DISPLAY_ENCODING && GAMMA_CORRECTION_TYPE <= 1 - -#endif // POST_PROCESS_SPACE_TYPE != 1 -#endif //SWAPCHAIN_SKIP_DECODE - - color.rgb *= postLinearizationScale; - - bool gamutMap = true; -#if DEVELOPMENT || TEST // Optionally clip in SDR to properly emulate SDR (dev only) - if (LumaSettings.DisplayMode >= 2) - { - color.rgb = saturate(color.rgb); - gamutMap = false; - } +#if CUSTOM_HDTVREC709 > 0 + float3 rec709Sign = Sign_Fast(x.rgb); + x.rgb = abs(x.rgb); + x.rgb = gamma_sRGB_to_linear(x.rgb, GCT_NONE); + x.rgb = rec709Sign * EncodeRec709(x.rgb); #endif - float aspectRatio = sourceWidth / (float)sourceHeight; - bool blackBar = false; - if (LumaSettings.DisplayMode == 1 && ShouldForceSDR(uv, false, blackBar, aspectRatio)) - { - if (blackBar) - { - color.rgb = 0.0; - } - else - { -#if defined(TEST_SDR_HDR_SPLIT_VIEW_MODE_NATIVE_IMPL) && TEST_SDR_HDR_SPLIT_VIEW_MODE_NATIVE_IMPL - color.rgb = saturate(color.rgb); -#else // Tonemap instead of raw clipping if the game didn't natively implement our split view mode in the tonemapper - const float peakWhite = LumaSettings.PeakWhiteNits / sRGB_WhiteLevelNits; - color.rgb = Reinhard::ReinhardRange(color.rgb, MidGray, peakWhite, 1.0, true); // Acknowledge the previous peak tonemapping to better compress from the HDR display to the SDR range +#if GAMMA_CORRECTION_TYPE == 0 + x.rgb = gamma_sRGB_to_linear(x.rgb, GCT_MIRROR); +#else + x.rgb = gamma_to_linear(x.rgb, GCT_MIRROR); #endif - color.rgb = gamma_to_linear(round(linear_to_gamma(color.rgb) * 255.0) / 255.0); // Quantize to 8 bit - } - } - - if (gamutMap) - { - // Applying gamma correction could both generate negative (invalid) luminances and colors beyond the human visible range, - // so here we try and fix them up. - // Depending on the HDR tonemapper, film grain and sharpening math we used, prior passes might have also generated invalid colors. - -#if GAMUT_MAPPING_TYPE > 0 - float3 preColor = color.rgb; - FixColorGradingLUTNegativeLuminance(color.rgb); - - bool sdr = LumaSettings.DisplayMode != 1; // "GAMUT_MAPPING_TYPE == 1" is "auto" -#if GAMUT_MAPPING_TYPE == 2 - sdr = true; -#else // GAMUT_MAPPING_TYPE >= 3 - sdr = false; -#endif // GAMUT_MAPPING_TYPE == 2 - if (sdr) - { - color.rgb = SimpleGamutClip(color.rgb, false); - } - else - { - color.rgb = BT2020_To_BT709(SimpleGamutClip(BT709_To_BT2020(color.rgb), true)); // For scRGB HDR we could go even wider than BT.2020 (e.g. AP0) but it should overall do fine. - } -#if DEVELOPMENT && 0 // Display gamut mapped colors - if (any(abs(color.rgb - preColor.rgb) > 0.00001)) - { - color.rgb = 100; - } -#endif -#endif // GAMUT_MAPPING_TYPE > 0 - } - - color.rgb *= gamePaperWhite; - } + x.rgb /= intScaling; #if SWAPCHAIN_CLAMP_PEAK == 1 - { - float p = PeakWhiteNits / sRGB_WhiteLevelNits; - color = min(color, p); - } + x = min(x, p); #elif SWAPCHAIN_CLAMP_PEAK == 2 { - float m = max(color.x, max(color.y, color.z)); - float p = PeakWhiteNits / sRGB_WhiteLevelNits; - if (m > p) color *= p /* * 1.05 */ / m; + float m = max(x.x, max(x.y, x.z)); + if (m > p) x *= p / m; } #endif - //clamp BT2020 -#if SWAPCHAIN_CLAMP_COLORSPACE > 0 - color.rgb = BT709_To_BT2020(color.rgb); - color.rgb = max(0, color.rgb); - color.rgb = BT2020_To_BT709(color.rgb); -#endif - -#if 0 // Test - // color.rgb = float3(1, 0, 0); - color.rgb = PeakWhiteNits / 80.; -#endif - #if SWAPCHAIN_TEST_USER_PEAK > 0 //black - color.rgb = 0; - color.rgb = DrawRect(uv, float4(0.35, 0.47, 0.65, 0.53), color.rgb, 10000.f/sRGB_WhiteLevelNits); - color.rgb = DrawRect(uv, float4(0.365, 0.483, 0.448, 0.517), color.rgb, PeakWhiteNits*2/sRGB_WhiteLevelNits); - color.rgb = DrawRect(uv, float4(0.458, 0.483, 0.542, 0.517), color.rgb, PeakWhiteNits/sRGB_WhiteLevelNits); - color.rgb = DrawRect(uv, float4(0.552, 0.483, 0.635, 0.517), color.rgb, PeakWhiteNits/2/sRGB_WhiteLevelNits); -#endif // SWAPCHAIN_TEST_USER_PEAK > 0 + x.rgb = 0; + x.rgb = DrawRect(uv, float4(0.35, 0.47, 0.65, 0.53), x.rgb, 10000.f / GamePaperWhiteNits); + x.rgb = DrawRect(uv, float4(0.365, 0.483, 0.448, 0.517), x.rgb, PeakWhiteNits * 2 / GamePaperWhiteNits); + x.rgb = DrawRect(uv, float4(0.458, 0.483, 0.542, 0.517), x.rgb, PeakWhiteNits / GamePaperWhiteNits); + x.rgb = DrawRect(uv, float4(0.552, 0.483, 0.635, 0.517), x.rgb, PeakWhiteNits / 2 / GamePaperWhiteNits); +#endif + +#if OUTPUT_MODE == 0 + // scRGB encode + x.rgb *= intScaling * (UIPaperWhiteNits / 80.f); +#elif OUTPUT_MODE == 1 + // HDR10 encode + x.xyz = BT709_To_BT2020(x.xyz); + x.xyz = max(0, x.xyz); + x.xyz *= intScaling * (UIPaperWhiteNits / HDR10_MaxWhiteNits); + x.xyz = Linear_to_PQ(x.xyz); +#elif OUTPUT_MODE == 2 + x.xyz = linear_to_sRGB_gamma(x.xyz, GCT_POSITIVE); +#endif + + return float4(x.rgb, x.a); +} - return float4(color.rgb, color.a); -} \ No newline at end of file +// float4 rec709override(float2 v0 : TEXCOORD0, float4 v1 : SV_POSITION0) : SV_Target0 +// { +// return sourceTexture.Load((int3)v1.xyz); +// } \ No newline at end of file diff --git a/Shaders/Call of Duty Black Ops 3/common1.hlsl b/Shaders/Call of Duty Black Ops 3/common1.hlsl index e16108a32..64be8db9b 100644 --- a/Shaders/Call of Duty Black Ops 3/common1.hlsl +++ b/Shaders/Call of Duty Black Ops 3/common1.hlsl @@ -56,40 +56,6 @@ float3 UCSFrom(float3 x, uint cs) { #endif } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//REC709 -#define DECODEREC709(T)\ -T DecodeRec709(T x) {\ - T r0, r2, r3, r4;\ - r0 = x;\ - r2 = 0.0989999995 + r0; \ - r2 = 0.909918129 * r2;\ - r2 = pow(r2, 2.22222233);\ - r3 = cmp(0.0810000002 >= r0);\ - r4 = 0.222222224 * r0;\ - r2 = r3 ? r4 : r2;\ - return r2;\ -} -DECODEREC709(float) -DECODEREC709(float3) -DECODEREC709(float4) -#undef DECODEREC709 - -#define ENCODEREC709(T)\ -T EncodeRec709(T x) {\ - T r0, r1, r2;\ - r1 = x;\ - r0 = pow(r1, 0.449999988);\ - r0 = r0 * 1.09899998 + -0.0989999995;\ - r2 = cmp(0.0179999992 >= r1);\ - r1 = 4.5 * r1;\ - r0 = r2 ? r1 : r0;\ - return r0;\ -} -ENCODEREC709(float) -ENCODEREC709(float3) -ENCODEREC709(float4) -#undef ENCODEREC709 -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //https://github.com/clshortfuse/renodx/blob/main/src/shaders/tonemap/reinhard.hlsl namespace Reinhard { float ReinhardPiecewiseExtended(float x, float white_max, float x_max = 1.f, float shoulder = 0.18f) diff --git a/Source/Core/core.hpp b/Source/Core/core.hpp index 64d9667c1..9ce2b6d8f 100644 --- a/Source/Core/core.hpp +++ b/Source/Core/core.hpp @@ -3258,6 +3258,14 @@ namespace const std::unique_lock lock_reshade(s_mutex_reshade); Display::GetHDRMaxLuminance(native_swapchain3, device_data.default_user_peak_white, srgb_white_level); // TODO: detect when the display changes and update peak brightness calibration, and set HDR10+ on the new display too etc Display::IsHDRSupportedAndEnabled(swapchain_desc.OutputWindow, hdr_supported_display, hdr_enabled_display, native_swapchain3); + + // Luma_ForcedHDR flag file + if (std::filesystem::exists("Luma_ForcedHDR")) + { + hdr_enabled_display = true; + hdr_supported_display = true; + } + #if ENABLE_NVAPI if (hdr_enabled_display) { @@ -6054,8 +6062,9 @@ namespace ID3D11SamplerState* const sampler_state_linear = device_data.sampler_state_linear.get(); native_device_context->PSSetSamplers(0, 1, &sampler_state_linear); } - + // Note: we don't really need to re-apply our custom cbuffers in most games (e.g. Prey), they are on indexes that are never used by the game's code + game->OnBeforeDisplayComposition(native_device, native_device_context, device_data); DrawCustomPixelShader(native_device_context, device_data.default_depth_stencil_state.get(), device_data.default_blend_state.get(), nullptr, device_data.native_vertex_shaders[CompileTimeStringHash("Copy VS")].get(), device_data.native_pixel_shaders[CompileTimeStringHash("Display Composition")].get(), device_data.display_composition_srv.get(), target_resource_texture_view.get(), target_desc.Width, target_desc.Height, false); #if DEVELOPMENT diff --git a/Source/Core/includes/game.h b/Source/Core/includes/game.h index 9c353e6e1..b6564768a 100644 --- a/Source/Core/includes/game.h +++ b/Source/Core/includes/game.h @@ -92,6 +92,8 @@ class Game // run. Must not take luma mutexes. // Requires "LUMA_PATCH_PROVIDERS". virtual void OnPatchedShadersPublished(DeviceData& device_data, const std::vector& published_shader_hashes) {} + // This is called every frame on present, just before drawing Display Composition. + virtual void OnBeforeDisplayComposition(ID3D11Device* native_device, ID3D11DeviceContext* native_device_context, DeviceData& device_data) {} // This is called every frame just before sending out the final image to the display (the swapchain). // You can reliable reset any per frame setting here. virtual void OnPresent(ID3D11Device* native_device, DeviceData& device_data) {} diff --git a/Source/Games/Call of Duty Black Ops 3/main.cpp b/Source/Games/Call of Duty Black Ops 3/main.cpp index abcc765dc..1f2ea904f 100644 --- a/Source/Games/Call of Duty Black Ops 3/main.cpp +++ b/Source/Games/Call of Duty Black Ops 3/main.cpp @@ -14,7 +14,6 @@ namespace Globals { - static auto SDR8Bit = false; static bool UIIsAdvanced = false; #if ENABLE_SR == 1 static bool IsUi = true; @@ -42,50 +41,62 @@ namespace Globals if (a == b) return 0.f; return (v - a) / (b - a); } - } -// void ShaderHashesLists_Setup() //TODO: del -// { -// ShaderHashesLists::ProbeCulling.compute_shaders.emplace(std::stoul("0x6759EF9E", nullptr, 16)); //Light -// ShaderHashesLists::ProbeCulling.compute_shaders.emplace(std::stoul("0xDA63105C", nullptr, 16)); //Reflection -// -// ShaderHashesLists::MotionVectors.pixel_shaders.emplace(std::stoul("0xB8A51615", nullptr, 16)); -// -// ShaderHashesLists::GTAOUltra0.pixel_shaders.emplace(std::stoul("0x8FC85155", nullptr, 16)); -// -// ShaderHashesLists::LensFlare.pixel_shaders.emplace(std::stoul("0x7740A983", nullptr, 16)); -// -// ShaderHashesLists::Tonemap.pixel_shaders.emplace(std::stoul("0x59F328E3", nullptr, 16)); //game -// ShaderHashesLists::Tonemap.pixel_shaders.emplace(std::stoul("0x1744B1D4", nullptr, 16)); //menu (CA) -// -// ShaderHashesLists::SMAAT2X.pixel_shaders.emplace(std::stoul("0xD9288CF8", nullptr, 16)); //final SMAA T2X (NOT FILMIC) resolve -// //ShaderHashesLists::SMAAT2X.pixel_shaders.emplace(std::stoul("0x15FF4E6D", nullptr, 16)); // T2XF -// -// // ShaderHashesLists::AAStart.compute_shaders.emplace(std::stoul("0x6312E037", nullptr, 16)); // FXAA Edge -// // ShaderHashesLists::AAStart.compute_shaders.emplace(std::stoul("0x6240554C", nullptr, 16)); // SMAA Edge -// -// ShaderHashesLists::SMAAT2XPrep.compute_shaders.emplace(std::stoul("0x6240554C", nullptr, 16)); // SMAA Edge -// ShaderHashesLists::SMAAT2XPrep.compute_shaders.emplace(std::stoul("0xB037D915", nullptr, 16)); // SMAA Prep Idk -// ShaderHashesLists::SMAAT2XPrep.compute_shaders.emplace(std::stoul("0x3B3C41EF", nullptr, 16)); // SMAA Resolve V -// ShaderHashesLists::SMAAT2XPrep.compute_shaders.emplace(std::stoul("0xCDEFC09A", nullptr, 16)); // SMAA Resolve H -// -// ShaderHashesLists::SMAAResolveH.compute_shaders.emplace(std::stoul("0xCDEFC09A", nullptr, 16)); // SMAA Resolve H -// -// ShaderHashesLists::Final.pixel_shaders.emplace(std::stoul("0x3D461B1A", nullptr, 16)); //game -// ShaderHashesLists::Final.pixel_shaders.emplace(std::stoul("0x224A8BF5", nullptr, 16)); //menu (noise, dither) -// -// ShaderHashesLists::FullscreenBlurLast.pixel_shaders.emplace(std::stoul("0xDA908072", nullptr, 16)); //last fullscreen blur that upsamples from lower res -// -// ShaderHashesLists::Rec709.pixel_shaders.emplace(std::stoul("0x8324B585", nullptr, 16)); //right-before-swapchain trash rec709, where rtv is 8bit texture -// -// ShaderHashesLists::Exposure.compute_shaders.emplace(std::stoul("0x14F64B50", nullptr, 16)); //(exposure tex 9x1 UAV 1) -// -// ShaderHashesLists::VolumetricFogDraw.pixel_shaders.emplace(std::stoul("0x48AE98F9", nullptr, 16)); //opposed to composite (OIT SRV 5) -// ShaderHashesLists::VolumetricFogDraw.pixel_shaders.emplace(std::stoul("0xDFC82E40", nullptr, 16)); -// -// ShaderHashesLists::Sprites.compute_shaders.emplace(std::stoul("0xCAF61E7A", nullptr, 16)); //flame, smoke, etc. particles (OIT UAV 4) -// } +namespace OutputHandler // partial +{ + enum State : uint8_t + { + scRGB, + HDR10, + sRGB + }; + static State state = scRGB; + static const char* GetStateName() + { + switch (state) + { + case scRGB: return "16-bit scRGB"; + case HDR10: return "10-bit HDR10"; + case sRGB: return "8-bit sRGB"; + default: return "Unknown"; + } + } + + static PUBLISHING_CONSTEXPR bool force_hdr10_false = false; + static bool IsHDR10() { return state == HDR10 && !force_hdr10_false; } + + static bool is_use16f = false; + static const char* GetResourceQualityName() + { + if (is_use16f) return "rgba16f"; + else return "r11g11b10f"; + } + + static bool is_displaycomp_custom = false; + void SetDisplayComposition(bool is_custom) // state should be parsed before calling this function + { + if (is_displaycomp_custom == is_custom) return; + is_displaycomp_custom = is_custom; + + native_shaders_definitions.erase(CompileTimeStringHash("Display Composition")); + if (is_displaycomp_custom) + { + auto mode = state == scRGB ? "0" : (state == HDR10 ? "1" : "2"); + native_shaders_definitions.emplace(CompileTimeStringHash("Display Composition"), ShaderDefinition{"Luma_CoDBO3_DisplayComposition", reshade::api::pipeline_subobject_type::pixel_shader, nullptr, "main", {{"OUTPUT_MODE", mode}}}); + } + else + { + native_shaders_definitions.emplace(CompileTimeStringHash("Display Composition"), ShaderDefinition{"Luma_DisplayComposition", reshade::api::pipeline_subobject_type::pixel_shader}); + } + } + void UIDrawDisplayCompositionToggle() + { + if (IsHDR10()) return; // only works for scRGB (& prob sRGB) + bool is_custom = is_displaycomp_custom; + if (ImGui::Checkbox("(DEVELOPMENT) Custom Display Composition", &is_custom)) SetDisplayComposition(is_custom); + } +} namespace ShaderDefineInfo { @@ -394,7 +405,7 @@ struct CallOfDutyBlackOps3GameDeviceData final : public GameDeviceData desc.ArraySize = 1; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS; desc.Usage = D3D11_USAGE_DEFAULT; - desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; + desc.Format = OutputHandler::is_use16f ? DXGI_FORMAT_R16G16B16A16_FLOAT : DXGI_FORMAT_R11G11B10_FLOAT; desc.SampleDesc = {1, 0}; desc.CPUAccessFlags = 0; desc.MiscFlags = 0; @@ -456,6 +467,7 @@ struct CallOfDutyBlackOps3GameDeviceData final : public GameDeviceData bool drawn_smaat2x = false; //not filmic bool drawn_smaat2x_prev = false; //prev frame bool drawn_final = false; + bool drawn_final_prev = false; //prev frame bool drawn_hdtv = false; bool drawn_hdtv_prev = false; //prev frame @@ -468,7 +480,7 @@ struct CallOfDutyBlackOps3GameDeviceData final : public GameDeviceData resources_smaa_color_input.desc.Height == resources_sr_output.desc.Height; } - void Reset(bool isSRReset) + void OnPresent(bool isSRReset) { drawn_probecull = false; drawn_motionvectors = false; @@ -483,6 +495,7 @@ struct CallOfDutyBlackOps3GameDeviceData final : public GameDeviceData drawn_smaat2xprep = false; drawn_smaat2x_prev = drawn_smaat2x; drawn_smaat2x = false; + drawn_final_prev = drawn_final; drawn_final = false; drawn_hdtv_prev = drawn_hdtv; drawn_hdtv = false; @@ -518,6 +531,180 @@ struct CallOfDutyBlackOps3GameDeviceData final : public GameDeviceData #endif }; +namespace OutputHandler // partial //TODO: this is mega goofy. Luma needs a RenoDX-like auto swap back buffer to rgba16f but DisplayComposition to rgb10a2. +{ + constexpr const char* const Flag_SDR = "Luma_8bitSDR"; + constexpr const char* const Flag_scRGB = "Luma_scRGB"; + constexpr const char* const Flag_16fResources = "Luma_16fResources"; + + namespace Resources + { + static ComPtr backbuffer_srv; + static ComPtr backbuffer_rtv; + static float2 size = {0, 0}; + + static bool IsReady(DeviceData& device_data) + { + return size.x == device_data.display_resolution.x && size.y == device_data.display_resolution.y; + } + + static void Create(ID3D11Device* device, DeviceData& device_data) + { + ASSERT(device_data.display_resolution.x > 0 && device_data.display_resolution.y > 0); + + size = {device_data.display_resolution.x, device_data.display_resolution.y}; + + D3D11_TEXTURE2D_DESC desc{}; + desc.Width = device_data.display_resolution.x; + desc.Height = device_data.display_resolution.y; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = OutputHandler::is_use16f ? DXGI_FORMAT_R16G16B16A16_FLOAT : DXGI_FORMAT_R11G11B10_FLOAT; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + + ComPtr tex; + HRESULT hr0 = device->CreateTexture2D(&desc, nullptr, tex.put()); + ASSERT_MSG(SUCCEEDED(hr0), "OutputHandler::Resources::Create(): hr0"); + + HRESULT hr1 = device->CreateShaderResourceView(tex.get(), nullptr, backbuffer_srv.put()); + ASSERT_MSG(SUCCEEDED(hr1), "OutputHandler::Resources::Create(): hr1"); + + HRESULT hr2 = device->CreateRenderTargetView(tex.get(), nullptr, backbuffer_rtv.put()); + ASSERT_MSG(SUCCEEDED(hr2), "OutputHandler::Resources::Create(): hr2"); + } + + static void Reset() + { + backbuffer_srv.reset(); + backbuffer_rtv.reset(); + size = {0, 0}; + } + } + + static void OnDll() + { + // state flag files + if (std::filesystem::exists(Flag_SDR)) state = sRGB; + else if (std::filesystem::exists(Flag_scRGB)) state = scRGB; + else state = HDR10; + + // swapchain upgrades + swapchain_format_upgrade_type = state == sRGB ? TextureFormatUpgradesType::None : TextureFormatUpgradesType::AllowedEnabled; + swapchain_upgrade_type = state == sRGB ? SwapchainUpgradeType::None : (state == scRGB ? SwapchainUpgradeType::scRGB : SwapchainUpgradeType::HDR10); + + // is_use16f flag file + is_use16f = std::filesystem::exists(Flag_16fResources); + + // texture upgrades + if (is_use16f) + { + texture_format_upgrades_type = TextureFormatUpgradesType::AllowedEnabled; + texture_upgrade_formats = { + reshade::api::format::r11g11b10_float, //color (not super necessary, but its nice) + }; + texture_format_upgrades_2d_size_filters = (uint32_t)TextureFormatUpgrades2DSizeFilters::SwapchainAspectRatio; + } + else + { + texture_format_upgrades_type = TextureFormatUpgradesType::None; + } + } + + static void OnInit() + { + message(reshade::log::level::info, std::format("OutputHandler::OnInit(): {} mode enabled.", GetStateName()).c_str()); + SetDisplayComposition(true); + } + + static void OnDrawOrDispatchOverrideBegin(ID3D11Device* native_device, ID3D11DeviceContext* native_device_context, CommandListData& cmd_list_data, DeviceData& device_data, CallOfDutyBlackOps3GameDeviceData& game_device_data, uint32_t ps) + { + if (!IsHDR10()) return; + + // create resources + [[unlikely]] if (!Resources::IsReady(device_data)) Resources::Create(native_device, device_data); + } + + + static void OnDrawFinal(ID3D11Device* native_device, ID3D11DeviceContext* native_device_context, CommandListData& cmd_list_data, DeviceData& device_data, CallOfDutyBlackOps3GameDeviceData& game_device_data, uint32_t ps) + { + if (IsHDR10()) + { + // set our RTV + ComPtr rtv0; + ComPtr dsv; + native_device_context->OMGetRenderTargets(1, rtv0.put(), dsv.put()); + native_device_context->OMSetRenderTargets(1, &Resources::backbuffer_rtv, dsv.get()); + } + } + + static void OnDrawOrDispatchOverride(ID3D11Device* native_device, ID3D11DeviceContext* native_device_context, CommandListData& cmd_list_data, DeviceData& device_data, CallOfDutyBlackOps3GameDeviceData& game_device_data, uint32_t ps) + { + if (IsHDR10()) + { + /* + After final, the game will PS some sprites to a resource (caching, so that later PS can sample with chromatic aberration). + Then, backbuffer is bound to RTV0, once at start of all UI. + There are some shaders at the end (FPS stats text, Rec709) that will rebind backbuffer to RTV0, so we must continually check to replace. + */ + + if (game_device_data.drawn_final_prev && !game_device_data.drawn_final) + return; // one frame delay, but avoids OMGetRenderTargets() spam when normal 3D rendering + + // get RTV0, Res, Handle + ComPtr rtv0; + ComPtr dsv; // TODO: required? it's bound but it's not used? + native_device_context->OMGetRenderTargets(1, rtv0.put(), dsv.put()); + [[unlikely]] if (!rtv0.get()) return; + + ComPtr res; + rtv0->GetResource(res.put()); + + uint64_t handle = reinterpret_cast(res.get()); + + // replace with our RTV? + if (device_data.back_buffers.contains(handle)) + native_device_context->OMSetRenderTargets(1, &Resources::backbuffer_rtv, dsv.get()); + } + } + + static DrawOrDispatchOverrideType OnDrawRec709(ID3D11Device* native_device, ID3D11DeviceContext* native_device_context, CommandListData& cmd_list_data, DeviceData& device_data, CallOfDutyBlackOps3GameDeviceData& game_device_data, uint32_t ps) + { + // if (state == sRGB) return DrawOrDispatchOverrideType::None; + return DrawOrDispatchOverrideType::Skip; // does CopyResource(), backbuffer to new 8bit... gg + } + + // TODO: please Pumbo, i need this. my custom swapchain proxy shader is kinda homeless. + static void OnBeforeDisplayComposition(ID3D11Device* native_device, ID3D11DeviceContext* native_device_context, DeviceData& device_data, CallOfDutyBlackOps3GameDeviceData& game_device_data) + { + if (IsHDR10()) + { + // clear RTV0 (avoid bind conflicts) + constexpr ID3D11RenderTargetView* rtv0 = nullptr; + native_device_context->OMSetRenderTargets(1, &rtv0, nullptr); + + // clear SRV0 (avoid bind conflicts) + constexpr ID3D11ShaderResourceView* srv0 = nullptr; + native_device_context->PSSetShaderResources(0, 1, &srv0); + + // set our RTV + native_device_context->PSSetShaderResources(10, 1, &Resources::backbuffer_srv); // DisplayComposition uses up to 8 + } + } + + // this is after Display Composition + static void OnPresent(ID3D11Device* native_device, DeviceData& device_data, CallOfDutyBlackOps3GameDeviceData& game_device_data) + { + + } + + static void HardReset() + { + Resources::Reset(); + } +} + namespace BlackFloorSDRTonemap { //between 0.88765 and 1.0 @@ -1211,7 +1398,7 @@ namespace SectionedImGui static void DrawCompletelyUselessButton(const char* text = nullptr) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.75f, 0.75f, 0.75f, 1.f)); - ImGui::TextWrapped(!text ? "This section is useless." : text); + ImGui::TextWrapped(!text ? "This section is useless for SDR." : text); ImGui::PopStyleColor(); } @@ -1229,11 +1416,23 @@ namespace SectionedImGui ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped("This acts as a wizard to fully configure."); ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped("Ordered by importance, please at least view all non-Advanced sections!"); ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped("For those new to ImGUI, you can CTRL click a slider for keyboard input."); -#if ENABLE_SR == 1 + + ImGui::Separator(); //////////////////// + + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.75f, 1.f, 1.f, 1.f)); + ImGui::TextWrapped("[Startup Settings]"); + ImGui::PopStyleColor(); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.5f, 1.f)); +#if ENABLE_SR == 1 ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped(std::format("Detected Executable: {}", GetExeTypeName(MemoryHack::exe_type)).c_str()); - ImGui::PopStyleColor(); #endif + ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped(std::format("Output Mode: {}", OutputHandler::GetStateName()).c_str()); + ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped(std::format("Color Buffer Quality: {}", OutputHandler::GetResourceQualityName()).c_str()); + ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped(std::format("HDR Supported Display: {}", hdr_supported_display).c_str()); + ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped(std::format("HDR Enabled Display: {}", hdr_enabled_display).c_str()); + + ImGui::PopStyleColor(); + if (Globals::UIIsAdvanced) { @@ -1249,99 +1448,99 @@ namespace SectionedImGui static void GammaCorrection(DeviceData& device_data, reshade::api::effect_runtime* runtime) { - if (IsSDRMode()) + if (cb_luma_global_settings.DisplayMode == DisplayModeType::HDR) { - DrawCompletelyUselessButton(); - return; - } - - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.75f, 1.f, 1.f, 1.f)); - ImGui::TextWrapped("[Reintroduce SDR's gamma mismatch to lower shadows.]"); - ImGui::PopStyleColor(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.75f, 1.f, 1.f, 1.f)); + ImGui::TextWrapped("[Reintroduce SDR's gamma mismatch to lower shadows.]"); + ImGui::PopStyleColor(); - //save prev - float gamma_prev = custom_sdr_gamma; + //save prev + float gamma_prev = custom_sdr_gamma; - // Dropdown for custom_sdr_gamma - const char* const items[] = { "Off (sRGB)", "Match SDR (2.2)", "Stronger (2.4)" }; - int custom_sdr_gamma_index = 0; - if (custom_sdr_gamma > 0) { //detect - if (abs(custom_sdr_gamma - 2.2f) < 0.001f) custom_sdr_gamma_index = 1; - else if (custom_sdr_gamma == 2.4f) custom_sdr_gamma_index = 2; - } - ImGui::PushID("GammaCorrection custom_sdr_gamma"); - if (ImGui::Combo("Correction", &custom_sdr_gamma_index, items, IM_ARRAYSIZE(items))) //user set & save - { - switch (custom_sdr_gamma_index) + // Dropdown for custom_sdr_gamma + const char* const items[] = { "Off (sRGB)", "Match SDR (2.2)", "Stronger (2.4)" }; + int custom_sdr_gamma_index = 0; + if (custom_sdr_gamma > 0) { //detect + if (abs(custom_sdr_gamma - 2.2f) < 0.001f) custom_sdr_gamma_index = 1; + else if (custom_sdr_gamma == 2.4f) custom_sdr_gamma_index = 2; + } + ImGui::PushID("GammaCorrection custom_sdr_gamma"); + if (ImGui::Combo("Correction", &custom_sdr_gamma_index, items, IM_ARRAYSIZE(items))) //user set & save { - default: custom_sdr_gamma = 0.f; break; - case 1: custom_sdr_gamma = 2.2f; break; - case 2: custom_sdr_gamma = 2.4f; break; + switch (custom_sdr_gamma_index) + { + default: custom_sdr_gamma = 0.f; break; + case 1: custom_sdr_gamma = 2.2f; break; + case 2: custom_sdr_gamma = 2.4f; break; + } + ShaderDefineInfo::Set(GAMMA_CORRECTION_TYPE_HASH, custom_sdr_gamma > 0); + defines_need_recompilation = true; + reshade::set_config_value(runtime, NAME, "custom_sdr_gamma", custom_sdr_gamma); } - ShaderDefineInfo::Set(GAMMA_CORRECTION_TYPE_HASH, custom_sdr_gamma > 0); - defines_need_recompilation = true; - reshade::set_config_value(runtime, NAME, "custom_sdr_gamma", custom_sdr_gamma); - } - ImGui::PopID(); - - //link - if (ImGui::Button("More Info & Test (Google Slides)")) - Website::OpenWebsite("https://docs.google.com/presentation/d/e/2PACX-1vSXeLHlbm6repcS7fels1-SXYGRmzziRrnuJ8nDO8J5rsWV3dT1-nVyCKp0Tj_stwx-9qlCI-N6rYIT/pub?start=false&loop=false&slide=id.g3e007eafba8_0_0"); + ImGui::PopID(); - //force define because its trolling sometimes - if (bool is_define_on = ShaderDefineInfo::Get(GAMMA_CORRECTION_TYPE_HASH) == 1; is_define_on != (custom_sdr_gamma > 0.f)) - ShaderDefineInfo::Set(GAMMA_CORRECTION_TYPE_HASH, is_define_on ? '0' : '1'); + //link + if (ImGui::Button("More Info & Test (Google Slides)")) + Website::OpenWebsite("https://docs.google.com/presentation/d/e/2PACX-1vSXeLHlbm6repcS7fels1-SXYGRmzziRrnuJ8nDO8J5rsWV3dT1-nVyCKp0Tj_stwx-9qlCI-N6rYIT/pub?start=false&loop=false&slide=id.g3e007eafba8_0_0"); - is_disabled = custom_sdr_gamma == 0.f; - if (is_disabled) ImGui::BeginDisabled(); - { - ImGui::Separator(); //////////////////// - - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.75f, 1.f, 1.f, 1.f)); - ImGui::TextWrapped("[\"Brightness\" slider replacement, due to the algorithm's SDR limitations.]"); - ImGui::PopStyleColor(); - - //Gamma Influence - if (ImGui::SliderFloat("Gamma Brightness", &cb_luma_global_settings.GameSettings.GammaInfluence, 0.f, 2.f)) - reshade::set_config_value(runtime, NAME, "GammaInfluence", cb_luma_global_settings.GameSettings.GammaInfluence); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) ImGui::SetTooltip("Lower to let the gamma correction influence more of the image, and decreasing overall brightness."); - DrawResetButton(cb_luma_global_settings.GameSettings.GammaInfluence, default_luma_global_game_settings.GammaInfluence, "GammaInfluence", runtime); - - if (Globals::UIIsAdvanced) - { + //force define because its trolling sometimes + if (bool is_define_on = ShaderDefineInfo::Get(GAMMA_CORRECTION_TYPE_HASH) == 1; is_define_on != (custom_sdr_gamma > 0.f)) + ShaderDefineInfo::Set(GAMMA_CORRECTION_TYPE_HASH, is_define_on ? '0' : '1'); + } + + if (cb_luma_global_settings.DisplayMode == DisplayModeType::HDR) + { + is_disabled = custom_sdr_gamma == 0.f; + if (is_disabled) ImGui::BeginDisabled(); + { ImGui::Separator(); //////////////////// ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.75f, 1.f, 1.f, 1.f)); - ImGui::TextWrapped("[Correction may become artificial without limits.]"); + ImGui::TextWrapped("[\"Brightness\" slider replacement, due to the algorithm's SDR limitations.]"); ImGui::PopStyleColor(); - - //CUSTOM_GAMMA_CORRECTION_MODE dropdown - bool is_disabled_mode = ShaderDefineInfo::Get(ShaderDefineInfo::CUSTOM_LUTBUILDER_COLORSPACE) == 0; - if (is_disabled_mode) ImGui::BeginDisabled(); + + //Gamma Influence + if (ImGui::SliderFloat("Gamma Brightness", &cb_luma_global_settings.GameSettings.GammaInfluence, 0.f, 2.f)) + reshade::set_config_value(runtime, NAME, "GammaInfluence", cb_luma_global_settings.GameSettings.GammaInfluence); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) ImGui::SetTooltip("Lower to let the gamma correction influence more of the image, and decreasing overall brightness."); + DrawResetButton(cb_luma_global_settings.GameSettings.GammaInfluence, default_luma_global_game_settings.GammaInfluence, "GammaInfluence", runtime); + + if (Globals::UIIsAdvanced) { - auto a = "Per-Channel (Hue Shifts / Vanilla)"; - auto b = "Perceptual (Hue Corrected)"; - ShaderDefineInfo::UIDropDown(ShaderDefineInfo::CUSTOM_GAMMA_CORRECTION_MODE, "Gamma Correction Mode", - {a, is_disabled_mode ? a : b}, - "(Only available when LUT Builder is upgraded to output wider in BT.2020!)\nHow should the gamma correction operate?\n\nPer-Channel is Vanilla Brightness Slider-like, with hue shifting shadows.\nPerceptual retains the hues of the original sRGB gamma output, only darkening luminance."); - } - if (is_disabled_mode) ImGui::EndDisabled(); + ImGui::Separator(); //////////////////// + + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.75f, 1.f, 1.f, 1.f)); + ImGui::TextWrapped("[Correction may become artificial without limits.]"); + ImGui::PopStyleColor(); - //GammaPerceptualChrominanceCorrect - bool is_disabled_perceptual = ShaderDefineInfo::Get(ShaderDefineInfo::CUSTOM_GAMMA_CORRECTION_MODE) != 1 || ShaderDefineInfo::Get(ShaderDefineInfo::CUSTOM_LUTBUILDER_COLORSPACE) == 0; - if (is_disabled_perceptual) ImGui::BeginDisabled(); - { - if (ImGui::SliderFloat("Perceptual Chrominance Gain Reduction", &cb_luma_global_settings.GameSettings.GammaPerceptualChrominanceCorrect, 0.f, 1.f, "%.4f")) - reshade::set_config_value(runtime, NAME, "GammaPerceptualChrominanceCorrect", cb_luma_global_settings.GameSettings.GammaPerceptualChrominanceCorrect); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) ImGui::SetTooltip("Reduce chrominance/saturation increase from Gamma Correction in the Perceptual mode, preventing it from becoming too artificial."); - DrawResetButton(cb_luma_global_settings.GameSettings.GammaPerceptualChrominanceCorrect, default_luma_global_game_settings.GammaPerceptualChrominanceCorrect, "GammaPerceptualChrominanceCorrect", runtime); + //CUSTOM_GAMMA_CORRECTION_MODE dropdown + bool is_disabled_mode = ShaderDefineInfo::Get(ShaderDefineInfo::CUSTOM_LUTBUILDER_COLORSPACE) == 0; + if (is_disabled_mode) ImGui::BeginDisabled(); + { + auto a = "Per-Channel (Hue Shifts / Vanilla)"; + auto b = "Perceptual (Hue Corrected)"; + ShaderDefineInfo::UIDropDown(ShaderDefineInfo::CUSTOM_GAMMA_CORRECTION_MODE, "Gamma Correction Mode", + {a, is_disabled_mode ? a : b}, + "(Only available when LUT Builder is upgraded to output wider in BT.2020!)\nHow should the gamma correction operate?\n\nPer-Channel is Vanilla Brightness Slider-like, with hue shifting shadows.\nPerceptual retains the hues of the original sRGB gamma output, only darkening luminance."); + } + if (is_disabled_mode) ImGui::EndDisabled(); + + //GammaPerceptualChrominanceCorrect + bool is_disabled_perceptual = ShaderDefineInfo::Get(ShaderDefineInfo::CUSTOM_GAMMA_CORRECTION_MODE) != 1 || ShaderDefineInfo::Get(ShaderDefineInfo::CUSTOM_LUTBUILDER_COLORSPACE) == 0; + if (is_disabled_perceptual) ImGui::BeginDisabled(); + { + if (ImGui::SliderFloat("Perceptual Chrominance Gain Reduction", &cb_luma_global_settings.GameSettings.GammaPerceptualChrominanceCorrect, 0.f, 1.f, "%.4f")) + reshade::set_config_value(runtime, NAME, "GammaPerceptualChrominanceCorrect", cb_luma_global_settings.GameSettings.GammaPerceptualChrominanceCorrect); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) ImGui::SetTooltip("Reduce chrominance/saturation increase from Gamma Correction in the Perceptual mode, preventing it from becoming too artificial."); + DrawResetButton(cb_luma_global_settings.GameSettings.GammaPerceptualChrominanceCorrect, default_luma_global_game_settings.GammaPerceptualChrominanceCorrect, "GammaPerceptualChrominanceCorrect", runtime); + } + if (is_disabled_perceptual) ImGui::EndDisabled(); } - if (is_disabled_perceptual) ImGui::EndDisabled(); } - } - if (is_disabled) ImGui::EndDisabled(); + if (is_disabled) ImGui::EndDisabled(); - ImGui::Separator(); //////////////////// + ImGui::Separator(); //////////////////// + } //CUSTOM_HDTVREC709 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.75f, 1.f, 1.f, 1.f)); @@ -1694,58 +1893,61 @@ namespace SectionedImGui } bool is_clicked; - - //CUSTOM_LUTBUILDER_COLORSPACE - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.75f, 1.f, 1.f, 1.f)); - ImGui::TextWrapped("[Fake Wide Color Gamut]"); - ImGui::PopStyleColor(); - ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped("Helps perceptually bring back mid-high orange, but may make everything too \"10000%% Digital Vibrance\" looking."); - ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped("(This is purely tomfoolery, as the original is non-wide BT.709 primaries. For real WCG, you need deep changes and regrading, e.g. Black Ops 4.)"); - - ImGui::PushID("Preset BT2020 Off"); - is_clicked = ImGui::Button("Off"); - ImGui::PopID(); - if (is_clicked) - { - ShaderDefineInfo::Set(ShaderDefineInfo::CUSTOM_LUTBUILDER_COLORSPACE, 0); - } - - ImGui::SameLine(); - ImGui::PushID("Preset BT2020 On 0"); - is_clicked = ImGui::Button("Low"); - ImGui::PopID(); - if (is_clicked) + + if (OutputHandler::is_use16f) { - ShaderDefineInfo::Set(ShaderDefineInfo::CUSTOM_LUTBUILDER_COLORSPACE, 1); - ShaderDefineInfo::Set(ShaderDefineInfo::CUSTOM_LUTBUILDER_SATBOOST, 1); - cb_luma_global_settings.GameSettings.LUTBuilderExpansionChrominance = default_luma_global_game_settings.LUTBuilderExpansionChrominance; - cb_luma_global_settings.GameSettings.LUTBuilderExpansionLuminance = default_luma_global_game_settings.LUTBuilderExpansionLuminance; - cb_luma_global_settings.GameSettings.LUTBuilderHighlightSat = default_luma_global_game_settings.LUTBuilderHighlightSat; - cb_luma_global_settings.GameSettings.LUTBuilderHighlightSatHighlightsOnly = default_luma_global_game_settings.LUTBuilderHighlightSatHighlightsOnly; - reshade::set_config_value(runtime, NAME, "LUTBuilderExpansionChrominance", cb_luma_global_settings.GameSettings.LUTBuilderExpansionChrominance); - reshade::set_config_value(runtime, NAME, "LUTBuilderExpansionLuminance", cb_luma_global_settings.GameSettings.LUTBuilderExpansionLuminance); - reshade::set_config_value(runtime, NAME, "LUTBuilderHighlightSat", cb_luma_global_settings.GameSettings.LUTBuilderHighlightSat); - reshade::set_config_value(runtime, NAME, "LUTBuilderHighlightSatHighlightsOnly", cb_luma_global_settings.GameSettings.LUTBuilderHighlightSatHighlightsOnly); + // CUSTOM_LUTBUILDER_COLORSPACE + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.75f, 1.f, 1.f, 1.f)); + ImGui::TextWrapped("[Fake Wide Color Gamut]"); + ImGui::PopStyleColor(); + ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped("Helps perceptually bring back mid-high orange, but may make everything too \"10000%% Digital Vibrance\" looking."); + ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped("(This is purely tomfoolery, as the original is non-wide BT.709 primaries. For real WCG, you need deep changes and regrading, e.g. Black Ops 4.)"); + + ImGui::PushID("Preset BT2020 Off"); + is_clicked = ImGui::Button("Off"); + ImGui::PopID(); + if (is_clicked) + { + ShaderDefineInfo::Set(ShaderDefineInfo::CUSTOM_LUTBUILDER_COLORSPACE, 0); + } + + ImGui::SameLine(); + ImGui::PushID("Preset BT2020 On 0"); + is_clicked = ImGui::Button("Low"); + ImGui::PopID(); + if (is_clicked) + { + ShaderDefineInfo::Set(ShaderDefineInfo::CUSTOM_LUTBUILDER_COLORSPACE, 1); + ShaderDefineInfo::Set(ShaderDefineInfo::CUSTOM_LUTBUILDER_SATBOOST, 1); + cb_luma_global_settings.GameSettings.LUTBuilderExpansionChrominance = default_luma_global_game_settings.LUTBuilderExpansionChrominance; + cb_luma_global_settings.GameSettings.LUTBuilderExpansionLuminance = default_luma_global_game_settings.LUTBuilderExpansionLuminance; + cb_luma_global_settings.GameSettings.LUTBuilderHighlightSat = default_luma_global_game_settings.LUTBuilderHighlightSat; + cb_luma_global_settings.GameSettings.LUTBuilderHighlightSatHighlightsOnly = default_luma_global_game_settings.LUTBuilderHighlightSatHighlightsOnly; + reshade::set_config_value(runtime, NAME, "LUTBuilderExpansionChrominance", cb_luma_global_settings.GameSettings.LUTBuilderExpansionChrominance); + reshade::set_config_value(runtime, NAME, "LUTBuilderExpansionLuminance", cb_luma_global_settings.GameSettings.LUTBuilderExpansionLuminance); + reshade::set_config_value(runtime, NAME, "LUTBuilderHighlightSat", cb_luma_global_settings.GameSettings.LUTBuilderHighlightSat); + reshade::set_config_value(runtime, NAME, "LUTBuilderHighlightSatHighlightsOnly", cb_luma_global_settings.GameSettings.LUTBuilderHighlightSatHighlightsOnly); + } + + ImGui::SameLine(); + ImGui::PushID("Preset BT2020 On 1"); + is_clicked = ImGui::Button("High"); + ImGui::PopID(); + if (is_clicked) + { + ShaderDefineInfo::Set(ShaderDefineInfo::CUSTOM_LUTBUILDER_COLORSPACE, 1); + ShaderDefineInfo::Set(ShaderDefineInfo::CUSTOM_LUTBUILDER_SATBOOST, 1); + cb_luma_global_settings.GameSettings.LUTBuilderExpansionChrominance = 0.85f; + cb_luma_global_settings.GameSettings.LUTBuilderExpansionLuminance = 0.25f; + cb_luma_global_settings.GameSettings.LUTBuilderHighlightSat = 0.2f; + cb_luma_global_settings.GameSettings.LUTBuilderHighlightSatHighlightsOnly = default_luma_global_game_settings.LUTBuilderHighlightSatHighlightsOnly; + reshade::set_config_value(runtime, NAME, "LUTBuilderExpansionChrominance", cb_luma_global_settings.GameSettings.LUTBuilderExpansionChrominance); + reshade::set_config_value(runtime, NAME, "LUTBuilderExpansionLuminance", cb_luma_global_settings.GameSettings.LUTBuilderExpansionLuminance); + reshade::set_config_value(runtime, NAME, "LUTBuilderHighlightSat", cb_luma_global_settings.GameSettings.LUTBuilderHighlightSat); + reshade::set_config_value(runtime, NAME, "LUTBuilderHighlightSatHighlightsOnly", cb_luma_global_settings.GameSettings.LUTBuilderHighlightSatHighlightsOnly); + } } - ImGui::SameLine(); - ImGui::PushID("Preset BT2020 On 1"); - is_clicked = ImGui::Button("High"); - ImGui::PopID(); - if (is_clicked) - { - ShaderDefineInfo::Set(ShaderDefineInfo::CUSTOM_LUTBUILDER_COLORSPACE, 1); - ShaderDefineInfo::Set(ShaderDefineInfo::CUSTOM_LUTBUILDER_SATBOOST, 1); - cb_luma_global_settings.GameSettings.LUTBuilderExpansionChrominance = 0.85f; - cb_luma_global_settings.GameSettings.LUTBuilderExpansionLuminance = 0.25f; - cb_luma_global_settings.GameSettings.LUTBuilderHighlightSat = 0.2f; - cb_luma_global_settings.GameSettings.LUTBuilderHighlightSatHighlightsOnly = default_luma_global_game_settings.LUTBuilderHighlightSatHighlightsOnly; - reshade::set_config_value(runtime, NAME, "LUTBuilderExpansionChrominance", cb_luma_global_settings.GameSettings.LUTBuilderExpansionChrominance); - reshade::set_config_value(runtime, NAME, "LUTBuilderExpansionLuminance", cb_luma_global_settings.GameSettings.LUTBuilderExpansionLuminance); - reshade::set_config_value(runtime, NAME, "LUTBuilderHighlightSat", cb_luma_global_settings.GameSettings.LUTBuilderHighlightSat); - reshade::set_config_value(runtime, NAME, "LUTBuilderHighlightSatHighlightsOnly", cb_luma_global_settings.GameSettings.LUTBuilderHighlightSatHighlightsOnly); - } - ImGui::Separator(); //CUSTOM_LUTBUILDER_NEUTRAL @@ -1859,7 +2061,7 @@ namespace SectionedImGui //Selection int tonemap_def = ShaderDefineInfo::UIDropDown(ShaderDefineInfo::CUSTOM_TONEMAP, "HDR Tonemapper Type", - {"Off", "Reinhard Piecewise (Gradual)", "Hermite Spline (Scalable)"}, + {"Off (For Testing)", "Reinhard Piecewise (Gradual) (DEPRECATED)", "Hermite Spline (Scalable)"}, "Select HDR tonemapping to preference.\nEach has a different curve and feel."); //Shoulder Start @@ -1881,9 +2083,9 @@ namespace SectionedImGui is_disabled = !(tonemap_def > 0); if (!is_disabled) { - if (ImGui::SliderFloat("HDR Tonemapper Expected Max", &cb_luma_global_settings.GameSettings.TonemapperMaxExpected, 20000, tonemap_def == 1 ? 100000.f : 200000.f, "%.0f")) + if (ImGui::SliderFloat("HDR Tonemapper Expected Max", &cb_luma_global_settings.GameSettings.TonemapperMaxExpected, 10000, tonemap_def == 1 ? 100000.f : 200000.f, "%.0f")) reshade::set_config_value(runtime, NAME, "TonemapperMaxExpected", cb_luma_global_settings.GameSettings.TonemapperMaxExpected); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) ImGui::SetTooltip("HDR tonemapper's expected max nits. Reduce to cause clipping."); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) ImGui::SetTooltip("HDR tonemapper's expected max nits.\nReduce to cause clipping."); if (!is_disabled && cb_luma_global_settings.GameSettings.TonemapperMaxExpected < cb_luma_global_settings.ScenePeakWhite) { ImGui::SameLine(); @@ -2094,6 +2296,13 @@ namespace SectionedImGui static void FakeWCG(DeviceData& device_data, reshade::api::effect_runtime* runtime) { + if (!OutputHandler::is_use16f) + { + ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped("Fake WCG is disabled because the color buffers format is not high quality 16-bit float."); + ImGui::Bullet(); ImGui::SameLine(); ImGui::TextWrapped("Create a flag file next to the executable named \"%s\" and restart.", OutputHandler::Flag_16fResources); + return; + } + if (IsSDRMode()) { DrawCompletelyUselessButton(); @@ -2286,6 +2495,9 @@ namespace SectionedImGui } } + // CUSTOM_LUTBUILDER_COLORSPACE + if (!OutputHandler::is_use16f) ShaderDefineInfo::Set(ShaderDefineInfo::CUSTOM_LUTBUILDER_COLORSPACE, 0); + //force no gamma correct in SDR if (cb_luma_global_settings.DisplayMode == DisplayModeType::SDR && ShaderDefineInfo::Get(GAMMA_CORRECTION_TYPE_HASH) == 1) { @@ -2372,6 +2584,10 @@ namespace SectionedImGui // Advanced Settings Toggle if (ImGui::Checkbox("Advanced Settings", &Globals::UIIsAdvanced)) reshade::set_config_value(runtime, NAME, "UIIsAdvanced", Globals::UIIsAdvanced); + +#if DEVELOPMENT + OutputHandler::UIDrawDisplayCompositionToggle(); +#endif } } @@ -2441,10 +2657,6 @@ class CallOfDutyBlackOps3 final : public Game native_shaders_definitions.emplace(CompileTimeStringHash("CoDBO3 PreSR"), ShaderDefinition{"Luma_CoDBO3_PreSR", reshade::api::pipeline_subobject_type::pixel_shader}); native_shaders_definitions.emplace(CompileTimeStringHash("CoDBO3 LinearizeVelocity"), ShaderDefinition{"Luma_CoDBO3_LinearizeVelocity", reshade::api::pipeline_subobject_type::pixel_shader}); #endif - - // Native Shaders: Display Composition replacement - native_shaders_definitions.erase(CompileTimeStringHash("Display Composition")); - native_shaders_definitions.emplace(CompileTimeStringHash("Display Composition"), ShaderDefinition{"Luma_CoDBO3_DisplayComposition", reshade::api::pipeline_subobject_type::pixel_shader}); //cb luma_settings_cbuffer_index = 13; @@ -2508,10 +2720,7 @@ class CallOfDutyBlackOps3 final : public Game std::filesystem::remove("blackops3.start", ec); } - //log Globals::SDR8Bit - if (Globals::SDR8Bit) message(reshade::log::level::info, "OnInit(): SDR8Bit is enabled."); - - // Alerts: users on Steam vanilla of crash if not t7patch + // Alerts: users on Steam vanilla will crash if not t7patch constexpr const char* NAME_ALERTS = "Alerts"; if (MemoryHack::exe_type == MemoryHack::Steam_Sep2026_24784313) { @@ -2534,27 +2743,8 @@ class CallOfDutyBlackOps3 final : public Game } } - // Alerts: smooth motion crash - if (swapchain_format_upgrade_type != TextureFormatUpgradesType::None) - { - constexpr const char* ENTRY = "HasShownSmoothMotionAlert"; - - bool has_shown = false; - reshade::get_config_value(nullptr, NAME_ALERTS, ENTRY, has_shown); - - if (!has_shown) - { - MessageBoxA( - nullptr, - "(Final alert, never shown again!)\n\nNVIDIA Smooth Motion is incompatible with scRGB 16bit, causing crash!", - "NVIDIA Smooth Motion", - MB_ICONINFORMATION | MB_OK - ); - - has_shown = true; - reshade::set_config_value(nullptr, NAME_ALERTS, ENTRY, has_shown); - } - } + // HDR10 + OutputHandler::OnInit(); } // void OnLoad(std::filesystem::path& file_path, bool failed) override @@ -2594,6 +2784,8 @@ class CallOfDutyBlackOps3 final : public Game device_data.force_reset_sr = true; game_device_data.HardResetSR(); #endif + + OutputHandler::HardReset(); // device_data.cb_luma_global_settings_dirty = true; // defines_need_recompilation = true; @@ -2657,6 +2849,11 @@ class CallOfDutyBlackOps3 final : public Game //game_device_data auto& game_device_data = CallOfDutyBlackOps3GameDeviceData::GetGameDeviceData(device_data); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // HDR10 + OutputHandler::OnDrawOrDispatchOverrideBegin(native_device, native_device_context, cmd_list_data, device_data, game_device_data, ps); //////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -3060,6 +3257,9 @@ class CallOfDutyBlackOps3 final : public Game game_device_data.drawn_final = true; device_data.has_drawn_main_post_processing = true; + // HDR10 + OutputHandler::OnDrawFinal(native_device, native_device_context, cmd_list_data, device_data, game_device_data, ps); + //disabled: no SR if (device_data.sr_type == SR::Type::None || device_data.sr_suppressed) return DrawOrDispatchOverrideType::None; @@ -3161,22 +3361,29 @@ class CallOfDutyBlackOps3 final : public Game #endif //case: HDTV rec.709 decode - if (game_device_data.drawn_final && ps == 0x8324B585) + if (/*game_device_data.drawn_final &&*/ ps == 0x8324B585) { - //progress - game_device_data.drawn_hdtv = true; - - return DrawOrDispatchOverrideType::Skip; //Skip this trash! It creates another 8bit tex to decode srgb and encode rec709. + game_device_data.drawn_hdtv = true; //progress + return OutputHandler::OnDrawRec709(native_device, native_device_context, cmd_list_data, device_data, game_device_data, ps); } //case: No UI after Final if (!Globals::IsUi && game_device_data.drawn_final) return DrawOrDispatchOverrideType::Skip; + // HDR10 + OutputHandler::OnDrawOrDispatchOverride(native_device, native_device_context, cmd_list_data, device_data, game_device_data, ps); + //case: normal return DrawOrDispatchOverrideType::None; } + void OnBeforeDisplayComposition(ID3D11Device* native_device, ID3D11DeviceContext* native_device_context, DeviceData& device_data) override + { + auto& game_device_data = CallOfDutyBlackOps3GameDeviceData::GetGameDeviceData(device_data); + OutputHandler::OnBeforeDisplayComposition(native_device, native_device_context, device_data, game_device_data); + } + void OnPresent(ID3D11Device* native_device, DeviceData& device_data) override { auto& game_device_data = CallOfDutyBlackOps3GameDeviceData::GetGameDeviceData(device_data); @@ -3212,8 +3419,11 @@ class CallOfDutyBlackOps3 final : public Game //ForcedLODBias ForcedLODBias::OnPresent(); + // HDR10 + OutputHandler::OnPresent(native_device, device_data, game_device_data); + //reset - game_device_data.Reset(device_data.force_reset_sr); + game_device_data.OnPresent(device_data.force_reset_sr); } void CleanExtraSRResources(DeviceData& device_data) override @@ -3323,13 +3533,6 @@ class CallOfDutyBlackOps3 final : public Game ////////////////////////////////////////////////////////////////////////////////////////////////////// - //Globals::SDR8Bit - if (Globals::SDR8Bit) - { - ImGui::Bullet(); ImGui::SameLine(); - ImGui::TextWrapped("Forcing 8bit gamma SDR output."); - } - //SR on but not SMAA T2X #if ENABLE_SR == 1 if (device_data.sr_type != SR::Type::None && game_device_data.drawn_tonemap_prev && !game_device_data.drawn_smaat2x_prev) @@ -3423,6 +3626,8 @@ class CallOfDutyBlackOps3 final : public Game if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) ImGui::SetTooltip("Skip the custom linearize pass at final when DLSS."); + ImGui::Checkbox("Force HDR10 False", &OutputHandler::force_hdr10_false); + ImGui::Checkbox("TiledDeferredCSTracking", &TiledDeferredCSTracking::enabled); ImGui::Checkbox("FSFXTracking", &FSFXTracking::enabled); } @@ -3456,6 +3661,7 @@ class CallOfDutyBlackOps3 final : public Game ImGui::BulletText("Bug Hunter: RooniVarooni"); ImGui::BulletText("Bug Hunter: soulshot96"); ImGui::BulletText("Bug Hunter: KING"); + ImGui::BulletText("Bug Hunter: bisquickpancakes"); ImGui::NewLine(); ImGui::Text("Third Party:"); @@ -3469,6 +3675,7 @@ class CallOfDutyBlackOps3 final : public Game ImGui::BulletText("NVIDIA"); ImGui::BulletText("AMD"); ImGui::BulletText("DICE"); + ImGui::BulletText("RenderDoc"); ImGui::BulletText("KsDumper"); ImGui::BulletText("Ghidra"); } @@ -3481,22 +3688,10 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv Globals::SetGlobals(PROJECT_NAME, "Call of Duty: Black Ops III - Luma"); Globals::VERSION = 1; - //swapchain upgrade - Globals::SDR8Bit = std::filesystem::exists("Luma_8bitSDR"); - swapchain_format_upgrade_type = !Globals::SDR8Bit ? TextureFormatUpgradesType::AllowedEnabled : TextureFormatUpgradesType::None; - swapchain_upgrade_type = SwapchainUpgradeType::scRGB; - force_disable_display_composition = Globals::SDR8Bit; - - //texture upgrade - texture_format_upgrades_type = TextureFormatUpgradesType::AllowedEnabled; - texture_upgrade_formats = { - #if ENABLE_SR == 1 - reshade::api::format::r16g16_snorm, //motion vectors - #endif - reshade::api::format::r11g11b10_float, //color (not super necessary, but its nice) - }; - texture_format_upgrades_2d_size_filters = (uint32_t)TextureFormatUpgrades2DSizeFilters::SwapchainAspectRatio; + // swapchain & texture upgrade + OutputHandler::OnDll(); + // #if ENABLE_SR == 1 // //sampler upgrade // enable_samplers_upgrade = true; //TODO: this has no effect... use other modes? From f5243f7cd3e89981a2374a013dd99a6ede260836 Mon Sep 17 00:00:00 2001 From: David Huynh Date: Sat, 3 Oct 2026 00:00:28 -0700 Subject: [PATCH 4/5] BO3: force not FSR --- Source/Games/Call of Duty Black Ops 3/main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Games/Call of Duty Black Ops 3/main.cpp b/Source/Games/Call of Duty Black Ops 3/main.cpp index 1f2ea904f..c2bd58833 100644 --- a/Source/Games/Call of Duty Black Ops 3/main.cpp +++ b/Source/Games/Call of Duty Black Ops 3/main.cpp @@ -2,7 +2,10 @@ #if CUSTOM_FAST == 0 #define ENABLE_NGX 1 - // #define ENABLE_FIDELITY_SK 1 //TODO: still dont know why FSR complete ruin main color Resource on draw... + #ifdef ENABLE_FIDELITY_SK + #undef ENABLE_FIDELITY_SK + #endif + #define ENABLE_FIDELITY_SK 0 //TODO: still dont know why FSR complete ruin main color Resource on draw... #endif #define DISABLE_AUTO_DEBUGGER 1 From 9b7265049eedc22b7c38a3f1f23abf0fcce6be60 Mon Sep 17 00:00:00 2001 From: David Huynh Date: Sat, 3 Oct 2026 12:41:06 -0700 Subject: [PATCH 5/5] BO3: typo --- Source/Games/Call of Duty Black Ops 3/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Games/Call of Duty Black Ops 3/main.cpp b/Source/Games/Call of Duty Black Ops 3/main.cpp index c2bd58833..5bc42f4d5 100644 --- a/Source/Games/Call of Duty Black Ops 3/main.cpp +++ b/Source/Games/Call of Duty Black Ops 3/main.cpp @@ -691,7 +691,7 @@ namespace OutputHandler // partial //TODO: this is mega goofy. Luma needs a Reno constexpr ID3D11ShaderResourceView* srv0 = nullptr; native_device_context->PSSetShaderResources(0, 1, &srv0); - // set our RTV + // set our 16f backbuffer as SRV native_device_context->PSSetShaderResources(10, 1, &Resources::backbuffer_srv); // DisplayComposition uses up to 8 } }