/* =========================================================================== Copyright (C) 2024 Gian 'myT' Schellenbaum This file is part of Challenge Quake 3 (CNQ3). Challenge Quake 3 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Challenge Quake 3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Challenge Quake 3. If not, see . =========================================================================== */ // Cinematic Rendering Pipeline - scatter-as-gather motion blur #include "crp_local.h" #include "compshaders/crp/fullscreen.h" #include "compshaders/crp/mblur_pack.h" #include "compshaders/crp/mblur_tile_gen.h" #include "compshaders/crp/mblur_tile_max.h" #include "compshaders/crp/mblur_blur.h" #pragma pack(push, 4) struct MBPackRC { float motionScale; float maxRadiusPx; }; struct MBTileGenRC { uint32_t inputTextureIndex; uint32_t outputTextureIndex; }; struct MBTileMaxRC { uint32_t inputTextureIndex; uint32_t outputTextureIndex; uint32_t samplerIndex; // point/clamp }; struct MBBlurRC { uint32_t colorTextureIndex; uint32_t tileTextureIndex; uint32_t packedTextureIndex; uint32_t blueNoiseTextureIndex; uint32_t pointSamplerIndex; // clamp uint32_t linearSamplerIndex; // clamp }; #pragma pack(pop) void MotionBlur::Init() { tileTextureWidth = (uint32_t)(glConfig.vidWidth + 15) / 16; tileTextureHeight = (uint32_t)(glConfig.vidHeight + 15) / 16; { GraphicsPipelineDesc desc("MBlur velocity/depth packing"); MakeFullScreenPipeline(desc, ShaderByteCode(g_pack_ps)); desc.AddRenderTarget(0, TextureFormat::R32G32_UInt); packPipeline = CreateGraphicsPipeline(desc); } tileGenPipeline = CreateComputePipeline("MBlur tile generation", ShaderByteCode(g_tile_gen_cs)); tileMaxPipeline = CreateComputePipeline("MBlur tile dilation", ShaderByteCode(g_tile_max_cs)); { GraphicsPipelineDesc desc("MBlur reconstruction filter"); MakeFullScreenPipeline(desc, ShaderByteCode(g_blur_ps)); desc.AddRenderTarget(0, crp.renderTargetFormat); blurPipeline = CreateGraphicsPipeline(desc); } { TextureDesc desc("MBlur velocity tile #1", tileTextureWidth, tileTextureHeight); desc.shortLifeTime = true; desc.committedResource = true; desc.initialState = ResourceStates::UnorderedAccessBit; desc.allowedState = ResourceStates::UnorderedAccessBit | ResourceStates::ComputeShaderAccessBit | ResourceStates::PixelShaderAccessBit; desc.format = TextureFormat::R16G16_Float; tileTexture = CreateTexture(desc); desc.name = "MBlur velocity tile #2"; tileTexture2 = CreateTexture(desc); } { TextureDesc desc("MBlur packed velocity/depth", glConfig.vidWidth, glConfig.vidHeight); desc.shortLifeTime = true; desc.committedResource = true; desc.initialState = ResourceStates::RenderTargetBit; desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::ComputeShaderAccessBit | ResourceStates::PixelShaderAccessBit; desc.format = TextureFormat::R32G32_UInt; packedTexture = CreateTexture(desc); } } void MotionBlur::Draw() { if(crp_mblur->integer == 0 || (backEnd.refdef.rdflags & RDF_CNQ3_TELEPORTED) != 0 || !IsViewportFullscreen(backEnd.viewParms)) { return; } SCOPED_RENDER_PASS("Motion Blur", 0.125f, 0.125f, 0.25f); DrawPack(); DrawTileGen(); DrawTileMax(); DrawBlur(); } void MotionBlur::DrawPack() { SCOPED_DEBUG_LABEL("MBlur Pack", 0.125f, 0.125f, 0.25f); CmdBeginBarrier(); CmdTextureBarrier(crp.motionVectorMBTexture, ResourceStates::PixelShaderAccessBit); CmdTextureBarrier(crp.depthTexture, ResourceStates::PixelShaderAccessBit); CmdTextureBarrier(packedTexture, ResourceStates::RenderTargetBit); CmdEndBarrier(); MBPackRC rc = {}; rc.motionScale = crp_mblur_exposure->value; rc.maxRadiusPx = min(glConfig.vidHeight / 25.0f, 30.0f); CmdBindRenderTargets(1, &packedTexture, NULL); CmdBindPipeline(packPipeline); CmdSetGraphicsRootConstants(0, sizeof(rc), &rc); CmdDraw(3, 0); } void MotionBlur::DrawTileGen() { SCOPED_DEBUG_LABEL("MBlur Tile Gen", 0.125f, 0.125f, 0.25f); CmdBeginBarrier(); CmdTextureBarrier(packedTexture, ResourceStates::ComputeShaderAccessBit); CmdTextureBarrier(tileTexture, ResourceStates::UnorderedAccessBit); CmdEndBarrier(); MBTileGenRC rc = {}; rc.inputTextureIndex = GetTextureIndexSRV(packedTexture); rc.outputTextureIndex = GetTextureIndexUAV(tileTexture, 0); CmdBindPipeline(tileGenPipeline); CmdSetComputeRootConstants(0, sizeof(rc), &rc); CmdDispatch((tileTextureWidth + 7) / 8, (tileTextureHeight + 7) / 8, 1); } void MotionBlur::DrawTileMax() { SCOPED_DEBUG_LABEL("MBlur Tile Max", 0.125f, 0.125f, 0.25f); CmdBeginBarrier(); CmdTextureBarrier(tileTexture, ResourceStates::ComputeShaderAccessBit); CmdTextureBarrier(tileTexture2, ResourceStates::UnorderedAccessBit); CmdEndBarrier(); MBTileMaxRC rc = {}; rc.inputTextureIndex = GetTextureIndexSRV(tileTexture); rc.outputTextureIndex = GetTextureIndexUAV(tileTexture2, 0); rc.samplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Point); CmdBindPipeline(tileMaxPipeline); CmdSetComputeRootConstants(0, sizeof(rc), &rc); CmdDispatch((tileTextureWidth + 7) / 8, (tileTextureHeight + 7) / 8, 1); } void MotionBlur::DrawBlur() { SCOPED_DEBUG_LABEL("MBlur Blur", 0.125f, 0.125f, 0.25f); crp.SwapRenderTargets(); CmdBeginBarrier(); CmdTextureBarrier(crp.GetReadRenderTarget(), ResourceStates::PixelShaderAccessBit); CmdTextureBarrier(tileTexture2, ResourceStates::PixelShaderAccessBit); CmdTextureBarrier(packedTexture, ResourceStates::PixelShaderAccessBit); CmdTextureBarrier(crp.renderTarget, ResourceStates::RenderTargetBit); CmdEndBarrier(); MBBlurRC rc = {}; rc.colorTextureIndex = GetTextureIndexSRV(crp.GetReadRenderTarget()); rc.tileTextureIndex = GetTextureIndexSRV(tileTexture2); rc.packedTextureIndex = GetTextureIndexSRV(packedTexture); rc.blueNoiseTextureIndex = GetTextureIndexSRV(crp.blueNoise2D); rc.pointSamplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Point); rc.linearSamplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear); CmdBindRenderTargets(1, &crp.renderTarget, NULL); CmdBindPipeline(blurPipeline); CmdSetGraphicsRootConstants(0, sizeof(rc), &rc); CmdDraw(3, 0); }