mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-03-17 08:00:49 +00:00
313 lines
11 KiB
C++
313 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
|
* Copyright (C) 2022-2023 Robert Beckebans (id Tech 4x integration)
|
|
*
|
|
* 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,
|
|
CommonRenderPasses* _commonPasses,
|
|
const viewDef_t* viewDef,
|
|
const CreateParameters& params )
|
|
{
|
|
m_CommonPasses = _commonPasses;
|
|
|
|
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 );
|
|
|
|
switch( r_antiAliasing.GetInteger() )
|
|
{
|
|
#if ID_MSAA
|
|
case ANTI_ALIASING_MSAA_2X:
|
|
{
|
|
auto taaResolveShaderInfo = renderProgManager.GetProgramInfo( BUILTIN_TAA_RESOLVE_MSAA_2X );
|
|
m_TemporalAntiAliasingCS = taaResolveShaderInfo.cs;
|
|
break;
|
|
}
|
|
|
|
case ANTI_ALIASING_MSAA_4X:
|
|
{
|
|
auto taaResolveShaderInfo = renderProgManager.GetProgramInfo( BUILTIN_TAA_RESOLVE_MSAA_4X );
|
|
m_TemporalAntiAliasingCS = taaResolveShaderInfo.cs;
|
|
break;
|
|
}
|
|
#elif defined( _MSC_VER ) // SRS: #pragma warning is MSVC specific
|
|
#pragma warning( push )
|
|
#pragma warning( disable : 4065 ) // C4065: switch statement contains 'default' but no 'case'
|
|
#endif
|
|
|
|
default:
|
|
{
|
|
auto taaResolveShaderInfo = renderProgManager.GetProgramInfo( BUILTIN_TAA_RESOLVE );
|
|
m_TemporalAntiAliasingCS = taaResolveShaderInfo.cs;
|
|
break;
|
|
}
|
|
}
|
|
#if !ID_MSAA && defined( _MSC_VER )
|
|
#pragma warning( pop )
|
|
#endif
|
|
|
|
nvrhi::SamplerDesc samplerDesc;
|
|
samplerDesc.addressU = samplerDesc.addressV = samplerDesc.addressW = nvrhi::SamplerAddressMode::Border;
|
|
samplerDesc.borderColor = nvrhi::Color( 0.0f );
|
|
m_BilinearSampler = device->createSampler( samplerDesc );
|
|
|
|
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 );
|
|
}
|
|
|
|
{
|
|
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 );
|
|
}
|
|
}
|
|
|
|
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 );
|
|
// RB: TODO figure out why 1 pixel is missing and the old code for resolving _currentImage adds 1 pixel to each side
|
|
taaConstants.inputViewSize = idVec2( viewportInput.width() + 1, viewportInput.height() + 1 );
|
|
taaConstants.outputViewOrigin = idVec2( viewportOutput.minX, viewportOutput.minY );
|
|
taaConstants.outputViewSize = idVec2( viewportOutput.width() + 1, viewportOutput.height() + 1 );
|
|
taaConstants.inputPixelOffset = GetCurrentPixelOffset();
|
|
taaConstants.outputTextureSizeInv = idVec2( 1.0f, 1.0f ) / idVec2( float( renderSystem->GetWidth() ), float( renderSystem->GetHeight() ) );
|
|
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( int )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( int )TemporalAntiAliasingJitter::Halton:
|
|
{
|
|
uint32_t index = ( m_FrameIndex % 16 ) + 1;
|
|
return idVec2{ VanDerCorput( 2, index ), VanDerCorput( 3, index ) } - idVec2( 0.5f, 0.5f );
|
|
}
|
|
case( int )TemporalAntiAliasingJitter::R2:
|
|
{
|
|
return m_R2Jitter - idVec2( 0.5f, 0.5f );
|
|
}
|
|
case( int )TemporalAntiAliasingJitter::WhiteNoise:
|
|
{
|
|
std::mt19937 rng( m_FrameIndex );
|
|
std::uniform_real_distribution<float> dist( -0.5f, 0.5f );
|
|
return idVec2{ dist( rng ), dist( rng ) };
|
|
}
|
|
case( int )TemporalAntiAliasingJitter::None:
|
|
{
|
|
return idVec2( 0, 0 );
|
|
}
|
|
}
|
|
}
|