Browse Source

Add VRS attachment support

Panagiotis Christopoulos Charitos 4 năm trước cách đây
mục cha
commit
4c5d4ff40d

+ 10 - 3
AnKi/Gr/Framebuffer.h

@@ -92,10 +92,17 @@ public:
 			return false;
 		}
 
-		if(m_shadingRateImage.m_textureView
-		   && (m_shadingRateImage.m_texelHeight == 0 || m_shadingRateImage.m_texelWidth))
+		if(m_shadingRateImage.m_textureView)
 		{
-			return false;
+			if(m_shadingRateImage.m_texelHeight == 0 || m_shadingRateImage.m_texelWidth == 0)
+			{
+				return false;
+			}
+
+			if(!isPowerOfTwo(m_shadingRateImage.m_texelHeight) || !isPowerOfTwo(m_shadingRateImage.m_texelWidth))
+			{
+				return false;
+			}
 		}
 
 		return true;

+ 0 - 2
AnKi/Gr/RenderGraph.cpp

@@ -169,8 +169,6 @@ public:
 
 void FramebufferDescription::bake()
 {
-	ANKI_ASSERT(m_hash == 0 && "Already baked");
-
 	m_hash = 0;
 	ANKI_ASSERT(m_colorAttachmentCount > 0 || !!m_depthStencilAttachment.m_aspect);
 

+ 1 - 1
AnKi/Gr/Vulkan/TextureImpl.cpp

@@ -590,7 +590,7 @@ VkImageLayout TextureImpl::computeLayout(TextureUsageBit usage, U level) const
 		// Color attachment
 		out = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 	}
