0
0
Fork 0
mirror of https://github.com/id-Software/DOOM-3-BFG.git synced 2025-03-16 23:51:28 +00:00
doom3-bfg/neo/renderer/Passes/SsaoPass.cpp

312 lines
13 KiB
C++

/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
* Copyright (C) 2022 Stephen Pridham (id Tech 4x integration)
* Copyright (C) 2023 Stephen Saunders (id Tech 4x integration)
* Copyright (C) 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 "renderer/RenderCommon.h"
#include "SsaoPass.h"
static idCVar r_ssaoBackgroundViewDepth( "r_ssaoBackgroundViewDepth", "100", CVAR_RENDERER | CVAR_FLOAT, "specified in meters" );
static idCVar r_ssaoRadiusWorld( "r_ssaoRadiusWorld", "1.0", CVAR_RENDERER | CVAR_FLOAT, "specified in meters" );
static idCVar r_ssaoSurfaceBias( "r_ssaoSurfaceBias", "0.05", CVAR_RENDERER | CVAR_FLOAT, "specified in meters" );
static idCVar r_ssaoPowerExponent( "r_ssaoPowerExponent", "2", CVAR_RENDERER | CVAR_FLOAT, "" );
static idCVar r_ssaoBlurSharpness( "r_ssaoBlurSharpness", "16", CVAR_RENDERER | CVAR_FLOAT, "" );
static idCVar r_ssaoAmount( "r_ssaoAmount", "4", CVAR_RENDERER | CVAR_FLOAT, "" );
struct SsaoConstants
{
idVec2 viewportOrigin;
idVec2 viewportSize;
idVec2 pixelOffset;
idVec2 unused;
idRenderMatrix matClipToView;
idRenderMatrix matWorldToView; // unused
idRenderMatrix matViewToWorld; // unused
idVec2 clipToView;
idVec2 invQuantizedGbufferSize;
idVec2i quantizedViewportOrigin;
float amount;
float invBackgroundViewDepth;
float radiusWorld;
float surfaceBias;
float radiusToScreen;
float powerExponent;
};
SsaoPass::SsaoPass(
nvrhi::IDevice* device,
const CreateParameters& params,
CommonRenderPasses* commonPasses )
: commonRenderPasses( commonPasses )
, m_Device( device )
{
nvrhi::BufferDesc constantBufferDesc;
constantBufferDesc.byteSize = sizeof( SsaoConstants );
constantBufferDesc.debugName = "SsaoConstants";
constantBufferDesc.isConstantBuffer = true;
constantBufferDesc.isVolatile = true;
constantBufferDesc.maxVersions = c_MaxRenderPassConstantBufferVersions;
m_ConstantBuffer = device->createBuffer( constantBufferDesc );
nvrhi::TextureDesc DeinterleavedTextureDesc;
DeinterleavedTextureDesc.width = ( params.dimensions.x + 3 ) / 4;
DeinterleavedTextureDesc.height = ( params.dimensions.y + 3 ) / 4;
DeinterleavedTextureDesc.arraySize = 16;
DeinterleavedTextureDesc.dimension = nvrhi::TextureDimension::Texture2DArray;
DeinterleavedTextureDesc.isUAV = true;
DeinterleavedTextureDesc.initialState = nvrhi::ResourceStates::ShaderResource;
DeinterleavedTextureDesc.keepInitialState = true;
DeinterleavedTextureDesc.debugName = "SSAO/DeinterleavedDepth";
DeinterleavedTextureDesc.format = nvrhi::Format::R32_FLOAT;
m_DeinterleavedDepth = device->createTexture( DeinterleavedTextureDesc );
m_QuantizedGbufferTextureSize = idVec2( float( DeinterleavedTextureDesc.width ), float( DeinterleavedTextureDesc.height ) ) * 4.f;
DeinterleavedTextureDesc.debugName = "SSAO/DeinterleavedOcclusion";
DeinterleavedTextureDesc.format = params.directionalOcclusion ? nvrhi::Format::RGBA16_FLOAT : nvrhi::Format::R8_UNORM;
m_DeinterleavedOcclusion = device->createTexture( DeinterleavedTextureDesc );
{
idList<shaderMacro_t> macros = { { "LINEAR_DEPTH", params.inputLinearDepth ? "1" : "0" } };
int shaderIdx = renderProgManager.FindShader( "builtin/SSAO/ssao_deinterleave", SHADER_STAGE_COMPUTE, "", macros, true, LAYOUT_DRAW_VERT );
m_Deinterleave.Shader = renderProgManager.GetShader( shaderIdx );
nvrhi::BindingLayoutDesc DeinterleaveBindings;
DeinterleaveBindings.visibility = nvrhi::ShaderType::Compute;
DeinterleaveBindings.bindings =
{
nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 ),
nvrhi::BindingLayoutItem::VolatileConstantBuffer( 1 ),
nvrhi::BindingLayoutItem::Texture_SRV( 0 ),
nvrhi::BindingLayoutItem::Texture_UAV( 0 ),
};
m_Deinterleave.BindingLayout = m_Device->createBindingLayout( DeinterleaveBindings );
nvrhi::ComputePipelineDesc DeinterleavePipelineDesc;
DeinterleavePipelineDesc.bindingLayouts = { m_Deinterleave.BindingLayout };
DeinterleavePipelineDesc.CS = m_Deinterleave.Shader;
m_Deinterleave.Pipeline = device->createComputePipeline( DeinterleavePipelineDesc );
m_Deinterleave.BindingSets.resize( params.numBindingSets );
}
{
idList<shaderMacro_t> macros =
{
{ "OCT_ENCODED_NORMALS", params.octEncodedNormals ? "1" : "0" },
{ "DIRECTIONAL_OCCLUSION", params.directionalOcclusion ? "1" : "0" }
};
int shaderIdx = renderProgManager.FindShader( "builtin/SSAO/ssao_compute", SHADER_STAGE_COMPUTE, "", macros, true, LAYOUT_DRAW_VERT );
m_Compute.Shader = renderProgManager.GetShader( shaderIdx );
nvrhi::BindingLayoutDesc ComputeBindings;
ComputeBindings.visibility = nvrhi::ShaderType::Compute;
ComputeBindings.bindings =
{
nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 ),
nvrhi::BindingLayoutItem::VolatileConstantBuffer( 1 ),
nvrhi::BindingLayoutItem::Texture_SRV( 0 ),
nvrhi::BindingLayoutItem::Texture_SRV( 1 ),
nvrhi::BindingLayoutItem::Texture_UAV( 0 ),
};
m_Compute.BindingLayout = m_Device->createBindingLayout( ComputeBindings );
nvrhi::ComputePipelineDesc ComputePipeline;
ComputePipeline.bindingLayouts = { m_Compute.BindingLayout };
ComputePipeline.CS = m_Compute.Shader;
m_Compute.Pipeline = device->createComputePipeline( ComputePipeline );
m_Compute.BindingSets.resize( params.numBindingSets );
}
{
idList<shaderMacro_t> macros =
{
{ "DIRECTIONAL_OCCLUSION", params.directionalOcclusion ? "1" : "0" }
};
int shaderIdx = renderProgManager.FindShader( "builtin/SSAO/ssao_blur", SHADER_STAGE_COMPUTE, "", macros, true, LAYOUT_DRAW_VERT );
m_Blur.Shader = renderProgManager.GetShader( shaderIdx );
nvrhi::BindingLayoutDesc BlurBindings;
BlurBindings.visibility = nvrhi::ShaderType::Compute;
BlurBindings.bindings =
{
nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 ),
nvrhi::BindingLayoutItem::VolatileConstantBuffer( 1 ),
nvrhi::BindingLayoutItem::Texture_SRV( 0 ),
nvrhi::BindingLayoutItem::Texture_SRV( 1 ),
nvrhi::BindingLayoutItem::Texture_UAV( 0 ),
nvrhi::BindingLayoutItem::Sampler( 0 ),
};
m_Blur.BindingLayout = m_Device->createBindingLayout( BlurBindings );
nvrhi::ComputePipelineDesc BlurPipeline;
BlurPipeline.bindingLayouts = { m_Blur.BindingLayout };
BlurPipeline.CS = m_Blur.Shader;
m_Blur.Pipeline = device->createComputePipeline( BlurPipeline );
m_Blur.BindingSets.resize( params.numBindingSets );
}
}
// Backwards compatibility constructor
SsaoPass::SsaoPass(
nvrhi::IDevice* device,
CommonRenderPasses* commonPasses,
nvrhi::ITexture* gbufferDepth,
nvrhi::ITexture* gbufferNormals,
nvrhi::ITexture* destinationTexture )
: SsaoPass( device, CreateParameters{ idVec2( gbufferDepth->getDesc().width, gbufferDepth->getDesc().height ), false, false, false, 1 }, commonPasses )
{
const nvrhi::TextureDesc& depthDesc = gbufferDepth->getDesc();
const nvrhi::TextureDesc& normalsDesc = gbufferNormals->getDesc();
assert( depthDesc.sampleCount == normalsDesc.sampleCount );
assert( depthDesc.sampleCount == 1 ); // more is currently unsupported
assert( depthDesc.dimension == nvrhi::TextureDimension::Texture2D ); // arrays are currently unsupported
CreateBindingSet( gbufferDepth, gbufferNormals, destinationTexture, 0 );
}
void SsaoPass::CreateBindingSet(
nvrhi::ITexture* gbufferDepth,
nvrhi::ITexture* gbufferNormals,
nvrhi::ITexture* destinationTexture,
int bindingSetIndex )
{
nvrhi::BindingSetDesc DeinterleaveBindings;
DeinterleaveBindings.bindings =
{
nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.ConstantBuffer() ),
nvrhi::BindingSetItem::ConstantBuffer( 1, m_ConstantBuffer ),
nvrhi::BindingSetItem::Texture_SRV( 0, gbufferDepth ),
nvrhi::BindingSetItem::Texture_UAV( 0, m_DeinterleavedDepth )
};
m_Deinterleave.BindingSets[bindingSetIndex] = m_Device->createBindingSet( DeinterleaveBindings, m_Deinterleave.BindingLayout );
nvrhi::BindingSetDesc ComputeBindings;
ComputeBindings.bindings =
{
nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.ConstantBuffer() ),
nvrhi::BindingSetItem::ConstantBuffer( 1, m_ConstantBuffer ),
nvrhi::BindingSetItem::Texture_SRV( 0, m_DeinterleavedDepth ),
nvrhi::BindingSetItem::Texture_SRV( 1, gbufferNormals ),
nvrhi::BindingSetItem::Texture_UAV( 0, m_DeinterleavedOcclusion )
};
m_Compute.BindingSets[bindingSetIndex] = m_Device->createBindingSet( ComputeBindings, m_Compute.BindingLayout );
nvrhi::BindingSetDesc BlurBindings;
BlurBindings.bindings =
{
nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.ConstantBuffer() ),
nvrhi::BindingSetItem::ConstantBuffer( 1, m_ConstantBuffer ),
nvrhi::BindingSetItem::Texture_SRV( 0, m_DeinterleavedDepth ),
nvrhi::BindingSetItem::Texture_SRV( 1, m_DeinterleavedOcclusion ),
nvrhi::BindingSetItem::Texture_UAV( 0, destinationTexture ),
nvrhi::BindingSetItem::Sampler( 0, commonRenderPasses->m_PointClampSampler )
};
m_Blur.BindingSets[bindingSetIndex] = m_Device->createBindingSet( BlurBindings, m_Blur.BindingLayout );
}
void SsaoPass::Render(
nvrhi::ICommandList* commandList,
const viewDef_t* viewDef,
int bindingSetIndex )
{
assert( m_Deinterleave.BindingSets[bindingSetIndex] );
assert( m_Compute.BindingSets[bindingSetIndex] );
assert( m_Blur.BindingSets[bindingSetIndex] );
{
// RB HACK: add one 1 extra pixel to width and height
nvrhi::Rect viewExtent( viewDef->viewport.x1, viewDef->viewport.x2 + 1, viewDef->viewport.y1, viewDef->viewport.y2 + 1 );
nvrhi::Rect quarterResExtent = viewExtent;
quarterResExtent.minX /= 4;
quarterResExtent.minY /= 4;
quarterResExtent.maxX = ( quarterResExtent.maxX + 3 ) / 4;
quarterResExtent.maxY = ( quarterResExtent.maxY + 3 ) / 4;
// TODO required and remove this by fixing the shaders
// because they include #include <global_inc.hlsl>
renderProgManager.BindShader_TextureVertexColor();
renderProgManager.CommitConstantBuffer( commandList, true );
SsaoConstants ssaoConstants = {};
ssaoConstants.viewportOrigin = idVec2( viewDef->viewport.x1, viewDef->viewport.y1 );
ssaoConstants.viewportSize = idVec2( viewDef->viewport.GetWidth(), viewDef->viewport.GetHeight() );
ssaoConstants.pixelOffset = tr.backend.GetCurrentPixelOffset();
// RB: this actually should work but it only works with the old SSAO method ...
//ssaoConstants.matClipToView = viewDef->unprojectionToCameraRenderMatrix;
idRenderMatrix::Inverse( *( idRenderMatrix* ) viewDef->projectionMatrix, ssaoConstants.matClipToView );
// RB: TODO: only need for DIRECTIONAL_OCCLUSION
// we don't store the view matrix separatly yet
//ssaoConstants.matViewToWorld = viewDef->worldSpace;
//idRenderMatrix::Inverse( ssaoConstants.matViewToWorld, ssaoConstants.matWorldToView );
ssaoConstants.clipToView = idVec2(
viewDef->projectionMatrix[2 * 4 + 3] / viewDef->projectionMatrix[0 * 4 + 0],
viewDef->projectionMatrix[2 * 4 + 3] / viewDef->projectionMatrix[1 * 4 + 1] );
ssaoConstants.invQuantizedGbufferSize = idVec2( 1.0f, 1.0f ) / m_QuantizedGbufferTextureSize;
ssaoConstants.quantizedViewportOrigin = idVec2i( quarterResExtent.minX, quarterResExtent.minY ) * 4;
ssaoConstants.amount = r_ssaoAmount.GetFloat();
ssaoConstants.invBackgroundViewDepth = ( r_ssaoBackgroundViewDepth.GetFloat() > 0.f ) ? 1.f / r_ssaoBackgroundViewDepth.GetFloat() : 0.f;
ssaoConstants.radiusWorld = r_ssaoRadiusWorld.GetFloat();
ssaoConstants.surfaceBias = r_ssaoSurfaceBias.GetFloat();
ssaoConstants.powerExponent = r_ssaoPowerExponent.GetFloat();
ssaoConstants.radiusToScreen = 0.5f * viewDef->viewport.GetHeight() * abs( viewDef->projectionMatrix[1 * 4 + 1] );
commandList->writeBuffer( m_ConstantBuffer, &ssaoConstants, sizeof( ssaoConstants ) );
uint32_t dispatchWidth = ( quarterResExtent.width() + 7 ) / 8;
uint32_t dispatchHeight = ( quarterResExtent.height() + 7 ) / 8;
nvrhi::ComputeState state;
state.pipeline = m_Deinterleave.Pipeline;
state.bindings = { m_Deinterleave.BindingSets[bindingSetIndex] };
commandList->setComputeState( state );
commandList->dispatch( dispatchWidth, dispatchHeight, 1 );
state.pipeline = m_Compute.Pipeline;
state.bindings = { m_Compute.BindingSets[bindingSetIndex] };
commandList->setComputeState( state );
commandList->dispatch( dispatchWidth, dispatchHeight, 16 );
dispatchWidth = ( viewExtent.width() + 15 ) / 16;
dispatchHeight = ( viewExtent.height() + 15 ) / 16;
state.pipeline = m_Blur.Pipeline;
state.bindings = { m_Blur.BindingSets[bindingSetIndex] };
commandList->setComputeState( state );
commandList->dispatch( dispatchWidth, dispatchHeight, 1 );
}
}