Browse Source

WIP: Deferred MSAA
- Shadowed light using standard deferred path now properly account for MSAA coverage

BearishSun 8 years ago
parent
commit
bee6a1a7b1

+ 4 - 0
Data/Raw/Engine/DataList.json

@@ -373,6 +373,10 @@
         {
             "Path": "MSAACoverage.bsl",
             "UUID": "151465e1-e123-475d-97f5-77bc04bc15ce"
+        },
+        {
+            "Path": "MSAACoverageStencil.bsl",
+            "UUID": "f4344aa8-d286-4f10-b25d-a76302487fa8"
         }
     ],
     "Skin": [

+ 34 - 3
Data/Raw/Engine/Shaders/DeferredDirectionalLight.bsl

@@ -9,8 +9,27 @@ technique DeferredDirectionalLight
 		read = false;
 	};
 	
+	#ifdef MSAA
+	stencil
+	{
+		enabled = true;
+		front = { keep, keep, keep, eq };
+		readmask = 0x80;
+		
+		#ifdef MSAA_RESOLVE_0TH
+		reference = 0;
+		#else
+		reference = 0x80;
+		#endif
+	};
+	#endif
+	
 	code 
 	{
+		#ifndef MSAA_RESOLVE_0TH
+			#define MSAA_RESOLVE_0TH 0
+		#endif	
+	
 		struct VStoFS
 		{
 			float4 position : SV_POSITION;
@@ -35,12 +54,20 @@ technique DeferredDirectionalLight
 			return output;
 		}
 
-		float4 fsmain(VStoFS input, uint sampleIdx : SV_SampleIndex) : SV_Target0
+		float4 fsmain(VStoFS input
+			#if MSAA_COUNT > 1 && !MSAA_RESOLVE_0TH
+			, uint sampleIdx : SV_SampleIndex
+			#endif
+			) : SV_Target0
 		{
 			uint2 pixelPos = (uint2)(input.uv0 * (float2)gViewportRectangle.zw - ((float2)gViewportRectangle.xy + 0.5f));
 			
 			#if MSAA_COUNT > 1
-			SurfaceData surfaceData = getGBufferData(pixelPos, sampleIdx);
+				#if MSAA_RESOLVE_0TH
+					SurfaceData surfaceData = getGBufferData(pixelPos, 0);
+				#else
+					SurfaceData surfaceData = getGBufferData(pixelPos, sampleIdx);
+				#endif
 			#else
 			SurfaceData surfaceData = getGBufferData(pixelPos);
 			#endif			
@@ -60,7 +87,11 @@ technique DeferredDirectionalLight
 				LightData lightData = getLightData();
 				
 				#if MSAA_COUNT > 1
-				float occlusion = gLightOcclusionTex.Load(pixelPos, sampleIdx).r;
+					#if MSAA_RESOLVE_0TH
+						float occlusion = gLightOcclusionTex.Load(pixelPos, 0).r;
+					#else
+						float occlusion = gLightOcclusionTex.Load(pixelPos, sampleIdx).r;
+					#endif
 				#else
 				float occlusion = gLightOcclusionTex.Load(int3(pixelPos, 0)).r;
 				#endif

+ 40 - 4
Data/Raw/Engine/Shaders/DeferredPointLight.bsl

@@ -4,8 +4,32 @@ technique DeferredPointLight
 {
 	mixin DeferredLightCommon;
 
+	#ifdef MSAA
+	stencil
+	{
+		enabled = true;
+		readmask = 0x80;
+		
+		#ifdef INSIDE_GEOMETRY
+		back = { keep, keep, keep, eq };
+		#else
+		front = { keep, keep, keep, eq };
+		#endif
+		
+		#ifdef MSAA_RESOLVE_0TH
+		reference = 0;
+		#else
+		reference = 0x80;
+		#endif
+	};
+	#endif
+	
 	code
 	{
+		#ifndef MSAA_RESOLVE_0TH
+			#define MSAA_RESOLVE_0TH 0
+		#endif		
+	
 		struct VStoFS
 		{
 			float4 position : SV_POSITION;
@@ -77,16 +101,24 @@ technique DeferredPointLight
 			return output;
 		}			
 
-		float4 fsmain(VStoFS input, uint sampleIdx : SV_SampleIndex) : SV_Target0
+		float4 fsmain(VStoFS input
+			#if MSAA_COUNT > 1 && !MSAA_RESOLVE_0TH
+			, uint sampleIdx : SV_SampleIndex
+			#endif
+			) : SV_Target0
 		{
 			float2 ndcPos = input.screenPos.xy / input.screenPos.w;
 			uint2 pixelPos = NDCToScreen(ndcPos);
 			
 			#if MSAA_COUNT > 1
-			SurfaceData surfaceData = getGBufferData(pixelPos, sampleIdx);
+				#if MSAA_RESOLVE_0TH
+					SurfaceData surfaceData = getGBufferData(pixelPos, 0);
+				#else
+					SurfaceData surfaceData = getGBufferData(pixelPos, sampleIdx);
+				#endif
 			#else
 			SurfaceData surfaceData = getGBufferData(pixelPos);
-			#endif
+			#endif	
 
 			if(surfaceData.worldNormal.w > 0.0f)
 			{
@@ -109,7 +141,11 @@ technique DeferredPointLight
 				LightData lightData = getLightData();
 				
 				#if MSAA_COUNT > 1
-				float occlusion = gLightOcclusionTex.Load(pixelPos, sampleIdx).r;
+					#if MSAA_RESOLVE_0TH
+						float occlusion = gLightOcclusionTex.Load(pixelPos, 0).r;
+					#else
+						float occlusion = gLightOcclusionTex.Load(pixelPos, sampleIdx).r;
+					#endif
 				#else
 				float occlusion = gLightOcclusionTex.Load(int3(pixelPos, 0)).r;
 				#endif

+ 32 - 0
Data/Raw/Engine/Shaders/MSAACoverageStencil.bsl

@@ -0,0 +1,32 @@
+#include "$ENGINE$\PPBase.bslinc"
+
+technique MSAACoverageStencil
+{
+	mixin PPBase;
+
+	stencil
+	{
+		enabled = true;
+		reference = 0x80;
+		front = { replace, replace, replace, always };
+		writemask = 0x80;
+	};		
+	
+	code
+	{	
+		Texture2D gMSAACoverage;
+	
+		float fsmain(VStoFS input) : SV_Target0
+		{
+			int2 pixelPos = (int2)input.position.xy;
+			float coverage = gMSAACoverage.Load(int3(pixelPos, 0));
+			
+			// Note: Consider checking 2x2 pixel block and only discard if none require per-sample
+			// evaluation. This should speed up HiStencil.
+			if(coverage < 0.5f)
+				discard;
+		
+			return 0.0f;			
+		}	
+	};
+};

+ 1 - 1
Source/RenderBeast/BsLightProbes.cpp

@@ -172,7 +172,7 @@ namespace bs { namespace ct
 
 		// Render
 		RenderAPI& rapi = RenderAPI::instance();
-		rapi.setRenderTarget(output, 0, RT_COLOR0);
+		rapi.setRenderTarget(output, FBT_DEPTH | FBT_STENCIL, RT_COLOR0);
 
 		gRendererUtility().setPass(mMaterial);
 		gRendererUtility().setPassParams(mParamsSet);

+ 20 - 0
Source/RenderBeast/BsPostProcessing.cpp

@@ -1838,4 +1838,24 @@ namespace bs { namespace ct
 			return get(VAR_8x);
 		}
 	}
+
+	MSAACoverageStencilMat::MSAACoverageStencilMat()
+	{
+		SPtr<GpuParams> params = mParamsSet->getGpuParams();
+		params->getTextureParam(GPT_FRAGMENT_PROGRAM, "gMSAACoverage", mCoverageTexParam);
+	}
+
+	void MSAACoverageStencilMat::_initVariations(ShaderVariations& variations)
+	{
+		// Do nothing
+	}
+
+	void MSAACoverageStencilMat::execute(const SPtr<Texture>& coverage)
+	{
+		mCoverageTexParam.set(coverage);
+
+		gRendererUtility().setPass(mMaterial);
+		gRendererUtility().setPassParams(mParamsSet);
+		gRendererUtility().drawScreenQuad();
+	}
 }}

+ 22 - 0
Source/RenderBeast/BsPostProcessing.h

@@ -850,5 +850,27 @@ namespace bs { namespace ct
 		static ShaderVariation VAR_8x;
 	};
 
+	/** 
+	 * Converts the coverage texture output by MSAACoverageMat and writes its information in the highest bit of the
+	 * currently bound stencil buffer. This allows coverage information to be used by normal (non-compute) rendering 
+	 * shaders.
+	 */
+	class MSAACoverageStencilMat : public RendererMaterial<MSAACoverageStencilMat>
+	{
+		RMAT_DEF("MSAACoverageStencil.bsl");
+
+	public:
+		MSAACoverageStencilMat();
+
+		/** 
+		 * Renders the effect with the provided parameters, using the currently bound render target. 
+		 * 
+		 * @param[in]	coverage	Coverage texture as output by MSAACoverageMat.
+		 */
+		void execute(const SPtr<Texture>& coverage);
+	private:
+		GpuParamTexture mCoverageTexParam;
+	};
+
 	/** @} */
 }}

+ 25 - 7
Source/RenderBeast/BsRenderCompositor.cpp

@@ -426,8 +426,13 @@ namespace bs { namespace ct
 
 		MSAACoverageMat* mat = MSAACoverageMat::getVariation(viewProps.numSamples);
 
-		RenderAPI::instance().setRenderTarget(output->renderTexture);
+		RenderAPI& rapi = RenderAPI::instance();
+		rapi.setRenderTarget(output->renderTexture);
 		mat->execute(inputs.view, gbuffer);
+
+		MSAACoverageStencilMat* stencilMat = MSAACoverageStencilMat::get();
+		rapi.setRenderTarget(sceneDepthNode->depthTex->renderTexture);
+		stencilMat->execute(output->texture);
 	}
 
 	void RCNodeMSAACoverage::clear()
@@ -446,6 +451,8 @@ namespace bs { namespace ct
 		GpuResourcePool& resPool = GpuResourcePool::instance();
 		const RendererViewProperties& viewProps = inputs.view.getProperties();
 
+		RCNodeSceneDepth* depthNode = static_cast<RCNodeSceneDepth*>(inputs.inputNodes[0]);
+
 		UINT32 width = viewProps.viewRect.width;
 		UINT32 height = viewProps.viewRect.height;
 		UINT32 numSamples = viewProps.numSamples;
@@ -474,7 +481,10 @@ namespace bs { namespace ct
 
 		bool rebuildRT;
 		if (renderTarget != nullptr)
+		{
 			rebuildRT = renderTarget->getColorTexture(0) != lightAccumulationTex->texture;
+			rebuildRT |= renderTarget->getDepthStencilTexture() != depthNode->depthTex->texture;
+		}
 		else
 			rebuildRT = true;
 
@@ -486,6 +496,11 @@ namespace bs { namespace ct
 			lightAccumulationRTDesc.colorSurfaces[0].numFaces = 1;
 			lightAccumulationRTDesc.colorSurfaces[0].mipLevel = 0;
 
+			lightAccumulationRTDesc.depthStencilSurface.texture = depthNode->depthTex->texture;
+			lightAccumulationRTDesc.depthStencilSurface.face = 0;
+			lightAccumulationRTDesc.depthStencilSurface.numFaces = 1;
+			lightAccumulationRTDesc.depthStencilSurface.mipLevel = 0;
+
 			renderTarget = RenderTexture::create(lightAccumulationRTDesc);
 		}
 	}
@@ -501,7 +516,7 @@ namespace bs { namespace ct
 
 	SmallVector<StringID, 4> RCNodeLightAccumulation::getDependencies(const RendererView& view)
 	{
-		return {};
+		return { RCNodeSceneDepth::getNodeId() };
 	}
 
 	void RCNodeTiledDeferredLighting::render(const RenderCompositorNodeInputs& inputs)
@@ -564,7 +579,10 @@ namespace bs { namespace ct
 
 		// If shadows are disabled we handle all lights through tiled deferred
 		if (!inputs.view.getRenderSettings().enableShadows)
+		{
+			mLightOcclusionRT = nullptr;
 			return;
+		}
 
 		GpuResourcePool& resPool = GpuResourcePool::instance();
 		const RendererViewProperties& viewProps = inputs.view.getProperties();
@@ -581,10 +599,10 @@ namespace bs { namespace ct
 			height, TU_RENDERTARGET, numSamples, false));
 
 		bool rebuildRT = false;
-		if (mRenderTarget != nullptr)
+		if (mLightOcclusionRT != nullptr)
 		{
-			rebuildRT |= mRenderTarget->getColorTexture(0) != lightOcclusionTex->texture;
-			rebuildRT |= mRenderTarget->getDepthStencilTexture() != sceneDepthNode->depthTex->texture;
+			rebuildRT |= mLightOcclusionRT->getColorTexture(0) != lightOcclusionTex->texture;
+			rebuildRT |= mLightOcclusionRT->getDepthStencilTexture() != sceneDepthNode->depthTex->texture;
 		}
 		else
 			rebuildRT = true;
@@ -602,7 +620,7 @@ namespace bs { namespace ct
 			lightOcclusionRTDesc.depthStencilSurface.numFaces = 1;
 			lightOcclusionRTDesc.depthStencilSurface.mipLevel = 0;
 
-			mRenderTarget = RenderTexture::create(lightOcclusionRTDesc);
+			mLightOcclusionRT = RenderTexture::create(lightOcclusionRTDesc);
 		}
 
 		GBufferTextures gbuffer;
@@ -625,7 +643,7 @@ namespace bs { namespace ct
 
 			for (UINT32 j = 0; j < count; j++)
 			{
-				rapi.setRenderTarget(mRenderTarget, FBT_DEPTH, RT_DEPTH_STENCIL);
+				rapi.setRenderTarget(mLightOcclusionRT, FBT_DEPTH, RT_DEPTH_STENCIL);
 
 				Rect2 area(0.0f, 0.0f, 1.0f, 1.0f);
 				rapi.setViewport(area);

+ 1 - 1
Source/RenderBeast/BsRenderCompositor.h

@@ -332,7 +332,7 @@ namespace ct
 		/** @copydoc RenderCompositorNode::clear */
 		void clear() override;
 
-		SPtr<RenderTexture> mRenderTarget;
+		SPtr<RenderTexture> mLightOcclusionRT;
 	};
 
 	/**

+ 79 - 18
Source/RenderBeast/BsStandardDeferredLighting.cpp

@@ -9,8 +9,15 @@
 namespace bs { namespace ct {
 	PerLightParamDef gPerLightParamDef;
 
-	ShaderVariation DirectionalLightMat::VAR_MSAA = ShaderVariation({
-		ShaderVariation::Param("MSAA_COUNT", 2)
+	ShaderVariation DirectionalLightMat::VAR_FullMSAA = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 2),
+		ShaderVariation::Param("MSAA", true)
+	});
+
+	ShaderVariation DirectionalLightMat::VAR_SingleMSAA = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 2),
+		ShaderVariation::Param("MSAA", true),
+		ShaderVariation::Param("MSAA_RESOLVE_0TH", true)
 	});
 
 	ShaderVariation DirectionalLightMat::VAR_NoMSAA = ShaderVariation({
@@ -26,7 +33,8 @@ namespace bs { namespace ct {
 
 	void DirectionalLightMat::_initVariations(ShaderVariations& variations)
 	{
-		variations.add(VAR_MSAA);
+		variations.add(VAR_FullMSAA);
+		variations.add(VAR_SingleMSAA);
 		variations.add(VAR_NoMSAA);
 	}
 
@@ -47,21 +55,41 @@ namespace bs { namespace ct {
 		gRendererUtility().setPassParams(mParamsSet);
 	}
 
-	DirectionalLightMat* DirectionalLightMat::getVariation(bool msaa)
+	DirectionalLightMat* DirectionalLightMat::getVariation(bool msaa, bool singleSampleMSAA)
 	{
 		if (msaa)
-			return get(VAR_MSAA);
+		{
+			if (singleSampleMSAA)
+				return get(VAR_SingleMSAA);
+			else
+				return get(VAR_FullMSAA);
+		}
 
 		return get(VAR_NoMSAA);
 	}
 
-	ShaderVariation PointLightMat::VAR_MSAA_Inside = ShaderVariation({
+	ShaderVariation PointLightMat::VAR_FullMSAA_Inside = ShaderVariation({
 		ShaderVariation::Param("MSAA_COUNT", 2),
+		ShaderVariation::Param("MSAA", true),
 		ShaderVariation::Param("INSIDE_GEOMETRY", true)
 	});
 
-	ShaderVariation PointLightMat::VAR_MSAA_Outside = ShaderVariation({
-		ShaderVariation::Param("MSAA_COUNT", 2)
+	ShaderVariation PointLightMat::VAR_SingleMSAA_Inside = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 2),
+		ShaderVariation::Param("MSAA", true),
+		ShaderVariation::Param("INSIDE_GEOMETRY", true),
+		ShaderVariation::Param("MSAA_RESOLVE_0TH", true)
+	});
+
+	ShaderVariation PointLightMat::VAR_FullMSAA_Outside = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 2),
+		ShaderVariation::Param("MSAA", true)
+	});
+
+	ShaderVariation PointLightMat::VAR_SingleMSAA_Outside = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 2),
+		ShaderVariation::Param("MSAA", true),
+		ShaderVariation::Param("MSAA_RESOLVE_0TH", true)
 	});
 
 	ShaderVariation PointLightMat::VAR_NoMSAA_Inside = ShaderVariation({
@@ -82,8 +110,10 @@ namespace bs { namespace ct {
 
 	void PointLightMat::_initVariations(ShaderVariations& variations)
 	{
-		variations.add(VAR_MSAA_Inside);
-		variations.add(VAR_MSAA_Outside);
+		variations.add(VAR_FullMSAA_Inside);
+		variations.add(VAR_SingleMSAA_Inside);
+		variations.add(VAR_FullMSAA_Outside);
+		variations.add(VAR_SingleMSAA_Outside);
 		variations.add(VAR_NoMSAA_Inside);
 		variations.add(VAR_NoMSAA_Outside);
 	}
@@ -105,14 +135,24 @@ namespace bs { namespace ct {
 		gRendererUtility().setPassParams(mParamsSet);
 	}
 
-	PointLightMat* PointLightMat::getVariation(bool msaa, bool inside)
+	PointLightMat* PointLightMat::getVariation(bool inside, bool msaa, bool singleSampleMSAA)
 	{
 		if(msaa)
 		{
 			if (inside)
-				return get(VAR_MSAA_Inside);
+			{
+				if (singleSampleMSAA)
+					return get(VAR_SingleMSAA_Inside);
+
+				return get(VAR_FullMSAA_Inside);
+			}
 			else
-				return get(VAR_MSAA_Outside);
+			{
+				if (singleSampleMSAA)
+					return get(VAR_SingleMSAA_Outside);
+
+				return get(VAR_FullMSAA_Outside);
+			}
 		}
 		else
 		{
@@ -140,11 +180,21 @@ namespace bs { namespace ct {
 
 		if (lightType == LightType::Directional)
 		{
-			DirectionalLightMat* material = DirectionalLightMat::getVariation(isMSAA);
+			DirectionalLightMat* material = DirectionalLightMat::getVariation(isMSAA, true);
 			material->bind(gBufferInput, lightOcclusion, perViewBuffer);
 			material->setPerLightParams(mPerLightBuffer);
 
 			gRendererUtility().drawScreenQuad();
+
+			// Draw pixels requiring per-sample evaluation
+			if(isMSAA)
+			{
+				DirectionalLightMat* msaaMaterial = DirectionalLightMat::getVariation(true, false);
+				msaaMaterial->bind(gBufferInput, lightOcclusion, perViewBuffer);
+				msaaMaterial->setPerLightParams(mPerLightBuffer);
+
+				gRendererUtility().drawScreenQuad();
+			}
 		}
 		else // Radial or spot
 		{
@@ -158,17 +208,28 @@ namespace bs { namespace ct {
 
 			bool isInside = distSqrd < (boundRadius * boundRadius);
 
-			PointLightMat* material = PointLightMat::getVariation(isMSAA, isInside);
-			material->bind(gBufferInput, lightOcclusion, perViewBuffer);
-			material->setPerLightParams(mPerLightBuffer);
-
 			SPtr<Mesh> stencilMesh;
 			if(lightType == LightType::Radial)
 				stencilMesh = RendererUtility::instance().getRadialLightStencil();
 			else // Spot
 				stencilMesh = RendererUtility::instance().getSpotLightStencil();
 
+			PointLightMat* material = PointLightMat::getVariation(isInside, isMSAA, true);
+			material->bind(gBufferInput, lightOcclusion, perViewBuffer);
+			material->setPerLightParams(mPerLightBuffer);
+
+			// Note: If MSAA is enabled this will be rendered multisampled (on polygon edges), see if this can be avoided
 			gRendererUtility().draw(stencilMesh);
+
+			// Draw pixels requiring per-sample evaluation
+			if(isMSAA)
+			{
+				PointLightMat* msaaMaterial = PointLightMat::getVariation(isInside, true, false);
+				msaaMaterial->bind(gBufferInput, lightOcclusion, perViewBuffer);
+				msaaMaterial->setPerLightParams(mPerLightBuffer);
+
+				gRendererUtility().draw(stencilMesh);
+			}
 		}
 	}
 }}

+ 25 - 7
Source/RenderBeast/BsStandardDeferredLighting.h

@@ -37,13 +37,21 @@ namespace bs { namespace ct {
 		/** Updates the per-light buffers used by the material. */
 		void setPerLightParams(const SPtr<GpuParamBlockBuffer>& perLight);
 		
-		/** Returns the material variation matching the provided parameters. */
-		static DirectionalLightMat* getVariation(bool msaa);
+		/** 
+		 * Returns the material variation matching the provided parameters. 
+		 * 
+		 * @param[in]	msaa				True if the shader will operate on a multisampled surface.
+		 * @param[in]	singleSampleMSAA	Only relevant of @p msaa is true. When enabled only the first sample will be
+		 *									evaluated. Otherwise all samples will be evaluated.
+		 * @return							Requested variation of the material.
+		 */
+		static DirectionalLightMat* getVariation(bool msaa, bool singleSampleMSAA = false);
 	private:
 		GBufferParams mGBufferParams;
 		GpuParamTexture mLightOcclusionTexParam;
 
-		static ShaderVariation VAR_MSAA;
+		static ShaderVariation VAR_FullMSAA;
+		static ShaderVariation VAR_SingleMSAA;
 		static ShaderVariation VAR_NoMSAA;
 	};
 
@@ -62,14 +70,24 @@ namespace bs { namespace ct {
 		/** Updates the per-light buffers used by the material. */
 		void setPerLightParams(const SPtr<GpuParamBlockBuffer>& perLight);
 
-		/** Returns the material variation matching the provided parameters. */
-		static PointLightMat* getVariation(bool msaa, bool inside);
+		/** 
+		 * Returns the material variation matching the provided parameters. 
+		 * 
+		 * @param[in]	inside				Set to true if viewer is inside the light's stencil geometry.
+		 * @param[in]	msaa				True if the shader will operate on a multisampled surface.
+		 * @param[in]	singleSampleMSAA	Only relevant of @p msaa is true. When enabled only the first sample will be
+		 *									evaluated. Otherwise all samples will be evaluated.
+		 * @return							Requested variation of the material.
+		 */
+		static PointLightMat* getVariation(bool inside, bool msaa, bool singleSampleMSAA = false);
 	private:
 		GBufferParams mGBufferParams;
 		GpuParamTexture mLightOcclusionTexParam;
 
-		static ShaderVariation VAR_MSAA_Inside;
-		static ShaderVariation VAR_MSAA_Outside;
+		static ShaderVariation VAR_FullMSAA_Inside;
+		static ShaderVariation VAR_SingleMSAA_Inside;
+		static ShaderVariation VAR_FullMSAA_Outside;
+		static ShaderVariation VAR_SingleMSAA_Outside;
 		static ShaderVariation VAR_NoMSAA_Inside;
 		static ShaderVariation VAR_NoMSAA_Outside;
 	};