Bläddra i källkod

Add a low quality version of FSR

Panagiotis Christopoulos Charitos 4 år sedan
förälder
incheckning
a4b83e4db8
4 ändrade filer med 115 tillägg och 2 borttagningar
  1. 1 1
      AnKi/Renderer/ConfigDefs.h
  2. 4 1
      AnKi/Renderer/Scale.cpp
  3. 8 0
      AnKi/Shaders/Fsr.ankiprog
  4. 102 0
      ThirdParty/Fsr/ffx_fsr1.h

+ 1 - 1
AnKi/Renderer/ConfigDefs.h

@@ -56,5 +56,5 @@ ANKI_CONFIG_OPTION(r_rtShadowsSvgf, 0, 0, 1)
 ANKI_CONFIG_OPTION(r_rtShadowsSvgfAtrousPassCount, 3, 1, 20)
 ANKI_CONFIG_OPTION(r_rtShadowsRaysPerPixel, 1, 1, 8)
 
-ANKI_CONFIG_OPTION(r_fsr, 1, 0, 1)
+ANKI_CONFIG_OPTION(r_fsr, 1, 0, 2, "0: Use bilinear, 1: FSR low quality, 2: FSR high quality")
 ANKI_CONFIG_OPTION(r_sharpen, 1, 0, 1)

+ 4 - 1
AnKi/Renderer/Scale.cpp

@@ -38,7 +38,8 @@ Error Scale::init(const ConfigSet& cfg)
 
 	ANKI_R_LOGI("Initializing (up|down)scale pass");
 
-	m_fsr = cfg.getBool("r_fsr");
+	const U32 fsrQuality = cfg.getNumberU8("r_fsr");
+	m_fsr = fsrQuality != 0;
 
 	// Program
 	if(needsScaling)
@@ -50,6 +51,7 @@ Error Scale::init(const ConfigSet& cfg)
 		{
 			ShaderProgramResourceVariantInitInfo variantInitInfo(m_scaleProg);
 			variantInitInfo.addMutation("SHARPEN", 0);
+			variantInitInfo.addMutation("FSR_QUALITY", fsrQuality - 1);
 			m_scaleProg->getOrCreateVariant(variantInitInfo, variant);
 		}
 		else
@@ -64,6 +66,7 @@ Error Scale::init(const ConfigSet& cfg)
 		ANKI_CHECK(getResourceManager().loadResource("Shaders/Fsr.ankiprog", m_sharpenProg));
 		ShaderProgramResourceVariantInitInfo variantInitInfo(m_sharpenProg);
 		variantInitInfo.addMutation("SHARPEN", 1);
+		variantInitInfo.addMutation("FSR_QUALITY", 0);
 		const ShaderProgramResourceVariant* variant;
 		m_sharpenProg->getOrCreateVariant(variantInitInfo, variant);
 		m_sharpenGrProg = variant->getProgram();

+ 8 - 0
AnKi/Shaders/Fsr.ankiprog

@@ -4,6 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #pragma anki mutator SHARPEN 0 1
+#pragma anki mutator FSR_QUALITY 0 1
 
 #pragma anki start comp
 
@@ -58,6 +59,11 @@ AH4 FsrEasuBH(AF2 p)
 {
 	return AH4(textureGather(sampler2D(u_tex, u_linearAnyClampSampler), p, 2));
 }
+
+AH3 FsrEasuSampleH(AF2 p)
+{
+	return AH3(textureLod(u_tex, u_linearAnyClampSampler, p, 0.0).xyz);
+}
 #endif
 
 #include <ThirdParty/Fsr/ffx_fsr1.h>
@@ -75,6 +81,8 @@ void main()
 	HVec3 color;
 #if SHARPEN
 	FsrRcasH(color.r, color.g, color.b, gl_GlobalInvocationID.xy, u_fsrConsts0);
+#elif FSR_QUALITY == 0
+	FsrEasuL(color, gl_GlobalInvocationID.xy, u_fsrConsts0, u_fsrConsts1, u_fsrConsts2, u_fsrConsts3);
 #else
 	FsrEasuH(color, gl_GlobalInvocationID.xy, u_fsrConsts0, u_fsrConsts1, u_fsrConsts2, u_fsrConsts3);
 #endif