-	else if(!(usage & ~TextureUsageBit::ALL_FRAMEBUFFER_ATTACHMENT))
+	else if(!(usage & ~TextureUsageBit::FRAMEBUFFER_SHADING_RATE))
 	{
 		// SRI
 		out = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;

+ 1 - 0
AnKi/Renderer/ConfigVars.defs.h

@@ -9,6 +9,7 @@ ANKI_CONFIG_VAR_U8(RTextureAnisotropy, 8, 1, 16, "Texture anisotropy for the mai
 ANKI_CONFIG_VAR_U32(RTileSize, 64, 8, 256, "Tile lighting tile size")
 ANKI_CONFIG_VAR_U32(RZSplitCount, 64, 8, 1024, "Clusterer number of Z splits")
 ANKI_CONFIG_VAR_BOOL(RPreferCompute, !ANKI_OS_ANDROID, "Prefer compute shaders")
+ANKI_CONFIG_VAR_BOOL(RVrs, true, "Enable VRS in multiple passes")
 
 ANKI_CONFIG_VAR_F32(RInternalRenderScaling, 1.0f, 0.5f, 1.0f,
 					"A factor over the requested swapchain resolution. Applies to all passes up to TAA")

+ 45 - 4
AnKi/Renderer/GBuffer.cpp

@@ -6,9 +6,10 @@
 #include <AnKi/Renderer/GBuffer.h>
 #include <AnKi/Renderer/Renderer.h>
 #include <AnKi/Renderer/RenderQueue.h>
-#include <AnKi/Renderer/LensFlare.h>
+#include <AnKi/Renderer/VrsSriGeneration.h>
 #include <AnKi/Util/Logger.h>
 #include <AnKi/Util/Tracer.h>
+#include <AnKi/Core/ConfigSet.h>
 
 namespace anki {
 
@@ -74,6 +75,13 @@ Error GBuffer::initInternal()
 	m_fbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
 	m_fbDescr.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0f;
 	m_fbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
+
+	if(getGrManager().getDeviceCapabilities().m_vrs && getConfig().getRVrs())
+	{
+		m_fbDescr.m_shadingRateAttachmentTexelWidth = m_r->getVrsSriGeneration().getSriTexelDimension();
+		m_fbDescr.m_shadingRateAttachmentTexelHeight = m_r->getVrsSriGeneration().getSriTexelDimension();
+	}
+
 	m_fbDescr.bake();
 
 	return Error::NONE;
@@ -149,6 +157,27 @@ void GBuffer::populateRenderGraph(RenderingContext& ctx)
 
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 
+	const Bool enableVrs = getGrManager().getDeviceCapabilities().m_vrs && getConfig().getRVrs();
+	const Bool fbDescrHasVrs = m_fbDescr.m_shadingRateAttachmentTexelWidth > 0;
+
+	if(enableVrs != fbDescrHasVrs)
+	{
+		// Re-bake the FB descriptor if the VRS state has changed
+
+		if(enableVrs)
+		{
+			m_fbDescr.m_shadingRateAttachmentTexelWidth = m_r->getVrsSriGeneration().getSriTexelDimension();
+			m_fbDescr.m_shadingRateAttachmentTexelHeight = m_r->getVrsSriGeneration().getSriTexelDimension();
+		}
+		else
+		{
+			m_fbDescr.m_shadingRateAttachmentTexelWidth = 0;
+			m_fbDescr.m_shadingRateAttachmentTexelHeight = 0;
+		}
+
+		m_fbDescr.bake();
+	}
+
 	// Create RTs
 	Array<RenderTargetHandle, MAX_COLOR_ATTACHMENTS> rts;
 	for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
@@ -172,11 +201,17 @@ void GBuffer::populateRenderGraph(RenderingContext& ctx)
 			rgraph.importRenderTarget(m_depthRts[(m_r->getFrameCount() + 1) & 1], TextureUsageBit::SAMPLED_FRAGMENT);
 	}
 
+	RenderTargetHandle sriRt;
+	if(enableVrs)
+	{
+		sriRt = m_r->getVrsSriGeneration().getSriRt();
+	}
+
 	// Create pass
 	GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("GBuffer");
 
 	pass.setFramebufferInfo(m_fbDescr, ConstWeakArray<RenderTargetHandle>(&rts[0], GBUFFER_COLOR_ATTACHMENT_COUNT),
-							m_runCtx.m_crntFrameDepthRt);
+							m_runCtx.m_crntFrameDepthRt, sriRt);
 	pass.setWork(computeNumberOfSecondLevelCommandBuffers(ctx.m_renderQueue->m_earlyZRenderables.getSize()
 														  + ctx.m_renderQueue->m_renderables.getSize()),
 				 [this, &ctx](RenderPassWorkContext& rgraphCtx) {
@@ -185,11 +220,17 @@ void GBuffer::populateRenderGraph(RenderingContext& ctx)
 
 	for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
 	{
-		pass.newDependency({m_runCtx.m_colorRts[i], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newDependency(RenderPassDependency(m_runCtx.m_colorRts[i], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE));
 	}
 
 	TextureSubresourceInfo subresource(DepthStencilAspectBit::DEPTH);
-	pass.newDependency({m_runCtx.m_crntFrameDepthRt, TextureUsageBit::ALL_FRAMEBUFFER_ATTACHMENT, subresource});
+	pass.newDependency(
+		RenderPassDependency(m_runCtx.m_crntFrameDepthRt, TextureUsageBit::ALL_FRAMEBUFFER_ATTACHMENT, subresource));
+
+	if(enableVrs)
+	{
+		pass.newDependency(RenderPassDependency(sriRt, TextureUsageBit::FRAMEBUFFER_SHADING_RATE));
+	}
 }
 
 } // end namespace anki

+ 5 - 0
AnKi/Renderer/Renderer.cpp

@@ -41,6 +41,7 @@
 #include <AnKi/Renderer/ClusterBinning.h>
 #include <AnKi/Renderer/Scale.h>
 #include <AnKi/Renderer/IndirectDiffuse.h>
+#include <AnKi/Renderer/VrsSriGeneration.h>
 
 namespace anki {
 
@@ -148,6 +149,9 @@ Error Renderer::initInternal(UVec2 swapchainResolution)
 	m_probeReflections.reset(m_alloc.newInstance<ProbeReflections>(this));
 	ANKI_CHECK(m_probeReflections->init());
 
+	m_vrsSriGeneration.reset(m_alloc.newInstance<VrsSriGeneration>(this));
+	ANKI_CHECK(m_vrsSriGeneration->init());
+
 	m_gbuffer.reset(m_alloc.newInstance<GBuffer>(this));
 	ANKI_CHECK(m_gbuffer->init());
 
@@ -337,6 +341,7 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 	{
 		m_accelerationStructureBuilder->populateRenderGraph(ctx);
 	}
+	m_vrsSriGeneration->populateRenderGraph(ctx);
 	m_shadowMapping->populateRenderGraph(ctx);
 	m_indirectDiffuseProbes->populateRenderGraph(ctx);
 	m_probeReflections->populateRenderGraph(ctx);

+ 1 - 0
AnKi/Renderer/RendererObject.defs.h

@@ -30,3 +30,4 @@ ANKI_RENDERER_OBJECT_DEF(MotionVectors, motionVectors)
 ANKI_RENDERER_OBJECT_DEF(ClusterBinning, clusterBinning)
 ANKI_RENDERER_OBJECT_DEF(Scale, scale)
 ANKI_RENDERER_OBJECT_DEF(IndirectDiffuse, indirectDiffuse)
+ANKI_RENDERER_OBJECT_DEF(VrsSriGeneration, vrsSriGeneration)

+ 100 - 0
AnKi/Renderer/VrsSriGeneration.cpp

@@ -0,0 +1,100 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/Renderer/VrsSriGeneration.h>
+#include <AnKi/Renderer/Renderer.h>
+#include <AnKi/Renderer/DownscaleBlur.h>
+#include <AnKi/Core/ConfigSet.h>
+
+namespace anki {
+
+VrsSriGeneration::VrsSriGeneration(Renderer* r)
+	: RendererObject(r)
+{
+	registerDebugRenderTarget("VRS");
+}
+
+VrsSriGeneration::~VrsSriGeneration()
+{
+}
+
+Error VrsSriGeneration::init()
+{
+	const Error err = initInternal();
+	if(err)
+	{
+		ANKI_R_LOGE("Failed to initialize VRS SRI generation");
+	}
+	return err;
+}
+
+Error VrsSriGeneration::initInternal()
+{
+	const UVec2 rez = (m_r->getInternalResolution() + m_sriTexelDimension - 1) / m_sriTexelDimension;
+
+	ANKI_R_LOGV("Intializing VRS SRI generation. SRI resolution %ux%u", rez.x(), rez.y());
+
+	// Bake descriptors
+	m_rtDescr = m_r->create2DRenderTargetDescription(rez.x(), rez.y(), Format::R8_UINT, "VRS SRI");
+	m_rtDescr.bake();
+
+	m_fbDescr.m_colorAttachmentCount = 1;
+	m_fbDescr.bake();
+
+	// Load programs
+	ANKI_CHECK(getResourceManager().loadResource("AnKi/Shaders/VrsSriGenerationCompute.ankiprog", m_prog));
+	ShaderProgramResourceVariantInitInfo variantInit(m_prog);
+	// SRI_TEXEL_DIMENSION is half because the input image is quarter resolution
+	variantInit.addMutation("SRI_TEXEL_DIMENSION", m_sriTexelDimension / 2u);
+	const ShaderProgramResourceVariant* variant;
+	m_prog->getOrCreateVariant(variantInit, variant);
+	m_grProg = variant->getProgram();
+
+	ANKI_CHECK(getResourceManager().loadResource("AnKi/Shaders/VrsSriVisualizeRenderTarget.ankiprog", m_visualizeProg));
+	m_visualizeProg->getOrCreateVariant(variant);
+	m_visualizeGrProg = variant->getProgram();
+
+	return Error::NONE;
+}
+
+void VrsSriGeneration::getDebugRenderTarget(CString rtName, RenderTargetHandle& handle,
+											ShaderProgramPtr& optionalShaderProgram) const
+{
+	ANKI_ASSERT(rtName == "VRS");
+	handle = m_runCtx.m_rt;
+	optionalShaderProgram = m_visualizeGrProg;
+}
+
+void VrsSriGeneration::populateRenderGraph(RenderingContext& ctx)
+{
+	const Bool enableVrs = getGrManager().getDeviceCapabilities().m_vrs && getConfig().getRVrs();
+	if(!enableVrs)
+	{
+		return;
+	}
+
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
+
+	ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("VRS SRI generation");
+
+	pass.newDependency(RenderPassDependency(m_runCtx.m_rt, TextureUsageBit::IMAGE_COMPUTE_WRITE));
+	pass.newDependency(RenderPassDependency(m_r->getDownscaleBlur().getRt(), TextureUsageBit::SAMPLED_COMPUTE));
+
+	pass.setWork([this](RenderPassWorkContext& rgraphCtx) {
+		CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+		cmdb->bindShaderProgram(m_grProg);
+
+		rgraphCtx.bindColorTexture(0, 0, m_r->getDownscaleBlur().getRt());
+		rgraphCtx.bindImage(0, 1, m_runCtx.m_rt);
+
+		const U32 workgroupSize = m_sriTexelDimension / 2u;
+		dispatchPPCompute(cmdb, workgroupSize, workgroupSize, m_r->getInternalResolution().x() / 2,
+						  m_r->getInternalResolution().y() / 2);
+	});
+}
+
+} // end namespace anki

+ 62 - 0
AnKi/Renderer/VrsSriGeneration.h

@@ -0,0 +1,62 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Renderer/RendererObject.h>
+
+namespace anki {
+
+/// @addtogroup renderer
+/// @{
+
+/// Computes the shading rate image to be used by a number of passes.
+class VrsSriGeneration : public RendererObject
+{
+public:
+	VrsSriGeneration(Renderer* r);
+	~VrsSriGeneration();
+
+	ANKI_USE_RESULT Error init();
+
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
+
+	RenderTargetHandle getSriRt() const
+	{
+		return m_runCtx.m_rt;
+	}
+
+	U32 getSriTexelDimension() const
+	{
+		return m_sriTexelDimension;
+	}
+
+public:
+	ShaderProgramResourcePtr m_prog;
+	ShaderProgramPtr m_grProg;
+
+	ShaderProgramResourcePtr m_visualizeProg;
+	ShaderProgramPtr m_visualizeGrProg;
+
+	RenderTargetDescription m_rtDescr;
+	FramebufferDescription m_fbDescr;
+
+	static constexpr U32 m_sriTexelDimension = 16;
+
+	class
+	{
+	public:
+		RenderTargetHandle m_rt;
+	} m_runCtx;
+
+	ANKI_USE_RESULT Error initInternal();
+
+	void getDebugRenderTarget(CString rtName, RenderTargetHandle& handle,
+							  ShaderProgramPtr& optionalShaderProgram) const override;
+};
+/// @}
+
+} // end namespace anki

+ 16 - 0
AnKi/Shaders/Functions.glsl

@@ -604,3 +604,19 @@ I32 findLSB2(U32 v)
 {
 	return findLSB(v);
 }
+
+/// Encode the shading rate to be stored in an SRI. The rates should be power of two, can't be zero and can't exceed 4.
+/// So the possible values are 1,2,4
+U32 encodeVrsRate(UVec2 rateXY)
+{
+	return (rateXY.y >> 1u) | ((rateXY.x << 1u) & 12u);
+}
+
+/// Decodes a number produced by encodeVrsRate(). Returns the shading rates.
+UVec2 decodeVrsRate(U32 texel)
+{
+	UVec2 rateXY;
+	rateXY.x = 1u << ((texel >> 2u) & 3u);
+	rateXY.y = 1u << (texel & 3u);
+	return rateXY;
+}

+ 60 - 0
AnKi/Shaders/VrsSriGeneration.glsl

@@ -0,0 +1,60 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma anki mutator SRI_TEXEL_DIMENSION 8 16 32
+
+#include <AnKi/Shaders/Functions.glsl>
+#include <AnKi/Shaders/TonemappingFunctions.glsl>
+
+layout(set = 0, binding = 0) uniform ANKI_RP texture2D u_inputTex;
+
+#if defined(ANKI_COMPUTE_SHADER)
+const UVec2 WORKGROUP_SIZE = UVec2(SRI_TEXEL_DIMENSION, SRI_TEXEL_DIMENSION);
+layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = 1) in;
+
+layout(set = 0, binding = 1) uniform writeonly uimage2D u_sriImg;
+#else
+layout(location = 0) out U32 out_shadingRate;
+#endif
+
+shared Vec2 s_lumas[WORKGROUP_SIZE.y * WORKGROUP_SIZE.x];
+
+void main()
+{
+	// Get luminance
+	const Vec3 color = invertibleTonemap(texelFetch(u_inputTex, IVec2(gl_GlobalInvocationID.xy), 0).xyz);
+	const F32 luma = computeLuminance(color);
+	const F32 lumaSquared = luma * luma;
+
+	// Store luminance
+	s_lumas[gl_LocalInvocationIndex] = Vec2(luma, lumaSquared);
+	memoryBarrierShared();
+	barrier();
+
+	// Gather the results into one
+	ANKI_LOOP for(U32 s = (WORKGROUP_SIZE.x * WORKGROUP_SIZE.y) / 2u; s > 0u; s >>= 1u)
+	{
+		if(gl_LocalInvocationIndex < s)
+		{
+			s_lumas[gl_LocalInvocationIndex] += s_lumas[gl_LocalInvocationIndex + s];
+		}
+
+		memoryBarrierShared();
+		barrier();
+	}
+
+	// Write the result
+	ANKI_BRANCH if(gl_LocalInvocationIndex == 0u)
+	{
+		const F32 variance = abs(s_lumas[0].y - s_lumas[0].x * s_lumas[0].x);
+		const F32 maxVariance = 100.0;
+
+		const F32 factor = 1.0 - min(1.0, variance / maxVariance);
+		const U32 rate = 1u << U32(factor * 2.0);
+
+		const UVec2 inputTexelCoord = gl_WorkGroupID.xy;
+		imageStore(u_sriImg, IVec2(inputTexelCoord), UVec4(encodeVrsRate(UVec2(rate))));
+	}
+}

+ 8 - 0
AnKi/Shaders/VrsSriGenerationCompute.ankiprog

@@ -0,0 +1,8 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma anki start comp
+#include <AnKi/Shaders/VrsSriGeneration.glsl>
+#pragma anki end

+ 41 - 0
AnKi/Shaders/VrsSriVisualizeRenderTarget.ankiprog

@@ -0,0 +1,41 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma anki start vert
+#include <AnKi/Shaders/QuadVert.glsl>
+#pragma anki end
+
+#pragma anki start frag
+#include <AnKi/Shaders/Functions.glsl>
+
+layout(set = 0, binding = 0) uniform utexture2D u_inTex;
+layout(set = 0, binding = 1) uniform sampler u_nearestAnyClampSampler;
+
+layout(location = 0) in Vec2 in_uv;
+layout(location = 0) out Vec3 out_color;
+
+void main()
+{
+	const U32 texel = textureLod(u_inTex, u_nearestAnyClampSampler, in_uv, 0.0).x;
+	const UVec2 rate = decodeVrsRate(texel);
+
+	if(rate == UVec2(1u))
+	{
+		out_color = Vec3(0.0, 0.0, 1.0);
+	}
+	else if(rate == UVec2(2u))
+	{
+		out_color = Vec3(0.0, 1.0, 0.0);
+	}
+	else if(rate == UVec2(4u))
+	{
+		out_color = Vec3(1.0, 0.0, 0.0);
+	}
+	else
+	{
+		out_color = Vec3(0.0, 0.0, 0.0);
+	}
+}
+#pragma anki end

+ 1 - 2
Samples/Common/Framework.cpp

@@ -90,8 +90,7 @@ Error SampleApp::userMainLoop(Bool& quit, Second elapsedTime)
 
 	if(in.getKey(KeyCode::P) == 1)
 	{
-		renderer.setCurrentDebugRenderTarget(
-			(renderer.getCurrentDebugRenderTarget() == "GBuffer_normals") ? "" : "GBuffer_normals");
+		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "VRS") ? "" : "VRS");
 	}
 
 	if(in.getKey(KeyCode::L) == 1)