Copied TAA from Donut framework

This commit is contained in:
Robert Beckebans 2022-04-12 18:15:48 +02:00
parent 024ad532e2
commit 1279a27a43
24 changed files with 1163 additions and 36 deletions

View file

@ -78,7 +78,7 @@ option(USE_DX12
"Use DirectX 12" ON)
option(USE_NVRHI_VULKAN
"Use Vulkan" ON)
"Use Vulkan" OFF)
set(CPU_TYPE "" CACHE STRING "When set, passes this string as CPU-ID which will be embedded into the binary.")

View file

@ -70,6 +70,7 @@ public:
float operator*( const idVec2& a ) const;
idVec2 operator*( const float a ) const;
idVec2 operator/( const float a ) const;
idVec2 operator/( const idVec2& a ) const;
idVec2 operator+( const idVec2& a ) const;
idVec2 operator-( const idVec2& a ) const;
idVec2& operator+=( const idVec2& a );
@ -286,6 +287,11 @@ ID_INLINE idVec2 idVec2::operator/( const float a ) const
return idVec2( x * inva, y * inva );
}
ID_INLINE idVec2 idVec2::operator/( const idVec2& a ) const
{
return idVec2( x / a.x, y / a.y );
}
ID_INLINE idVec2 operator*( const float a, const idVec2 b )
{
return idVec2( b.x * a, b.y * a );

View file

@ -2,7 +2,7 @@
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 2014-2016 Robert Beckebans
Copyright (C) 2014-2022 Robert Beckebans
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -144,15 +144,13 @@ struct globalFramebuffers_t
Framebuffer* hdrFBO;
Framebuffer* ldrFBO;
Framebuffer* postProcFBO;
#if defined(USE_HDR_MSAA)
Framebuffer* hdrNonMSAAFBO;
#endif
// Framebuffer* hdrQuarterFBO;
Framebuffer* hdr64FBO;
Framebuffer* taaMotionVectorsFBO;
Framebuffer* taaResolvedFBO;
Framebuffer* hdr64FBO; // TODO remove, not needed with new NVRHI tonemapping anymore
Framebuffer* envprobeFBO;
Framebuffer* bloomRenderFBO[MAX_BLOOM_BUFFERS];
Framebuffer* glowFBO[MAX_GLOW_BUFFERS];
Framebuffer* transparencyFBO;
Framebuffer* glowFBO[MAX_GLOW_BUFFERS]; // unused
Framebuffer* transparencyFBO; // unused
Framebuffer* ambientOcclusionFBO[MAX_SSAO_BUFFERS];
Framebuffer* csDepthFBO[MAX_HIERARCHICAL_ZBUFFERS];
Framebuffer* geometryBufferFBO;

View file

@ -487,6 +487,14 @@ void R_SetupProjectionMatrix( viewDef_t* viewDef )
jittery = 0.0f;
}
if( r_useTemporalAA.GetBool() )
{
idVec2 jitter = tr.backend.GetCurrentPixelOffset();
jitterx = jitter.x;
jittery = jitter.y;
}
//
// set up projection matrix
//
@ -504,23 +512,32 @@ void R_SetupProjectionMatrix( viewDef_t* viewDef )
const int viewWidth = viewDef->viewport.x2 - viewDef->viewport.x1 + 1;
const int viewHeight = viewDef->viewport.y2 - viewDef->viewport.y1 + 1;
#if 1
jitterx = jitterx * width / viewWidth;
jitterx += r_centerX.GetFloat();
jitterx += viewDef->renderView.stereoScreenSeparation;
xmin += jitterx * width;
xmax += jitterx * width;
const float xoffset = ( xmax + xmin ) / width; // 0 without jitter
jittery = jittery * height / viewHeight;
jittery += r_centerY.GetFloat();
ymin += jittery * height;
ymax += jittery * height;
const float yoffset = ( ymax + ymin ) / height;
#else
// better for TAA: https://alextardif.com/TAA.html
const float xoffset = 1.0f * jitterx / viewWidth;
const float yoffset = 1.0f * jittery / viewHeight;
#endif
// RB: IMPORTANT - the projectionMatrix has a few changes to make it work with Vulkan
// for a detailed explanation see https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/
viewDef->projectionMatrix[0 * 4 + 0] = 2.0f * zNear / width;
viewDef->projectionMatrix[1 * 4 + 0] = 0.0f;
viewDef->projectionMatrix[2 * 4 + 0] = ( xmax + xmin ) / width; // normally 0
viewDef->projectionMatrix[2 * 4 + 0] = xoffset;
viewDef->projectionMatrix[3 * 4 + 0] = 0.0f;
viewDef->projectionMatrix[0 * 4 + 1] = 0.0f;
@ -531,7 +548,7 @@ void R_SetupProjectionMatrix( viewDef_t* viewDef )
#else
viewDef->projectionMatrix[1 * 4 + 1] = 2.0f * zNear / height;
#endif
viewDef->projectionMatrix[2 * 4 + 1] = ( ymax + ymin ) / height; // normally 0
viewDef->projectionMatrix[2 * 4 + 1] = yoffset;
viewDef->projectionMatrix[3 * 4 + 1] = 0.0f;
// this is the far-plane-at-infinity formulation, and
@ -690,6 +707,7 @@ void R_ObliqueProjection( viewDef_t* parms )
idPlane pB = parms->clipPlanes[0];
idPlane cp; // camera space plane
R_MatrixTranspose( parms->worldSpace.modelViewMatrix, mvt );
// transform plane (which is set to the surface we're mirroring about's plane) to camera space
R_GlobalPlaneToLocal( mvt, pB, cp );

View file

