/* =========================================================================== 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 - G-Buffer texture visualization #include "crp_local.h" #include "../client/cl_imgui.h" #include "compshaders/crp/fullscreen.h" #include "compshaders/crp/gbufferviz_depth.h" #include "compshaders/crp/gbufferviz_normal.h" #include "compshaders/crp/gbufferviz_position.h" #include "compshaders/crp/gbufferviz_motion.h" #pragma pack(push, 4) struct DecodeNormalsRC { uint32_t normalTextureIndex; }; struct DecodePositionRC { uint32_t textureIndex; uint32_t coloredDelta; }; struct MotionVectorRC { uint32_t textureIndex; }; #pragma pack(pop) void GBufferViz::Init() { { GraphicsPipelineDesc desc("G-Buffer Depth"); MakeFullScreenPipeline(desc, ShaderByteCode(g_gbufferviz_depth_ps)); desc.AddRenderTarget(0, crp.renderTargetFormat); linearizeDepthPipeline = CreateGraphicsPipeline(desc); } { GraphicsPipelineDesc desc("G-Buffer Normal"); MakeFullScreenPipeline(desc, ShaderByteCode(g_gbufferviz_normal_ps)); desc.AddRenderTarget(0, crp.renderTargetFormat); decodeNormalsPipeline = CreateGraphicsPipeline(desc); } { GraphicsPipelineDesc desc("G-Buffer Position"); MakeFullScreenPipeline(desc, ShaderByteCode(g_gbufferviz_position_ps)); desc.AddRenderTarget(0, crp.renderTargetFormat); decodeShadingPositionPipeline = CreateGraphicsPipeline(desc); } { GraphicsPipelineDesc desc("G-Buffer Motion"); MakeFullScreenPipeline(desc, ShaderByteCode(g_gbufferviz_motion_ps)); desc.AddRenderTarget(0, crp.renderTargetFormat); motionVectorPipeline = CreateGraphicsPipeline(desc); } } void GBufferViz::DrawGUI() { GUI_AddMainMenuItem(GUI_MainMenu::Tools, "Show G-Buffer", "", &windowActive); if(!windowActive) { return; } const HTexture renderTarget = crp.GetReadRenderTarget(); if(textureIndex == GBufferTexture::Depth) { srp.renderMode = RenderMode::None; SCOPED_DEBUG_LABEL("Debug Depth", 1.0f, 1.0f, 1.0f); CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight); CmdBeginBarrier(); CmdTextureBarrier(crp.depthTexture, ResourceStates::PixelShaderAccessBit); CmdTextureBarrier(renderTarget, ResourceStates::RenderTargetBit); CmdEndBarrier(); CmdBindRenderTargets(1, &renderTarget, NULL); CmdBindPipeline(linearizeDepthPipeline); CmdDraw(3, 0); } else if(textureIndex == GBufferTexture::Normal) { srp.renderMode = RenderMode::None; SCOPED_DEBUG_LABEL("Debug Normal", 1.0f, 1.0f, 1.0f); CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight); CmdBeginBarrier(); CmdTextureBarrier(crp.normalTexture, ResourceStates::PixelShaderAccessBit); CmdTextureBarrier(renderTarget, ResourceStates::RenderTargetBit); CmdEndBarrier(); DecodeNormalsRC rc = {}; rc.normalTextureIndex = GetTextureIndexSRV(crp.normalTexture); CmdBindRenderTargets(1, &renderTarget, NULL); CmdBindPipeline(decodeNormalsPipeline); CmdSetGraphicsRootConstants(0, sizeof(rc), &rc); CmdDraw(3, 0); } else if(textureIndex == GBufferTexture::ShadingPositionDelta) { srp.renderMode = RenderMode::None; SCOPED_DEBUG_LABEL("Debug Position", 1.0f, 1.0f, 1.0f); CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight); CmdBeginBarrier(); CmdTextureBarrier(crp.shadingPositionTexture, ResourceStates::PixelShaderAccessBit); CmdTextureBarrier(renderTarget, ResourceStates::RenderTargetBit); CmdEndBarrier(); DecodePositionRC rc = {}; rc.textureIndex = GetTextureIndexSRV(crp.shadingPositionTexture); rc.coloredDelta = coloredPositionDelta ? 1 : 0; CmdBindRenderTargets(1, &renderTarget, NULL); CmdBindPipeline(decodeShadingPositionPipeline); CmdSetGraphicsRootConstants(0, sizeof(rc), &rc); CmdDraw(3, 0); } else if(textureIndex == GBufferTexture::MotionVectorRaw || textureIndex == GBufferTexture::MotionVectorMB) { srp.renderMode = RenderMode::None; const HTexture texture = textureIndex == GBufferTexture::MotionVectorRaw ? crp.motionVectorTexture : crp.motionVectorMBTexture; SCOPED_DEBUG_LABEL("Debug Motion", 1.0f, 1.0f, 1.0f); CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight); CmdBeginBarrier(); CmdTextureBarrier(texture, ResourceStates::PixelShaderAccessBit); CmdTextureBarrier(renderTarget, ResourceStates::RenderTargetBit); CmdEndBarrier(); MotionVectorRC rc = {}; rc.textureIndex = GetTextureIndexSRV(texture); CmdBindRenderTargets(1, &renderTarget, NULL); CmdBindPipeline(motionVectorPipeline); CmdSetGraphicsRootConstants(0, sizeof(rc), &rc); CmdDraw(3, 0); } if(ImGui::Begin("G-Buffer", &windowActive, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Checkbox("Full-res", &fullResolution); ImGui::SameLine(); ImGui::RadioButton("Depth", &textureIndex, GBufferTexture::Depth); ImGui::SameLine(); ImGui::RadioButton("Normal", &textureIndex, GBufferTexture::Normal); ImGui::SameLine(); ImGui::RadioButton("Light", &textureIndex, GBufferTexture::Light); ImGui::RadioButton("Sunlight", &textureIndex, GBufferTexture::Sunlight); ImGui::SameLine(); ImGui::RadioButton("Sunlight Visibility", &textureIndex, GBufferTexture::SunlightVisibility); ImGui::SameLine(); ImGui::RadioButton("Sunlight Penumbra", &textureIndex, GBufferTexture::SunlightPenumbra); ImGui::RadioButton("Motion Raw", &textureIndex, GBufferTexture::MotionVectorRaw); ImGui::SameLine(); ImGui::RadioButton("Motion MB", &textureIndex, GBufferTexture::MotionVectorMB); ImGui::SameLine(); ImGui::RadioButton("Shading Position Delta", &textureIndex, GBufferTexture::ShadingPositionDelta); if(textureIndex == GBufferTexture::ShadingPositionDelta) { ImGui::SameLine(); ImGui::Checkbox("Per-axis delta", &coloredPositionDelta); } ImVec2 resolution = ImVec2(glConfig.vidWidth, glConfig.vidHeight); if(!fullResolution) { resolution.x /= 2; resolution.y /= 2; } HTexture texture; switch(textureIndex) { case GBufferTexture::Depth: texture = renderTarget; break; case GBufferTexture::Normal: texture = renderTarget; break; case GBufferTexture::Light: texture = crp.lightTexture; break; case GBufferTexture::ShadingPositionDelta: texture = renderTarget; break; case GBufferTexture::MotionVectorRaw: texture = renderTarget; break; case GBufferTexture::MotionVectorMB: texture = renderTarget; break; case GBufferTexture::SunlightVisibility: texture = crp.sunlight.visibilityTexture; break; case GBufferTexture::SunlightPenumbra: texture = crp.sunlight.penumbraTexture; break; case GBufferTexture::Sunlight: texture = crp.sunlightTexture; break; default: Q_assert(!"Invalid G-Buffer texture index"); texture = crp.lightTexture; break; } ImGui::Image((ImTextureID)GetTextureIndexSRV(texture), resolution); CmdBeginBarrier(); CmdTextureBarrier(texture, ResourceStates::PixelShaderAccessBit); CmdEndBarrier(); } ImGui::End(); }