+ 102 - 0
ThirdParty/Fsr/ffx_fsr1.h

@@ -591,6 +591,108 @@ A_STATIC void FsrEasuConOffset(
 //------------------------------------------------------------------------------------------------------------------------------
   // Slightly different for FP16 version due to combined min and max.
   pix=min(AH3(bothR.y,bothG.y,bothB.y),max(-AH3(bothR.x,bothG.x,bothB.x),aC*AH3_(ARcpH1(aW))));}
+//==============================================================================================================================
+// An optimized AMD FSR's EASU implementation for Mobiles
+// Based on https://github.com/GPUOpen-Effects/FidelityFX-FSR/blob/master/ffx-fsr/ffx_fsr1.h
+// Details can be found: https://atyuwen.github.io/posts/optimizing-fsr/
+// Distributed under the MIT License.
+// -- FsrEasuSampleH should be implemented by calling shader, like following:
+//    half3 FsrEasuSampleH(float2 p) { return MyTex.SampleLevel(LinearSampler, p, 0).xyz; }
+//==============================================================================================================================
+ void FsrEasuL(
+ out AH3 pix,
+ AU2 ip,
+ AU4 con0,
+ AU4 con1,
+ AU4 con2,
+ AU4 con3){
+//------------------------------------------------------------------------------------------------------------------------------
+  // Direction is the '+' diff.
+  //    A
+  //  B C D
+  //    E
+  AF2 pp=AF2(ip)*AF2_AU2(con0.xy)+AF2_AU2(con0.zw);
+  AF2 tc=(pp+AF2_(0.5))*AF2_AU2(con1.xy);
+  AH3 sA=FsrEasuSampleH(tc-AF2(0, AF1_AU1(con1.y)));
+  AH3 sB=FsrEasuSampleH(tc-AF2(AF1_AU1(con1.x), 0));
+  AH3 sC=FsrEasuSampleH(tc);
+  AH3 sD=FsrEasuSampleH(tc+AF2(AF1_AU1(con1.x), 0));
+  AH3 sE=FsrEasuSampleH(tc+AF2(0, AF1_AU1(con1.y)));
+  AH1 lA=sA.r*AH1_(0.5)+sA.g;
+  AH1 lB=sB.r*AH1_(0.5)+sB.g;
+  AH1 lC=sC.r*AH1_(0.5)+sC.g;
+  AH1 lD=sD.r*AH1_(0.5)+sD.g;
+  AH1 lE=sE.r*AH1_(0.5)+sE.g;
+  // Then takes magnitude from abs average of both sides of 'C'.
+  // Length converts gradient reversal to 0, smoothly to non-reversal at 1, shaped, then adding horz and vert terms.
+  AH1 dc=lD-lC;
+  AH1 cb=lC-lB;
+  AH1 lenX=max(abs(dc),abs(cb));
+  lenX=ARcpH1(lenX);
+  AH1 dirX=lD-lB;
+  lenX=ASatH1(abs(dirX)*lenX);
+  lenX*=lenX;
+  // Repeat for the y axis.
+  AH1 ec=lE-lC;
+  AH1 ca=lC-lA;
+  AH1 lenY=max(abs(ec),abs(ca));
+  lenY=ARcpH1(lenY);
+  AH1 dirY=lE-lA;
+  lenY=ASatH1(abs(dirY)*lenY);
+  AH1 len = lenY * lenY + lenX;
+  AH2 dir = AH2(dirX, dirY);
+ //------------------------------------------------------------------------------------------------------------------------------ 
+  AH2 dir2=dir*dir;
+  AH1 dirR=dir2.x+dir2.y;
+  if (dirR<AH1_(1.0/64.0)) {
+    pix = sC;
+    return;
+  }
+  dirR=ARsqH1(dirR);
+  dir*=AH2_(dirR);
+  len=len*AH1_(0.5);
+  len*=len;
+  AH1 stretch=(dir.x*dir.x+dir.y*dir.y)*ARcpH1(max(abs(dir.x),abs(dir.y)));
+  AH2 len2=AH2(AH1_(1.0)+(stretch-AH1_(1.0))*len,AH1_(1.0)+AH1_(-0.5)*len);
+  AH1 lob=AH1_(0.5)+AH1_((1.0/4.0-0.04)-0.5)*len;
+  AH1 clp=ARcpH1(lob);
+//------------------------------------------------------------------------------------------------------------------------------
+  AF2 fp=floor(pp);
+  pp-=fp;
+  AH2 ppp=AH2(pp);
+  AF2 p0=fp*AF2_AU2(con1.xy)+AF2_AU2(con1.zw);
+  AF2 p1=p0+AF2_AU2(con2.xy);
+  AF2 p2=p0+AF2_AU2(con2.zw);
+  AF2 p3=p0+AF2_AU2(con3.xy);
+  p0.y-=AF1_AU1(con1.w); p3.y+=AF1_AU1(con1.w);
+  AH4 fgcbR=FsrEasuRH(p0);
+  AH4 fgcbG=FsrEasuGH(p0);
+  AH4 fgcbB=FsrEasuBH(p0);
+  AH4 ijfeR=FsrEasuRH(p1);
+  AH4 ijfeG=FsrEasuGH(p1);
+  AH4 ijfeB=FsrEasuBH(p1);
+  AH4 klhgR=FsrEasuRH(p2);
+  AH4 klhgG=FsrEasuGH(p2);
+  AH4 klhgB=FsrEasuBH(p2);
+  AH4 nokjR=FsrEasuRH(p3);
+  AH4 nokjG=FsrEasuGH(p3);
+  AH4 nokjB=FsrEasuBH(p3);
+//------------------------------------------------------------------------------------------------------------------------------
+  // This part is different for FP16, working pairs of taps at a time.
+  AH2 pR=AH2_(0.0);
+  AH2 pG=AH2_(0.0);
+  AH2 pB=AH2_(0.0);
+  AH2 pW=AH2_(0.0);
+  FsrEasuTapH(pR,pG,pB,pW,AH2( 1.0, 0.0)-ppp.xx,AH2(-1.0,-1.0)-ppp.yy,dir,len2,lob,clp,fgcbR.zw,fgcbG.zw,fgcbB.zw);
+  FsrEasuTapH(pR,pG,pB,pW,AH2(-1.0, 0.0)-ppp.xx,AH2( 1.0, 1.0)-ppp.yy,dir,len2,lob,clp,ijfeR.xy,ijfeG.xy,ijfeB.xy);
+  FsrEasuTapH(pR,pG,pB,pW,AH2( 0.0,-1.0)-ppp.xx,AH2( 0.0, 0.0)-ppp.yy,dir,len2,lob,clp,ijfeR.zw,ijfeG.zw,ijfeB.zw);
+  FsrEasuTapH(pR,pG,pB,pW,AH2( 1.0, 2.0)-ppp.xx,AH2( 1.0, 1.0)-ppp.yy,dir,len2,lob,clp,klhgR.xy,klhgG.xy,klhgB.xy);
+  FsrEasuTapH(pR,pG,pB,pW,AH2( 2.0, 1.0)-ppp.xx,AH2( 0.0, 0.0)-ppp.yy,dir,len2,lob,clp,klhgR.zw,klhgG.zw,klhgB.zw);
+  FsrEasuTapH(pR,pG,pB,pW,AH2( 0.0, 1.0)-ppp.xx,AH2( 2.0, 2.0)-ppp.yy,dir,len2,lob,clp,nokjR.xy,nokjG.xy,nokjB.xy);
+  AH3 aC=AH3(pR.x+pR.y,pG.x+pG.y,pB.x+pB.y);
+  AH1 aW=pW.x+pW.y;
+//------------------------------------------------------------------------------------------------------------------------------
+  pix=aC*AH3_(ARcpH1(aW));} 
 #endif
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////