@ -97,7 +97,7 @@ enum textureFormat_t
FMT_Y16_X16, // 32 bpp
FMT_RGB565, // 16 bpp
// RB: don't change above for legacy .bimage compatibility
// RB: don't change above for .bimage compatibility up until RBDOOM-3-BFG 1.1
FMT_ETC1_RGB8_OES, // 4 bpp
FMT_SHADOW_ARRAY, // 32 bpp * 6
FMT_RG16F, // 32 bpp
@ -105,9 +105,11 @@ enum textureFormat_t
FMT_RGBA32F, // 128 bpp
FMT_R32F, // 32 bpp
FMT_R11G11B10F, // 32 bpp
// ^-- used up until RBDOOM-3-BFG 1.3
FMT_R8,
FMT_DEPTH_STENCIL, // 32 bpp
// RB end
FMT_RGBA16S, // 64 bpp
FMT_SRGB8,
};
@ -165,6 +167,7 @@ public:
bool gammaMips; // if true, mips will be generated with gamma correction
bool readback; // 360 specific - cpu reads back from this texture, so allocate with cached memory
bool isRenderTarget;
bool isUAV;
};
/*
@ -184,6 +187,7 @@ ID_INLINE idImageOpts::idImageOpts()
gammaMips = false;
readback = false;
isRenderTarget = false;
isUAV = false;
}
/*
@ -233,6 +237,7 @@ typedef enum
TD_SHADOW_ARRAY, // 2D depth buffer array for shadow mapping
TD_RG16F,
TD_RGBA16F,
TD_RGBA16S,
TD_RGBA32F,
TD_R32F,
TD_R11G11B10F, // memory efficient HDR RGB format with only 32bpp
@ -456,6 +461,7 @@ public:
textureUsage_t usage,
nvrhi::ICommandList* commandList,
bool isRenderTarget = false,
bool isUAV = false,
textureSamples_t samples = SAMPLE_1,
cubeFiles_t cubeFiles = CF_2D );
@ -675,12 +681,13 @@ public:
idImage* randomImage256;
idImage* blueNoiseImage256;
idImage* currentRenderHDRImage;
#if defined(USE_HDR_MSAA)
idImage* currentRenderHDRImageNoMSAA;
#endif
idImage* currentRenderHDRImageQuarter;
idImage* currentRenderHDRImage64;
idImage* currentRenderLDR;
idImage* taaMotionVectorsImage; // motion vectors for TAA projection
idImage* taaResolvedImage;
idImage* taaFeedback1Image;
idImage* taaFeedback2Image;
idImage* bloomRenderImage[2];
idImage* glowImage[2]; // contains any glowable surface information.
idImage* glowDepthImage[2];

View file

@ -242,7 +242,17 @@ static void R_DepthImage( idImage* image, nvrhi::ICommandList* commandList )
// RB begin
static void R_HDR_RGBA16FImage_ResNative( idImage* image, nvrhi::ICommandList* commandList )
{
image->GenerateImage( NULL, renderSystem->GetWidth(), renderSystem->GetHeight(), TF_NEAREST, TR_CLAMP, TD_RGBA16F, nullptr, true );//, msaaSamples );
image->GenerateImage( NULL, renderSystem->GetWidth(), renderSystem->GetHeight(), TF_NEAREST, TR_CLAMP, TD_RGBA16F, nullptr, true );
}
static void R_HDR_RGBA16FImage_ResNative_UAV( idImage* image, nvrhi::ICommandList* commandList )
{
image->GenerateImage( NULL, renderSystem->GetWidth(), renderSystem->GetHeight(), TF_NEAREST, TR_CLAMP, TD_RGBA16F, nullptr, true, true );
}
static void R_HDR_RGBA16SImage_ResNative_UAV( idImage* image, nvrhi::ICommandList* commandList )
{
image->GenerateImage( NULL, renderSystem->GetWidth(), renderSystem->GetHeight(), TF_NEAREST, TR_CLAMP, TD_RGBA16S, nullptr, true, true );
}
static void R_HDR_RGBA16FImage_ResGui( idImage* image, nvrhi::ICommandList* commandList )
@ -979,12 +989,12 @@ static void R_CreateBrdfLutImage( idImage* image, nvrhi::ICommandList* commandLi
static void R_CreateEnvprobeImage_UAC_lobby_irradiance( idImage* image, nvrhi::ICommandList* commandList )
{
image->GenerateImage( ( byte* )IMAGE_ENV_UAC_LOBBY_AMB_H_Bytes, IMAGE_ENV_UAC_LOBBY_AMB_H_TEX_WIDTH, IMAGE_ENV_UAC_LOBBY_AMB_H_TEX_HEIGHT, TF_DEFAULT, TR_CLAMP, TD_R11G11B10F, commandList, false, SAMPLE_1, CF_2D_PACKED_MIPCHAIN );
image->GenerateImage( ( byte* )IMAGE_ENV_UAC_LOBBY_AMB_H_Bytes, IMAGE_ENV_UAC_LOBBY_AMB_H_TEX_WIDTH, IMAGE_ENV_UAC_LOBBY_AMB_H_TEX_HEIGHT, TF_DEFAULT, TR_CLAMP, TD_R11G11B10F, commandList, false, false, SAMPLE_1, CF_2D_PACKED_MIPCHAIN );
}
static void R_CreateEnvprobeImage_UAC_lobby_radiance( idImage* image, nvrhi::ICommandList* commandList )
{
image->GenerateImage( ( byte* )IMAGE_ENV_UAC_LOBBY_SPEC_H_Bytes, IMAGE_ENV_UAC_LOBBY_SPEC_H_TEX_WIDTH, IMAGE_ENV_UAC_LOBBY_SPEC_H_TEX_HEIGHT, TF_DEFAULT, TR_CLAMP, TD_R11G11B10F, commandList, false, SAMPLE_1, CF_2D_PACKED_MIPCHAIN );
image->GenerateImage( ( byte* )IMAGE_ENV_UAC_LOBBY_SPEC_H_Bytes, IMAGE_ENV_UAC_LOBBY_SPEC_H_TEX_WIDTH, IMAGE_ENV_UAC_LOBBY_SPEC_H_TEX_HEIGHT, TF_DEFAULT, TR_CLAMP, TD_R11G11B10F, commandList, false, false, SAMPLE_1, CF_2D_PACKED_MIPCHAIN );
}
// RB end
@ -1032,6 +1042,11 @@ void idImageManager::CreateIntrinsicImages()
currentRenderHDRImage64 = globalImages->ImageFromFunction( "_currentRenderHDR64", R_HDR_RGBA16FImage_Res64 );
currentRenderLDR = globalImages->ImageFromFunction( "_currentRenderLDR", R_LdrNativeImage );
taaMotionVectorsImage = ImageFromFunction( "_motionVectors", R_HDR_RGBA16FImage_ResNative ); // RB: could be shared with _currentNormals.zw
taaResolvedImage = ImageFromFunction( "_taaResolved", R_HDR_RGBA16FImage_ResNative_UAV );
taaFeedback1Image = ImageFromFunction( "_taaFeedback1", R_HDR_RGBA16SImage_ResNative_UAV );
taaFeedback2Image = ImageFromFunction( "_taaFeedback2", R_HDR_RGBA16SImage_ResNative_UAV );
envprobeHDRImage = globalImages->ImageFromFunction( "_envprobeHDR", R_EnvprobeImage_HDR );
envprobeDepthImage = ImageFromFunction( "_envprobeDepth", R_EnvprobeImage_Depth );

View file

@ -74,6 +74,8 @@ int BitsForFormat( textureFormat_t format )
return 32;
case FMT_RGBA16F:
return 64;
case FMT_RGBA16S:
return 64;
case FMT_RGBA32F:
return 128;
case FMT_R32F:
@ -187,6 +189,10 @@ ID_INLINE void idImage::DeriveOpts()
opts.format = FMT_RGBA16F;
break;
case TD_RGBA16S:
opts.format = FMT_RGBA16S;
break;
case TD_RGBA32F:
opts.format = FMT_RGBA32F;
break;
@ -959,7 +965,7 @@ void idImage::Reload( bool force, nvrhi::ICommandList* commandList )
GenerateImage
================
*/
void idImage::GenerateImage( const byte* pic, int width, int height, textureFilter_t filterParm, textureRepeat_t repeatParm, textureUsage_t usageParm, nvrhi::ICommandList* commandList, bool isRenderTarget, textureSamples_t samples, cubeFiles_t _cubeFiles )
void idImage::GenerateImage( const byte* pic, int width, int height, textureFilter_t filterParm, textureRepeat_t repeatParm, textureUsage_t usageParm, nvrhi::ICommandList* commandList, bool isRenderTarget, bool isUAV, textureSamples_t samples, cubeFiles_t _cubeFiles )
{
PurgeImage();
@ -974,6 +980,7 @@ void idImage::GenerateImage( const byte* pic, int width, int height, textureFilt
opts.numLevels = 0;
opts.samples = samples;
opts.isRenderTarget = isRenderTarget;
opts.isUAV = isUAV;
// RB
if( cubeFiles == CF_2D_PACKED_MIPCHAIN )

View file

@ -118,6 +118,10 @@ void Framebuffer::ResizeFramebuffers()
}
globalImages->hierarchicalZbufferImage->Reload( false, tr.backend.commandList );
globalImages->currentNormalsImage->Reload( false, tr.backend.commandList );
globalImages->taaMotionVectorsImage->Reload( false, tr.backend.commandList );
globalImages->taaResolvedImage->Reload( false, tr.backend.commandList );
globalImages->taaFeedback1Image->Reload( false, tr.backend.commandList );
globalImages->taaFeedback2Image->Reload( false, tr.backend.commandList );
globalImages->smaaEdgesImage->Reload( false, tr.backend.commandList );
globalImages->smaaBlendImage->Reload( false, tr.backend.commandList );
globalImages->shadowAtlasImage->Reload( false, tr.backend.commandList );
@ -170,6 +174,14 @@ void Framebuffer::ResizeFramebuffers()
nvrhi::FramebufferDesc()
.addColorAttachment( globalImages->currentRenderImage->texture ) );
globalFramebuffers.taaMotionVectorsFBO = new Framebuffer( "_taaMotionVectors",
nvrhi::FramebufferDesc()
.addColorAttachment( globalImages->taaMotionVectorsImage->texture ) );
globalFramebuffers.taaResolvedFBO = new Framebuffer( "_taaResolved",
nvrhi::FramebufferDesc()
.addColorAttachment( globalImages->taaResolvedImage->texture ) );
globalFramebuffers.envprobeFBO = new Framebuffer( "_envprobeRender",
nvrhi::FramebufferDesc()
.addColorAttachment( globalImages->envprobeHDRImage->texture )

View file

@ -300,6 +300,10 @@ void idImage::AllocImage()
format = nvrhi::Format::RGBA16_FLOAT;
break;
case FMT_RGBA16S:
format = nvrhi::Format::RGBA16_SNORM;
break;
case FMT_RGBA32F:
format = nvrhi::Format::RGBA32_FLOAT;
break;
@ -382,6 +386,7 @@ void idImage::AllocImage()
.setWidth( scaledWidth )
.setHeight( scaledHeight )
.setFormat( format )
.setIsUAV( opts.isUAV )
.setSampleCount( opts.samples )
.setMipLevels( opts.numLevels );

View file

@ -170,6 +170,10 @@ void idRenderBackend::Init()
currentBindingSets.SetNum( currentBindingSets.Max() );
pendingBindingSetDescs.SetNum( pendingBindingSetDescs.Max() );
prevMVP[0] = renderMatrix_identity;
prevMVP[1] = renderMatrix_identity;
prevViewsValid = false;
// RB: prepare ImGui system
//ImGui_Init();
}
@ -718,6 +722,9 @@ void idRenderBackend::GL_EndFrame()
commandList->close();
deviceManager->GetDevice()->executeCommandList( commandList );
// update jitter for perspective matrix
taaPass->AdvanceFrame();
}
/*
@ -1062,6 +1069,12 @@ void idRenderBackend::ClearCaches()
delete toneMapPass;
toneMapPass = nullptr;
}
if( taaPass )
{
delete taaPass;
taaPass = nullptr;
}
}
/*

View file

@ -107,8 +107,8 @@ protected:
std::unordered_map<PsoCacheKey, nvrhi::GraphicsPipelineHandle, PsoCacheKey::Hash> m_BlitPsoCache;
public:
nvrhi::ShaderHandle m_FullscreenVS;
nvrhi::ShaderHandle m_FullscreenAtOneVS;
//nvrhi::ShaderHandle m_FullscreenVS;
//nvrhi::ShaderHandle m_FullscreenAtOneVS;
nvrhi::ShaderHandle m_RectVS;
nvrhi::ShaderHandle m_BlitPS;
nvrhi::ShaderHandle m_BlitArrayPS;

View file

@ -0,0 +1,356 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "precompiled.h"
#pragma hdrstop
#include "TemporalAntiAliasingPass.h"
#include "CommonPasses.h"
#include "renderer/RenderCommon.h"
#include <nvrhi/utils.h>
#include <assert.h>
#include <random>
TemporalAntiAliasingPass::TemporalAntiAliasingPass()
: m_CommonPasses( NULL )
, m_FrameIndex( 0 )
, m_StencilMask( 0 )//params.motionVectorStencilMask )
, m_R2Jitter( 0.0f, 0.0f )
{
}
void TemporalAntiAliasingPass::Init(
nvrhi::IDevice* device,
//std::shared_ptr<ShaderFactory> shaderFactory,
CommonRenderPasses* _commonPasses,
const viewDef_t* viewDef,
const CreateParameters& params )
{
m_CommonPasses = _commonPasses;
//const IView* sampleView = compositeView.GetChildView( ViewType::PLANAR, 0 );
const nvrhi::TextureDesc& unresolvedColorDesc = params.unresolvedColor->getDesc();
const nvrhi::TextureDesc& resolvedColorDesc = params.resolvedColor->getDesc();
const nvrhi::TextureDesc& feedback1Desc = params.feedback1->getDesc();
const nvrhi::TextureDesc& feedback2Desc = params.feedback2->getDesc();
assert( feedback1Desc.width == feedback2Desc.width );
assert( feedback1Desc.height == feedback2Desc.height );
assert( feedback1Desc.format == feedback2Desc.format );
assert( feedback1Desc.isUAV );
assert( feedback2Desc.isUAV );
assert( resolvedColorDesc.isUAV );
bool useStencil = false;
nvrhi::Format stencilFormat = nvrhi::Format::UNKNOWN;
if( params.motionVectorStencilMask )
{
useStencil = true;
nvrhi::Format depthFormat = params.sourceDepth->getDesc().format;
if( depthFormat == nvrhi::Format::D24S8 )
{
stencilFormat = nvrhi::Format::X24G8_UINT;
}
else if( depthFormat == nvrhi::Format::D32S8 )
{
stencilFormat = nvrhi::Format::X32G8_UINT;
}
else
{
common->Error( "the format of sourceDepth texture doesn't have a stencil plane" );
}
}
//std::vector<ShaderMacro> MotionVectorMacros;
//MotionVectorMacros.push_back( ShaderMacro( "USE_STENCIL", useStencil ? "1" : "0" ) );
//m_MotionVectorPS = shaderFactory->CreateShader( "donut/passes/motion_vectors_ps.hlsl", "main", &MotionVectorMacros, nvrhi::ShaderType::Pixel );
auto taaMotionVectorsShaderInfo = renderProgManager.GetProgramInfo( BUILTIN_TAA_MOTION_VECTORS );
m_MotionVectorPS = taaMotionVectorsShaderInfo.ps;
//std::vector<ShaderMacro> ResolveMacros;
//ResolveMacros.push_back( ShaderMacro( "SAMPLE_COUNT", std::to_string( unresolvedColorDesc.sampleCount ) ) );
//ResolveMacros.push_back( ShaderMacro( "USE_CATMULL_ROM_FILTER", params.useCatmullRomFilter ? "1" : "0" ) );
//m_TemporalAntiAliasingCS = shaderFactory->CreateShader( "donut/passes/taa_cs.hlsl", "main", &ResolveMacros, nvrhi::ShaderType::Compute );
auto taaResolveShaderInfo = renderProgManager.GetProgramInfo( BUILTIN_TAA_RESOLVE );
m_TemporalAntiAliasingCS = taaResolveShaderInfo.cs;
nvrhi::SamplerDesc samplerDesc;
samplerDesc.addressU = samplerDesc.addressV = samplerDesc.addressW = nvrhi::SamplerAddressMode::Border;
samplerDesc.borderColor = nvrhi::Color( 0.0f );
m_BilinearSampler = device->createSampler( samplerDesc );
m_ResolvedColorSize = idVec2( float( resolvedColorDesc.width ), float( resolvedColorDesc.height ) );
nvrhi::BufferDesc constantBufferDesc;
constantBufferDesc.byteSize = sizeof( TemporalAntiAliasingConstants );
constantBufferDesc.debugName = "TemporalAntiAliasingConstants";
constantBufferDesc.isConstantBuffer = true;
constantBufferDesc.isVolatile = true;
constantBufferDesc.maxVersions = params.numConstantBufferVersions;
m_TemporalAntiAliasingCB = device->createBuffer( constantBufferDesc );
if( params.sourceDepth )
{
nvrhi::BindingLayoutDesc layoutDesc;
layoutDesc.visibility = nvrhi::ShaderType::Pixel;
layoutDesc.bindings =
{
nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 ),
nvrhi::BindingLayoutItem::Texture_SRV( 0 )
};
if( useStencil )
{
layoutDesc.bindings.push_back( nvrhi::BindingLayoutItem::Texture_SRV( 1 ) );
}
m_MotionVectorsBindingLayout = device->createBindingLayout( layoutDesc );
nvrhi::BindingSetDesc bindingSetDesc;
bindingSetDesc.bindings =
{
nvrhi::BindingSetItem::ConstantBuffer( 0, m_TemporalAntiAliasingCB ),
nvrhi::BindingSetItem::Texture_SRV( 0, params.sourceDepth ),
};
if( useStencil )
{
bindingSetDesc.bindings.push_back( nvrhi::BindingSetItem::Texture_SRV( 1, params.sourceDepth, stencilFormat ) );
}
m_MotionVectorsBindingSet = device->createBindingSet( bindingSetDesc, m_MotionVectorsBindingLayout );
//m_MotionVectorsFramebufferFactory = std::make_unique<FramebufferFactory>( device );
//m_MotionVectorsFramebufferFactory->RenderTargets = { params.motionVectors };
nvrhi::GraphicsPipelineDesc pipelineDesc;
pipelineDesc.primType = nvrhi::PrimitiveType::TriangleStrip;
pipelineDesc.VS = taaMotionVectorsShaderInfo.vs; //m_CommonPasses->m_FullscreenVS;
pipelineDesc.PS = taaMotionVectorsShaderInfo.ps; //m_MotionVectorPS;
pipelineDesc.bindingLayouts = { m_MotionVectorsBindingLayout };
pipelineDesc.renderState.rasterState.setCullNone();
pipelineDesc.renderState.depthStencilState.depthTestEnable = false;
pipelineDesc.renderState.depthStencilState.stencilEnable = false;
//nvrhi::IFramebuffer* sampleFramebuffer = m_MotionVectorsFramebufferFactory->GetFramebuffer( *sampleView );
nvrhi::IFramebuffer* sampleFramebuffer = globalFramebuffers.taaMotionVectorsFBO->GetApiObject();
m_MotionVectorsPso = device->createGraphicsPipeline( pipelineDesc, sampleFramebuffer );
}
{
nvrhi::BindingSetDesc bindingSetDesc;
bindingSetDesc.bindings =
{
nvrhi::BindingSetItem::ConstantBuffer( 0, m_TemporalAntiAliasingCB ),
nvrhi::BindingSetItem::Sampler( 0, m_BilinearSampler ),
nvrhi::BindingSetItem::Texture_SRV( 0, params.unresolvedColor ),
nvrhi::BindingSetItem::Texture_SRV( 1, params.motionVectors ),
nvrhi::BindingSetItem::Texture_SRV( 2, params.feedback1 ),
nvrhi::BindingSetItem::Texture_UAV( 0, params.resolvedColor ),
nvrhi::BindingSetItem::Texture_UAV( 1, params.feedback2 )
};
nvrhi::utils::CreateBindingSetAndLayout( device, nvrhi::ShaderType::Compute, 0, bindingSetDesc, m_ResolveBindingLayout, m_ResolveBindingSet );
// Swap resolvedColor and resolvedColorPrevious (t2 and u0)
bindingSetDesc.bindings[4].resourceHandle = params.feedback2;
bindingSetDesc.bindings[6].resourceHandle = params.feedback1;
m_ResolveBindingSetPrevious = device->createBindingSet( bindingSetDesc, m_ResolveBindingLayout );
nvrhi::ComputePipelineDesc pipelineDesc;
pipelineDesc.CS = m_TemporalAntiAliasingCS;
pipelineDesc.bindingLayouts = { m_ResolveBindingLayout };
m_ResolvePso = device->createComputePipeline( pipelineDesc );
AdvanceFrame();
}
}
#if 0
void TemporalAntiAliasingPass::RenderMotionVectors(
nvrhi::ICommandList* commandList,
const ICompositeView& compositeView,
const ICompositeView& compositeViewPrevious,
dm::float3 preViewTranslationDifference )
{
assert( compositeView.GetNumChildViews( ViewType::PLANAR ) == compositeViewPrevious.GetNumChildViews( ViewType::PLANAR ) );
assert( m_MotionVectorsPso );
commandList->beginMarker( "MotionVectors" );
for( uint viewIndex = 0; viewIndex < compositeView.GetNumChildViews( ViewType::PLANAR ); viewIndex++ )
{
const IView* view = compositeView.GetChildView( ViewType::PLANAR, viewIndex );
const IView* viewPrevious = compositeViewPrevious.GetChildView( ViewType::PLANAR, viewIndex );
const nvrhi::ViewportState viewportState = view->GetViewportState();
// This pass only works for planar, single-viewport views
assert( viewportState.viewports.size() == 1 );
const nvrhi::Viewport& inputViewport = viewportState.viewports[0];
TemporalAntiAliasingConstants taaConstants = {};
affine3 viewReprojection = inverse( view->GetViewMatrix() ) * translation( -preViewTranslationDifference ) * viewPrevious->GetViewMatrix();
taaConstants.reprojectionMatrix = inverse( view->GetProjectionMatrix( false ) ) * affineToHomogeneous( viewReprojection ) * viewPrevious->GetProjectionMatrix( false );
taaConstants.inputViewOrigin = float2( inputViewport.minX, inputViewport.minY );
taaConstants.inputViewSize = float2( inputViewport.width(), inputViewport.height() );
taaConstants.stencilMask = m_StencilMask;
commandList->writeBuffer( m_TemporalAntiAliasingCB, &taaConstants, sizeof( taaConstants ) );
nvrhi::GraphicsState state;
state.pipeline = m_MotionVectorsPso;
state.framebuffer = m_MotionVectorsFramebufferFactory->GetFramebuffer( *view );
state.bindings = { m_MotionVectorsBindingSet};
state.viewport = viewportState;
commandList->setGraphicsState( state );
nvrhi::DrawArguments args;
args.instanceCount = 1;
args.vertexCount = 4;
commandList->draw( args );
}
commandList->endMarker();
}
#endif
void TemporalAntiAliasingPass::TemporalResolve(
nvrhi::ICommandList* commandList,
const TemporalAntiAliasingParameters& params,
bool feedbackIsValid,
const viewDef_t* viewDef )
{
nvrhi::Viewport viewportInput{ ( float )viewDef->viewport.x1,
( float )viewDef->viewport.x2,
( float )viewDef->viewport.y1,
( float )viewDef->viewport.y2,
viewDef->viewport.zmin,
viewDef->viewport.zmax };
const nvrhi::Viewport viewportOutput = viewportInput;
TemporalAntiAliasingConstants taaConstants = {};
const float marginSize = 1.f;
taaConstants.inputViewOrigin = idVec2( viewportInput.minX, viewportInput.minY );
taaConstants.inputViewSize = idVec2( viewportInput.width(), viewportInput.height() );
taaConstants.outputViewOrigin = idVec2( viewportOutput.minX, viewportOutput.minY );
taaConstants.outputViewSize = idVec2( viewportOutput.width(), viewportOutput.height() );
taaConstants.inputPixelOffset.Set( 0, 0 ); // TODO = viewInput->GetPixelOffset();
taaConstants.outputTextureSizeInv = 1.0f / m_ResolvedColorSize;
taaConstants.inputOverOutputViewSize = taaConstants.inputViewSize / taaConstants.outputViewSize;
taaConstants.outputOverInputViewSize = taaConstants.outputViewSize / taaConstants.inputViewSize;
taaConstants.clampingFactor = params.enableHistoryClamping ? params.clampingFactor : -1.f;
taaConstants.newFrameWeight = feedbackIsValid ? params.newFrameWeight : 1.f;
taaConstants.pqC = idMath::ClampFloat( 1e-4f, 1e8f, params.maxRadiance );
taaConstants.invPqC = 1.f / taaConstants.pqC;
commandList->writeBuffer( m_TemporalAntiAliasingCB, &taaConstants, sizeof( taaConstants ) );
idVec2i viewportSize = idVec2i( taaConstants.outputViewSize.x, taaConstants.outputViewSize.y );
idVec2i gridSize = ( viewportSize + 15 ) / 16;
nvrhi::ComputeState state;
state.pipeline = m_ResolvePso;
state.bindings = { m_ResolveBindingSet };
commandList->setComputeState( state );
commandList->dispatch( gridSize.x, gridSize.y, 1 );
}
void TemporalAntiAliasingPass::AdvanceFrame()
{
m_FrameIndex++;
std::swap( m_ResolveBindingSet, m_ResolveBindingSetPrevious );
if( TemporalAntiAliasingJitter( r_taaJitter.GetInteger() ) == TemporalAntiAliasingJitter::R2 )
{
// Advance R2 jitter sequence
// http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
static const float g = 1.32471795724474602596f;
static const float a1 = 1.0f / g;
static const float a2 = 1.0f / ( g * g );
m_R2Jitter[0] = fmodf( m_R2Jitter[0] + a1, 1.0f );
m_R2Jitter[1] = fmodf( m_R2Jitter[1] + a2, 1.0f );
}
}
static float VanDerCorput( size_t base, size_t index )
{
float ret = 0.0f;
float denominator = float( base );
while( index > 0 )
{
size_t multiplier = index % base;
ret += float( multiplier ) / denominator;
index = index / base;
denominator *= base;
}
return ret;
}
idVec2 TemporalAntiAliasingPass::GetCurrentPixelOffset()
{
switch( r_taaJitter.GetInteger() )
{
default:
case TemporalAntiAliasingJitter::MSAA:
{
const idVec2 offsets[] =
{
idVec2( 0.0625f, -0.1875f ), idVec2( -0.0625f, 0.1875f ), idVec2( 0.3125f, 0.0625f ), idVec2( -0.1875f, -0.3125f ),
idVec2( -0.3125f, 0.3125f ), idVec2( -0.4375f, 0.0625f ), idVec2( 0.1875f, 0.4375f ), idVec2( 0.4375f, -0.4375f )
};
return offsets[m_FrameIndex % 8];
}
case TemporalAntiAliasingJitter::Halton:
{
uint32_t index = ( m_FrameIndex % 16 ) + 1;
return idVec2{ VanDerCorput( 2, index ), VanDerCorput( 3, index ) } - idVec2( 0.5f, 0.5f );
}
case TemporalAntiAliasingJitter::R2:
{
return m_R2Jitter - idVec2( 0.5f, 0.5f );
}
case TemporalAntiAliasingJitter::WhiteNoise:
{
std::mt19937 rng( m_FrameIndex );
std::uniform_real_distribution<float> dist( -0.5f, 0.5f );
return idVec2{ dist( rng ), dist( rng ) };
}
case TemporalAntiAliasingJitter::None:
{
return idVec2( 0, 0 );
}
}
}

View file

@ -0,0 +1,148 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#pragma once
//#include <donut/core/math/math.h>
#include <nvrhi/nvrhi.h>
#include <memory>
/*
namespace donut::engine
{
class ShaderFactory;
class ShadowMap;
class FramebufferFactory;
class ICompositeView;
}
namespace donut::render
{
*/
class CommonRenderPasses;
enum class TemporalAntiAliasingJitter
{
None,
MSAA,
Halton,
R2,
WhiteNoise
};
struct TemporalAntiAliasingParameters
{
float newFrameWeight = 0.1f;
float clampingFactor = 1.0f;
float maxRadiance = 10000.f;
bool enableHistoryClamping = true;
};
struct TemporalAntiAliasingConstants
{
idRenderMatrix reprojectionMatrix;
idVec2 inputViewOrigin;
idVec2 inputViewSize;
idVec2 outputViewOrigin;
idVec2 outputViewSize;
idVec2 inputPixelOffset;
idVec2 outputTextureSizeInv;
idVec2 inputOverOutputViewSize;
idVec2 outputOverInputViewSize;
float clampingFactor;
float newFrameWeight;
float pqC;
float invPqC;
uint stencilMask;
};
class TemporalAntiAliasingPass
{
private:
CommonRenderPasses* m_CommonPasses;
nvrhi::ShaderHandle m_MotionVectorPS;
nvrhi::ShaderHandle m_TemporalAntiAliasingCS;
nvrhi::SamplerHandle m_BilinearSampler;
nvrhi::BufferHandle m_TemporalAntiAliasingCB;
nvrhi::BindingLayoutHandle m_MotionVectorsBindingLayout;
nvrhi::BindingSetHandle m_MotionVectorsBindingSet;
nvrhi::GraphicsPipelineHandle m_MotionVectorsPso;
//std::unique_ptr<engine::FramebufferFactory> m_MotionVectorsFramebufferFactory;
nvrhi::BindingLayoutHandle m_ResolveBindingLayout;
nvrhi::BindingSetHandle m_ResolveBindingSet;
nvrhi::BindingSetHandle m_ResolveBindingSetPrevious;
nvrhi::ComputePipelineHandle m_ResolvePso;
uint32_t m_FrameIndex;
uint32_t m_StencilMask;
idVec2 m_ResolvedColorSize;
idVec2 m_R2Jitter;
public:
struct CreateParameters
{
nvrhi::ITexture* sourceDepth = nullptr;
nvrhi::ITexture* motionVectors = nullptr;
nvrhi::ITexture* unresolvedColor = nullptr;
nvrhi::ITexture* resolvedColor = nullptr;
nvrhi::ITexture* feedback1 = nullptr;
nvrhi::ITexture* feedback2 = nullptr;
bool useCatmullRomFilter = true;
uint32_t motionVectorStencilMask = 0;
uint32_t numConstantBufferVersions = 16;
};
TemporalAntiAliasingPass();
void Init(
nvrhi::IDevice* device,
//std::shared_ptr<engine::ShaderFactory> shaderFactory,
CommonRenderPasses* commonPasses,
const viewDef_t* viewDef,
const CreateParameters& params );
void RenderMotionVectors(
nvrhi::ICommandList* commandList,
const viewDef_t* viewDef,
const viewDef_t* viewDefPrevious,
idVec3 preViewTranslationDifference = vec3_zero );
void TemporalResolve(
nvrhi::ICommandList* commandList,
const TemporalAntiAliasingParameters& params,
bool feedbackIsValid,
const viewDef_t* viewDef );
void AdvanceFrame();
idVec2 GetCurrentPixelOffset();
};

View file

@ -5437,6 +5437,38 @@ void idRenderBackend::CalculateAutomaticExposure()
//GL_CheckErrors();
}
void idRenderBackend::TemporalAAPass( const viewDef_t* _viewDef )
{
// if we are just doing 2D rendering, no need for HDR TAA
if( viewDef->viewEntitys == NULL )
{
return;
}
if( viewDef->renderView.rdflags & RDF_NOAMBIENT )
{
return;
}
renderLog.OpenBlock( "TemporalAA" );
TemporalAntiAliasingParameters params = {};
taaPass->TemporalResolve( commandList, params, prevViewsValid, _viewDef );
//m_ViewPrevious = m_View;
prevViewsValid = true;
renderLog.CloseBlock();
}
idVec2 idRenderBackend::GetCurrentPixelOffset() const
{
if( taaPass )
{
return taaPass->GetCurrentPixelOffset();
}
return idVec2( 0, 0 );
}
void idRenderBackend::Tonemap( const viewDef_t* _viewDef )
{
@ -6409,9 +6441,24 @@ void idRenderBackend::ExecuteBackEndCommands( const emptyCommand_t* cmds )
if( !toneMapPass )
{
TonemapPass::CreateParameters tonemapParms;
TonemapPass::CreateParameters createParms;
toneMapPass = new TonemapPass();
toneMapPass->Init( deviceManager->GetDevice(), &commonPasses, tonemapParms, globalFramebuffers.ldrFBO->GetApiObject() );
toneMapPass->Init( deviceManager->GetDevice(), &commonPasses, createParms, globalFramebuffers.ldrFBO->GetApiObject() );
}
if( !taaPass )
{
TemporalAntiAliasingPass::CreateParameters taaParams;
taaParams.sourceDepth = globalImages->currentDepthImage->GetTextureHandle();
taaParams.motionVectors = globalImages->taaMotionVectorsImage->GetTextureHandle();
taaParams.unresolvedColor = globalImages->currentRenderHDRImage->GetTextureHandle();
taaParams.resolvedColor = globalImages->taaResolvedImage->GetTextureHandle();
taaParams.feedback1 = globalImages->taaFeedback1Image->GetTextureHandle();
taaParams.feedback2 = globalImages->taaFeedback2Image->GetTextureHandle();
taaParams.motionVectorStencilMask = 0; //0x01;
taaParams.useCatmullRomFilter = true;
taaPass = new TemporalAntiAliasingPass();
taaPass->Init( deviceManager->GetDevice(), &commonPasses, NULL, taaParams );
}
#endif
@ -6642,8 +6689,6 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
//-------------------------------------------------
DrawInteractions( _viewDef );
//GL_EndRenderPass();
//-------------------------------------------------
// capture the depth for the motion blur before rendering any post process surfaces that may contribute to the depth
//-------------------------------------------------
@ -6680,8 +6725,6 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
renderLog.CloseMainBlock();
}
//GL_EndRenderPass();
//-------------------------------------------------
// use direct light and emissive light contributions to add indirect screen space light
//-------------------------------------------------
@ -6703,8 +6746,6 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
int w = viewDef->viewport.x2 - viewDef->viewport.x1 + 1;
int h = viewDef->viewport.y2 - viewDef->viewport.y1 + 1;
//RENDERLOG_PRINTF( "Resolve to %i x %i buffer\n", w, h );
GL_SelectTexture( 0 );
// resolve the screen
@ -6751,21 +6792,30 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
renderLog.CloseMainBlock();
}
//GL_EndRenderPass();
//-------------------------------------------------
// render debug tools
//-------------------------------------------------
DBG_RenderDebugTools( drawSurfs, numDrawSurfs );
#if !defined(USE_VULKAN)
//-------------------------------------------------
// resolve of HDR target using temporal anti aliasing before any tonemapping and post processing
//
// use this to eat all stochastic noise like from volumetric light sampling,
// runs at full resolution
//-------------------------------------------------
TemporalAAPass( _viewDef );
//-------------------------------------------------
// tonemapping: convert back from HDR to LDR range
//-------------------------------------------------
#if !defined( USE_VULKAN )
// SRS - For OSX OpenGL record the final portion of GPU time while no other elapsed time query is active (after final shader pass and before post processing)
#if defined(__APPLE__)
renderLog.OpenMainBlock( MRB_GPU_TIME );
#endif
// RB: convert back from HDR to LDR range
if( useHDR && !( _viewDef->renderView.rdflags & RDF_IRRADIANCE ) && !_viewDef->targetRender )
{
#if !defined( USE_NVRHI )
@ -6784,10 +6834,21 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
Tonemap( _viewDef );
#else
ToneMappingParameters parms;
toneMapPass->SimpleRender( commandList, parms, viewDef, globalImages->currentRenderHDRImage->GetTextureHandle(), globalFramebuffers.ldrFBO->GetApiObject() );
if( r_useTemporalAA.GetBool() )
{
toneMapPass->SimpleRender( commandList, parms, viewDef, globalImages->taaResolvedImage->GetTextureHandle(), globalFramebuffers.ldrFBO->GetApiObject() );
}
else
{
toneMapPass->SimpleRender( commandList, parms, viewDef, globalImages->currentRenderHDRImage->GetTextureHandle(), globalFramebuffers.ldrFBO->GetApiObject() );
}
#endif
}
//-------------------------------------------------
// bloom post processing
//-------------------------------------------------
if( !r_skipBloom.GetBool() )
{
// TODO(Stephen): implement bloom
@ -6802,11 +6863,12 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
#if defined( USE_NVRHI )
//TODO(Stephen): Move somewhere else?
// RB: this needs to be done after next post processing steps later on
{
BlitParameters blitParms;
blitParms.sourceTexture = ( nvrhi::ITexture* )globalImages->currentRenderLDR->GetTextureID();
blitParms.targetFramebuffer = deviceManager->GetCurrentFramebuffer();
blitParms.targetViewport = nvrhi::Viewport( renderSystem->GetWidth(), renderSystem->GetHeight() );;
blitParms.targetViewport = nvrhi::Viewport( renderSystem->GetWidth(), renderSystem->GetHeight() );
commonPasses.BlitTexture( commandList, blitParms, &bindingCache );
}
#endif

View file

@ -40,6 +40,7 @@ If you have questions concerning this license or the applicable additional terms
#include "Passes/FowardShadingPass.h"
#include "Passes/SsaoPass.h"
#include "Passes/TonemapPass.h"
#include "Passes/TemporalAntiAliasingPass.h"
#include "PipelineCache.h"
@ -340,6 +341,8 @@ private:
void StencilShadowPass( const drawSurf_t* drawSurfs, const viewLight_t* vLight );
void StencilSelectLight( const viewLight_t* vLight );
void TemporalAAPass( const viewDef_t* _viewDef );
// RB: HDR stuff
// TODO optimize and replace with compute shader
@ -363,6 +366,8 @@ private:
public:
uint64 GL_GetCurrentState() const;
idVec2 GetCurrentPixelOffset() const;
private:
uint64 GL_GetCurrentStateMinusStencil() const;
void GL_SetDefaultState();
@ -494,6 +499,7 @@ private:
bool currentRenderCopied; // true if any material has already referenced _currentRender
idRenderMatrix prevMVP[2]; // world MVP from previous frame for motion blur
bool prevViewsValid;
// RB begin
// TODO remove
@ -529,6 +535,7 @@ private:
SsaoPass* ssaoPass;
MipMapGenPass* hiZGenPass;
TonemapPass* toneMapPass;
TemporalAntiAliasingPass* taaPass;
BindingCache bindingCache;
SamplerCache samplerCache;

View file

@ -860,9 +860,15 @@ enum bindingLayoutType_t
BINDING_LAYOUT_DRAW_FOG,
BINDING_LAYOUT_POST_PROCESS_CNM,
BINDING_LAYOUT_NORMAL_CUBE,
// NVRHI render passes specific
BINDING_LAYOUT_TAA_MOTION_VECTORS,
BINDING_LAYOUT_TAA_RESOLVE,
BINDING_LAYOUT_TONEMAP,
BINDING_LAYOUT_HISTOGRAM,
BINDING_LAYOUT_EXPOSURE,
NUM_BINDING_LAYOUTS
};
@ -1280,6 +1286,9 @@ extern idCVar r_showLightGrid; // show Quake 3 style light grid points
extern idCVar r_useLightGrid;
extern idCVar r_exposure;
extern idCVar r_useTemporalAA;
extern idCVar r_taaJitter;
// RB end
/*

View file

@ -463,6 +463,9 @@ void idRenderProgManager::Init( nvrhi::IDevice* _device )
{ BUILTIN_SMAA_BLENDING_WEIGHT_CALCULATION, "builtin/post/SMAA_blending_weight_calc", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT },
{ BUILTIN_SMAA_NEIGHBORHOOD_BLENDING, "builtin/post/SMAA_final", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT },
{ BUILTIN_TAA_MOTION_VECTORS, "builtin/post/motion_vectors", "", { { "USE_STENCIL", "0" }, { "QUAD_Z", "0" } }, false, SHADER_STAGE_DEFAULT, LAYOUT_UNKNOWN, BINDING_LAYOUT_TAA_MOTION_VECTORS },
{ BUILTIN_TAA_RESOLVE, "builtin/post/taa", "", { { "SAMPLE_COUNT", "1" }, { "USE_CATMULL_ROM_FILTER", "1" } }, false, SHADER_STAGE_COMPUTE, LAYOUT_UNKNOWN, BINDING_LAYOUT_TAA_RESOLVE },
{ BUILTIN_AMBIENT_OCCLUSION, "builtin/SSAO/AmbientOcclusion_AO", "", { { "BRIGHTPASS", "0" } }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DRAW_AO },
{ BUILTIN_AMBIENT_OCCLUSION_AND_OUTPUT, "builtin/SSAO/AmbientOcclusion_AO", "_write", { { "BRIGHTPASS", "1" } }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DRAW_AO },
{ BUILTIN_AMBIENT_OCCLUSION_BLUR, "builtin/SSAO/AmbientOcclusion_blur", "", { { "BRIGHTPASS", "0" } }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DRAW_AO },

View file

@ -381,6 +381,9 @@ enum
BUILTIN_SMAA_BLENDING_WEIGHT_CALCULATION,
BUILTIN_SMAA_NEIGHBORHOOD_BLENDING,
BUILTIN_TAA_MOTION_VECTORS,
BUILTIN_TAA_RESOLVE,
BUILTIN_AMBIENT_OCCLUSION,
BUILTIN_AMBIENT_OCCLUSION_AND_OUTPUT,
BUILTIN_AMBIENT_OCCLUSION_BLUR,

View file

@ -334,6 +334,9 @@ idCVar r_showLightGrid( "r_showLightGrid", "0", CVAR_RENDERER | CVAR_INTEGER, "s
idCVar r_useLightGrid( "r_useLightGrid", "1", CVAR_RENDERER | CVAR_BOOL, "" );
idCVar r_exposure( "r_exposure", "0.5", CVAR_ARCHIVE | CVAR_RENDERER | CVAR_FLOAT, "HDR exposure or LDR brightness [-4.0 .. 4.0]", -4.0f, 4.0f );
idCVar r_useTemporalAA( "r_useTemporalAA", "1", CVAR_RENDERER | CVAR_BOOL, "only disable for debugging" );
idCVar r_taaJitter( "r_taaJitter", "1", CVAR_RENDERER | CVAR_INTEGER, "0: None, 1: MSAA, 2: Halton, 3: R2 Sequence, 4: White Noise" );
// RB end
const char* fileExten[4] = { "tga", "png", "jpg", "exr" };

View file

@ -54,12 +54,14 @@ void main( PS_IN fragment, out PS_OUT result )
{
// only draw on half the screen for comparison
discard;
return;
}
#endif
// don't motion blur the hands, which were drawn with alpha = 0
if( t_ViewColor.Sample( LinearSampler, fragment.texcoord0 ).w == 0.0 )
{
discard;
return;
}
// derive clip space from the depth buffer and screen position

View file

@ -0,0 +1,102 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#pragma pack_matrix(row_major)
//#include <donut/shaders/taa_cb.h>
struct TemporalAntiAliasingConstants
{
float4x4 reprojectionMatrix;
float2 inputViewOrigin;
float2 inputViewSize;
float2 outputViewOrigin;
float2 outputViewSize;
float2 inputPixelOffset;
float2 outputTextureSizeInv;
float2 inputOverOutputViewSize;
float2 outputOverInputViewSize;
float clampingFactor;
float newFrameWeight;
float pqC;
float invPqC;
uint stencilMask;
};
cbuffer c_TemporalAA :
register( b0 )
{
TemporalAntiAliasingConstants g_TemporalAA;
};
Texture2D<float> t_GBufferDepth :
register( t0 );
#if USE_STENCIL
Texture2D<uint2> t_GBufferStencil :
register( t1 );
#endif
void main(
in float4 i_position : SV_Position,
in float2 i_uv : UV,
out float4 o_color : SV_Target0
)
{
o_color = 0;
#if USE_STENCIL
uint stencil = t_GBufferStencil[i_position.xy].y;
if( ( stencil & g_TemporalAA.stencilMask ) == g_TemporalAA.stencilMask )
{
discard;
}
#endif
float depth = t_GBufferDepth[i_position.xy].x;
float4 clipPos;
clipPos.x = i_uv.x * 2 - 1;
clipPos.y = 1 - i_uv.y * 2;
clipPos.z = depth;
clipPos.w = 1;
float4 prevClipPos = mul( clipPos, g_TemporalAA.reprojectionMatrix );
if( prevClipPos.w <= 0 )
{
return;
}
prevClipPos.xyz /= prevClipPos.w;
float2 prevUV;
prevUV.x = 0.5 + prevClipPos.x * 0.5;
prevUV.y = 0.5 - prevClipPos.y * 0.5;
float2 prevWindowPos = prevUV * g_TemporalAA.inputViewSize + g_TemporalAA.inputViewOrigin;
o_color.xy = prevWindowPos.xy - i_position.xy;
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
void main(
in uint iVertex : SV_VertexID,
out float4 o_posClip : SV_Position,
out float2 o_uv : UV )
{
uint u = iVertex & 1;
uint v = ( iVertex >> 1 ) & 1;
o_posClip = float4( float( u ) * 2 - 1, 1 - float( v ) * 2, QUAD_Z, 1 );
o_uv = float2( u, v );
}

View file

@ -0,0 +1,315 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#pragma pack_matrix(row_major)
//#include <donut/shaders/taa_cb.h>
struct TemporalAntiAliasingConstants
{
float4x4 reprojectionMatrix;
float2 inputViewOrigin;
float2 inputViewSize;
float2 outputViewOrigin;
float2 outputViewSize;
float2 inputPixelOffset;
float2 outputTextureSizeInv;
float2 inputOverOutputViewSize;
float2 outputOverInputViewSize;
float clampingFactor;
float newFrameWeight;
float pqC;
float invPqC;
uint stencilMask;
};
cbuffer c_TemporalAA :
register( b0 )
{
TemporalAntiAliasingConstants g_TemporalAA;
};
#ifndef SAMPLE_COUNT
#define SAMPLE_COUNT 1
#endif
#if SAMPLE_COUNT == 1
Texture2D<float4> t_UnfilteredRT :
register( t0 );
Texture2D<float2> t_MotionVectors :
register( t1 );
#else
Texture2DMS<float4> t_UnfilteredRT :
register( t0 );
Texture2DMS<float2> t_MotionVectors :
register( t1 );
#endif
Texture2D<float4> t_FeedbackInput :
register( t2 );
SamplerState s_Sampler :
register( s0 );
RWTexture2D<float4> u_ColorOutput :
register( u0 );
RWTexture2D<float4> u_FeedbackOutput :
register( u1 );
#define GROUP_X 16
#define GROUP_Y 16
#define BUFFER_X (GROUP_X + 3)
#define BUFFER_Y (GROUP_Y + 3)
#define RENAMED_GROUP_Y ((GROUP_X * GROUP_Y) / BUFFER_X)
groupshared float4 s_ColorsAndLengths[BUFFER_Y][BUFFER_X];
groupshared float2 s_MotionVectors[BUFFER_Y][BUFFER_X];
static const float pq_m1 = 0.1593017578125;
static const float pq_m2 = 78.84375;
static const float pq_c1 = 0.8359375;
static const float pq_c2 = 18.8515625;
static const float pq_c3 = 18.6875;
float3 PQDecode( float3 image )
{
float3 Np = pow( max( image, 0.0 ), 1.0 / pq_m2 );
float3 L = Np - pq_c1;
L = L / ( pq_c2 - pq_c3 * Np );
L = pow( max( L, 0.0 ), 1.0 / pq_m1 );
return L * g_TemporalAA.pqC; // returns cd/m^2
}
float3 PQEncode( float3 image )
{
float3 L = image * g_TemporalAA.invPqC;
float3 Lm = pow( max( L, 0.0 ), pq_m1 );
float3 N = ( pq_c1 + pq_c2 * Lm ) / ( 1.0 + pq_c3 * Lm );
image = pow( N, pq_m2 );
return saturate( image );
}
float3 BicubicSampleCatmullRom( Texture2D tex, SamplerState samp, float2 samplePos, float2 invTextureSize )
{
float2 tc = floor( samplePos - 0.5 ) + 0.5;
float2 f = saturate( samplePos - tc );
float2 f2 = f * f;
float2 f3 = f2 * f;
float2 w0 = f2 - 0.5 * ( f3 + f );
float2 w1 = 1.5 * f3 - 2.5 * f2 + 1;
float2 w3 = 0.5 * ( f3 - f2 );
float2 w2 = 1 - w0 - w1 - w3;
float2 w12 = w1 + w2;
float2 tc0 = ( tc - 1 ) * invTextureSize;
float2 tc12 = ( tc + w2 / w12 ) * invTextureSize;
float2 tc3 = ( tc + 2 ) * invTextureSize;
float3 result =
tex.SampleLevel( samp, float2( tc0.x, tc0.y ), 0 ).rgb * ( w0.x * w0.y ) +
tex.SampleLevel( samp, float2( tc0.x, tc12.y ), 0 ).rgb * ( w0.x * w12.y ) +
tex.SampleLevel( samp, float2( tc0.x, tc3.y ), 0 ).rgb * ( w0.x * w3.y ) +
tex.SampleLevel( samp, float2( tc12.x, tc0.y ), 0 ).rgb * ( w12.x * w0.y ) +
tex.SampleLevel( samp, float2( tc12.x, tc12.y ), 0 ).rgb * ( w12.x * w12.y ) +
tex.SampleLevel( samp, float2( tc12.x, tc3.y ), 0 ).rgb * ( w12.x * w3.y ) +
tex.SampleLevel( samp, float2( tc3.x, tc0.y ), 0 ).rgb * ( w3.x * w0.y ) +
tex.SampleLevel( samp, float2( tc3.x, tc12.y ), 0 ).rgb * ( w3.x * w12.y ) +
tex.SampleLevel( samp, float2( tc3.x, tc3.y ), 0 ).rgb * ( w3.x * w3.y );
return max( 0, result );
}
void Preload( int2 sharedID, int2 globalID )
{
#if SAMPLE_COUNT == 1
float3 color = PQEncode( t_UnfilteredRT[globalID].rgb );
float2 motion = t_MotionVectors[globalID].rg;
float motionLength = dot( motion, motion );
#else
float3 color = 0;
float2 motion = 0;
float motionLength = -1;
// Resolve MSAA color using average filter, motion vectors using max filter
[unroll]
for( int nSample = 0; nSample < SAMPLE_COUNT; nSample++ )
{
float3 sampleColor = PQEncode( t_UnfilteredRT.Load( globalID, nSample ).rgb );
float2 sampleMotion = t_MotionVectors.Load( globalID, nSample ).rg;
float sampleMotionLength = dot( sampleMotion, sampleMotion );
color += sampleColor;
if( sampleMotionLength > motionLength )
{
motion = sampleMotion;
motionLength = sampleMotionLength;
}
}
color /= float( SAMPLE_COUNT );
#endif
s_ColorsAndLengths[sharedID.y][sharedID.x] = float4( color.rgb, motionLength );
s_MotionVectors[sharedID.y][sharedID.x] = motion;
}
float2 OutputToInput( int2 pixelPosRelativeToOrigin )
{
return ( float2( pixelPosRelativeToOrigin ) + 0.5 ) * g_TemporalAA.inputOverOutputViewSize
- 0.5 + g_TemporalAA.inputViewOrigin + g_TemporalAA.inputPixelOffset;
}
[numthreads( GROUP_X, GROUP_Y, 1 )]
void main(
in int2 i_groupIdx : SV_GroupID,
in int2 i_threadIdx : SV_GroupThreadID,
in int2 i_globalIdx : SV_DispatchThreadID
)
{
// Rename the 16x16 group into a 19x13 group + 9 idle threads in the end
int2 newID;
float linearID = i_threadIdx.y * GROUP_X + i_threadIdx.x;
linearID = ( linearID + 0.5 ) / float( BUFFER_X );
newID.y = int( floor( linearID ) );
newID.x = int( floor( frac( linearID ) * BUFFER_X ) );
int2 groupBase = int2( OutputToInput( i_groupIdx * int2( GROUP_X, GROUP_Y ) ) - 1 );
// Preload the colors and motion vectors into shared memory
if( newID.y < RENAMED_GROUP_Y )
{
Preload( newID, groupBase + newID );
}
newID.y += RENAMED_GROUP_Y;
if( newID.y < BUFFER_Y )
{
Preload( newID, groupBase + newID );
}
GroupMemoryBarrierWithGroupSync();
// Calculate the color distribution and find the longest MV in the neighbourhood
int2 outputPixelPosition = i_globalIdx + int2( g_TemporalAA.outputViewOrigin );
float2 inputPos = OutputToInput( i_globalIdx );
int2 inputPosInt = int2( round( inputPos ) );
int2 inputPosShared = inputPosInt - groupBase - 1;
float3 colorMoment1 = 0;
float3 colorMoment2 = 0;
float longestMVLength = -1;
int2 longestMVPos = 0;
float3 thisPixelColor = 0;
[unroll]
for( int dy = 0; dy <= 2; dy++ )
{
[unroll]
for( int dx = 0; dx <= 2; dx++ )
{
int2 pos = inputPosShared + int2( dx, dy );
float4 colorAndLength = s_ColorsAndLengths[pos.y][pos.x];
float3 color = colorAndLength.rgb;
float motionLength = colorAndLength.a;
if( dx == 1 && dy == 1 )
{
thisPixelColor = color;
}
colorMoment1 += color;
colorMoment2 += color * color;
if( motionLength > longestMVLength )
{
longestMVPos = pos;
longestMVLength = motionLength;
}
}
}
float2 longestMV = s_MotionVectors[longestMVPos.y][longestMVPos.x];
colorMoment1 /= 9.0;
colorMoment2 /= 9.0;
float3 colorVariance = colorMoment2 - colorMoment1 * colorMoment1;
float3 colorSigma = sqrt( max( 0, colorVariance ) ) * g_TemporalAA.clampingFactor;
float3 colorMin = colorMoment1 - colorSigma;
float3 colorMax = colorMoment1 + colorSigma;
// Sample the previous frame using the longest MV
longestMV *= g_TemporalAA.outputOverInputViewSize;
float2 sourcePos = float2( outputPixelPosition.xy ) + longestMV + 0.5;
float3 resultPQ;
if( g_TemporalAA.newFrameWeight < 1.0 && all( sourcePos.xy > g_TemporalAA.outputViewOrigin )
&& all( sourcePos.xy < g_TemporalAA.outputViewOrigin + g_TemporalAA.outputViewSize ) )
{
#if USE_CATMULL_ROM_FILTER
float3 history = BicubicSampleCatmullRom( t_FeedbackInput, s_Sampler, sourcePos, g_TemporalAA.outputTextureSizeInv );
#else
float3 history = t_FeedbackInput.SampleLevel( s_Sampler, sourcePos * g_TemporalAA.outputTextureSizeInv, 0 ).rgb;
#endif
// Clamp the old color to the new color distribution
float3 historyClamped = history;
if( g_TemporalAA.clampingFactor >= 0 )
{
historyClamped = min( colorMax, max( colorMin, history ) );
}
// Blend the old color with the new color and store output
float motionWeight = smoothstep( 0, 1, length( longestMV ) );
float2 distanceToLowResPixel = inputPos - float2( inputPosInt );
float upscalingFactor = g_TemporalAA.outputOverInputViewSize.x;
float sampleWeight = saturate( 1.0 - upscalingFactor * dot( distanceToLowResPixel, distanceToLowResPixel ) );
float blendWeight = saturate( max( motionWeight, sampleWeight ) * g_TemporalAA.newFrameWeight );
resultPQ = lerp( historyClamped, thisPixelColor, blendWeight );
}
else
{
resultPQ = thisPixelColor;
}
float3 result = PQDecode( resultPQ );
u_ColorOutput[outputPixelPosition] = float4( result, 1.0 );
u_FeedbackOutput[outputPixelPosition] = float4( resultPQ, 0.0 );
}

View file

@ -74,6 +74,9 @@ builtin/post/exposure.cs.hlsl -T cs_5_0 -D HISTOGRAM_BINS=256
builtin/post/histogram.cs.hlsl -T cs_5_0 -D HISTOGRAM_BINS=256 -D SOURCE_ARRAY={0,1}
builtin/post/tonemapping.ps.hlsl -T ps_5_0 -D HISTOGRAM_BINS=256 -D SOURCE_ARRAY={0,1} -D QUAD_Z={0,1}
builtin/post/tonemapping.vs.hlsl -T vs_5_0 -D HISTOGRAM_BINS=256 -D SOURCE_ARRAY={0,1} -D QUAD_Z={0,1}
builtin/post/motion_vectors.vs.hlsl -T vs_5_0 -D USE_STENCIL=0 -D QUAD_Z={0,1}
builtin/post/motion_vectors.ps.hlsl -T ps_5_0 -D USE_STENCIL=0 -D QUAD_Z={0,1}
builtin/post/taa.cs.hlsl -T cs_5_0 -D SAMPLE_COUNT={1,2,4} -D USE_CATMULL_ROM_FILTER=1
builtin/SSAO/AmbientOcclusion_AO.vs.hlsl -T vs_5_0 -D BRIGHTPASS={0,1}
builtin/SSAO/AmbientOcclusion_AO.ps.hlsl -T ps_5_0 -D BRIGHTPASS={0,1}