added the Cinematic Rendering Pipeline

This commit is contained in:
myT 2024-01-13 22:40:13 +01:00
parent 0884a857da
commit ad3f942a8d
102 changed files with 9129 additions and 1649 deletions

1
.gitignore vendored
View file

@ -8,7 +8,6 @@
*.idb *.idb
code/qcommon/git.h code/qcommon/git.h
code/win32/winquake.res code/win32/winquake.res
code/renderer/hlsl/*.h
makefiles/windows_vs*/*sdf makefiles/windows_vs*/*sdf
makefiles/*/obj makefiles/*/obj
.build .build

View file

@ -65,21 +65,55 @@ add: r_shadingRate <0 to 6> (default: 0) sets the variable-rate shading (VRS) mo
prefer horizontal subsampling as many maps have textures with thin horizontal lines prefer horizontal subsampling as many maps have textures with thin horizontal lines
which become an aliased mess when vertically subsampled which become an aliased mess when vertically subsampled
add: Cinematic Rendering Pipeline CVars
depth of field:
crp_dof <0|1|2> (default: 1) selects the depth of field mode
0 - disabled
1 - scatter-as-gather
2 - accumulation
crp_dof_overlay <0|1|2> (default: 0) draws a debug overlay mode
0 - disabled
1 - colorized blur
2 - focus plane
crp_dof_blades <0 to 16> (default: 6) is the aperture's blade count
set to less than 3 for a disk shape
crp_dof_angle <0 to 360> (default: 20) is the aperture's angle, in degrees
scatter-as-gather depth of field (fast, doesn't handle transparency properly):
crp_gatherDof_focusNearDist <1 to 2048> (default: 192) is the near-field's focus distance
crp_gatherDof_focusNearRange <1 to 2048> (default: 256) is the near-field's focus range
crp_gatherDof_focusFarDist <1 to 2048> (default: 512) is the far-field's focus distance
crp_gatherDof_focusFarRange <1 to 2048> (default: 384) is the far-field's focus range
crp_gatherDof_brightness <0 to 8> (default: 2) is the blur brightness' weight
accumulation depth of field (extremely slow, looks perfect with enough samples):
crp_accumDof_focusDist <2 to 2048> (default: 256) is the focus distance
crp_accumDof_radius <0.001 to 20> (default: 0.1) is the aperture radius in world units
crp_accumDof_samples <1 to 12> (default: 2) is per-axis sampling density
density N means (2N + 1)^2 scene renders in total, so be careful or you'll trigger TDR
crp_accumDof_preview <0|1|2> (default: 0) selects the real-time preview mode
0 - disabled
1 - 1/4 pixel count, 9 samples total
2 - 1/16 pixel count, 25 samples total
chg: dropped 32-bit support chg: dropped 32-bit support
chg: dropped Linux/FreeBSD client chg: dropped Linux/FreeBSD client
chg: Windows support is limited to 10 and 11 chg: Windows support is limited to 10 and 11
chg: much improved rendering: chg: reworked renderer with 2 new rendering pipelines
- removed all Direct3D 11 and OpenGL code - removed all the Direct3D 11 and OpenGL code, now using Direct3D 12
- rendering with Direct3D 12 (improved performance and better worst case input latency)
- much improved input latency when V-Sync is enabled - much improved input latency when V-Sync is enabled
- improved frame-time consistency - improved frame-time consistency ("frame pacing")
- surfaces are sorted and rendered more efficiently
- fog handling has been completely overhauled (faster, simpler, decoupled from surfaces) - fog handling has been completely overhauled (faster, simpler, decoupled from surfaces)
- MSAA and alpha-to-coverage have been removed - MSAA and alpha-to-coverage have been removed
- added SMAA for anti-aliasing (gamma-corrected and not applied to UI for best results) - Gameplay Rendering Pipeline (GRP)
- improved performance and better worst case input latency
- added SMAA for anti-aliasing (gamma-corrected and not applied to UI for best results)
- added VRS (Variable Rate Shading) support
- Cinematic Rendering Pipeline (CRP)
- order-independent transparency
- depth of field (scatter-as-gather or accumulation)
- all corresponding CVars have the "crp_" prefix
chg: removed cl_drawMouseLag, r_backend, r_frameSleep, r_gpuMipGen, r_alphaToCoverage, r_alphaToCoverageMipBoost chg: removed cl_drawMouseLag, r_backend, r_frameSleep, r_gpuMipGen, r_alphaToCoverage, r_alphaToCoverageMipBoost
removed r_d3d11_syncOffsets, r_d3d11_presentMode, r_gl3_geoStream, r_ignoreGLErrors, r_finish, r_khr_debug removed r_d3d11_syncOffsets, r_d3d11_presentMode, r_gl3_geoStream, r_ignoreGLErrors, r_finish, r_khr_debug

View file

@ -1591,12 +1591,35 @@ static void CL_CheckUserinfo()
} }
static void CL_CheckCRP()
{
// demo playback and listen servers are always OK
if ( Cvar_VariableIntegerValue( "r_pipeline" ) != 1 ||
CL_DemoPlaying() ||
Cvar_VariableIntegerValue( "sv_running" ) )
return;
switch ( cls.state ) {
case CA_CHALLENGING:
case CA_CONNECTING:
case CA_CONNECTED:
Cbuf_AddText( "r_pipeline 0\nvid_restart\nreconnect\n" );
Com_Printf( "^3WARNING: switched to GRP (r_pipeline 0) for online play\n" );
break;
default:
break;
}
}
void CL_Frame( int msec ) void CL_Frame( int msec )
{ {
if ( !com_cl_running->integer ) { if ( !com_cl_running->integer ) {
return; return;
} }
CL_CheckCRP();
if ( cls.cddialog ) { if ( cls.cddialog ) {
// bring up the cd error dialog if needed // bring up the cd error dialog if needed
cls.cddialog = qfalse; cls.cddialog = qfalse;
@ -3014,3 +3037,9 @@ void CL_SetMenuData( qboolean typeOnly )
} }
} }
} }
qbool CL_DemoPlaying()
{
return clc.demoplaying;
}

View file

@ -1222,10 +1222,3 @@ qbool UI_GameCommand()
{ {
return (uivm && VM_Call( uivm, UI_CONSOLE_COMMAND, cls.realtime )); return (uivm && VM_Call( uivm, UI_CONSOLE_COMMAND, cls.realtime ));
} }
qbool CL_DemoPlaying()
{
return clc.demoplaying;
}

View file

@ -25,8 +25,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "q_shared.h" #include "q_shared.h"
const vec3_t vec2_zero = { 0, 0 };
const vec3_t vec2_one = { 1, 1 };
const vec3_t vec3_origin = { 0, 0, 0 }; const vec3_t vec3_origin = { 0, 0, 0 };
const vec3_t vec3_zero = { 0, 0, 0 };
const vec3_t vec3_one = { 1, 1, 1 };
const vec4_t vec4_zero = { 0, 0, 0, 0 }; const vec4_t vec4_zero = { 0, 0, 0, 0 };
const vec4_t vec4_one = { 1, 1, 1, 1 };
#if defined(Q3_VM) // lcc can't cope with "const vec3_t []" #if defined(Q3_VM) // lcc can't cope with "const vec3_t []"
vec3_t axisDefault[3] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }; vec3_t axisDefault[3] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
#else #else

View file

@ -178,6 +178,13 @@ typedef int clipHandle_t;
#define STRINGIZE_NE(x) #x // no expansion #define STRINGIZE_NE(x) #x // no expansion
#define STRINGIZE(x) STRINGIZE_NE(x) // with expansion #define STRINGIZE(x) STRINGIZE_NE(x) // with expansion
// #define A 42
// #define B 69
// CONCAT_NE(A, B) -> AB
// CONCAT(A, B) -> 4269
#define CONCAT_NE(x, y) x ## y // no expansion
#define CONCAT(x, y) CONCAT_NE(x, y) // with expansion
// angle indexes // angle indexes
#define PITCH 0 // up / down #define PITCH 0 // up / down
#define YAW 1 // left / right #define YAW 1 // left / right
@ -259,13 +266,25 @@ typedef float vec_t;
typedef vec_t vec2_t[2]; typedef vec_t vec2_t[2];
typedef vec_t vec3_t[3]; typedef vec_t vec3_t[3];
typedef vec_t vec4_t[4]; typedef vec_t vec4_t[4];
extern const vec3_t vec2_zero;
extern const vec3_t vec2_one;
extern const vec3_t vec3_origin; extern const vec3_t vec3_origin;
extern const vec3_t vec3_zero;
extern const vec3_t vec3_one;
extern const vec4_t vec4_zero; extern const vec4_t vec4_zero;
extern const vec4_t vec4_one;
#ifndef M_PI #ifndef M_PI
#define M_PI 3.14159265358979323846f // matches value in gcc v2 math.h #define M_PI 3.14159265358979323846f // matches value in gcc v2 math.h
#endif #endif
#define M_PI_D2 (M_PI / 2.0f)
#define M_PI_D4 (M_PI / 4.0f)
#define M_PI_D8 (M_PI / 8.0f)
#define M_PI_M2 (M_PI * 2.0f)
#define M_PI_M4 (M_PI * 4.0f)
#define M_PI_M8 (M_PI * 8.0f)
// all drawing is done to a 640*480 virtual screen size // all drawing is done to a 640*480 virtual screen size
// and will be automatically scaled to the real resolution // and will be automatically scaled to the real resolution

2
code/renderer/compshaders/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.h
*.temp

View file

@ -0,0 +1,366 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Cinematic Rendering Pipeline - accumulation depth of field
#include "crp_local.h"
namespace dof_accum
{
#include "compshaders/crp/accumdof_accum_vs.h"
#include "compshaders/crp/accumdof_accum_ps.h"
}
namespace dof_norm
{
#include "compshaders/crp/accumdof_norm_vs.h"
#include "compshaders/crp/accumdof_norm_ps.h"
}
namespace dof_debug
{
#include "compshaders/crp/accumdof_debug_vs.h"
#include "compshaders/crp/accumdof_debug_ps.h"
}
#pragma pack(push, 4)
struct DOFAccumRC
{
uint32_t textureIndex;
};
struct DOFNormRC
{
uint32_t textureIndex;
};
struct DOFDebugRC
{
float mvp[16]; // displaced view, to project to CS
float invMvp[16]; // main view, to unproject to WS
uint32_t colorTextureIndex;
uint32_t depthTextureIndex;
uint32_t debugMode; // 1: colorized coc, 2: constant intensity far field
uint32_t tcScale;
float focusDist;
float linearDepthA; // main view, to unproject to WS
float linearDepthB;
float maxNearCocCS;
float maxFarCocCS;
};
#pragma pack(pop)
// the input is in [0,1]^2, the output polygon is centered at the origin
static void MapUnitSquareToPolygon(const vec2_t square01, float apertureBladeCount, float apertureAngleRad, vec2_t polygon)
{
// needed to avoid inf/nan propagation through theta for samples
// that are exactly in the middle of the quad on either axis
// (i.e. square.x|y == 0.5f gets remapped to 0.0f)
const float epsilon = 0.000001f;
// morph into a square in [-1,1]^2
vec2_t square;
square[0] = square01[0] * 2.0f - 1.0f;
square[1] = square01[1] * 2.0f - 1.0f;
// morph the square into a disk
// "A Low Distortion Map Between Disk and Square" by Peter Shirley and Kenneth Chiu
float radius, angle;
vec2_t square2;
square2[0] = square[0] * square[0];
square2[1] = square[1] * square[1];
if(square2[0] > square2[1])
{
// left and right quadrants
radius = square[0];
angle = (square[1] * M_PI_D4) / (square[0] + epsilon);
}
else
{
// top and bottom quadrants
radius = square[1];
angle = M_PI_D2 - (square[0] * M_PI_D4) / (square[1] + epsilon);
}
if(radius < 0.0f)
{
radius = -radius;
angle += M_PI;
}
// morph the disk into a polygon
// "Graphics Gems from CryENGINE 3" by Tiago Sousa
const float edgeCount = apertureBladeCount;
if(edgeCount >= 3.0f)
{
const float num = cosf(M_PI / edgeCount);
const float den0 = M_PI_M2 / edgeCount;
const float den1 = (angle * edgeCount + M_PI) / M_PI_M2;
const float den = angle - (den0 * floorf(den1));
radius *= num / cosf(den);
angle += apertureAngleRad;
}
polygon[0] = cosf(angle) * radius;
polygon[1] = sinf(angle) * radius;
}
static int GetResolutionScale()
{
switch(crp_accumDof_preview->integer)
{
case 0: return 1;
case 1: return 2;
case 2: return 4;
default: Q_assert(0); return 1;
}
}
void AccumDepthOfField::Init()
{
{
GraphicsPipelineDesc desc("DOF Accumulate");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(dof_accum::g_vs);
desc.pixelShader = ShaderByteCode(dof_accum::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE, crp.renderTargetFormat);
accumPipeline = CreateGraphicsPipeline(desc);
}
{
GraphicsPipelineDesc desc("DOF Normalize");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(dof_norm::g_vs);
desc.pixelShader = ShaderByteCode(dof_norm::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, crp.renderTargetFormat);
normPipeline = CreateGraphicsPipeline(desc);
}
{
GraphicsPipelineDesc desc("DOF Debug");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(dof_debug::g_vs);
desc.pixelShader = ShaderByteCode(dof_debug::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, crp.renderTargetFormat);
debugPipeline = CreateGraphicsPipeline(desc);
}
{
TextureDesc desc("DOF accumulation", glConfig.vidWidth, glConfig.vidHeight);
desc.initialState = ResourceStates::RenderTargetBit;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit;
Vector4Clear(desc.clearColor);
desc.usePreferredClearValue = true;
desc.committedResource = true;
desc.format = crp.renderTargetFormat;
desc.shortLifeTime = true;
accumTexture = CreateTexture(desc);
}
}
void AccumDepthOfField::Begin(const drawSceneViewCommand_t& cmd)
{
CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight);
const TextureBarrier texBarriers[] =
{
TextureBarrier(accumTexture, ResourceStates::RenderTargetBit)
};
CmdBarrier(ARRAY_LEN(texBarriers), texBarriers);
CmdClearColorTarget(accumTexture, vec4_zero);
// project a point a few units in front of the main view onto a viewpoint with maximum displacement
// the distance to the clip space center (i.e. the 2D origin) is the maximum expected CoC
vec3_t testPoint;
VectorMA(cmd.viewParms.world.viewOrigin, 16.0f, cmd.viewParms.orient.axis[0], testPoint);
drawSceneViewCommand_t newCmd;
FixCommand(newCmd, cmd, 0, 0);
vec4_t eye, clip;
R_TransformModelToClip(testPoint, newCmd.viewParms.world.modelMatrix, newCmd.viewParms.projectionMatrix, eye, clip);
Vector4Scale(clip, 1.0f / clip[3], clip);
maxNearCocCS = sqrtf(clip[0] * clip[0] + clip[1] * clip[1]);
// same thing for a point far away
VectorMA(cmd.viewParms.world.viewOrigin, 69420.0f, cmd.viewParms.orient.axis[0], testPoint);
FixCommand(newCmd, cmd, 0, 0);
R_TransformModelToClip(testPoint, newCmd.viewParms.world.modelMatrix, newCmd.viewParms.projectionMatrix, eye, clip);
Vector4Scale(clip, 1.0f / clip[3], clip);
maxFarCocCS = sqrtf(clip[0] * clip[0] + clip[1] * clip[1]);
}
uint32_t AccumDepthOfField::GetSampleCount()
{
switch(crp_accumDof_preview->integer)
{
case 1: return 3;
case 2: return 5;
default: break;
}
return 2 * crp_accumDof_samples->integer + 1;
}
void AccumDepthOfField::FixCommand(drawSceneViewCommand_t& newCmd, const drawSceneViewCommand_t& cmd, uint32_t x, uint32_t y)
{
const float radius = crp_accumDof_radius->value;
const float tcScale = 1.0f / (float)(GetSampleCount() - 1);
vec2_t square01;
square01[0] = x * tcScale;
square01[1] = y * tcScale;
vec2_t polygon;
MapUnitSquareToPolygon(square01, crp_dof_blades->value, DEG2RAD(crp_dof_angle->value), polygon);
// 0=forward, 1=left, 2=up
vec3_t axis[3];
VectorCopy(cmd.viewParms.orient.axis[0], axis[0]);
VectorCopy(cmd.viewParms.orient.axis[1], axis[1]);
VectorCopy(cmd.viewParms.orient.axis[2], axis[2]);
vec3_t origin;
VectorMA(cmd.viewParms.world.viewOrigin, radius * polygon[0], axis[1], origin);
VectorMA(origin, radius * polygon[1], axis[2], origin);
vec3_t focusPoint;
VectorMA(cmd.viewParms.world.viewOrigin, crp_accumDof_focusDist->value, axis[0], focusPoint);
VectorSubtract(focusPoint, origin, axis[0]); // forward
VectorNormalize(axis[0]);
CrossProduct(axis[2], axis[0], axis[1]); // left
VectorNormalize(axis[1]);
CrossProduct(axis[0], axis[1], axis[2]); // up
VectorNormalize(axis[2]);
newCmd = cmd;
VectorCopy(origin, newCmd.viewParms.orient.origin);
VectorCopy(origin, newCmd.viewParms.world.viewOrigin);
R_CreateWorldModelMatrix(origin, axis, newCmd.viewParms.world.modelMatrix);
newCmd.viewParms.viewportWidth /= GetResolutionScale();
newCmd.viewParms.viewportHeight /= GetResolutionScale();
if(x == 0 && y == 0)
{
memcpy(modelViewMatrix, newCmd.viewParms.world.modelMatrix, sizeof(modelViewMatrix));
memcpy(projMatrix, newCmd.viewParms.projectionMatrix, sizeof(projMatrix));
}
}
void AccumDepthOfField::Accumulate()
{
srp.renderMode = RenderMode::None;
SCOPED_RENDER_PASS("DOF Accum", 0.5f, 1.0f, 0.5f);
const TextureBarrier texBarriers[] =
{
TextureBarrier(crp.renderTarget, ResourceStates::PixelShaderAccessBit),
TextureBarrier(accumTexture, ResourceStates::RenderTargetBit)
};
CmdBarrier(ARRAY_LEN(texBarriers), texBarriers);
DOFAccumRC rc = {};
rc.textureIndex = GetTextureIndexSRV(crp.renderTarget);
CmdBindRenderTargets(1, &accumTexture, NULL);
CmdBindPipeline(accumPipeline);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}
void AccumDepthOfField::Normalize()
{
srp.renderMode = RenderMode::None;
{
SCOPED_RENDER_PASS("DOF Norm", 0.5f, 1.0f, 0.5f);
const TextureBarrier texBarriers[] =
{
TextureBarrier(accumTexture, ResourceStates::PixelShaderAccessBit),
TextureBarrier(crp.renderTarget, ResourceStates::RenderTargetBit)
};
CmdBarrier(ARRAY_LEN(texBarriers), texBarriers);
DOFNormRC rc = {};
rc.textureIndex = GetTextureIndexSRV(accumTexture);
CmdBindRenderTargets(1, &crp.renderTarget, NULL);
CmdBindPipeline(normPipeline);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}
if(crp_accumDof_preview->integer)
{
crp.SwapRenderTargets();
const float scale = 1.0f / (float)GetResolutionScale();
const vec2_t tcScale = { scale, scale };
crp.Blit(crp.GetWriteRenderTarget(), crp.GetReadRenderTarget(), "DOF Upscale", true, tcScale, vec2_zero);
}
}
void AccumDepthOfField::DrawDebug()
{
if(crp_dof_overlay->integer == 0)
{
return;
}
srp.renderMode = RenderMode::None;
SCOPED_RENDER_PASS("DOF Debug", 0.5f, 1.0f, 0.5f);
crp.SwapRenderTargets();
const TextureBarrier texBarriers[] =
{
TextureBarrier(crp.GetReadRenderTarget(), ResourceStates::PixelShaderAccessBit),
TextureBarrier(crp.depthTexture, ResourceStates::PixelShaderAccessBit),
TextureBarrier(crp.GetWriteRenderTarget(), ResourceStates::RenderTargetBit)
};
CmdBarrier(ARRAY_LEN(texBarriers), texBarriers);
float mvp[16];
DOFDebugRC rc = {};
rc.colorTextureIndex = GetTextureIndexSRV(crp.GetReadRenderTarget());
rc.depthTextureIndex = GetTextureIndexSRV(crp.depthTexture);
rc.debugMode = crp_dof_overlay->integer;
rc.focusDist = crp_accumDof_focusDist->value;
rc.maxNearCocCS = maxNearCocCS;
rc.maxFarCocCS = maxFarCocCS;
rc.tcScale = GetResolutionScale();
R_MultMatrix(modelViewMatrix, projMatrix, rc.mvp);
RB_LinearDepthConstants(&rc.linearDepthA, &rc.linearDepthB);
R_MultMatrix(backEnd.viewParms.world.modelMatrix, backEnd.viewParms.projectionMatrix, mvp);
R_InvMatrix(mvp, rc.invMvp);
CmdBindRenderTargets(1, &crp.renderTarget, NULL);
CmdBindPipeline(debugPipeline);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}

View file

@ -0,0 +1,479 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Cinematic Rendering Pipeline - scatter-as-gather depth of field
#include "crp_local.h"
namespace debug
{
#include "compshaders/crp/gatherdof_debug_vs.h"
#include "compshaders/crp/gatherdof_debug_ps.h"
}
namespace split
{
#include "compshaders/crp/gatherdof_split.h"
}
namespace near_coc_tile_gen
{
#include "compshaders/crp/gatherdof_coc_tile_gen.h"
}
namespace near_coc_tile_max
{
#include "compshaders/crp/gatherdof_coc_tile_max.h"
}
namespace blur
{
#include "compshaders/crp/gatherdof_blur.h"
}
namespace fill
{
#include "compshaders/crp/gatherdof_fill.h"
}
namespace combine
{
#include "compshaders/crp/gatherdof_combine_vs.h"
#include "compshaders/crp/gatherdof_combine_ps.h"
}
#pragma pack(push, 4)
struct DOFDebugRC
{
uint32_t colorTextureIndex;
uint32_t depthTextureIndex;
uint32_t debugMode;
float linearDepthA;
float linearDepthB;
float focusNearMin;
float focusNearMax;
float focusFarMin;
float focusFarMax;
float focusDist;
};
struct DOFSplitRC
{
uint32_t depthTextureIndex;
uint32_t colorTextureIndex;
uint32_t nearColorTextureIndex;
uint32_t farColorTextureIndex;
uint32_t nearCocTextureIndex;
uint32_t farCocTextureIndex;
float linearDepthA;
float linearDepthB;
float focusNearMin;
float focusNearMax;
float focusFarMin;
float focusFarMax;
float brightnessScale;
};
struct DOFNearCocMaxRC
{
uint32_t inputTextureIndex;
uint32_t outputTextureIndex;
uint32_t samplerIndex;
int32_t kernelRadius;
float kernelDirectionX;
float kernelDirectionY;
};
struct DOFNearCocBlurRC
{
uint32_t inputTextureIndex;
uint32_t outputTextureIndex;
uint32_t samplerIndex;
int32_t kernelRadius;
float kernelDirectionX;
float kernelDirectionY;
};
struct DOFNearCocTileGenRC
{
uint32_t inputTextureIndex;
uint32_t outputTextureIndex;
};
struct DOFNearCocTileMaxRC
{
uint32_t inputTextureIndex;
uint32_t outputTextureIndex;
uint32_t samplerIndex; // point/clamp
};
struct DOFBlurRC
{
uint32_t colorTextureIndex;
uint32_t nearColorTextureIndex;
uint32_t nearMaxCocTextureIndex;
uint32_t nearCocTextureIndex; // blurry
uint32_t nearOutputTextureIndex;
uint32_t farColorTextureIndex;
uint32_t farCocTextureIndex; // sharp
uint32_t farOutputTextureIndex;
uint32_t samplerIndex; // linear/clamp
float brightnessScale;
float bladeCount;
float bokehAngleRad;
};
struct DOFFillRC
{
uint32_t nearInputTextureIndex;
uint32_t nearOutputTextureIndex;
uint32_t farInputTextureIndex;
uint32_t farOutputTextureIndex;
uint32_t samplerIndex; // point/clamp
};
struct DOFCombineRC
{
uint32_t nearTextureIndex;
uint32_t farTextureIndex;
uint32_t nearCocTextureIndex;
uint32_t farCocTextureIndex;
uint32_t sharpTextureIndex;
uint32_t samplerIndex; // point/clamp
};
#pragma pack(pop)
void GatherDepthOfField::Init()
{
const TextureFormat::Id renderTargetFormat = TextureFormat::RGBA64_Float;
tileWidth = (uint32_t)(glConfig.vidWidth + 15) / 16;
tileHeight = (uint32_t)(glConfig.vidHeight + 15) / 16;
{
ComputePipelineDesc desc("DOF split");
desc.shortLifeTime = true;
desc.shader = ShaderByteCode(split::g_cs);
splitPipeline = CreateComputePipeline(desc);
}
{
ComputePipelineDesc desc("DOF near CoC tile generation");
desc.shortLifeTime = true;
desc.shader = ShaderByteCode(near_coc_tile_gen::g_cs);
nearCocTileGenPipeline = CreateComputePipeline(desc);
}
{
ComputePipelineDesc desc("DOF near CoC tile dilation");
desc.shortLifeTime = true;
desc.shader = ShaderByteCode(near_coc_tile_max::g_cs);
nearCocTileMaxPipeline = CreateComputePipeline(desc);
}
{
ComputePipelineDesc desc("DOF blur");
desc.shortLifeTime = true;
desc.shader = ShaderByteCode(blur::g_cs);
blurPipeline = CreateComputePipeline(desc);
}
{
ComputePipelineDesc desc("DOF fill");
desc.shortLifeTime = true;
desc.shader = ShaderByteCode(fill::g_cs);
fillPipeline = CreateComputePipeline(desc);
}
{
GraphicsPipelineDesc desc("DOF combine");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(combine::g_vs);
desc.pixelShader = ShaderByteCode(combine::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, renderTargetFormat);
combinePipeline = CreateGraphicsPipeline(desc);
}
{
GraphicsPipelineDesc desc("DOF viz");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(debug::g_vs);
desc.pixelShader = ShaderByteCode(debug::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, renderTargetFormat);
debugPipeline = CreateGraphicsPipeline(desc);
}
{
TextureDesc desc("DOF far field color", glConfig.vidWidth, glConfig.vidHeight);
desc.shortLifeTime = true;
desc.committedResource = true;
desc.initialState = ResourceStates::UnorderedAccessBit;
desc.allowedState = ResourceStates::UnorderedAccessBit | ResourceStates::ComputeShaderAccessBit | ResourceStates::PixelShaderAccessBit;
desc.format = renderTargetFormat;
farColorTexture = CreateTexture(desc);
desc.name = "DOF near field color";
nearColorTexture = CreateTexture(desc);
desc.name = "DOF near field blurred color";
nearBlurTexture = CreateTexture(desc);
desc.name = "DOF far field blurred color";
farBlurTexture = CreateTexture(desc);
desc.format = TextureFormat::R8_UNorm;
desc.name = "DOF near field CoC #1";
nearCocTexture = CreateTexture(desc);
desc.name = "DOF near field CoC #2";
nearCocTexture2 = CreateTexture(desc);
desc.name = "DOF far field CoC";
farCocTexture = CreateTexture(desc);
desc.width = tileWidth;
desc.height = tileHeight;
desc.name = "DOF near field CoC tile #1";
nearCocTileTexture = CreateTexture(desc);
desc.name = "DOF near field CoC tile #2";
nearCocTileTexture2 = CreateTexture(desc);
}
}
void GatherDepthOfField::Draw()
{
if(crp_dof->integer != DOFMethod::Gather)
{
return;
}
if(backEnd.viewParms.viewportX != 0 ||
backEnd.viewParms.viewportY != 0 ||
backEnd.viewParms.viewportWidth != glConfig.vidWidth ||
backEnd.viewParms.viewportHeight != glConfig.vidHeight)
{
return;
}
DrawSplit();
DrawNearCocTileGen();
DrawNearCocTileMax();
DrawBlur();
DrawFill();
DrawCombine();
DrawDebug();
}
void GatherDepthOfField::DrawDebug()
{
if(crp_dof_overlay->integer == 0)
{
return;
}
SCOPED_RENDER_PASS("DOF Debug", 0.125f, 0.125f, 0.25f);
crp.SwapRenderTargets();
const TextureBarrier barriers[] =
{
TextureBarrier(crp.GetReadRenderTarget(), ResourceStates::PixelShaderAccessBit),
TextureBarrier(crp.depthTexture, ResourceStates::PixelShaderAccessBit),
TextureBarrier(crp.GetWriteRenderTarget(), ResourceStates::RenderTargetBit)
};
CmdBarrier(ARRAY_LEN(barriers), barriers);
DOFDebugRC rc = {};
rc.colorTextureIndex = GetTextureIndexSRV(crp.GetReadRenderTarget());
rc.depthTextureIndex = GetTextureIndexSRV(crp.depthTexture);
rc.debugMode = crp_dof_overlay->integer;
RB_LinearDepthConstants(&rc.linearDepthA, &rc.linearDepthB);
rc.focusNearMin = crp_gatherDof_focusNearDist->value - 0.5f * crp_gatherDof_focusNearRange->value;
rc.focusNearMax = crp_gatherDof_focusNearDist->value + 0.5f * crp_gatherDof_focusNearRange->value;
rc.focusFarMin = crp_gatherDof_focusFarDist->value - 0.5f * crp_gatherDof_focusFarRange->value;
rc.focusFarMax = crp_gatherDof_focusFarDist->value + 0.5f * crp_gatherDof_focusFarRange->value;
rc.focusDist = 0.5f * (rc.focusNearMax + rc.focusFarMin);
CmdBindRenderTargets(1, &crp.renderTarget, NULL);
CmdBindPipeline(debugPipeline);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}
void GatherDepthOfField::DrawSplit()
{
SCOPED_RENDER_PASS("DOF Split", 0.125f, 0.125f, 0.25f);
const TextureBarrier barriers[] =
{
TextureBarrier(crp.depthTexture, ResourceStates::ComputeShaderAccessBit),
TextureBarrier(crp.renderTarget, ResourceStates::ComputeShaderAccessBit),
TextureBarrier(nearColorTexture, ResourceStates::UnorderedAccessBit),
TextureBarrier(farColorTexture, ResourceStates::UnorderedAccessBit),
TextureBarrier(nearCocTexture, ResourceStates::UnorderedAccessBit),
TextureBarrier(farCocTexture, ResourceStates::UnorderedAccessBit)
};
CmdBarrier(ARRAY_LEN(barriers), barriers);
DOFSplitRC rc = {};
rc.depthTextureIndex = GetTextureIndexSRV(crp.depthTexture);
rc.colorTextureIndex = GetTextureIndexSRV(crp.renderTarget);
rc.nearColorTextureIndex = GetTextureIndexUAV(nearColorTexture, 0);
rc.farColorTextureIndex = GetTextureIndexUAV(farColorTexture, 0);
rc.nearCocTextureIndex = GetTextureIndexUAV(nearCocTexture, 0);
rc.farCocTextureIndex = GetTextureIndexUAV(farCocTexture, 0);
RB_LinearDepthConstants(&rc.linearDepthA, &rc.linearDepthB);
rc.focusNearMin = crp_gatherDof_focusNearDist->value - 0.5f * crp_gatherDof_focusNearRange->value;
rc.focusNearMax = crp_gatherDof_focusNearDist->value + 0.5f * crp_gatherDof_focusNearRange->value;
rc.focusFarMin = crp_gatherDof_focusFarDist->value - 0.5f * crp_gatherDof_focusFarRange->value;
rc.focusFarMax = crp_gatherDof_focusFarDist->value + 0.5f * crp_gatherDof_focusFarRange->value;
rc.brightnessScale = crp_gatherDof_brightness->value;
CmdBindPipeline(splitPipeline);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
CmdDispatch((glConfig.vidWidth + 7) / 8, (glConfig.vidHeight + 7) / 8, 1);
}
void GatherDepthOfField::DrawNearCocTileGen()
{
SCOPED_RENDER_PASS("DOF Tile Gen", 0.125f, 0.125f, 0.25f);
const TextureBarrier barriers[] =
{
TextureBarrier(nearCocTexture, ResourceStates::ComputeShaderAccessBit),
TextureBarrier(nearCocTileTexture, ResourceStates::UnorderedAccessBit)
};
CmdBarrier(ARRAY_LEN(barriers), barriers);
DOFNearCocTileGenRC rc = {};
rc.inputTextureIndex = GetTextureIndexSRV(nearCocTexture);
rc.outputTextureIndex = GetTextureIndexUAV(nearCocTileTexture, 0);
CmdBindPipeline(nearCocTileGenPipeline);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
CmdDispatch((tileWidth + 7) / 8, (tileHeight + 7) / 8, 1);
}
void GatherDepthOfField::DrawNearCocTileMax()
{
SCOPED_RENDER_PASS("DOF Tile Max", 0.125f, 0.125f, 0.25f);
const TextureBarrier barriers[] =
{
TextureBarrier(nearCocTileTexture, ResourceStates::ComputeShaderAccessBit),
TextureBarrier(nearCocTileTexture2, ResourceStates::UnorderedAccessBit)
};
CmdBarrier(ARRAY_LEN(barriers), barriers);
DOFNearCocTileMaxRC rc = {};
rc.inputTextureIndex = GetTextureIndexSRV(nearCocTileTexture);
rc.outputTextureIndex = GetTextureIndexUAV(nearCocTileTexture2, 0);
rc.samplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Point);
CmdBindPipeline(nearCocTileMaxPipeline);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
CmdDispatch((tileWidth + 7) / 8, (tileHeight + 7) / 8, 1);
}
void GatherDepthOfField::DrawBlur()
{
SCOPED_RENDER_PASS("DOF Blur", 0.125f, 0.125f, 0.25f);
const TextureBarrier barriers[] =
{
TextureBarrier(crp.renderTarget, ResourceStates::ComputeShaderAccessBit),
TextureBarrier(nearColorTexture, ResourceStates::ComputeShaderAccessBit),
TextureBarrier(farColorTexture, ResourceStates::ComputeShaderAccessBit),
TextureBarrier(nearCocTexture, ResourceStates::ComputeShaderAccessBit),
TextureBarrier(nearCocTileTexture2, ResourceStates::ComputeShaderAccessBit),
TextureBarrier(farCocTexture, ResourceStates::ComputeShaderAccessBit),
TextureBarrier(nearBlurTexture, ResourceStates::UnorderedAccessBit),
TextureBarrier(farBlurTexture, ResourceStates::UnorderedAccessBit)
};
CmdBarrier(ARRAY_LEN(barriers), barriers);
DOFBlurRC rc = {};
rc.colorTextureIndex = GetTextureIndexSRV(crp.renderTarget);
rc.nearColorTextureIndex = GetTextureIndexSRV(nearColorTexture);
rc.nearMaxCocTextureIndex = GetTextureIndexSRV(nearCocTileTexture2);
rc.nearCocTextureIndex = GetTextureIndexSRV(nearCocTexture);
rc.nearOutputTextureIndex = GetTextureIndexUAV(nearBlurTexture, 0);
rc.farColorTextureIndex = GetTextureIndexSRV(farColorTexture);
rc.farCocTextureIndex = GetTextureIndexSRV(farCocTexture);
rc.farOutputTextureIndex = GetTextureIndexUAV(farBlurTexture, 0);
rc.samplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
rc.brightnessScale = crp_gatherDof_brightness->value;
rc.bladeCount = crp_dof_blades->value;
rc.bokehAngleRad = DEG2RAD(crp_dof_angle->value);
CmdBindPipeline(blurPipeline);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
CmdDispatch((glConfig.vidWidth + 7) / 8, (glConfig.vidHeight + 7) / 8, 1);
}
void GatherDepthOfField::DrawFill()
{
SCOPED_RENDER_PASS("DOF Fill", 0.125f, 0.125f, 0.25f);
const TextureBarrier barriers[] =
{
TextureBarrier(nearBlurTexture, ResourceStates::ComputeShaderAccessBit),
TextureBarrier(farBlurTexture, ResourceStates::ComputeShaderAccessBit),
TextureBarrier(nearColorTexture, ResourceStates::UnorderedAccessBit),
TextureBarrier(farColorTexture, ResourceStates::UnorderedAccessBit)
};
CmdBarrier(ARRAY_LEN(barriers), barriers);
DOFFillRC rc = {};
rc.nearInputTextureIndex = GetTextureIndexSRV(nearBlurTexture);
rc.farInputTextureIndex = GetTextureIndexSRV(farBlurTexture);
rc.nearOutputTextureIndex = GetTextureIndexUAV(nearColorTexture, 0);
rc.farOutputTextureIndex = GetTextureIndexUAV(farColorTexture, 0);
rc.samplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Point);
CmdBindPipeline(fillPipeline);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
CmdDispatch((glConfig.vidWidth + 7) / 8, (glConfig.vidHeight + 7) / 8, 1);
}
void GatherDepthOfField::DrawCombine()
{
SCOPED_RENDER_PASS("DOF Combine", 0.125f, 0.125f, 0.25f);
const TextureBarrier barriers[] =
{
TextureBarrier(nearColorTexture, ResourceStates::PixelShaderAccessBit),
TextureBarrier(farColorTexture, ResourceStates::PixelShaderAccessBit),
TextureBarrier(nearCocTexture, ResourceStates::PixelShaderAccessBit),
TextureBarrier(farCocTexture, ResourceStates::PixelShaderAccessBit),
TextureBarrier(crp.GetReadRenderTarget(), ResourceStates::PixelShaderAccessBit),
TextureBarrier(crp.GetWriteRenderTarget(), ResourceStates::RenderTargetBit)
};
CmdBarrier(ARRAY_LEN(barriers), barriers);
DOFCombineRC rc = {};
rc.nearTextureIndex = GetTextureIndexSRV(nearColorTexture);
rc.farTextureIndex = GetTextureIndexSRV(farColorTexture);
rc.nearCocTextureIndex = GetTextureIndexSRV(nearCocTexture);
rc.farCocTextureIndex = GetTextureIndexSRV(farCocTexture);
rc.sharpTextureIndex = GetTextureIndexSRV(crp.renderTarget);
rc.samplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Point);
CmdBindRenderTargets(1, &crp.renderTarget, NULL);
CmdBindPipeline(combinePipeline);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}

208
code/renderer/crp_fog.cpp Normal file
View file

@ -0,0 +1,208 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Cinematic Rendering Pipeline - fog volumes
#include "crp_local.h"
namespace fog_outside
{
#include "compshaders/crp/fog_outside_vs.h"
#include "compshaders/crp/fog_outside_ps.h"
}
namespace fog_inside
{
#include "compshaders/crp/fog_inside_vs.h"
#include "compshaders/crp/fog_inside_ps.h"
}
#pragma pack(push, 4)
struct FogRC
{
float modelViewMatrix[16];
float projectionMatrix[16];
float boxMin[4];
float boxMax[4];
float color[4];
float depth;
float linearDepthA;
float linearDepthB;
uint32_t depthTextureIndex;
};
#pragma pack(pop)
void Fog::Init()
{
{
const uint32_t indices[] =
{
0, 1, 2, 2, 1, 3,
4, 0, 6, 6, 0, 2,
7, 5, 6, 6, 5, 4,
3, 1, 7, 7, 1, 5,
4, 5, 0, 0, 5, 1,
3, 7, 2, 2, 7, 6
};
BufferDesc desc("box index", sizeof(indices), ResourceStates::IndexBufferBit);
desc.shortLifeTime = true;
boxIndexBuffer = CreateBuffer(desc);
uint8_t* mapped = BeginBufferUpload(boxIndexBuffer);
memcpy(mapped, indices, sizeof(indices));
EndBufferUpload(boxIndexBuffer);
}
{
const float vertices[] =
{
0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f
};
BufferDesc desc("box vertex", sizeof(vertices), ResourceStates::VertexBufferBit);
desc.shortLifeTime = true;
boxVertexBuffer = CreateBuffer(desc);
uint8_t* mapped = BeginBufferUpload(boxVertexBuffer);
memcpy(mapped, vertices, sizeof(vertices));
EndBufferUpload(boxVertexBuffer);
}
{
GraphicsPipelineDesc desc("fog outside");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(fog_outside::g_vs);
desc.pixelShader = ShaderByteCode(fog_outside::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_BACK_SIDED;
desc.rasterizer.polygonOffset = false;
desc.rasterizer.clampDepth = true;
desc.AddRenderTarget(GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA, crp.renderTargetFormat);
desc.vertexLayout.AddAttribute(0, ShaderSemantic::Position, DataType::Float32, 3, 0);
fogOutsidePipeline = CreateGraphicsPipeline(desc);
}
{
GraphicsPipelineDesc desc("fog inside");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(fog_inside::g_vs);
desc.pixelShader = ShaderByteCode(fog_inside::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_FRONT_SIDED;
desc.rasterizer.polygonOffset = false;
desc.rasterizer.clampDepth = true;
desc.AddRenderTarget(GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA, crp.renderTargetFormat);
desc.vertexLayout.AddAttribute(0, ShaderSemantic::Position, DataType::Float32, 3, 0);
fogInsidePipeline = CreateGraphicsPipeline(desc);
}
}
void Fog::Draw()
{
// @NOTE: fog 0 is invalid, it must be skipped
if(tr.world == NULL ||
tr.world->numfogs <= 1 ||
(backEnd.refdef.rdflags & RDF_NOWORLDMODEL) != 0)
{
return;
}
SCOPED_RENDER_PASS("Fog", 0.25f, 0.125f, 0.0f);
srp.renderMode = RenderMode::World;
const uint32_t stride = sizeof(vec3_t);
CmdBindVertexBuffers(1, &boxVertexBuffer, &stride, NULL);
CmdBindIndexBuffer(boxIndexBuffer, IndexType::UInt32, 0);
const TextureBarrier barriers[] =
{
TextureBarrier(crp.depthTexture, ResourceStates::PixelShaderAccessBit),
TextureBarrier(crp.renderTarget, ResourceStates::RenderTargetBit)
};
CmdBarrier(ARRAY_LEN(barriers), barriers);
CmdBindRenderTargets(1, &crp.renderTarget, NULL);
int insideIndex = -1;
for(int f = 1; f < tr.world->numfogs; ++f)
{
const fog_t& fog = tr.world->fogs[f];
bool inside = true;
for(int a = 0; a < 3; ++a)
{
if(backEnd.viewParms.orient.origin[a] <= fog.bounds[0][a] ||
backEnd.viewParms.orient.origin[a] >= fog.bounds[1][a])
{
inside = false;
break;
}
}
if(inside)
{
insideIndex = f;
break;
}
}
FogRC rc = {};
memcpy(rc.modelViewMatrix, backEnd.viewParms.world.modelMatrix, sizeof(rc.modelViewMatrix));
memcpy(rc.projectionMatrix, backEnd.viewParms.projectionMatrix, sizeof(rc.projectionMatrix));
RB_LinearDepthConstants(&rc.linearDepthA, &rc.linearDepthB);
rc.depthTextureIndex = GetTextureIndexSRV(crp.depthTexture);
CmdBindPipeline(fogOutsidePipeline);
for(int f = 1; f < tr.world->numfogs; ++f)
{
if(f == insideIndex)
{
continue;
}
const fog_t& fog = tr.world->fogs[f];
VectorScale(fog.parms.color, tr.identityLight, rc.color);
rc.depth = fog.parms.depthForOpaque;
VectorCopy(fog.bounds[0], rc.boxMin);
VectorCopy(fog.bounds[1], rc.boxMax);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDrawIndexed(36, 0, 0);
}
if(insideIndex > 0)
{
CmdBindPipeline(fogInsidePipeline);
const fog_t& fog = tr.world->fogs[insideIndex];
VectorScale(fog.parms.color, tr.identityLight, rc.color);
rc.depth = fog.parms.depthForOpaque;
VectorCopy(fog.bounds[0], rc.boxMin);
VectorCopy(fog.bounds[1], rc.boxMax);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDrawIndexed(36, 0, 0);
}
}

View file

@ -0,0 +1,155 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Cinematic Rendering Pipeline - vertex and index buffer management
#include "crp_local.h"
void GeoBuffers::Create(const char* name, uint32_t vertexCount, uint32_t indexCount)
{
baseVertexBuffers[BaseBufferId::Position].CreateVertexBuffer(
va("%s position", name), MemoryUsage::Upload, vertexCount, sizeof(vec3_t));
baseVertexBuffers[BaseBufferId::Normal].CreateVertexBuffer(
va("%s normal", name), MemoryUsage::Upload, vertexCount, sizeof(vec3_t));
stageVertexBuffers[StageBufferId::TexCoords].CreateVertexBuffer(
va("%s tc", name), MemoryUsage::Upload, vertexCount * MAX_SHADER_STAGES, sizeof(vec2_t));
stageVertexBuffers[StageBufferId::Color].CreateVertexBuffer(
va("%s color", name), MemoryUsage::Upload, vertexCount * MAX_SHADER_STAGES, sizeof(color4ub_t));
indexBuffer.Create(name, MemoryUsage::Upload, indexCount);
vertexBuffers[0] = baseVertexBuffers[BaseBufferId::Position].buffer;
vertexBuffers[1] = baseVertexBuffers[BaseBufferId::Normal].buffer;
vertexBuffers[2] = stageVertexBuffers[StageBufferId::TexCoords].buffer;
vertexBuffers[3] = stageVertexBuffers[StageBufferId::Color].buffer;
vertexBufferStrides[0] = sizeof(vec3_t);
vertexBufferStrides[1] = sizeof(vec3_t);
vertexBufferStrides[2] = sizeof(vec2_t);
vertexBufferStrides[3] = sizeof(color4ub_t);
}
void GeoBuffers::Rewind()
{
for(uint32_t b = 0; b < ARRAY_LEN(baseVertexBuffers); ++b)
{
baseVertexBuffers[b].Rewind();
}
for(uint32_t b = 0; b < ARRAY_LEN(stageVertexBuffers); ++b)
{
stageVertexBuffers[b].Rewind();
}
indexBuffer.Rewind();
}
void GeoBuffers::BeginUpload()
{
for(uint32_t b = 0; b < ARRAY_LEN(baseVertexBuffers); ++b)
{
baseVertexBuffers[b].BeginUpload();
}
for(uint32_t b = 0; b < ARRAY_LEN(stageVertexBuffers); ++b)
{
stageVertexBuffers[b].BeginUpload();
}
indexBuffer.BeginUpload();
}
void GeoBuffers::EndUpload()
{
for(uint32_t b = 0; b < ARRAY_LEN(baseVertexBuffers); ++b)
{
baseVertexBuffers[b].EndUpload();
}
for(uint32_t b = 0; b < ARRAY_LEN(stageVertexBuffers); ++b)
{
stageVertexBuffers[b].EndUpload();
}
indexBuffer.EndUpload();
}
void GeoBuffers::UploadBase()
{
indexBuffer.Upload();
const uint32_t batchOffset = baseVertexBuffers[0].batchFirst + baseVertexBuffers[0].batchCount;
float* pos = (float*)baseVertexBuffers[BaseBufferId::Position].mapped + 3 * batchOffset;
for(int v = 0; v < tess.numVertexes; ++v)
{
pos[0] = tess.xyz[v][0];
pos[1] = tess.xyz[v][1];
pos[2] = tess.xyz[v][2];
pos += 3;
}
float* nor = (float*)baseVertexBuffers[BaseBufferId::Normal].mapped + 3 * batchOffset;
for(int v = 0; v < tess.numVertexes; ++v)
{
nor[0] = tess.normal[v][0];
nor[1] = tess.normal[v][1];
nor[2] = tess.normal[v][2];
nor += 3;
}
}
void GeoBuffers::UploadStage(uint32_t svarsIndex)
{
const uint32_t batchOffset = stageVertexBuffers[0].batchFirst + stageVertexBuffers[0].batchCount;
const stageVars_t& sv = tess.svars[svarsIndex];
uint8_t* const tcBuffer = stageVertexBuffers[StageBufferId::TexCoords].mapped;
float* tc = (float*)tcBuffer + 2 * batchOffset;
memcpy(tc, &sv.texcoords[0], tess.numVertexes * sizeof(vec2_t));
uint8_t* const colBuffer = stageVertexBuffers[StageBufferId::Color].mapped;
uint32_t* col = (uint32_t*)colBuffer + batchOffset;
memcpy(col, &sv.colors[0], tess.numVertexes * sizeof(color4ub_t));
}
void GeoBuffers::EndBaseBatch(uint32_t vertexCount)
{
baseVertexBuffers[BaseBufferId::Position].EndBatch(vertexCount);
baseVertexBuffers[BaseBufferId::Normal].EndBatch(vertexCount);
indexBuffer.EndBatch(tess.numIndexes);
}
bool GeoBuffers::CanAdd(uint32_t vertexCount, uint32_t indexCount, uint32_t stageCount)
{
return
baseVertexBuffers[0].CanAdd(vertexCount) &&
stageVertexBuffers[0].CanAdd(vertexCount * stageCount) &&
indexBuffer.CanAdd(indexCount);
}
void GeoBuffers::DrawStage(uint32_t vertexCount, uint32_t indexCount)
{
const uint32_t vertexOffset = stageVertexBuffers[0].batchFirst - baseVertexBuffers[0].batchFirst;
uint32_t byteOffsets[BaseBufferId::Count + StageBufferId::Count] = {};
byteOffsets[BaseBufferId::Count + StageBufferId::TexCoords] = vertexOffset * sizeof(vec2_t);
byteOffsets[BaseBufferId::Count + StageBufferId::Color] = vertexOffset * sizeof(color4ub_t);
CmdBindVertexBuffers(ARRAY_LEN(vertexBuffers), vertexBuffers, vertexBufferStrides, byteOffsets);
CmdDrawIndexed(indexCount, indexBuffer.batchFirst, baseVertexBuffers[0].batchFirst);
// @NOTE: must happen after the final vertex buffer byte offsets have been computed
stageVertexBuffers[StageBufferId::TexCoords].EndBatch(vertexCount);
stageVertexBuffers[StageBufferId::Color].EndBatch(vertexCount);
}

330
code/renderer/crp_local.h Normal file
View file

@ -0,0 +1,330 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Cinematic Rendering Pipeline - private declarations
#pragma once
#include "srp_local.h"
extern cvar_t* crp_dof;
extern cvar_t* crp_dof_overlay;
extern cvar_t* crp_dof_blades;
extern cvar_t* crp_dof_angle;
extern cvar_t* crp_gatherDof_focusNearDist;
extern cvar_t* crp_gatherDof_focusNearRange;
extern cvar_t* crp_gatherDof_focusFarDist;
extern cvar_t* crp_gatherDof_focusFarRange;
extern cvar_t* crp_gatherDof_brightness;
extern cvar_t* crp_accumDof_focusDist;
extern cvar_t* crp_accumDof_radius;
extern cvar_t* crp_accumDof_samples;
extern cvar_t* crp_accumDof_preview;
struct DOFMethod
{
enum Id
{
None,
Gather,
Accumulation,
Count
};
};
struct Tessellator
{
enum Id
{
None,
Opaque,
Transp,
Count
};
};
using namespace RHI;
struct WorldVertexRC
{
float modelViewMatrix[16];
float projectionMatrix[16];
float clipPlane[4];
};
struct PSOCache
{
struct Entry
{
GraphicsPipelineDesc desc;
HPipeline handle;
};
void Init(Entry* entries, uint32_t maxEntryCount);
int AddPipeline(const GraphicsPipelineDesc& desc, const char* name);
Entry* entries = NULL;
uint32_t maxEntryCount = 0;
uint32_t entryCount = 1; // we treat index 0 as invalid
};
struct WorldOpaque
{
void Init();
void Draw(const drawSceneViewCommand_t& cmd);
void ProcessShader(shader_t& shader);
void TessellationOverflow();
void DrawSkyBox();
void DrawClouds();
private:
void BeginBatch(const shader_t* shader);
void EndBatch();
void EndSkyBatch();
PSOCache::Entry psoCacheEntries[128];
PSOCache psoCache;
float clipPlane[4];
bool batchOldDepthHack;
bool batchDepthHack;
};
struct WorldTransp
{
void Init();
void Draw(const drawSceneViewCommand_t& cmd);
void ProcessShader(shader_t& shader);
void TessellationOverflow();
private:
void BeginBatch(const shader_t* shader);
void EndBatch();
PSOCache::Entry psoCacheEntries[32];
PSOCache psoCache;
float clipPlane[4];
bool batchOldDepthHack;
bool batchDepthHack;
};
struct Fog
{
void Init();
void Draw();
private:
HBuffer boxIndexBuffer;
HBuffer boxVertexBuffer;
HPipeline fogInsidePipeline;
HPipeline fogOutsidePipeline;
};
struct TranspResolve
{
void Init();
void Draw(const drawSceneViewCommand_t& cmd);
private:
HPipeline pipeline;
};
struct ToneMap
{
void Init();
void DrawToneMap();
void DrawInverseToneMap();
private:
HPipeline pipeline;
HPipeline inversePipeline;
};
struct AccumDepthOfField
{
void Init();
void Begin(const drawSceneViewCommand_t& cmd);
uint32_t GetSampleCount();
void FixCommand(drawSceneViewCommand_t& newCmd, const drawSceneViewCommand_t& cmd, uint32_t x, uint32_t y);
void Accumulate();
void Normalize();
void DrawDebug();
private:
HPipeline accumPipeline;
HPipeline normPipeline;
HPipeline debugPipeline;
HTexture accumTexture;
float maxNearCocCS;
float maxFarCocCS;
float modelViewMatrix[16];
float projMatrix[16];
};
struct GatherDepthOfField
{
void Init();
void Draw();
private:
void DrawDebug();
void DrawSplit();
void DrawNearCocTileGen();
void DrawNearCocTileMax();
void DrawBlur();
void DrawFill();
void DrawCombine();
HPipeline debugPipeline;
HPipeline splitPipeline;
HPipeline nearCocTileGenPipeline;
HPipeline nearCocTileMaxPipeline;
HPipeline blurPipeline;
HPipeline fillPipeline;
HPipeline combinePipeline;
HTexture nearColorTexture;
HTexture farColorTexture;
HTexture nearBlurTexture;
HTexture farBlurTexture;
HTexture nearCocTexture;
HTexture nearCocTexture2;
HTexture nearCocTileTexture;
HTexture nearCocTileTexture2;
HTexture farCocTexture;
uint32_t tileWidth;
uint32_t tileHeight;
};
struct BaseBufferId
{
enum Id
{
Position,
Normal,
Count
};
};
struct StageBufferId
{
enum Id
{
TexCoords,
Color,
Count
};
};
struct GeoBuffers
{
void Create(const char* name, uint32_t vertexCount, uint32_t indexCount);
void Rewind();
void BeginUpload();
void EndUpload();
void UploadBase();
void UploadStage(uint32_t svarsIndex);
void EndBaseBatch(uint32_t vertexCount);
bool CanAdd(uint32_t vertexCount, uint32_t indexCount, uint32_t stageCount);
void DrawStage(uint32_t vertexCount, uint32_t indexCount);
GeometryBuffer baseVertexBuffers[BaseBufferId::Count];
GeometryBuffer stageVertexBuffers[StageBufferId::Count];
IndexBuffer indexBuffer;
HBuffer vertexBuffers[BaseBufferId::Count + StageBufferId::Count];
uint32_t vertexBufferStrides[BaseBufferId::Count + StageBufferId::Count];
};
struct CRP : IRenderPipeline
{
void Init() override;
void ShutDown(bool fullShutDown) override;
void ProcessWorld(world_t& world) override;
void ProcessModel(model_t& model) override;
void ProcessShader(shader_t& shader) override;
void CreateTexture(image_t* image, int mipCount, int width, int height) override;
void UpoadTextureAndGenerateMipMaps(image_t* image, const byte* data) override;
void BeginTextureUpload(MappedTexture& mappedTexture, image_t* image) override;
void EndTextureUpload() override;
void ExecuteRenderCommands(const byte* data, bool readbackRequested) override;
void TessellationOverflow() override;
void DrawSkyBox() override { opaque.DrawSkyBox(); }
void DrawClouds() override { opaque.DrawClouds(); }
void ReadPixels(int w, int h, int alignment, colorSpace_t colorSpace, void* out) override;
uint32_t GetSamplerDescriptorIndexFromBaseIndex(uint32_t baseIndex) override;
void BeginFrame();
void EndFrame();
void Blit(HTexture destination, HTexture source, const char* passName, bool hdr, const vec2_t tcScale, const vec2_t tcBias);
void BlitRenderTarget(HTexture destination, const char* passName);
void DrawSceneView(const drawSceneViewCommand_t& cmd);
HTexture GetReadRenderTarget();
HTexture GetWriteRenderTarget();
void SwapRenderTargets();
// general
float frameSeed;
HTexture readbackRenderTarget;
HTexture depthTexture;
HTexture renderTarget;
TextureFormat::Id renderTargetFormat;
HTexture renderTargets[2];
uint32_t renderTargetIndex; // the one to write to
HSampler samplers[BASE_SAMPLER_COUNT]; // all base samplers
uint32_t samplerIndices[BASE_SAMPLER_COUNT]; // descriptor heap indices
// blit
HPipeline blitPipelineLDR;
HPipeline blitPipelineHDR;
// world geometry
GeoBuffers dynBuffers[FrameCount]; // for rendering world surfaces
// for rendering transparent world surfaces
HTexture oitIndexTexture;
HBuffer oitFragmentBuffer;
HBuffer oitCounterBuffer;
HBuffer oitCounterStagingBuffer;
UI ui;
MipMapGenerator mipMapGen;
ImGUI imgui;
Nuklear nuklear;
WorldOpaque opaque;
WorldTransp transp;
TranspResolve transpResolve;
ToneMap toneMap;
GatherDepthOfField gatherDof;
AccumDepthOfField accumDof;
Fog fog;
};
extern CRP crp;

677
code/renderer/crp_main.cpp Normal file
View file

@ -0,0 +1,677 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Cinematic Rendering Pipeline - main interface
#include "crp_local.h"
#include "../client/cl_imgui.h"
#include "shaders/crp/oit.h.hlsli"
namespace blit
{
#include "compshaders/crp/blit_vs.h"
#include "compshaders/crp/blit_ps.h"
}
namespace ui
{
#include "compshaders/crp/ui_vs.h"
#include "compshaders/crp/ui_ps.h"
}
namespace imgui
{
#include "compshaders/crp/imgui_vs.h"
#include "compshaders/crp/imgui_ps.h"
}
namespace nuklear
{
#include "compshaders/crp/nuklear_vs.h"
#include "compshaders/crp/nuklear_ps.h"
}
namespace mip_1
{
#include "compshaders/crp/mip_1_cs.h"
}
namespace mip_2
{
#include "compshaders/crp/mip_2_cs.h"
}
namespace mip_3
{
#include "compshaders/crp/mip_3_cs.h"
}
CRP crp;
IRenderPipeline* crpp = &crp;
cvar_t* crp_dof;
cvar_t* crp_dof_overlay;
cvar_t* crp_dof_blades;
cvar_t* crp_dof_angle;
cvar_t* crp_gatherDof_focusNearDist;
cvar_t* crp_gatherDof_focusNearRange;
cvar_t* crp_gatherDof_focusFarDist;
cvar_t* crp_gatherDof_focusFarRange;
cvar_t* crp_gatherDof_brightness;
cvar_t* crp_accumDof_focusDist;
cvar_t* crp_accumDof_radius;
cvar_t* crp_accumDof_samples;
cvar_t* crp_accumDof_preview;
static const cvarTableItem_t crp_cvars[] =
{
{
&crp_dof, "crp_dof", "1", CVAR_ARCHIVE, CVART_INTEGER, "0", "2",
"enables depth of field\n"
S_COLOR_VAL " 0 " S_COLOR_HELP "= Disabled\n"
S_COLOR_VAL " 1 " S_COLOR_HELP "= Gather (fast, more flexible, issues with transparency)\n"
S_COLOR_VAL " 2 " S_COLOR_HELP "= Accumulation (slow, less flexible, great IQ)\n",
"DoF mode", CVARCAT_GRAPHICS, "Depth of field mode", "",
CVAR_GUI_VALUE("0", "Disabled", "")
CVAR_GUI_VALUE("1", "Gather", "Fast, lower IQ")
CVAR_GUI_VALUE("2", "Accumulation", "Very slow, great IQ")
},
{
&crp_dof_overlay, "crp_dof_overlay", "0", CVAR_ARCHIVE, CVART_INTEGER, "0", "2",
"debug overlay mode\n"
S_COLOR_VAL " 0 " S_COLOR_HELP "= Disabled\n"
S_COLOR_VAL " 1 " S_COLOR_HELP "= Colorized Blur\n"
S_COLOR_VAL " 2 " S_COLOR_HELP "= Focus Plane",
"DoF overlay mode", CVARCAT_GRAPHICS, "Debug overlay mode", "",
CVAR_GUI_VALUE("0", "Disabled", "")
CVAR_GUI_VALUE("1", "Colorized Blur", "")
CVAR_GUI_VALUE("2", "Focus Plane", "")
},
{
&crp_dof_blades, "crp_dof_blades", "6", CVAR_ARCHIVE, CVART_FLOAT, "0", "16",
"aperture blade count\n"
"Set to less than 3 for a disk shape.",
"DoF blade count", CVARCAT_GRAPHICS, "Aperture blade count", "Set to less than 3 for a disk shape."
},
{
&crp_dof_angle, "crp_dof_angle", "20", CVAR_ARCHIVE, CVART_FLOAT, "0", "360", "aperture angle, in degrees",
"DoF aperture angle", CVARCAT_GRAPHICS, "Aperture angle, in degrees", ""
},
{
&crp_accumDof_focusDist, "crp_accumDof_focusDist", "256", CVAR_ARCHIVE, CVART_FLOAT, "2", "2048", "focus distance",
"Accum DoF focus distance", CVARCAT_GRAPHICS, "Focus distance", ""
},
{
&crp_accumDof_radius, "crp_accumDof_blurRadius", "0.1", CVAR_ARCHIVE, CVART_FLOAT, "0.001", "20", "aperture radius in world units",
"Accum DoF aperture radius", CVARCAT_GRAPHICS, "Aperture radius in world units", ""
},
{
&crp_accumDof_samples, "crp_accumDof_samples", "2", CVAR_ARCHIVE, CVART_INTEGER, "1", "12",
"per-axis sampling density\n"
"Density N means (2N + 1)(2N + 1) scene renders in total.",
"Accum DoF sample count", CVARCAT_GRAPHICS, "Per-axis sampling density", "Density N means (2N + 1)^2 scene renders in total."
},
{
&crp_accumDof_preview, "crp_accumDof_preview", "0", CVAR_ARCHIVE, CVART_INTEGER, "0", "2",
"low-res preview mode\n"
S_COLOR_VAL " 0 " S_COLOR_HELP "= Disabled\n"
S_COLOR_VAL " 1 " S_COLOR_HELP "= 1/4 pixel count, 9 samples total\n"
S_COLOR_VAL " 2 " S_COLOR_HELP "= 1/16 pixel count, 25 samples total",
"Accum DoF preview mode", CVARCAT_GRAPHICS, "Low-resolution preview modes", "",
CVAR_GUI_VALUE("0", "Disabled", "")
CVAR_GUI_VALUE("1", "1/4 pixel count", "9 samples total")
CVAR_GUI_VALUE("2", "1/16 pixel count", "25 samples total")
},
{
&crp_gatherDof_focusNearDist, "crp_gatherDof_focusNearDist", "192", CVAR_ARCHIVE, CVART_FLOAT, "1", "2048", "near focus distance",
"Gather DoF near focus distance", CVARCAT_GRAPHICS, "Near focus distance", ""
},
{
&crp_gatherDof_focusNearRange, "crp_gatherDof_focusNearRange", "256", CVAR_ARCHIVE, CVART_FLOAT, "1", "2048", "near focus range",
"Gather DoF near focus range", CVARCAT_GRAPHICS, "Near focus range", ""
},
{
&crp_gatherDof_focusFarDist, "crp_gatherDof_focusFarDist", "512", CVAR_ARCHIVE, CVART_FLOAT, "1", "2048", "far focus distance",
"Gather DoF far focus distance", CVARCAT_GRAPHICS, "Far focus distance", ""
},
{
&crp_gatherDof_focusFarRange, "crp_gatherDof_focusFarRange", "384", CVAR_ARCHIVE, CVART_FLOAT, "1", "2048", "far focus range",
"Gather DoF far focus range", CVARCAT_GRAPHICS, "Far focus range", ""
},
{
&crp_gatherDof_brightness, "crp_gatherDof_brightness", "2", CVAR_ARCHIVE, CVART_FLOAT, "0", "8", "blur brightness weight",
"Gather DoF bokeh brightness", CVARCAT_GRAPHICS, "Blur brightness weight", ""
}
};
void PSOCache::Init(Entry* entries_, uint32_t maxEntryCount_)
{
entries = entries_;
maxEntryCount = maxEntryCount_;
entryCount = 1; // we treat index 0 as invalid
}
int PSOCache::AddPipeline(const GraphicsPipelineDesc& desc, const char* name)
{
// we treat index 0 as invalid, so start at 1
for(uint32_t i = 1; i < entryCount; ++i)
{
Entry& entry = entries[i];
if(memcmp(&entry.desc, &desc, sizeof(desc)) == 0)
{
return (int)i;
}
}
ASSERT_OR_DIE(entryCount < maxEntryCount, "Not enough entries in the PSO cache");
GraphicsPipelineDesc namedDesc = desc;
namedDesc.name = name;
const uint32_t index = entryCount++;
Entry& entry = entries[index];
entry.desc = desc; // keep the original desc for proper comparison results
entry.handle = CreateGraphicsPipeline(namedDesc);
return (int)index;
}
void CRP::Init()
{
static bool veryFirstInit = true;
if(veryFirstInit)
{
ri.Cvar_RegisterTable(crp_cvars, ARRAY_LEN(crp_cvars));
veryFirstInit = false;
}
InitDesc initDesc;
initDesc.directDescriptorHeapIndexing = true;
srp.firstInit = RHI::Init(initDesc);
srp.psoStatsValid = false;
if(srp.firstInit)
{
srp.CreateShaderTraceBuffers();
for(uint32_t f = 0; f < FrameCount; ++f)
{
// the doubled index count is for the depth pre-pass
const int MaxDynamicVertexCount = 16 << 20;
const int MaxDynamicIndexCount = MaxDynamicVertexCount * 4;
GeoBuffers& db = dynBuffers[f];
db.Create(va("world #%d", f + 1), MaxDynamicVertexCount, MaxDynamicIndexCount);
}
}
// we recreate the samplers on every vid_restart to create the right level
// of anisotropy based on the latched CVar
for(uint32_t w = 0; w < TW_COUNT; ++w)
{
for(uint32_t f = 0; f < TextureFilter::Count; ++f)
{
for(uint32_t m = 0; m < MaxTextureMips; ++m)
{
const textureWrap_t wrap = (textureWrap_t)w;
const TextureFilter::Id filter = (TextureFilter::Id)f;
const uint32_t s = GetBaseSamplerIndex(wrap, filter, m);
SamplerDesc desc(wrap, filter, (float)m);
desc.shortLifeTime = true;
samplers[s] = CreateSampler(desc);
samplerIndices[s] = RHI::GetSamplerIndex(samplers[s]);
}
}
}
{
renderTargetFormat = TextureFormat::RGBA64_Float;
TextureDesc desc("render target #1", glConfig.vidWidth, glConfig.vidHeight);
desc.initialState = ResourceStates::RenderTargetBit;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit;
Vector4Clear(desc.clearColor);
desc.usePreferredClearValue = true;
desc.committedResource = true;
desc.format = renderTargetFormat;
desc.shortLifeTime = true;
renderTargets[0] = RHI::CreateTexture(desc);
desc.name = "render target #2";
renderTargets[1] = RHI::CreateTexture(desc);
renderTargetIndex = 0;
renderTarget = renderTargets[0];
}
{
TextureDesc desc("readback render target", glConfig.vidWidth, glConfig.vidHeight);
desc.initialState = ResourceStates::RenderTargetBit;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit;
Vector4Clear(desc.clearColor);
desc.usePreferredClearValue = true;
desc.committedResource = true;
desc.format = TextureFormat::RGBA32_UNorm;
desc.shortLifeTime = true;
readbackRenderTarget = RHI::CreateTexture(desc);
}
{
TextureDesc desc("OIT index", glConfig.vidWidth, glConfig.vidHeight);
desc.initialState = ResourceStates::UnorderedAccessBit;
desc.allowedState = ResourceStates::UnorderedAccessBit | ResourceStates::PixelShaderAccessBit | ResourceStates::ComputeShaderAccessBit;
desc.committedResource = true;
desc.format = TextureFormat::R32_UInt;
desc.shortLifeTime = true;
oitIndexTexture = RHI::CreateTexture(desc);
}
uint32_t oitMaxFragmentCount = 0;
{
const int byteCountPerFragment = sizeof(OIT_Fragment);
const int fragmentCount = glConfig.vidWidth * glConfig.vidHeight * OIT_AVG_FRAGMENTS_PER_PIXEL;
const int byteCount = byteCountPerFragment * fragmentCount;
oitMaxFragmentCount = fragmentCount;
BufferDesc desc("OIT fragment", byteCount, ResourceStates::UnorderedAccessBit);
desc.committedResource = true;
desc.memoryUsage = MemoryUsage::GPU;
desc.structureByteCount = byteCountPerFragment;
desc.shortLifeTime = true;
oitFragmentBuffer = CreateBuffer(desc);
}
{
const int byteCount = sizeof(OIT_Counter);
{
BufferDesc desc("OIT counter", byteCount, ResourceStates::UnorderedAccessBit);
desc.committedResource = true;
desc.memoryUsage = MemoryUsage::GPU;
desc.structureByteCount = byteCount;
desc.shortLifeTime = true;
oitCounterBuffer = CreateBuffer(desc);
}
{
BufferDesc desc("OIT counter staging", byteCount, ResourceStates::Common);
desc.committedResource = false;
desc.memoryUsage = MemoryUsage::Upload;
desc.structureByteCount = byteCount;
desc.shortLifeTime = true;
oitCounterStagingBuffer = CreateBuffer(desc);
uint32_t* dst = (uint32_t*)MapBuffer(oitCounterStagingBuffer);
dst[0] = 1; // fragment index 0 is the end-of-list value
dst[1] = oitMaxFragmentCount;
dst[2] = 0;
UnmapBuffer(oitCounterStagingBuffer);
}
}
{
TextureDesc desc("depth buffer", glConfig.vidWidth, glConfig.vidHeight);
desc.committedResource = true;
desc.shortLifeTime = true;
desc.initialState = ResourceStates::DepthWriteBit;
desc.allowedState = ResourceStates::DepthAccessBits | ResourceStates::PixelShaderAccessBit;
desc.format = TextureFormat::Depth32_Float;
desc.SetClearDepthStencil(0.0f, 0);
depthTexture = RHI::CreateTexture(desc);
}
{
GraphicsPipelineDesc desc("blit LDR");
desc.vertexShader = ShaderByteCode(blit::g_vs);
desc.pixelShader = ShaderByteCode(blit::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, TextureFormat::RGBA32_UNorm);
blitPipelineLDR = CreateGraphicsPipeline(desc);
desc.name = "blit HDR";
desc.renderTargets[0].format = TextureFormat::RGBA64_Float;
blitPipelineHDR = CreateGraphicsPipeline(desc);
}
ui.Init(true, ShaderByteCode(ui::g_vs), ShaderByteCode(ui::g_ps), renderTargetFormat, RHI_MAKE_NULL_HANDLE(), NULL);
imgui.Init(true, ShaderByteCode(imgui::g_vs), ShaderByteCode(imgui::g_ps), renderTargetFormat, RHI_MAKE_NULL_HANDLE(), NULL);
nuklear.Init(true, ShaderByteCode(nuklear::g_vs), ShaderByteCode(nuklear::g_ps), renderTargetFormat, RHI_MAKE_NULL_HANDLE(), NULL);
mipMapGen.Init(true, ShaderByteCode(mip_1::g_cs), ShaderByteCode(mip_2::g_cs), ShaderByteCode(mip_3::g_cs));
opaque.Init();
transp.Init();
transpResolve.Init();
toneMap.Init();
gatherDof.Init();
accumDof.Init();
fog.Init();
srp.firstInit = false;
}
void CRP::ShutDown(bool fullShutDown)
{
RHI::ShutDown(fullShutDown);
}
void CRP::BeginFrame()
{
renderTargetIndex = 0;
renderTarget = renderTargets[0];
srp.BeginFrame();
// have it be first to we can use ImGUI in the other components too
imgui.BeginFrame();
RHI::BeginFrame();
ui.BeginFrame();
nuklear.BeginFrame();
const float clearColor[4] = { 0.0f, 0.5f, 0.0f, 0.0f };
const TextureBarrier barrier(renderTarget, ResourceStates::RenderTargetBit);
CmdBarrier(1, &barrier);
CmdClearColorTarget(renderTarget, clearColor);
frameSeed = (float)rand() / (float)RAND_MAX;
dynBuffers[GetFrameIndex()].Rewind();
}
void CRP::EndFrame()
{
srp.DrawGUI();
imgui.Draw(renderTarget);
toneMap.DrawToneMap();
BlitRenderTarget(GetSwapChainTexture(), "Blit to Swap Chain");
BlitRenderTarget(readbackRenderTarget, "Blit to Readback Texture");
srp.EndFrame();
}
void CRP::Blit(HTexture destination, HTexture source, const char* passName, bool hdr, const vec2_t tcScale, const vec2_t tcBias)
{
SCOPED_RENDER_PASS(passName, 0.125f, 0.125f, 0.5f);
const TextureBarrier barriers[2] =
{
TextureBarrier(source, ResourceStates::PixelShaderAccessBit),
TextureBarrier(destination, ResourceStates::RenderTargetBit)
};
CmdBarrier(ARRAY_LEN(barriers), barriers);
#pragma pack(push, 4)
struct BlitRC
{
uint32_t textureIndex;
uint32_t samplerIndex;
float tcScale[2];
float tcBias[2];
};
#pragma pack(pop)
BlitRC rc;
rc.textureIndex = GetTextureIndexSRV(source);
rc.samplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
rc.tcScale[0] = tcScale[0];
rc.tcScale[1] = tcScale[1];
rc.tcBias[0] = tcBias[0];
rc.tcBias[1] = tcBias[0];
CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight);
CmdBindRenderTargets(1, &destination, NULL);
CmdBindPipeline(hdr ? blitPipelineHDR : blitPipelineLDR);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}
void CRP::BlitRenderTarget(HTexture destination, const char* passName)
{
Blit(destination, crp.renderTarget, passName, false, vec2_one, vec2_zero);
}
void CRP::CreateTexture(image_t* image, int mipCount, int width, int height)
{
TextureDesc desc(image->name, width, height, mipCount);
desc.committedResource = width * height >= (1 << 20);
desc.shortLifeTime = true;
if(mipCount > 1)
{
desc.allowedState |= ResourceStates::UnorderedAccessBit; // for mip-map generation
}
image->texture = ::RHI::CreateTexture(desc);
image->textureIndex = GetTextureIndexSRV(image->texture);
}
void CRP::UpoadTextureAndGenerateMipMaps(image_t* image, const byte* data)
{
MappedTexture texture;
RHI::BeginTextureUpload(texture, image->texture);
for(uint32_t r = 0; r < texture.rowCount; ++r)
{
memcpy(texture.mappedData + r * texture.dstRowByteCount, data + r * texture.srcRowByteCount, texture.srcRowByteCount);
}
RHI::EndTextureUpload();
mipMapGen.GenerateMipMaps(image->texture);
}
void CRP::BeginTextureUpload(MappedTexture& mappedTexture, image_t* image)
{
RHI::BeginTextureUpload(mappedTexture, image->texture);
}
void CRP::EndTextureUpload()
{
RHI::EndTextureUpload();
}
void CRP::ProcessWorld(world_t&)
{
}
void CRP::ProcessModel(model_t&)
{
}
void CRP::ProcessShader(shader_t& shader)
{
if(shader.isOpaque)
{
opaque.ProcessShader(shader);
}
else
{
transp.ProcessShader(shader);
}
}
void CRP::ExecuteRenderCommands(const byte* data, bool /*readbackRequested*/)
{
// @NOTE: the CRP always blits the final result to the readback texture
for(;;)
{
const int commandId = ((const renderCommandBase_t*)data)->commandId;
if(commandId < 0 || commandId >= RC_COUNT)
{
assert(!"Invalid render command type");
return;
}
if(commandId == RC_END_OF_LIST)
{
return;
}
switch(commandId)
{
case RC_UI_SET_COLOR:
ui.CmdSetColor(*(const uiSetColorCommand_t*)data);
break;
case RC_UI_DRAW_QUAD:
ui.CmdDrawQuad(*(const uiDrawQuadCommand_t*)data);
break;
case RC_UI_DRAW_TRIANGLE:
ui.CmdDrawTriangle(*(const uiDrawTriangleCommand_t*)data);
break;
case RC_DRAW_SCENE_VIEW:
DrawSceneView(*(const drawSceneViewCommand_t*)data);
break;
case RC_BEGIN_FRAME:
BeginFrame();
break;
case RC_SWAP_BUFFERS:
EndFrame();
break;
case RC_BEGIN_UI:
ui.Begin(renderTarget);
break;
case RC_END_UI:
ui.End();
break;
case RC_BEGIN_3D:
// @TODO:
srp.renderMode = RenderMode::None;
break;
case RC_END_3D:
// @TODO:
srp.renderMode = RenderMode::None;
break;
case RC_END_SCENE:
// @TODO: post-processing
break;
case RC_BEGIN_NK:
nuklear.Begin(renderTarget);
break;
case RC_END_NK:
nuklear.End();
break;
case RC_NK_UPLOAD:
nuklear.Upload(*(const nuklearUploadCommand_t*)data);
break;
case RC_NK_DRAW:
nuklear.Draw(*(const nuklearDrawCommand_t*)data);
break;
default:
Q_assert(!"Unsupported render command type");
return;
}
data += renderCommandSizes[commandId];
}
}
void CRP::TessellationOverflow()
{
switch(tess.tessellator)
{
case Tessellator::Opaque: opaque.TessellationOverflow(); break;
case Tessellator::Transp: transp.TessellationOverflow(); break;
default: break;
}
tess.numIndexes = 0;
tess.numVertexes = 0;
}
void CRP::DrawSceneView(const drawSceneViewCommand_t& cmd)
{
const viewParms_t& vp = cmd.viewParms;
if(cmd.shouldClearColor)
{
const Rect rect(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight);
const TextureBarrier tb(renderTarget, ResourceStates::RenderTargetBit);
CmdBarrier(1, &tb);
CmdClearColorTarget(renderTarget, cmd.clearColor, &rect);
}
if(cmd.numDrawSurfs <= 0 || !cmd.shouldDrawScene)
{
return;
}
if(crp_dof->integer == DOFMethod::Accumulation &&
vp.viewportX == 0 &&
vp.viewportY == 0 &&
vp.viewportWidth == glConfig.vidWidth &&
vp.viewportHeight == glConfig.vidHeight)
{
const Rect rect(0, 0, glConfig.vidWidth, glConfig.vidHeight);
accumDof.Begin(cmd);
const uint32_t sampleCount = accumDof.GetSampleCount();
for(uint32_t y = 0; y < sampleCount; y++)
{
for(uint32_t x = 0; x < sampleCount; x++)
{
srp.enableRenderPassQueries = x == 0 && y == 0;
drawSceneViewCommand_t newCmd;
accumDof.FixCommand(newCmd, cmd, x, y);
const TextureBarrier tb(renderTarget, ResourceStates::RenderTargetBit);
CmdBarrier(1, &tb);
CmdClearColorTarget(renderTarget, cmd.clearColor, &rect);
opaque.Draw(newCmd);
fog.Draw();
transp.Draw(newCmd);
transpResolve.Draw(newCmd);
accumDof.Accumulate();
// geometry allocation is a linear allocation instead of a ring buffer
// we force a CPU-GPU sync point after every full scene render
// that way, we can keep the buffer sizes at least somewhat reasonable
SubmitAndContinue();
dynBuffers[GetFrameIndex()].Rewind();
}
}
CmdSetViewportAndScissor(backEnd.viewParms);
srp.enableRenderPassQueries = true;
accumDof.Normalize();
backEnd.viewParms = cmd.viewParms;
backEnd.refdef = cmd.refdef;
accumDof.DrawDebug();
}
else
{
opaque.Draw(cmd);
fog.Draw();
transp.Draw(cmd);
transpResolve.Draw(cmd);
CmdSetViewportAndScissor(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight);
gatherDof.Draw();
}
}
void CRP::ReadPixels(int w, int h, int alignment, colorSpace_t colorSpace, void* outPixels)
{
ReadTextureImage(outPixels, readbackRenderTarget, w, h, alignment, colorSpace);
}
uint32_t CRP::GetSamplerDescriptorIndexFromBaseIndex(uint32_t baseIndex)
{
Q_assert(baseIndex < ARRAY_LEN(samplerIndices));
return samplerIndices[baseIndex];
}
HTexture CRP::GetReadRenderTarget()
{
return renderTargets[renderTargetIndex ^ 1];
}
HTexture CRP::GetWriteRenderTarget()
{
return renderTargets[renderTargetIndex];
}
void CRP::SwapRenderTargets()
{
renderTargetIndex ^= 1;
renderTarget = GetWriteRenderTarget();
}

View file

@ -0,0 +1,376 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Cinematic Rendering Pipeline - opaque surfaces
#include "crp_local.h"
namespace opaque
{
#include "compshaders/crp/opaque_vs.h"
#include "compshaders/crp/opaque_ps.h"
}
#pragma pack(push, 4)
struct OpaqueVertexRC : WorldVertexRC
{
};
struct OpaquePixelRC
{
// general
uint32_t textureIndex;
uint32_t samplerIndex;
uint32_t shaderIndexBufferIndex;
uint32_t alphaTest;
float greyscale;
// shader trace
uint32_t shaderTrace; // shader index: 14 - frame index: 2 - enable: 1
uint16_t centerPixelX;
uint16_t centerPixelY;
};
#pragma pack(pop)
void WorldOpaque::Init()
{
psoCache.Init(psoCacheEntries, ARRAY_LEN(psoCacheEntries));
}
void WorldOpaque::Draw(const drawSceneViewCommand_t& cmd)
{
if(cmd.numDrawSurfs - cmd.numTranspSurfs <= 0)
{
return;
}
srp.renderMode = RenderMode::World;
backEnd.refdef = cmd.refdef;
backEnd.viewParms = cmd.viewParms;
if(backEnd.viewParms.isPortal)
{
float plane[4];
plane[0] = backEnd.viewParms.portalPlane.normal[0];
plane[1] = backEnd.viewParms.portalPlane.normal[1];
plane[2] = backEnd.viewParms.portalPlane.normal[2];
plane[3] = backEnd.viewParms.portalPlane.dist;
float plane2[4];
plane2[0] = DotProduct(backEnd.viewParms.orient.axis[0], plane);
plane2[1] = DotProduct(backEnd.viewParms.orient.axis[1], plane);
plane2[2] = DotProduct(backEnd.viewParms.orient.axis[2], plane);
plane2[3] = DotProduct(plane, backEnd.viewParms.orient.origin) - plane[3];
float* o = plane;
const float* m = s_flipMatrix;
const float* v = plane2;
o[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3];
o[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3];
o[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];
o[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
memcpy(clipPlane, plane, sizeof(clipPlane));
}
else
{
memset(clipPlane, 0, sizeof(clipPlane));
}
CmdSetViewportAndScissor(backEnd.viewParms);
batchOldDepthHack = false;
batchDepthHack = false;
TextureBarrier tb(crp.depthTexture, ResourceStates::DepthWriteBit);
BufferBarrier bb(srp.traceRenderBuffer, ResourceStates::UnorderedAccessBit);
CmdBarrier(1, &tb, 1, &bb);
CmdClearDepthStencilTarget(crp.depthTexture, true, 0.0f);
GeoBuffers& db = crp.dynBuffers[GetFrameIndex()];
db.BeginUpload();
SCOPED_RENDER_PASS("Opaque", 1.0f, 0.5f, 0.5f);
CmdBindRenderTargets(1, &crp.renderTarget, &crp.depthTexture);
CmdBindVertexBuffers(ARRAY_LEN(db.vertexBuffers), db.vertexBuffers, db.vertexBufferStrides, NULL);
CmdBindIndexBuffer(db.indexBuffer.buffer, IndexType::UInt32, 0);
const drawSurf_t* drawSurfs = cmd.drawSurfs;
const int surfCount = cmd.numDrawSurfs - cmd.numTranspSurfs;
const double originalTime = backEnd.refdef.floatTime;
const shader_t* shader = NULL;
const shader_t* oldShader = NULL;
int oldEntityNum = -1;
backEnd.currentEntity = &tr.worldEntity;
tess.numVertexes = 0;
tess.numIndexes = 0;
int ds;
const drawSurf_t* drawSurf;
for(ds = 0, drawSurf = drawSurfs; ds < surfCount; ++ds, ++drawSurf)
{
int entityNum;
R_DecomposeSort(drawSurf->sort, &entityNum, &shader);
Q_assert(shader != NULL);
Q_assert(shader->isOpaque);
// sky shaders can have no stages and be valid (box drawn with no clouds)
if(!shader->isSky)
{
if(shader->numPipelines == 0 ||
shader->pipelines[0].pipeline <= 0 ||
shader->pipelines[0].numStages <= 0)
{
continue;
}
}
const bool shaderChanged = shader != oldShader;
const bool entityChanged = entityNum != oldEntityNum;
if(shaderChanged || entityChanged)
{
oldShader = shader;
oldEntityNum = entityNum;
EndSkyBatch();
EndBatch();
BeginBatch(shader);
tess.greyscale = drawSurf->greyscale;
}
if(entityChanged)
{
UpdateEntityData(batchDepthHack, entityNum, originalTime);
}
R_TessellateSurface(drawSurf->surface);
}
backEnd.refdef.floatTime = originalTime;
EndSkyBatch();
EndBatch();
db.EndUpload();
// restores the potentially "hacked" depth range as well
CmdSetViewportAndScissor(backEnd.viewParms);
batchOldDepthHack = false;
batchDepthHack = false;
}
void WorldOpaque::ProcessShader(shader_t& shader)
{
Q_assert(shader.isOpaque || shader.isSky);
if(shader.numStages < 1)
{
shader.numPipelines = 0;
return;
}
const bool clampDepth = r_depthClamp->integer != 0 || shader.isSky;
for(int s = 0; s < shader.numStages; ++s)
{
const shaderStage_t& stage = *shader.stages[s];
const unsigned int stateBits = stage.stateBits & (~GLS_POLYMODE_LINE);
int a = 0;
// @NOTE: we are not using any CTOR because we deliberately want to 0-init the struct
// this is necessary for padding bytes not to mess up comparisons in the PSO cache
GraphicsPipelineDesc desc = {};
desc.name = "opaque";
desc.rootSignature = RHI_MAKE_NULL_HANDLE();
desc.shortLifeTime = true; // the PSO cache is only valid for this map!
desc.vertexShader = opaque::g_vs;
desc.pixelShader = opaque::g_ps;
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Position, DataType::Float32, 3, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Normal, DataType::Float32, 2, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::TexCoord, DataType::Float32, 2, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Color, DataType::UNorm8, 4, 0);
desc.depthStencil.depthStencilFormat = TextureFormat::Depth32_Float;
desc.depthStencil.depthComparison =
(stateBits & GLS_DEPTHFUNC_EQUAL) != 0 ?
ComparisonFunction::Equal :
ComparisonFunction::GreaterEqual;
desc.depthStencil.enableDepthTest = (stateBits & GLS_DEPTHTEST_DISABLE) == 0;
desc.depthStencil.enableDepthWrites = (stateBits & GLS_DEPTHMASK_TRUE) != 0;
desc.rasterizer.cullMode = shader.cullType;
desc.rasterizer.polygonOffset = shader.polygonOffset != 0;
desc.rasterizer.clampDepth = clampDepth;
desc.AddRenderTarget(stateBits & GLS_BLEND_BITS, crp.renderTargetFormat);
pipeline_t& p = shader.pipelines[s];
p.firstStage = s;
p.numStages = 1;
p.pipeline = psoCache.AddPipeline(desc, va("opaque %d %d", psoCache.entryCount, s + 1));
desc.rasterizer.cullMode = GetMirrorredCullType(desc.rasterizer.cullMode);
p.mirrorPipeline = psoCache.AddPipeline(desc, va("opaque %d %d mirrored", psoCache.entryCount, s + 1));
}
shader.numPipelines = shader.numStages;
}
void WorldOpaque::TessellationOverflow()
{
EndBatch();
BeginBatch(tess.shader);
}
void WorldOpaque::BeginBatch(const shader_t* shader)
{
tess.tessellator = Tessellator::Opaque;
tess.numVertexes = 0;
tess.numIndexes = 0;
tess.depthFade = DFT_NONE;
tess.deformsPreApplied = qfalse;
tess.xstages = (const shaderStage_t**)shader->stages;
tess.shader = shader;
tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
if(tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime)
{
tess.shaderTime = tess.shader->clampTime;
}
}
void WorldOpaque::EndBatch()
{
const int vertexCount = tess.numVertexes;
const int indexCount = tess.numIndexes;
if(vertexCount <= 0 ||
indexCount <= 0 ||
tess.shader->numStages == 0 ||
tess.shader->numPipelines <= 0)
{
goto clean_up;
}
const shader_t* const shader = tess.shader;
GeoBuffers& db = crp.dynBuffers[GetFrameIndex()];
if(!db.CanAdd(vertexCount, indexCount, shader->numStages))
{
Q_assert(!"World surface geometry buffer too small!");
goto clean_up;
}
RB_DeformTessGeometry(0, vertexCount, 0, indexCount);
db.UploadBase();
if(batchDepthHack != batchOldDepthHack)
{
const viewParms_t& vp = backEnd.viewParms;
CmdSetViewport(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight, batchDepthHack ? 0.7f : 0.0f, 1.0f);
batchOldDepthHack = batchDepthHack;
}
OpaqueVertexRC vertexRC = {};
memcpy(vertexRC.modelViewMatrix, backEnd.orient.modelMatrix, sizeof(vertexRC.modelViewMatrix));
memcpy(vertexRC.projectionMatrix, backEnd.viewParms.projectionMatrix, sizeof(vertexRC.projectionMatrix));
memcpy(vertexRC.clipPlane, clipPlane, sizeof(vertexRC.clipPlane));
CmdSetGraphicsRootConstants(0, sizeof(vertexRC), &vertexRC);
for(int s = 0; s < shader->numStages; ++s)
{
const shaderStage_t* const stage = shader->stages[s];
R_ComputeColors(stage, tess.svars[0], 0, vertexCount);
R_ComputeTexCoords(stage, tess.svars[0], 0, vertexCount, qfalse);
db.UploadStage(0);
const pipeline_t& pipeline = shader->pipelines[s];
const int psoIndex = backEnd.viewParms.isMirror ? pipeline.mirrorPipeline : pipeline.pipeline;
Q_assert(psoIndex > 0);
CmdBindPipeline(psoCache.entries[psoIndex].handle);
const image_t* image = GetBundleImage(stage->bundle);
const uint32_t texIdx = image->textureIndex;
const uint32_t sampIdx = GetSamplerIndex(image);
const uint32_t alphaTest = AlphaTestShaderConstFromStateBits(stage->stateBits);
const uint32_t enableShaderTrace = tr.traceWorldShader && s == 0 ? 1 : 0;
const uint32_t bufferIndex = GetBufferIndexUAV(srp.traceRenderBuffer);
Q_assert(sampIdx < ARRAY_LEN(crp.samplers));
OpaquePixelRC pixelRC = {};
pixelRC.textureIndex = texIdx;
pixelRC.samplerIndex = sampIdx;
pixelRC.shaderIndexBufferIndex = bufferIndex;
pixelRC.alphaTest = alphaTest;
pixelRC.greyscale = tess.greyscale;
pixelRC.shaderTrace = ((uint32_t)shader->index << 3) | (RHI::GetFrameIndex() << 1) | enableShaderTrace;
pixelRC.centerPixelX = glConfig.vidWidth / 2;
pixelRC.centerPixelY = glConfig.vidHeight / 2;
CmdSetGraphicsRootConstants(sizeof(vertexRC), sizeof(pixelRC), &pixelRC);
db.DrawStage(vertexCount, indexCount);
}
db.EndBaseBatch(vertexCount);
clean_up:
tess.tessellator = Tessellator::None;
tess.numVertexes = 0;
tess.numIndexes = 0;
}
void WorldOpaque::EndSkyBatch()
{
// this only exists as a separate function from EndBatch so that
// we don't have to deal with recursion (through the call to RB_DrawSky)
if(tess.shader == NULL ||
!tess.shader->isSky ||
tess.numVertexes <= 0 ||
tess.numIndexes <= 0)
{
return;
}
SCOPED_RENDER_PASS("Sky", 0.0, 0.5f, 1.0f);
const viewParms_t& vp = backEnd.viewParms;
CmdSetViewport(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight, 0.0f, 0.0f);
RB_DrawSky();
CmdSetViewport(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight, 0.0f, 1.0f);
tess.numVertexes = 0;
tess.numIndexes = 0;
}
void WorldOpaque::DrawSkyBox()
{
// force creation of a PSO for the temp shader
ProcessShader((shader_t&)*tess.shader);
tess.deformsPreApplied = qtrue;
EndBatch();
}
void WorldOpaque::DrawClouds()
{
EndBatch();
}

View file

@ -0,0 +1,139 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Cinematic Rendering Pipeline - tone mapping
#include "crp_local.h"
namespace tone_map
{
#include "compshaders/crp/tone_map_vs.h"
#include "compshaders/crp/tone_map_ps.h"
}
namespace inverse_tone_map
{
#include "compshaders/crp/tone_map_inverse_vs.h"
#include "compshaders/crp/tone_map_inverse_ps.h"
}
#pragma pack(push, 4)
struct ToneMapRC
{
uint32_t textureIndex;
uint32_t samplerIndex;
float invGamma;
float brightness;
float greyscale;
};
struct InverseToneMapRC
{
uint32_t textureIndex;
uint32_t samplerIndex;
float gamma;
float invBrightness;
};
#pragma pack(pop)
void ToneMap::Init()
{
{
GraphicsPipelineDesc desc("Tone Map");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(tone_map::g_vs);
desc.pixelShader = ShaderByteCode(tone_map::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, crp.renderTargetFormat);
pipeline = CreateGraphicsPipeline(desc);
}
{
GraphicsPipelineDesc desc("Inverse Tone Map");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(tone_map::g_vs);
desc.pixelShader = ShaderByteCode(tone_map::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, crp.renderTargetFormat);
pipeline = CreateGraphicsPipeline(desc);
}
}
void ToneMap::DrawToneMap()
{
srp.renderMode = RenderMode::None;
SCOPED_RENDER_PASS("Tone Map", 1.0f, 1.0f, 1.0f);
CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight);
crp.SwapRenderTargets();
const TextureBarrier texBarriers[] =
{
TextureBarrier(crp.GetReadRenderTarget(), ResourceStates::PixelShaderAccessBit),
TextureBarrier(crp.GetWriteRenderTarget(), ResourceStates::RenderTargetBit)
};
CmdBarrier(ARRAY_LEN(texBarriers), texBarriers, 0, NULL);
ToneMapRC rc = {};
rc.textureIndex = GetTextureIndexSRV(crp.GetReadRenderTarget());
rc.samplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
rc.invGamma = 1.0f / r_gamma->value;
rc.brightness = r_brightness->value;
rc.greyscale = r_greyscale->value;
CmdBindRenderTargets(1, &crp.renderTarget, NULL);
CmdBindPipeline(pipeline);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}
void ToneMap::DrawInverseToneMap()
{
srp.renderMode = RenderMode::None;
SCOPED_RENDER_PASS("Inverse Tone Map", 1.0f, 1.0f, 1.0f);
CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight);
crp.SwapRenderTargets();
const TextureBarrier texBarriers[] =
{
TextureBarrier(crp.GetReadRenderTarget(), ResourceStates::PixelShaderAccessBit),
TextureBarrier(crp.GetWriteRenderTarget(), ResourceStates::RenderTargetBit)
};
CmdBarrier(ARRAY_LEN(texBarriers), texBarriers, 0, NULL);
InverseToneMapRC rc = {};
rc.textureIndex = GetTextureIndexSRV(crp.GetReadRenderTarget());
rc.samplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
rc.gamma = r_gamma->value;
rc.invBrightness = 1.0f / r_brightness->value;
CmdBindRenderTargets(1, &crp.renderTarget, NULL);
CmdBindPipeline(inversePipeline);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}

View file

@ -0,0 +1,368 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Cinematic Rendering Pipeline - OIT geometry pass
#include "crp_local.h"
namespace transp_draw
{
#include "compshaders/crp/transp_draw_vs.h"
#include "compshaders/crp/transp_draw_ps.h"
}
#pragma pack(push, 4)
struct TranspDrawVertexRC : WorldVertexRC
{
};
struct TranspDrawPixelRC
{
uint32_t textureIndex;
uint32_t samplerIndex;
uint32_t alphaTest;
uint32_t counterBuffer;
uint32_t indexTexture;
uint32_t fragmentBuffer;
float greyscale;
uint32_t stateBits;
uint32_t shaderTrace;
uint16_t hFadeDistance;
uint16_t hFadeOffset;
uint32_t depthFadeScaleBias; // color bias: 4 - color scale: 4
};
#pragma pack(pop)
static uint32_t GetFixedStageBits(uint32_t stateBits, uint32_t stageIndex)
{
// makes sure we're not overwriting anything useful
assert((stateBits & GLS_STAGEINDEX_BITS) == 0);
// transform "no blend" into a "replace" blend mode
if((stateBits & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS)) == 0)
{
stateBits |= GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO;
}
stateBits |= stageIndex << GLS_STAGEINDEX_SHIFT;
return stateBits;
}
void WorldTransp::Init()
{
psoCache.Init(psoCacheEntries, ARRAY_LEN(psoCacheEntries));
}
void WorldTransp::Draw(const drawSceneViewCommand_t& cmd)
{
if(cmd.numTranspSurfs <= 0)
{
return;
}
srp.renderMode = RenderMode::World;
backEnd.refdef = cmd.refdef;
backEnd.viewParms = cmd.viewParms;
if(backEnd.viewParms.isPortal)
{
float plane[4];
plane[0] = backEnd.viewParms.portalPlane.normal[0];
plane[1] = backEnd.viewParms.portalPlane.normal[1];
plane[2] = backEnd.viewParms.portalPlane.normal[2];
plane[3] = backEnd.viewParms.portalPlane.dist;
float plane2[4];
plane2[0] = DotProduct(backEnd.viewParms.orient.axis[0], plane);
plane2[1] = DotProduct(backEnd.viewParms.orient.axis[1], plane);
plane2[2] = DotProduct(backEnd.viewParms.orient.axis[2], plane);
plane2[3] = DotProduct(plane, backEnd.viewParms.orient.origin) - plane[3];
float* o = plane;
const float* m = s_flipMatrix;
const float* v = plane2;
o[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3];
o[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3];
o[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];
o[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
memcpy(clipPlane, plane, sizeof(clipPlane));
}
else
{
memset(clipPlane, 0, sizeof(clipPlane));
}
SCOPED_RENDER_PASS("Transparent", 1.0f, 0.5f, 0.5f);
CmdSetViewportAndScissor(backEnd.viewParms);
batchOldDepthHack = false;
batchDepthHack = false;
const TextureBarrier texBarriers[] =
{
TextureBarrier(crp.depthTexture, ResourceStates::DepthWriteBit),
TextureBarrier(crp.oitIndexTexture, ResourceStates::UnorderedAccessBit)
};
const BufferBarrier bufBarriers[] =
{
BufferBarrier(crp.oitFragmentBuffer, ResourceStates::UnorderedAccessBit),
BufferBarrier(crp.oitCounterBuffer, ResourceStates::UnorderedAccessBit)
};
CmdBarrier(ARRAY_LEN(texBarriers), texBarriers, ARRAY_LEN(bufBarriers), bufBarriers);
GeoBuffers& db = crp.dynBuffers[GetFrameIndex()];
db.BeginUpload();
CmdBindRenderTargets(0, NULL, &crp.depthTexture);
CmdBindVertexBuffers(ARRAY_LEN(db.vertexBuffers), db.vertexBuffers, db.vertexBufferStrides, NULL);
CmdBindIndexBuffer(db.indexBuffer.buffer, IndexType::UInt32, 0);
// reset the fragment counter
{
BufferBarrier b0(crp.oitCounterBuffer, ResourceStates::CopyDestinationBit);
CmdBarrier(0, NULL, 1, &b0);
CmdCopyBuffer(crp.oitCounterBuffer, crp.oitCounterStagingBuffer);
BufferBarrier b1(crp.oitCounterBuffer, ResourceStates::UnorderedAccessBit);
CmdBarrier(0, NULL, 1, &b1);
}
// clear the index texture
{
const uint32_t zeroes[4] = {};
CmdClearTextureUAV(crp.oitIndexTexture, 0, zeroes);
}
// really should just be just for the counter buffer and the index texture
CmdBarrier(ARRAY_LEN(texBarriers), texBarriers, ARRAY_LEN(bufBarriers), bufBarriers);
const drawSurf_t* drawSurfs = cmd.drawSurfs;
const int opaqueSurfCount = cmd.numDrawSurfs - cmd.numTranspSurfs;
const int transpSurfCount = cmd.numTranspSurfs;
const double originalTime = backEnd.refdef.floatTime;
const shader_t* shader = NULL;
const shader_t* oldShader = NULL;
int oldEntityNum = -1;
backEnd.currentEntity = &tr.worldEntity;
tess.numVertexes = 0;
tess.numIndexes = 0;
const drawSurf_t* drawSurf = drawSurfs + opaqueSurfCount;
for(int ds = 0; ds < transpSurfCount; ++ds, ++drawSurf)
{
int entityNum;
R_DecomposeSort(drawSurf->sort, &entityNum, &shader);
Q_assert(shader != NULL);
Q_assert(!shader->isOpaque);
const bool shaderChanged = shader != oldShader;
const bool entityChanged = entityNum != oldEntityNum;
if(shaderChanged || entityChanged)
{
oldShader = shader;
oldEntityNum = entityNum;
EndBatch();
BeginBatch(shader);
tess.greyscale = drawSurf->greyscale;
}
if(entityChanged)
{
UpdateEntityData(batchDepthHack, entityNum, originalTime);
}
R_TessellateSurface(drawSurf->surface);
}
backEnd.refdef.floatTime = originalTime;
EndBatch();
db.EndUpload();
// restores the potentially "hacked" depth range as well
CmdSetViewportAndScissor(backEnd.viewParms);
batchOldDepthHack = false;
batchDepthHack = false;
}
void WorldTransp::ProcessShader(shader_t& shader)
{
Q_assert(!shader.isOpaque);
if(shader.numStages < 1)
{
shader.numTranspPipelines = 0;
return;
}
const bool clampDepth = r_depthClamp->integer != 0 || shader.isSky;
for(int s = 0; s < shader.numStages; ++s)
{
int a = 0;
// @NOTE: we are not using any CTOR because we deliberately want to 0-init the struct
// this is necessary for padding bytes not to mess up comparisons in the PSO cache
GraphicsPipelineDesc desc = {};
desc.name = "transp";
desc.rootSignature = RHI_MAKE_NULL_HANDLE();
desc.shortLifeTime = true; // the PSO cache is only valid for this map!
desc.vertexShader = transp_draw::g_vs;
desc.pixelShader = transp_draw::g_ps;
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Position, DataType::Float32, 3, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Normal, DataType::Float32, 2, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::TexCoord, DataType::Float32, 2, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Color, DataType::UNorm8, 4, 0);
desc.depthStencil.depthStencilFormat = TextureFormat::Depth32_Float;
desc.depthStencil.depthComparison = ComparisonFunction::GreaterEqual;
desc.depthStencil.enableDepthTest = true;
desc.depthStencil.enableDepthWrites = false;
desc.rasterizer.cullMode = shader.cullType;
desc.rasterizer.polygonOffset = shader.polygonOffset != 0;
desc.rasterizer.clampDepth = clampDepth;
pipeline_t& p = shader.transpPipelines[s];
p.firstStage = s;
p.numStages = 1;
p.pipeline = psoCache.AddPipeline(desc, va("transp %d %d", psoCache.entryCount, s + 1));
desc.rasterizer.cullMode = GetMirrorredCullType(desc.rasterizer.cullMode);
p.mirrorPipeline = psoCache.AddPipeline(desc, va("transp %d %d mirrored", psoCache.entryCount, s + 1));
}
shader.numTranspPipelines = shader.numStages;
}
void WorldTransp::TessellationOverflow()
{
EndBatch();
BeginBatch(tess.shader);
}
void WorldTransp::BeginBatch(const shader_t* shader)
{
tess.tessellator = Tessellator::Transp;
tess.numVertexes = 0;
tess.numIndexes = 0;
tess.depthFade = DFT_NONE;
tess.deformsPreApplied = qfalse;
tess.xstages = (const shaderStage_t**)shader->stages;
tess.shader = shader;
tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
if(tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime)
{
tess.shaderTime = tess.shader->clampTime;
}
}
void WorldTransp::EndBatch()
{
const int vertexCount = tess.numVertexes;
const int indexCount = tess.numIndexes;
if(vertexCount <= 0 ||
indexCount <= 0 ||
tess.shader->numStages == 0 ||
tess.shader->numTranspPipelines <= 0)
{
goto clean_up;
}
const shader_t* const shader = tess.shader;
GeoBuffers& db = crp.dynBuffers[GetFrameIndex()];
if(!db.CanAdd(vertexCount, indexCount, shader->numStages))
{
Q_assert(!"World surface geometry buffer too small!");
goto clean_up;
}
RB_DeformTessGeometry(0, vertexCount, 0, indexCount);
db.UploadBase();
if(batchDepthHack != batchOldDepthHack)
{
const viewParms_t& vp = backEnd.viewParms;
CmdSetViewport(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight, batchDepthHack ? 0.7f : 0.0f, 1.0f);
batchOldDepthHack = batchDepthHack;
}
TranspDrawVertexRC vertexRC = {};
memcpy(vertexRC.modelViewMatrix, backEnd.orient.modelMatrix, sizeof(vertexRC.modelViewMatrix));
memcpy(vertexRC.projectionMatrix, backEnd.viewParms.projectionMatrix, sizeof(vertexRC.projectionMatrix));
memcpy(vertexRC.clipPlane, clipPlane, sizeof(vertexRC.clipPlane));
CmdSetGraphicsRootConstants(0, sizeof(vertexRC), &vertexRC);
for(int s = 0; s < shader->numStages; ++s)
{
const shaderStage_t* const stage = shader->stages[s];
R_ComputeColors(stage, tess.svars[0], 0, vertexCount);
R_ComputeTexCoords(stage, tess.svars[0], 0, vertexCount, qfalse);
db.UploadStage(0);
const pipeline_t& pipeline = shader->transpPipelines[s];
const int psoIndex = backEnd.viewParms.isMirror ? pipeline.mirrorPipeline : pipeline.pipeline;
Q_assert(psoIndex > 0);
CmdBindPipeline(psoCache.entries[psoIndex].handle);
const image_t* image = GetBundleImage(stage->bundle);
const uint32_t texIdx = image->textureIndex;
const uint32_t sampIdx = GetSamplerIndex(image);
const uint32_t alphaTest = AlphaTestShaderConstFromStateBits(stage->stateBits);
const uint32_t enableShaderTrace = tr.traceWorldShader && s == 0 ? 1 : 0;
const uint32_t enableDepthFade = shader->dfType != DFT_NONE ? 1 : 0;
Q_assert(sampIdx < ARRAY_LEN(crp.samplers));
TranspDrawPixelRC pixelRC = {};
pixelRC.alphaTest = alphaTest;
pixelRC.counterBuffer = GetBufferIndexUAV(crp.oitCounterBuffer);
pixelRC.fragmentBuffer = GetBufferIndexUAV(crp.oitFragmentBuffer);
pixelRC.greyscale = tess.greyscale;
pixelRC.indexTexture = GetTextureIndexUAV(crp.oitIndexTexture, 0);
pixelRC.samplerIndex = sampIdx;
pixelRC.stateBits = GetFixedStageBits(stage->stateBits, s);
pixelRC.textureIndex = texIdx;
pixelRC.shaderTrace = ((uint32_t)shader->index << 3) | (RHI::GetFrameIndex() << 1) | enableShaderTrace;
pixelRC.hFadeDistance = f32tof16(shader->dfInvDist);
pixelRC.hFadeOffset = f32tof16(shader->dfBias);
pixelRC.depthFadeScaleBias = (enableDepthFade << 8) | (uint32_t)r_depthFadeScaleAndBias[shader->dfType];
CmdSetGraphicsRootConstants(sizeof(vertexRC), sizeof(pixelRC), &pixelRC);
db.DrawStage(vertexCount, indexCount);
}
db.EndBaseBatch(vertexCount);
clean_up:
tess.tessellator = Tessellator::None;
tess.numVertexes = 0;
tess.numIndexes = 0;
}

View file

@ -0,0 +1,111 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Cinematic Rendering Pipeline - OIT resolve pass
#include "crp_local.h"
namespace transp_resolve
{
#include "compshaders/crp/transp_resolve_vs.h"
#include "compshaders/crp/transp_resolve_ps.h"
}
#pragma pack(push, 4)
struct TranspResolveRC
{
uint32_t renderTargetTexture;
uint32_t shaderIndexBuffer;
uint32_t indexTexture;
uint32_t fragmentBuffer;
uint16_t centerPixelX;
uint16_t centerPixelY;
uint32_t depthTexture;
float proj22;
float proj32;
float scissorMinX;
float scissorMinY;
float scissorMaxX;
float scissorMaxY;
};
#pragma pack(pop)
void TranspResolve::Init()
{
GraphicsPipelineDesc desc("OIT Resolve");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(transp_resolve::g_vs);
desc.pixelShader = ShaderByteCode(transp_resolve::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, crp.renderTargetFormat);
pipeline = CreateGraphicsPipeline(desc);
}
void TranspResolve::Draw(const drawSceneViewCommand_t& cmd)
{
if(cmd.numTranspSurfs <= 0)
{
return;
}
srp.renderMode = RenderMode::World;
SCOPED_RENDER_PASS("OIT Resolve", 1.0f, 0.5f, 0.5f);
CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight);
crp.SwapRenderTargets();
const TextureBarrier texBarriers[] =
{
TextureBarrier(crp.GetReadRenderTarget(), ResourceStates::PixelShaderAccessBit),
TextureBarrier(crp.GetWriteRenderTarget(), ResourceStates::RenderTargetBit),
TextureBarrier(crp.oitIndexTexture, ResourceStates::UnorderedAccessBit),
TextureBarrier(crp.depthTexture, ResourceStates::PixelShaderAccessBit)
};
const BufferBarrier bufBarriers[] =
{
BufferBarrier(crp.oitFragmentBuffer, ResourceStates::UnorderedAccessBit),
BufferBarrier(srp.traceRenderBuffer, ResourceStates::UnorderedAccessBit)
};
CmdBarrier(ARRAY_LEN(texBarriers), texBarriers, ARRAY_LEN(bufBarriers), bufBarriers);
TranspResolveRC rc = {};
rc.fragmentBuffer = GetBufferIndexUAV(crp.oitFragmentBuffer);
rc.indexTexture = GetTextureIndexUAV(crp.oitIndexTexture, 0);
rc.renderTargetTexture = GetTextureIndexSRV(crp.GetReadRenderTarget());
rc.shaderIndexBuffer = GetBufferIndexUAV(srp.traceRenderBuffer);
rc.centerPixelX = glConfig.vidWidth / 2;
rc.centerPixelY = glConfig.vidHeight / 2;
rc.depthTexture = GetTextureIndexSRV(crp.depthTexture);
rc.proj22 = -backEnd.viewParms.projectionMatrix[2 * 4 + 2];
rc.proj32 = backEnd.viewParms.projectionMatrix[3 * 4 + 2];
rc.scissorMinX = backEnd.viewParms.viewportX;
rc.scissorMinY = backEnd.viewParms.viewportY;
rc.scissorMaxX = rc.scissorMinX + backEnd.viewParms.viewportWidth - 1;
rc.scissorMaxY = rc.scissorMinY + backEnd.viewParms.viewportHeight - 1;
CmdBindRenderTargets(1, &crp.renderTarget, NULL);
CmdBindPipeline(pipeline);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}

View file

@ -0,0 +1,113 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Gameplay Rendering Pipeline - vertex and index buffer management
#include "grp_local.h"
void VertexBuffers::Create(const char* name, MemoryUsage::Id memoryUsage, uint32_t vertexCount)
{
totalCount = vertexCount;
BufferDesc desc = {};
desc.committedResource = true;
desc.initialState = ResourceStates::VertexBufferBit;
desc.memoryUsage = memoryUsage;
desc.name = va("%s position vertex", name);
desc.byteCount = vertexCount * sizeof(vec3_t);
buffers[BasePosition] = CreateBuffer(desc);
strides[BasePosition] = sizeof(vec3_t);
desc.name = va("%s normal vertex", name);
desc.byteCount = vertexCount * sizeof(vec3_t);
buffers[BaseNormal] = CreateBuffer(desc);
strides[BaseNormal] = sizeof(vec3_t);
for(uint32_t s = 0; s < MAX_SHADER_STAGES; ++s)
{
desc.name = va("%s tex coords #%d vertex", name, (int)s + 1);
desc.byteCount = vertexCount * sizeof(vec2_t);
buffers[BaseCount + s * StageCount + StageTexCoords] = CreateBuffer(desc);
strides[BaseCount + s * StageCount + StageTexCoords] = sizeof(vec2_t);
desc.name = va("%s color #%d vertex", name, (int)s + 1);
desc.byteCount = vertexCount * sizeof(color4ub_t);
buffers[BaseCount + s * StageCount + StageColors] = CreateBuffer(desc);
strides[BaseCount + s * StageCount + StageColors] = sizeof(color4ub_t);
}
}
void VertexBuffers::BeginUpload()
{
for(uint32_t b = 0; b < BufferCount; ++b)
{
mapped[b] = BeginBufferUpload(buffers[b]);
}
}
void VertexBuffers::EndUpload()
{
for(uint32_t b = 0; b < BufferCount; ++b)
{
EndBufferUpload(buffers[b]);
mapped[b] = NULL;
}
}
void VertexBuffers::Upload(uint32_t firstStage, uint32_t stageCount)
{
Q_assert(mapped[0] != NULL);
const uint32_t batchOffset = batchFirst + batchCount;
float* pos = (float*)mapped[BasePosition] + 3 * batchOffset;
for(int v = 0; v < tess.numVertexes; ++v)
{
pos[0] = tess.xyz[v][0];
pos[1] = tess.xyz[v][1];
pos[2] = tess.xyz[v][2];
pos += 3;
}
float* nor = (float*)mapped[BaseNormal] + 3 * batchOffset;
for(int v = 0; v < tess.numVertexes; ++v)
{
nor[0] = tess.normal[v][0];
nor[1] = tess.normal[v][1];
nor[2] = tess.normal[v][2];
nor += 3;
}
for(uint32_t s = 0; s < stageCount; ++s)
{
const stageVars_t& sv = tess.svars[s + firstStage];
uint8_t* const tcBuffer = mapped[BaseCount + s * StageCount + StageTexCoords];
float* tc = (float*)tcBuffer + 2 * batchOffset;
memcpy(tc, &sv.texcoords[0], tess.numVertexes * sizeof(vec2_t));
uint8_t* const colBuffer = mapped[BaseCount + s * StageCount + StageColors];
uint32_t* col = (uint32_t*)colBuffer + batchOffset;
memcpy(col, &sv.colors[0], tess.numVertexes * sizeof(color4ub_t));
}
}

View file

@ -1,6 +1,6 @@
/* /*
=========================================================================== ===========================================================================
Copyright (C) 2022-2023 Gian 'myT' Schellenbaum Copyright (C) 2022-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3). This file is part of Challenge Quake 3 (CNQ3).
@ -24,16 +24,7 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
#pragma once #pragma once
#include "tr_local.h" #include "srp_local.h"
#include "rhi_local.h"
using namespace RHI;
// @TODO: move out
#define CONCAT_IMM(x, y) x ## y
#define CONCAT(x, y) CONCAT_IMM(x, y)
#pragma pack(push, 4) #pragma pack(push, 4)
@ -70,36 +61,6 @@ struct WorldPixelRC
#pragma pack(pop) #pragma pack(pop)
struct BufferBase
{
bool CanAdd(uint32_t count_)
{
return batchFirst + batchCount + count_ <= totalCount;
}
void EndBatch()
{
batchFirst += batchCount;
batchCount = 0;
}
void EndBatch(uint32_t size)
{
batchFirst += size;
batchCount = 0;
}
void Rewind()
{
batchFirst = 0;
batchCount = 0;
}
uint32_t totalCount = 0;
uint32_t batchFirst = 0;
uint32_t batchCount = 0;
};
struct VertexBuffers : BufferBase struct VertexBuffers : BufferBase
{ {
enum BaseId enum BaseId
@ -116,93 +77,10 @@ struct VertexBuffers : BufferBase
StageCount StageCount
}; };
void Create(const char* name, MemoryUsage::Id memoryUsage, uint32_t vertexCount) void Create(const char* name, MemoryUsage::Id memoryUsage, uint32_t vertexCount);
{ void BeginUpload();
totalCount = vertexCount; void EndUpload();
void Upload(uint32_t firstStage, uint32_t stageCount);
BufferDesc desc = {};
desc.committedResource = true;
desc.initialState = ResourceStates::VertexBufferBit;
desc.memoryUsage = memoryUsage;
desc.name = va("%s position vertex", name);
desc.byteCount = vertexCount * sizeof(vec3_t);
buffers[BasePosition] = CreateBuffer(desc);
strides[BasePosition] = sizeof(vec3_t);
desc.name = va("%s normal vertex", name);
desc.byteCount = vertexCount * sizeof(vec3_t);
buffers[BaseNormal] = CreateBuffer(desc);
strides[BaseNormal] = sizeof(vec3_t);
for(uint32_t s = 0; s < MAX_SHADER_STAGES; ++s)
{
desc.name = va("%s tex coords #%d vertex", name, (int)s + 1);
desc.byteCount = vertexCount * sizeof(vec2_t);
buffers[BaseCount + s * StageCount + StageTexCoords] = CreateBuffer(desc);
strides[BaseCount + s * StageCount + StageTexCoords] = sizeof(vec2_t);
desc.name = va("%s color #%d vertex", name, (int)s + 1);
desc.byteCount = vertexCount * sizeof(color4ub_t);
buffers[BaseCount + s * StageCount + StageColors] = CreateBuffer(desc);
strides[BaseCount + s * StageCount + StageColors] = sizeof(color4ub_t);
}
}
void BeginUpload()
{
for(uint32_t b = 0; b < BufferCount; ++b)
{
mapped[b] = BeginBufferUpload(buffers[b]);
}
}
void EndUpload()
{
for(uint32_t b = 0; b < BufferCount; ++b)
{
EndBufferUpload(buffers[b]);
mapped[b] = NULL;
}
}
void Upload(uint32_t firstStage, uint32_t stageCount)
{
Q_assert(mapped[0] != NULL);
const uint32_t batchOffset = batchFirst + batchCount;
float* pos = (float*)mapped[BasePosition] + 3 * batchOffset;
for(int v = 0; v < tess.numVertexes; ++v)
{
pos[0] = tess.xyz[v][0];
pos[1] = tess.xyz[v][1];
pos[2] = tess.xyz[v][2];
pos += 3;
}
float* nor = (float*)mapped[BaseNormal] + 3 * batchOffset;
for(int v = 0; v < tess.numVertexes; ++v)
{
nor[0] = tess.normal[v][0];
nor[1] = tess.normal[v][1];
nor[2] = tess.normal[v][2];
nor += 3;
}
for(uint32_t s = 0; s < stageCount; ++s)
{
const stageVars_t& sv = tess.svars[s + firstStage];
uint8_t* const tcBuffer = mapped[BaseCount + s * StageCount + StageTexCoords];
float* tc = (float*)tcBuffer + 2 * batchOffset;
memcpy(tc, &sv.texcoords[0], tess.numVertexes * sizeof(vec2_t));
uint8_t* const colBuffer = mapped[BaseCount + s * StageCount + StageColors];
uint32_t* col = (uint32_t*)colBuffer + batchOffset;
memcpy(col, &sv.colors[0], tess.numVertexes * sizeof(color4ub_t));
}
}
static const uint32_t BufferCount = BaseCount + StageCount * MAX_SHADER_STAGES; static const uint32_t BufferCount = BaseCount + StageCount * MAX_SHADER_STAGES;
HBuffer buffers[BufferCount] = {}; HBuffer buffers[BufferCount] = {};
@ -210,66 +88,6 @@ struct VertexBuffers : BufferBase
uint8_t* mapped[BufferCount] = {}; uint8_t* mapped[BufferCount] = {};
}; };
struct IndexBuffer : BufferBase
{
void Create(const char* name, MemoryUsage::Id memoryUsage, uint32_t indexCount)
{
totalCount = indexCount;
BufferDesc desc = {};
desc.committedResource = true;
desc.initialState = ResourceStates::IndexBufferBit;
desc.memoryUsage = memoryUsage;
desc.name = va("%s index", name);
desc.byteCount = indexCount * sizeof(uint32_t);
buffer = CreateBuffer(desc);
}
void BeginUpload()
{
mapped = (uint32_t*)BeginBufferUpload(buffer);
}
void EndUpload()
{
EndBufferUpload(buffer);
mapped = NULL;
}
void Upload()
{
Q_assert(mapped != NULL);
uint32_t* const idx = mapped + batchFirst + batchCount;
memcpy(idx, &tess.indexes[0], tess.numIndexes * sizeof(uint32_t));
}
uint32_t* GetCurrentAddress()
{
return mapped + batchFirst + batchCount;
}
HBuffer buffer = RHI_MAKE_NULL_HANDLE();
uint32_t* mapped = NULL;
};
struct GeometryBuffer : BufferBase
{
void Init(uint32_t count_, uint32_t stride_)
{
buffer = RHI_MAKE_NULL_HANDLE();
byteCount = count_ * stride_;
stride = stride_;
totalCount = count_;
batchFirst = 0;
batchCount = 0;
}
HBuffer buffer = RHI_MAKE_NULL_HANDLE();
uint32_t byteCount = 0;
uint32_t stride = 0;
};
struct GeometryBuffers struct GeometryBuffers
{ {
void Rewind() void Rewind()
@ -291,20 +109,6 @@ struct StaticGeometryChunk
uint32_t firstCPUIndex; uint32_t firstCPUIndex;
}; };
struct FrameStats
{
enum { MaxFrames = 1024 };
void EndFrame();
float temp[MaxFrames];
float p2pMS[MaxFrames];
stats_t p2pStats;
int frameCount;
int frameIndex;
int skippedFrames;
};
struct BatchType struct BatchType
{ {
enum Id enum Id
@ -396,10 +200,6 @@ struct World
HBuffer boxVertexBuffer; HBuffer boxVertexBuffer;
HBuffer boxIndexBuffer; HBuffer boxIndexBuffer;
// shader trace
HBuffer traceRenderBuffer;
HBuffer traceReadbackBuffer;
// dynamic lights // dynamic lights
HRootSignature dlRootSignature; HRootSignature dlRootSignature;
HPipeline dlPipelines[CT_COUNT * 2 * 2]; // { cull type, polygon offset, depth test } HPipeline dlPipelines[CT_COUNT * 2 * 2]; // { cull type, polygon offset, depth test }
@ -412,176 +212,6 @@ struct World
// we get "light holes" in opaque surfaces, which is not what we want // we get "light holes" in opaque surfaces, which is not what we want
}; };
struct UI
{
void Init();
void BeginFrame();
void Begin();
void End();
void DrawBatch();
void UISetColor(const uiSetColorCommand_t& cmd);
void UIDrawQuad(const uiDrawQuadCommand_t& cmd);
void UIDrawTriangle(const uiDrawTriangleCommand_t& cmd);
// 32-bit needed until the render logic is fixed!
typedef uint32_t Index;
const IndexType::Id indexType = IndexType::UInt32;
uint32_t renderPassIndex;
#pragma pack(push, 1)
struct Vertex
{
vec2_t position;
vec2_t texCoords;
uint32_t color;
};
#pragma pack(pop)
int maxIndexCount;
int maxVertexCount;
int firstIndex;
int firstVertex;
int indexCount;
int vertexCount;
HRootSignature rootSignature;
HPipeline pipeline;
HBuffer indexBuffer;
HBuffer vertexBuffer;
Index* indices;
Vertex* vertices;
uint32_t color;
const shader_t* shader;
};
struct MipMapGenerator
{
void Init();
void GenerateMipMaps(HTexture texture);
struct Stage
{
enum Id
{
Start, // gamma to linear
DownSample, // down sample on 1 axis
End, // linear to gamma
Count
};
HRootSignature rootSignature;
HDescriptorTable descriptorTable;
HPipeline pipeline;
};
struct MipSlice
{
enum Id
{
Float16_0,
Float16_1,
Count
};
};
HTexture textures[MipSlice::Count];
Stage stages[3];
};
struct ImGUI
{
void Init();
void RegisterFontAtlas();
void Draw();
void SafeBeginFrame();
void SafeEndFrame();
struct FrameResources
{
HBuffer indexBuffer;
HBuffer vertexBuffer;
};
HRootSignature rootSignature;
HPipeline pipeline;
HTexture fontAtlas;
FrameResources frameResources[FrameCount];
bool frameStarted = false;
};
struct Nuklear
{
void Init();
void BeginFrame();
void Begin();
void End();
void Upload(const nuklearUploadCommand_t& cmd);
void Draw(const nuklearDrawCommand_t& cmd);
struct FrameResources
{
HBuffer indexBuffer;
HBuffer vertexBuffer;
};
HRootSignature rootSignature;
HPipeline pipeline;
FrameResources frameResources[FrameCount];
uint32_t renderPassIndex;
int prevScissorRect[4];
// reset every frame
int firstVertex;
int firstIndex;
int numVertexes; // set in Upload
int numIndexes; // set in Upload
};
struct RenderMode
{
enum Id
{
None,
UI,
World,
ImGui,
Nuklear,
Count
};
};
struct RenderPassQueries
{
char name[64];
uint32_t gpuDurationUS;
uint32_t cpuDurationUS;
int64_t cpuStartUS;
uint32_t queryIndex;
};
enum
{
MaxRenderPasses = 64, // cg_draw3dIcons forces tons of 2D/3D transitions...
MaxStatsFrameCount = 64
};
struct RenderPassStats
{
void EndFrame(uint32_t cpu, uint32_t gpu);
uint32_t samplesCPU[MaxStatsFrameCount];
uint32_t samplesGPU[MaxStatsFrameCount];
stats_t statsCPU;
stats_t statsGPU;
uint32_t count;
uint32_t index;
};
struct RenderPassFrame
{
RenderPassQueries passes[MaxRenderPasses];
uint32_t count;
};
#pragma pack(push, 1) #pragma pack(push, 1)
struct PSODesc struct PSODesc
@ -616,6 +246,7 @@ struct PostProcess
void SetToneMapInput(HTexture toneMapInput); void SetToneMapInput(HTexture toneMapInput);
void SetInverseToneMapInput(HTexture inverseToneMapInput); void SetInverseToneMapInput(HTexture inverseToneMapInput);
private:
HPipeline toneMapPipeline; HPipeline toneMapPipeline;
HRootSignature toneMapRootSignature; HRootSignature toneMapRootSignature;
HDescriptorTable toneMapDescriptorTable; HDescriptorTable toneMapDescriptorTable;
@ -645,6 +276,7 @@ struct SMAA
}; };
}; };
private:
// fixed // fixed
HTexture areaTexture; HTexture areaTexture;
HTexture searchTexture; HTexture searchTexture;
@ -686,27 +318,18 @@ struct GRP : IRenderPipeline
void ProcessShader(shader_t& shader) override; void ProcessShader(shader_t& shader) override;
void ExecuteRenderCommands(const byte* data, bool readbackRequested) override; void ExecuteRenderCommands(const byte* data, bool readbackRequested) override;
void UISetColor(const uiSetColorCommand_t& cmd) override { ui.UISetColor(cmd); }
void UIDrawQuad(const uiDrawQuadCommand_t& cmd) override { ui.UIDrawQuad(cmd); }
void UIDrawTriangle(const uiDrawTriangleCommand_t& cmd) override { ui.UIDrawTriangle(cmd); }
void DrawSceneView(const drawSceneViewCommand_t& cmd) override { world.DrawSceneView(cmd); }
void TessellationOverflow() override { world.RestartBatch(); } void TessellationOverflow() override { world.RestartBatch(); }
void DrawSkyBox() override { world.DrawSkyBox(); } void DrawSkyBox() override { world.DrawSkyBox(); }
void DrawClouds() override { world.DrawClouds(); } void DrawClouds() override { world.DrawClouds(); }
void ReadPixels(int w, int h, int alignment, colorSpace_t colorSpace, void* out) override; void ReadPixels(int w, int h, int alignment, colorSpace_t colorSpace, void* out) override;
uint32_t GetSamplerDescriptorIndexFromBaseIndex(uint32_t baseIndex) override { return baseIndex; }
void BeginFrame(); void BeginFrame();
void EndFrame(); void EndFrame();
uint32_t RegisterTexture(HTexture htexture); uint32_t RegisterTexture(HTexture htexture);
uint32_t BeginRenderPass(const char* name, float r, float g, float b);
void EndRenderPass(uint32_t index);
void DrawGUI();
uint32_t CreatePSO(CachedPSO& cache, const char* name); uint32_t CreatePSO(CachedPSO& cache, const char* name);
void UpdateReadbackTexture(); void UpdateReadbackTexture();
@ -718,14 +341,9 @@ struct GRP : IRenderPipeline
PostProcess post; PostProcess post;
SMAA smaa; SMAA smaa;
Nuklear nuklear; Nuklear nuklear;
bool firstInit = true;
RenderMode::Id renderMode; // necessary for sampler selection, useful for debugging
float frameSeed; float frameSeed;
bool updateReadbackTexture; bool updateReadbackTexture;
// @TODO: what's up with rootSignature and uberRootSignature?
// probably need to nuke one of them...
HTexture renderTarget; HTexture renderTarget;
TextureFormat::Id renderTargetFormat; TextureFormat::Id renderTargetFormat;
HTexture readbackRenderTarget; HTexture readbackRenderTarget;
@ -733,14 +351,7 @@ struct GRP : IRenderPipeline
HRootSignature rootSignature; HRootSignature rootSignature;
HDescriptorTable descriptorTable; HDescriptorTable descriptorTable;
uint32_t textureIndex; uint32_t textureIndex;
HSampler samplers[TW_COUNT * TextureFilter::Count * MaxTextureMips]; HSampler samplers[BASE_SAMPLER_COUNT]; // all base samplers
RenderPassFrame renderPasses[FrameCount];
RenderPassFrame tempRenderPasses;
RenderPassStats renderPassStats[MaxRenderPasses];
RenderPassStats wholeFrameStats;
FrameStats frameStats;
CachedPSO psos[1024]; CachedPSO psos[1024];
uint32_t psoCount; uint32_t psoCount;
@ -748,43 +359,3 @@ struct GRP : IRenderPipeline
}; };
extern GRP grp; extern GRP grp;
struct ScopedRenderPass
{
ScopedRenderPass(const char* name, float r, float g, float b)
{
index = grp.BeginRenderPass(name, r, g, b);
}
~ScopedRenderPass()
{
grp.EndRenderPass(index);
}
uint32_t index;
};
#define SCOPED_RENDER_PASS(Name, R, G, B) ScopedRenderPass CONCAT(rp_, __LINE__)(Name, R, G, B)
inline void CmdSetViewportAndScissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
CmdSetViewport(x, y, w, h);
CmdSetScissor(x, y, w, h);
}
inline void CmdSetViewportAndScissor(const viewParms_t& vp)
{
CmdSetViewportAndScissor(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight);
}
inline bool IsDepthFadeEnabled(const shader_t& shader)
{
return
r_depthFade->integer != 0 &&
shader.dfType > DFT_NONE &&
shader.dfType < DFT_TBD;
}
const image_t* GetBundleImage(const textureBundle_t& bundle);
uint32_t GetSamplerIndex(textureWrap_t wrap, TextureFilter::Id filter, uint32_t minLOD = 0);
uint32_t GetSamplerIndex(const image_t* image);

View file

@ -1,6 +1,6 @@
/* /*
=========================================================================== ===========================================================================
Copyright (C) 2022-2023 Gian 'myT' Schellenbaum Copyright (C) 2022-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3). This file is part of Challenge Quake 3 (CNQ3).
@ -22,14 +22,42 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
#include "grp_local.h" #include "grp_local.h"
#include "uber_shaders.h" #include "grp_uber_shaders.h"
#include "hlsl/uber_shader.h" #include "compshaders/grp/uber_shader.h"
#include "hlsl/complete_uber_vs.h" #include "compshaders/grp/complete_uber_vs.h"
#include "hlsl/complete_uber_ps.h" #include "compshaders/grp/complete_uber_ps.h"
#include "../client/cl_imgui.h" #include "../client/cl_imgui.h"
namespace ui
{
#include "compshaders/grp/ui_vs.h"
#include "compshaders/grp/ui_ps.h"
}
namespace imgui
{
#include "compshaders/grp/imgui_vs.h"
#include "compshaders/grp/imgui_ps.h"
}
namespace nuklear
{
#include "compshaders/grp/nuklear_vs.h"
#include "compshaders/grp/nuklear_ps.h"
}
namespace mip_1
{
#include "compshaders/grp/mip_1_cs.h"
}
namespace mip_2
{
#include "compshaders/grp/mip_2_cs.h"
}
namespace mip_3
{
#include "compshaders/grp/mip_3_cs.h"
}
GRP grp; GRP grp;
IRenderPipeline* grpp = &grp;
static const ShaderByteCode vertexShaderByteCodes[8] = static const ShaderByteCode vertexShaderByteCodes[8] =
{ {
@ -64,79 +92,6 @@ static const uint32_t uberPixelShaderCacheSize = UBER_SHADER_PS_LIST(PS) 0;
static UberPixelShaderState uberPixelShaderStates[uberPixelShaderCacheSize]; static UberPixelShaderState uberPixelShaderStates[uberPixelShaderCacheSize];
static ImPlotPoint FrameTimeGetter(int index, void*)
{
const FrameStats& fs = grp.frameStats;
const int realIndex = (fs.frameIndex + index) % fs.frameCount;
const float value = fs.p2pMS[realIndex];
ImPlotPoint p;
p.x = index;
p.y = value;
return p;
}
static void UpdateAnimatedImage(image_t* image, int w, int h, const byte* data, qbool dirty)
{
if(w != image->width || h != image->height)
{
// @TODO: ?
/*image->width = w;
image->height = h;
CreateTexture(&d3d.textures[image->texnum], image, 1, w, h);
GAL_UpdateTexture(image, 0, 0, 0, w, h, data);*/
}
else if(dirty)
{
// @TODO: ?
//GAL_UpdateTexture(image, 0, 0, 0, w, h, data);
}
}
const image_t* GetBundleImage(const textureBundle_t& bundle)
{
return R_UpdateAndGetBundleImage(&bundle, &UpdateAnimatedImage);
}
uint32_t GetSamplerIndex(textureWrap_t wrap, TextureFilter::Id filter, uint32_t minLOD)
{
Q_assert((uint32_t)wrap < TW_COUNT);
Q_assert((uint32_t)filter < TextureFilter::Count);
const uint32_t index =
(uint32_t)filter +
(uint32_t)TextureFilter::Count * (uint32_t)wrap +
(uint32_t)TextureFilter::Count * (uint32_t)TW_COUNT * minLOD;
return index;
}
uint32_t GetSamplerIndex(const image_t* image)
{
TextureFilter::Id filter = TextureFilter::Anisotropic;
if(r_lego->integer &&
grp.renderMode == RenderMode::World &&
(image->flags & (IMG_LMATLAS | IMG_EXTLMATLAS | IMG_NOPICMIP)) == 0)
{
filter = TextureFilter::Point;
}
else if((image->flags & IMG_NOAF) != 0 ||
grp.renderMode != RenderMode::World)
{
filter = TextureFilter::Linear;
}
int minLOD = 0;
if(grp.renderMode == RenderMode::World &&
(image->flags & IMG_NOPICMIP) == 0)
{
minLOD = Com_ClampInt(0, MaxTextureMips - 1, r_picmip->integer);
}
return GetSamplerIndex(image->wrapClampMode, filter, (uint32_t)minLOD);
}
static bool IsCommutativeBlendState(unsigned int stateBits) static bool IsCommutativeBlendState(unsigned int stateBits)
{ {
const unsigned int blendStates[] = const unsigned int blendStates[] =
@ -159,42 +114,14 @@ static bool IsCommutativeBlendState(unsigned int stateBits)
return false; return false;
} }
static cullType_t GetMirrorredCullType(cullType_t cullType)
{
switch(cullType)
{
case CT_BACK_SIDED: return CT_FRONT_SIDED;
case CT_FRONT_SIDED: return CT_BACK_SIDED;
default: return CT_TWO_SIDED;
}
}
void FrameStats::EndFrame()
{
frameCount = min(frameCount + 1, (int)MaxFrames);
frameIndex = (frameIndex + 1) % MaxFrames;
Com_StatsFromArray(p2pMS, frameCount, temp, &p2pStats);
}
void RenderPassStats::EndFrame(uint32_t cpu, uint32_t gpu)
{
static uint32_t tempSamples[MaxStatsFrameCount];
samplesCPU[index] = cpu;
samplesGPU[index] = gpu;
count = min(count + 1, (uint32_t)MaxStatsFrameCount);
index = (index + 1) % MaxStatsFrameCount;
Com_StatsFromArray((const int*)samplesCPU, count, (int*)tempSamples, &statsCPU);
Com_StatsFromArray((const int*)samplesGPU, count, (int*)tempSamples, &statsGPU);
}
void GRP::Init() void GRP::Init()
{ {
firstInit = RHI::Init(); InitDesc initDesc;
initDesc.directDescriptorHeapIndexing = false;
srp.firstInit = RHI::Init(initDesc);
if(firstInit) if(srp.firstInit)
{ {
RootSignatureDesc desc("main"); RootSignatureDesc desc("main");
desc.usingVertexBuffers = true; desc.usingVertexBuffers = true;
@ -223,6 +150,11 @@ void GRP::Init()
Q_assert(!"ParseUberPixelShaderState failed!"); Q_assert(!"ParseUberPixelShaderState failed!");
} }
} }
srp.CreateShaderTraceBuffers();
DescriptorTableUpdate update;
update.SetRWBuffers(1, &srp.traceRenderBuffer, MAX_DRAWIMAGES * 2);
UpdateDescriptorTable(descriptorTable, update);
} }
// we recreate the samplers on every vid_restart to create the right level // we recreate the samplers on every vid_restart to create the right level
@ -235,7 +167,7 @@ void GRP::Init()
{ {
const textureWrap_t wrap = (textureWrap_t)w; const textureWrap_t wrap = (textureWrap_t)w;
const TextureFilter::Id filter = (TextureFilter::Id)f; const TextureFilter::Id filter = (TextureFilter::Id)f;
const uint32_t s = GetSamplerIndex(wrap, filter, m); const uint32_t s = GetBaseSamplerIndex(wrap, filter, m);
SamplerDesc desc(wrap, filter, (float)m); SamplerDesc desc(wrap, filter, (float)m);
desc.shortLifeTime = true; desc.shortLifeTime = true;
samplers[s] = CreateSampler(desc); samplers[s] = CreateSampler(desc);
@ -291,16 +223,18 @@ void GRP::Init()
readbackRenderTarget = RHI::CreateTexture(desc); readbackRenderTarget = RHI::CreateTexture(desc);
} }
ui.Init(); ui.Init(false, ShaderByteCode(ui::g_vs), ShaderByteCode(ui::g_ps), renderTargetFormat, descriptorTable, &rootSignatureDesc);
world.Init(); world.Init();
mipMapGen.Init(); mipMapGen.Init(false, ShaderByteCode(mip_1::g_cs), ShaderByteCode(mip_2::g_cs), ShaderByteCode(mip_3::g_cs));
imgui.Init(); const HTexture fontAtlas = imgui.Init(false, ShaderByteCode(imgui::g_vs), ShaderByteCode(imgui::g_ps), renderTargetFormat, descriptorTable, &rootSignatureDesc);
nuklear.Init(); const uint32_t fontAtlasSRV = RegisterTexture(fontAtlas);
imgui.RegisterFontAtlas(fontAtlasSRV);
nuklear.Init(false, ShaderByteCode(nuklear::g_vs), ShaderByteCode(nuklear::g_ps), renderTargetFormat, descriptorTable, &rootSignatureDesc);
post.Init(); post.Init();
post.SetToneMapInput(renderTarget); post.SetToneMapInput(renderTarget);
smaa.Init(); // must be after post smaa.Init(); // must be after post
firstInit = false; srp.firstInit = false;
} }
void GRP::ShutDown(bool fullShutDown) void GRP::ShutDown(bool fullShutDown)
@ -310,14 +244,15 @@ void GRP::ShutDown(bool fullShutDown)
void GRP::BeginFrame() void GRP::BeginFrame()
{ {
renderPasses[tr.frameCount % FrameCount].count = 0; srp.psoCount = psoCount;
srp.psoChangeCount = world.psoChangeCount;
R_SetColorMappings(); srp.psoStatsValid = true;
srp.BeginFrame();
smaa.Update(); smaa.Update();
// have it be first to we can use ImGUI in the other components too // have it be first to we can use ImGUI in the other components too
grp.imgui.SafeBeginFrame(); grp.imgui.BeginFrame();
RHI::BeginFrame(); RHI::BeginFrame();
ui.BeginFrame(); ui.BeginFrame();
@ -330,35 +265,19 @@ void GRP::BeginFrame()
CmdClearColorTarget(renderTarget, clearColor); CmdClearColorTarget(renderTarget, clearColor);
// nothing is bound to the command list yet! // nothing is bound to the command list yet!
renderMode = RenderMode::None; srp.renderMode = RenderMode::None;
frameSeed = (float)rand() / (float)RAND_MAX; frameSeed = (float)rand() / (float)RAND_MAX;
} }
void GRP::EndFrame() void GRP::EndFrame()
{ {
DrawGUI(); srp.DrawGUI();
R_DrawGUI(); imgui.Draw(renderTarget);
imgui.Draw();
post.Draw("Post-process", GetSwapChainTexture()); post.Draw("Post-process", GetSwapChainTexture());
world.EndFrame(); world.EndFrame();
UpdateReadbackTexture(); UpdateReadbackTexture();
RHI::EndFrame(); srp.EndFrame();
if(rhie.presentToPresentUS > 0)
{
frameStats.p2pMS[frameStats.frameIndex] = (float)rhie.presentToPresentUS / 1000.0f;
frameStats.EndFrame();
}
else
{
frameStats.skippedFrames++;
}
if(backEnd.renderFrame)
{
Sys_V_EndFrame();
}
} }
void GRP::UpdateReadbackTexture() void GRP::UpdateReadbackTexture()
@ -525,206 +444,6 @@ uint32_t GRP::RegisterTexture(HTexture htexture)
return index; return index;
} }
uint32_t GRP::BeginRenderPass(const char* name, float r, float g, float b)
{
RenderPassFrame& f = renderPasses[tr.frameCount % FrameCount];
if(f.count >= ARRAY_LEN(f.passes))
{
Q_assert(0);
return UINT32_MAX;
}
CmdBeginDebugLabel(name, r, g, b);
const uint32_t index = f.count++;
RenderPassQueries& q = f.passes[index];
Q_strncpyz(q.name, name, sizeof(q.name));
q.cpuStartUS = Sys_Microseconds();
q.queryIndex = CmdBeginDurationQuery();
return index;
}
void GRP::EndRenderPass(uint32_t index)
{
RenderPassFrame& f = renderPasses[tr.frameCount % FrameCount];
if(index >= f.count)
{
Q_assert(0);
return;
}
CmdEndDebugLabel();
RenderPassQueries& q = f.passes[index];
q.cpuDurationUS = (uint32_t)(Sys_Microseconds() - q.cpuStartUS);
CmdEndDurationQuery(q.queryIndex);
}
void GRP::DrawGUI()
{
uint32_t durations[MaxDurationQueries];
GetDurations(durations);
wholeFrameStats.EndFrame(rhie.renderToPresentUS, durations[0]);
const RenderPassFrame& currFrame = renderPasses[(tr.frameCount % FrameCount) ^ 1];
RenderPassFrame& tempFrame = tempRenderPasses;
// see if the render pass list is the same as the previous frame's
bool sameRenderPass = true;
if(currFrame.count == tempRenderPasses.count)
{
for(uint32_t p = 0; p < currFrame.count; ++p)
{
if(Q_stricmp(currFrame.passes[p].name, tempRenderPasses.passes[p].name) != 0)
{
sameRenderPass = false;
break;
}
}
}
else
{
sameRenderPass = false;
}
// write out the displayed timings into the temp buffer
tempFrame.count = currFrame.count;
if(sameRenderPass)
{
for(uint32_t p = 0; p < currFrame.count; ++p)
{
const uint32_t index = currFrame.passes[p].queryIndex;
if(index < MaxDurationQueries)
{
renderPassStats[p].EndFrame(currFrame.passes[p].cpuDurationUS, durations[index]);
tempFrame.passes[p].gpuDurationUS = renderPassStats[p].statsGPU.median;
tempFrame.passes[p].cpuDurationUS = renderPassStats[p].statsCPU.median;
}
}
}
else
{
for(uint32_t p = 0; p < currFrame.count; ++p)
{
const uint32_t index = currFrame.passes[p].queryIndex;
if(index < MaxDurationQueries)
{
tempFrame.passes[p].gpuDurationUS = durations[index];
tempFrame.passes[p].cpuDurationUS = currFrame.passes[p].cpuDurationUS;
}
}
}
static bool breakdownActive = false;
ToggleBooleanWithShortcut(breakdownActive, ImGuiKey_F);
GUI_AddMainMenuItem(GUI_MainMenu::Perf, "Frame breakdown", "Ctrl+F", &breakdownActive);
if(breakdownActive)
{
if(ImGui::Begin("Frame breakdown", &breakdownActive, ImGuiWindowFlags_AlwaysAutoResize))
{
if(BeginTable("Frame breakdown", 3))
{
TableHeader(3, "Pass", "GPU [us]", "CPU [us]");
TableRow(3, "Whole frame",
va("%d", (int)wholeFrameStats.statsGPU.median),
va("%d", (int)wholeFrameStats.statsCPU.median));
for(uint32_t p = 0; p < currFrame.count; ++p)
{
const RenderPassQueries& rp = tempFrame.passes[p];
if(rp.queryIndex < MaxDurationQueries)
{
TableRow(3, rp.name,
va("%d", (int)rp.gpuDurationUS),
va("%d", (int)rp.cpuDurationUS));
}
}
ImGui::EndTable();
}
ImGui::Text("PSO count: %d", (int)grp.psoCount);
ImGui::Text("PSO changes: %d", (int)grp.world.psoChangeCount);
}
ImGui::End();
}
// save the current render pass list in the temp buffer
memcpy(&tempFrame, &currFrame, sizeof(tempFrame));
static bool frameTimeActive = false;
GUI_AddMainMenuItem(GUI_MainMenu::Perf, "Frame stats", NULL, &frameTimeActive);
if(frameTimeActive)
{
if(ImGui::Begin("Frame stats", &frameTimeActive, ImGuiWindowFlags_AlwaysAutoResize))
{
if(BeginTable("Frame stats", 2))
{
const FrameStats& fs = frameStats;
const stats_t& s = fs.p2pStats;
TableRow2("Skipped frames", fs.skippedFrames);
TableRow2("Frame time target", rhie.targetFrameDurationMS);
TableRow2("Frame time average", s.average);
TableRow2("Frame time std dev.", s.stdDev);
TableRow2("Input to render", (float)rhie.inputToRenderUS / 1000.0f);
TableRow2("Input to present", (float)rhie.inputToPresentUS / 1000.0f);
ImGui::EndTable();
}
}
ImGui::End();
}
static bool graphsActive = false;
ToggleBooleanWithShortcut(graphsActive, ImGuiKey_G);
GUI_AddMainMenuItem(GUI_MainMenu::Perf, "Frame time graphs", "Ctrl+G", &graphsActive);
if(graphsActive)
{
const int windowFlags =
ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoMove;
ImGui::SetNextWindowSize(ImVec2(glConfig.vidWidth, glConfig.vidHeight / 2), ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(0, glConfig.vidHeight / 2), ImGuiCond_Always);
if(ImGui::Begin("Frame time graphs", &graphsActive, windowFlags))
{
const FrameStats& fs = frameStats;
const double target = (double)rhie.targetFrameDurationMS;
static bool autoFit = false;
ImGui::Checkbox("Auto-fit", &autoFit);
if(ImPlot::BeginPlot("Frame Times", ImVec2(-1, -1), ImPlotFlags_NoInputs))
{
const int axisFlags = 0; // ImPlotAxisFlags_NoTickLabels
const int axisFlagsY = axisFlags | (autoFit ? ImPlotAxisFlags_AutoFit : 0);
ImPlot::SetupAxes(NULL, NULL, axisFlags, axisFlagsY);
ImPlot::SetupAxisLimits(ImAxis_X1, 0, FrameStats::MaxFrames, ImGuiCond_Always);
if(!autoFit)
{
ImPlot::SetupAxisLimits(ImAxis_Y1, max(target - 2.0, 0.0), target + 2.0, ImGuiCond_Always);
}
ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 1.0f);
ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL, 1.0f);
ImPlot::PlotInfLines("Target", &target, 1, ImPlotInfLinesFlags_Horizontal);
ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 1.0f);
ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL, 1.0f);
ImPlot::PlotLineG("Frame Time", &FrameTimeGetter, NULL, fs.frameCount, ImPlotLineFlags_None);
ImPlot::EndPlot();
}
}
ImGui::End();
}
GUI_DrawMainMenu();
}
uint32_t GRP::CreatePSO(CachedPSO& cache, const char* name) uint32_t GRP::CreatePSO(CachedPSO& cache, const char* name)
{ {
Q_assert(cache.stageCount > 0); Q_assert(cache.stageCount > 0);
@ -785,6 +504,9 @@ uint32_t GRP::CreatePSO(CachedPSO& cache, const char* name)
{ {
uint32_t macroCount = 0; uint32_t macroCount = 0;
ShaderMacro macros[64]; ShaderMacro macros[64];
macros[macroCount].name = "DISABLE_PRAGMA_ONCE";
macros[macroCount].value = "1";
macroCount++;
macros[macroCount].name = "STAGE_COUNT"; macros[macroCount].name = "STAGE_COUNT";
macros[macroCount].value = va("%d", cache.stageCount); macros[macroCount].value = va("%d", cache.stageCount);
macroCount++; macroCount++;
@ -908,16 +630,16 @@ void GRP::ExecuteRenderCommands(const byte* data, bool readbackRequested)
switch(commandId) switch(commandId)
{ {
case RC_UI_SET_COLOR: case RC_UI_SET_COLOR:
UISetColor(*(const uiSetColorCommand_t*)data); ui.CmdSetColor(*(const uiSetColorCommand_t*)data);
break; break;
case RC_UI_DRAW_QUAD: case RC_UI_DRAW_QUAD:
UIDrawQuad(*(const uiDrawQuadCommand_t*)data); ui.CmdDrawQuad(*(const uiDrawQuadCommand_t*)data);
break; break;
case RC_UI_DRAW_TRIANGLE: case RC_UI_DRAW_TRIANGLE:
UIDrawTriangle(*(const uiDrawTriangleCommand_t*)data); ui.CmdDrawTriangle(*(const uiDrawTriangleCommand_t*)data);
break; break;
case RC_DRAW_SCENE_VIEW: case RC_DRAW_SCENE_VIEW:
DrawSceneView(*(const drawSceneViewCommand_t*)data); world.DrawSceneView(*(const drawSceneViewCommand_t*)data);
break; break;
case RC_BEGIN_FRAME: case RC_BEGIN_FRAME:
BeginFrame(); BeginFrame();
@ -926,7 +648,7 @@ void GRP::ExecuteRenderCommands(const byte* data, bool readbackRequested)
EndFrame(); EndFrame();
break; break;
case RC_BEGIN_UI: case RC_BEGIN_UI:
ui.Begin(); ui.Begin(renderTarget);
break; break;
case RC_END_UI: case RC_END_UI:
ui.End(); ui.End();
@ -941,7 +663,7 @@ void GRP::ExecuteRenderCommands(const byte* data, bool readbackRequested)
smaa.Draw(((const endSceneCommand_t*)data)->viewParms); smaa.Draw(((const endSceneCommand_t*)data)->viewParms);
break; break;
case RC_BEGIN_NK: case RC_BEGIN_NK:
nuklear.Begin(); nuklear.Begin(renderTarget);
break; break;
case RC_END_NK: case RC_END_NK:
nuklear.End(); nuklear.End();
@ -963,49 +685,5 @@ void GRP::ExecuteRenderCommands(const byte* data, bool readbackRequested)
void GRP::ReadPixels(int w, int h, int alignment, colorSpace_t colorSpace, void* outPixels) void GRP::ReadPixels(int w, int h, int alignment, colorSpace_t colorSpace, void* outPixels)
{ {
MappedTexture mapped; ReadTextureImage(outPixels, readbackRenderTarget, w, h, alignment, colorSpace);
BeginTextureReadback(mapped, grp.readbackRenderTarget);
byte* const out0 = (byte*)outPixels;
const byte* const in0 = mapped.mappedData;
if(colorSpace == CS_RGBA)
{
const int dstRowSizeNoPadding = w * 4;
mapped.dstRowByteCount = AlignUp(dstRowSizeNoPadding, alignment);
for(int y = 0; y < mapped.rowCount; ++y)
{
byte* out = out0 + (mapped.rowCount - 1 - y) * mapped.dstRowByteCount;
const byte* in = in0 + y * mapped.srcRowByteCount;
memcpy(out, in, dstRowSizeNoPadding);
}
}
else if(colorSpace == CS_BGR)
{
mapped.dstRowByteCount = AlignUp(w * 3, alignment);
for(int y = 0; y < mapped.rowCount; ++y)
{
byte* out = out0 + (mapped.rowCount - 1 - y) * mapped.dstRowByteCount;
const byte* in = in0 + y * mapped.srcRowByteCount;
for(int x = 0; x < mapped.columnCount; ++x)
{
out[2] = in[0];
out[1] = in[1];
out[0] = in[2];
out += 3;
in += 4;
}
}
}
else
{
Q_assert(!"Unsupported color space");
}
EndTextureReadback();
} }
// @TODO: move out once the cinematic render pipeline is added
IRenderPipeline* renderPipeline = &grp;

View file

@ -1,6 +1,6 @@
/* /*
=========================================================================== ===========================================================================
Copyright (C) 2023 Gian 'myT' Schellenbaum Copyright (C) 2023-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3). This file is part of Challenge Quake 3 (CNQ3).
@ -24,13 +24,13 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
#include "grp_local.h" #include "grp_local.h"
namespace tone_map namespace tone_map
{ {
#include "hlsl/post_gamma_vs.h" #include "compshaders/grp/post_gamma_vs.h"
#include "hlsl/post_gamma_ps.h" #include "compshaders/grp/post_gamma_ps.h"
} }
namespace inverse_tone_map namespace inverse_tone_map
{ {
#include "hlsl/post_inverse_gamma_vs.h" #include "compshaders/grp/post_inverse_gamma_vs.h"
#include "hlsl/post_inverse_gamma_ps.h" #include "compshaders/grp/post_inverse_gamma_ps.h"
} }
@ -60,7 +60,7 @@ struct InverseGammaPixelRC
void PostProcess::Init() void PostProcess::Init()
{ {
if(!grp.firstInit) if(!srp.firstInit)
{ {
return; return;
} }

View file

@ -1,6 +1,6 @@
/* /*
=========================================================================== ===========================================================================
Copyright (C) 2022-2023 Gian 'myT' Schellenbaum Copyright (C) 2022-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3). This file is part of Challenge Quake 3 (CNQ3).
@ -24,7 +24,7 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
#include "grp_local.h" #include "grp_local.h"
#include "smaa_area_texture.h" #include "smaa_area_texture.h"
#include "smaa_search_texture.h" #include "smaa_search_texture.h"
#include "hlsl/complete_smaa.h" #include "compshaders/grp/complete_smaa.h"
#define SMAA_PRESET_LIST(P) \ #define SMAA_PRESET_LIST(P) \
@ -96,7 +96,7 @@ void SMAA::Update()
bool createPresetDep = justEnabled || (alwaysEnabled && presetChanged); bool createPresetDep = justEnabled || (alwaysEnabled && presetChanged);
bool destroyPresetDep = justDisabled || (alwaysEnabled && presetChanged); bool destroyPresetDep = justDisabled || (alwaysEnabled && presetChanged);
if(grp.firstInit) if(srp.firstInit)
{ {
// first init or device change: we have nothing to destroy // first init or device change: we have nothing to destroy
const bool enableSMAA = newMode != Mode::Disabled; const bool enableSMAA = newMode != Mode::Disabled;

View file

@ -1,3 +1,29 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Gameplay Rendering Pipeline - defines all pre-compiled uber pixel shader combinations
#pragma once
// format: stage_count global_state (stage_state_in_hex)+ // format: stage_count global_state (stage_state_in_hex)+
#define UBER_SHADER_PS_LIST(PS) \ #define UBER_SHADER_PS_LIST(PS) \
PS(1_0_0) \ PS(1_0_0) \

View file

@ -1,6 +1,6 @@
/* /*
=========================================================================== ===========================================================================
Copyright (C) 2022-2023 Gian 'myT' Schellenbaum Copyright (C) 2022-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3). This file is part of Challenge Quake 3 (CNQ3).
@ -28,25 +28,25 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
#include "../client/cl_imgui.h" #include "../client/cl_imgui.h"
namespace zpp namespace zpp
{ {
#include "hlsl/depth_pre_pass_vs.h" #include "compshaders/grp/depth_pre_pass_vs.h"
#include "hlsl/depth_pre_pass_ps.h" #include "compshaders/grp/depth_pre_pass_ps.h"
} }
namespace fog namespace fog
{ {
#include "hlsl/fog_vs.h" #include "compshaders/grp/fog_vs.h"
} }
namespace fog_inside namespace fog_inside
{ {
#include "hlsl/fog_inside_ps.h" #include "compshaders/grp/fog_inside_ps.h"
} }
namespace fog_outside namespace fog_outside
{ {
#include "hlsl/fog_outside_ps.h" #include "compshaders/grp/fog_outside_ps.h"
} }
namespace dyn_light namespace dyn_light
{ {
#include "hlsl/dynamic_light_vs.h" #include "compshaders/grp/dynamic_light_vs.h"
#include "hlsl/dynamic_light_ps.h" #include "compshaders/grp/dynamic_light_ps.h"
} }
@ -126,40 +126,6 @@ static bool HasStaticGeo(int staticGeoChunk, const shader_t* shader)
staticGeoChunk < ARRAY_LEN(grp.world.statChunks); staticGeoChunk < ARRAY_LEN(grp.world.statChunks);
} }
static void UpdateEntityData(bool& depthHack, int entityNum, double originalTime)
{
depthHack = false;
if(entityNum != ENTITYNUM_WORLD)
{
backEnd.currentEntity = &backEnd.refdef.entities[entityNum];
if(backEnd.currentEntity->intShaderTime)
backEnd.refdef.floatTime = originalTime - (double)backEnd.currentEntity->e.shaderTime.iShaderTime / 1000.0;
else
backEnd.refdef.floatTime = originalTime - backEnd.currentEntity->e.shaderTime.fShaderTime;
// we have to reset the shaderTime as well otherwise image animations start
// from the wrong frame
tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
// set up the transformation matrix
R_RotateForEntity(backEnd.currentEntity, &backEnd.viewParms, &backEnd.orient);
if(backEnd.currentEntity->e.renderfx & RF_DEPTHHACK)
{
depthHack = true;
}
}
else
{
backEnd.currentEntity = &tr.worldEntity;
backEnd.refdef.floatTime = originalTime;
backEnd.orient = backEnd.viewParms.world;
// we have to reset the shaderTime as well otherwise image animations on
// the world (like water) continue with the wrong frame
tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
}
}
static int GetDynamicLightPipelineIndex(cullType_t cullType, qbool polygonOffset, qbool depthTestEquals) static int GetDynamicLightPipelineIndex(cullType_t cullType, qbool polygonOffset, qbool depthTestEquals)
{ {
return (int)cullType + CT_COUNT * (int)polygonOffset + CT_COUNT * 2 * (int)depthTestEquals; return (int)cullType + CT_COUNT * (int)polygonOffset + CT_COUNT * 2 * (int)depthTestEquals;
@ -168,7 +134,7 @@ static int GetDynamicLightPipelineIndex(cullType_t cullType, qbool polygonOffset
void World::Init() void World::Init()
{ {
if(grp.firstInit) if(srp.firstInit)
{ {
fogDescriptorTable = RHI_MAKE_NULL_HANDLE(); fogDescriptorTable = RHI_MAKE_NULL_HANDLE();
} }
@ -192,7 +158,7 @@ void World::Init()
} }
} }
if(grp.firstInit) if(srp.firstInit)
{ {
// //
// depth pre-pass // depth pre-pass
@ -313,23 +279,6 @@ void World::Init()
EndBufferUpload(boxVertexBuffer); EndBufferUpload(boxVertexBuffer);
} }
//
// shader trace
//
{
BufferDesc desc("shader trace opaque", 2 * sizeof(uint32_t), ResourceStates::UnorderedAccessBit);
traceRenderBuffer = CreateBuffer(desc);
DescriptorTableUpdate update;
update.SetRWBuffers(1, &traceRenderBuffer, MAX_DRAWIMAGES * 2);
UpdateDescriptorTable(grp.descriptorTable, update);
}
{
BufferDesc desc("shader trace opaque readback", 2 * sizeof(uint32_t), ResourceStates::Common);
desc.memoryUsage = MemoryUsage::Readback;
traceReadbackBuffer = CreateBuffer(desc);
}
// //
// dynamic lights // dynamic lights
// //
@ -425,34 +374,11 @@ void World::BeginFrame()
void World::EndFrame() void World::EndFrame()
{ {
tr.tracedWorldShaderIndex = -1;
if(tr.traceWorldShader && tr.world != NULL)
{
// schedule a GPU -> CPU transfer
{
BufferBarrier barrier(traceRenderBuffer, ResourceStates::CopySourceBit);
CmdBarrier(0, NULL, 1, &barrier);
}
CmdCopyBuffer(traceReadbackBuffer, traceRenderBuffer);
{
BufferBarrier barrier(traceRenderBuffer, ResourceStates::UnorderedAccessBit);
CmdBarrier(0, NULL, 1, &barrier);
}
// grab last frame's result
uint32_t* shaderIndices = (uint32_t*)MapBuffer(traceReadbackBuffer);
const uint32_t shaderIndex = shaderIndices[RHI::GetFrameIndex() ^ 1];
UnmapBuffer(traceReadbackBuffer);
if(shaderIndex < (uint32_t)tr.numShaders)
{
tr.tracedWorldShaderIndex = (int)shaderIndex;
}
}
} }
void World::Begin() void World::Begin()
{ {
grp.renderMode = RenderMode::World; srp.renderMode = RenderMode::World;
if(backEnd.viewParms.isPortal) if(backEnd.viewParms.isPortal)
{ {
@ -495,7 +421,7 @@ void World::Begin()
void World::End() void World::End()
{ {
grp.renderMode = RenderMode::None; srp.renderMode = RenderMode::None;
} }
void World::DrawPrePass(const drawSceneViewCommand_t& cmd) void World::DrawPrePass(const drawSceneViewCommand_t& cmd)

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/* /*
=========================================================================== ===========================================================================
Copyright (C) 2022-2023 Gian 'myT' Schellenbaum Copyright (C) 2022-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3). This file is part of Challenge Quake 3 (CNQ3).
@ -169,6 +169,7 @@ namespace RHI
R8_UNorm, R8_UNorm,
Depth24_Stencil8, Depth24_Stencil8,
R10G10B10A2_UNorm, R10G10B10A2_UNorm,
R32_UInt,
Count Count
}; };
}; };
@ -353,6 +354,11 @@ namespace RHI
name = name_; name = name_;
rootSignature = rootSignature_; rootSignature = rootSignature_;
} }
explicit GraphicsPipelineDesc(const char* name_)
{
name = name_;
rootSignature = RHI_MAKE_NULL_HANDLE();
}
const char* name = NULL; const char* name = NULL;
bool shortLifeTime = false; bool shortLifeTime = false;
@ -434,6 +440,11 @@ namespace RHI
name = name_; name = name_;
rootSignature = rootSignature_; rootSignature = rootSignature_;
} }
explicit ComputePipelineDesc(const char* name_)
{
name = name_;
rootSignature = RHI_MAKE_NULL_HANDLE();
}
const char* name = NULL; const char* name = NULL;
bool shortLifeTime = false; bool shortLifeTime = false;
@ -677,7 +688,15 @@ namespace RHI
const ShaderMacro* macros = NULL; const ShaderMacro* macros = NULL;
}; };
bool Init(); // true when a full init happened (the device was created) struct InitDesc
{
// HLSL 6.6 Dynamic Resources
// - all shader resources are exclusively used through ResourceDescriptorHeap and SamplerDescriptorHeap
// - all root signature and descriptor table functions are disabled
bool directDescriptorHeapIndexing = false;
};
bool Init(const InitDesc& desc); // true when a full init happened (the device was created)
void ShutDown(bool destroyWindow); void ShutDown(bool destroyWindow);
void BeginFrame(); void BeginFrame();
@ -721,6 +740,8 @@ namespace RHI
void CmdSetViewport(uint32_t x, uint32_t y, uint32_t w, uint32_t h, float minDepth = 0.0f, float maxDepth = 1.0f); void CmdSetViewport(uint32_t x, uint32_t y, uint32_t w, uint32_t h, float minDepth = 0.0f, float maxDepth = 1.0f);
void CmdSetScissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h); void CmdSetScissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void CmdSetRootConstants(HRootSignature rootSignature, ShaderStage::Id shaderType, const void* constants); void CmdSetRootConstants(HRootSignature rootSignature, ShaderStage::Id shaderType, const void* constants);
void CmdSetGraphicsRootConstants(uint32_t byteOffset, uint32_t byteCount, const void* constants);
void CmdSetComputeRootConstants(uint32_t byteOffset, uint32_t byteCount, const void* constants);
void CmdDraw(uint32_t vertexCount, uint32_t firstVertex); void CmdDraw(uint32_t vertexCount, uint32_t firstVertex);
void CmdDrawIndexed(uint32_t indexCount, uint32_t firstIndex, uint32_t firstVertex); void CmdDrawIndexed(uint32_t indexCount, uint32_t firstIndex, uint32_t firstVertex);
void CmdDispatch(uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); void CmdDispatch(uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
@ -729,7 +750,7 @@ namespace RHI
void CmdBarrier(uint32_t texCount, const TextureBarrier* textures, uint32_t buffCount = 0, const BufferBarrier* buffers = NULL); void CmdBarrier(uint32_t texCount, const TextureBarrier* textures, uint32_t buffCount = 0, const BufferBarrier* buffers = NULL);
void CmdClearColorTarget(HTexture texture, const vec4_t clearColor, const Rect* rect = NULL); void CmdClearColorTarget(HTexture texture, const vec4_t clearColor, const Rect* rect = NULL);
void CmdClearDepthStencilTarget(HTexture texture, bool clearDepth, float depth, bool clearStencil = false, uint8_t stencil = 0, const Rect* rect = NULL); void CmdClearDepthStencilTarget(HTexture texture, bool clearDepth, float depth, bool clearStencil = false, uint8_t stencil = 0, const Rect* rect = NULL);
void CmdClearTextureUAV(HTexture texture, HDescriptorTable descTable, uint32_t tableIndex, uint32_t mipIndex, const uint32_t* values); void CmdClearTextureUAV(HTexture texture, uint32_t mipIndex, const uint32_t* values);
void CmdInsertDebugLabel(const char* name, float r = 1.0f, float g = 1.0f, float b = 1.0f); void CmdInsertDebugLabel(const char* name, float r = 1.0f, float g = 1.0f, float b = 1.0f);
void CmdBeginDebugLabel(const char* name, float r = 1.0f, float g = 1.0f, float b = 1.0f); void CmdBeginDebugLabel(const char* name, float r = 1.0f, float g = 1.0f, float b = 1.0f);
void CmdEndDebugLabel(); void CmdEndDebugLabel();
@ -737,6 +758,15 @@ namespace RHI
void CmdCopyBuffer(HBuffer dest, HBuffer source); void CmdCopyBuffer(HBuffer dest, HBuffer source);
void CmdSetShadingRate(ShadingRate::Id shadingRate); void CmdSetShadingRate(ShadingRate::Id shadingRate);
// only available when dynamic resources are enabled
uint32_t GetTextureIndexSRV(HTexture texture);
uint32_t GetTextureIndexUAV(HTexture texture, uint32_t mipIndex);
uint32_t GetBufferIndexSRV(HBuffer buffer);
uint32_t GetBufferIndexUAV(HBuffer buffer);
uint32_t GetBufferIndexCBV(HBuffer buffer);
uint32_t GetSamplerIndex(HSampler sampler);
void CmdBarrierUAV();
// the duration at index 0 is for the entire frame // the duration at index 0 is for the entire frame
uint32_t GetDurationCount(); uint32_t GetDurationCount();
void GetDurations(uint32_t* gpuMicroSeconds); void GetDurations(uint32_t* gpuMicroSeconds);
@ -756,6 +786,8 @@ namespace RHI
void WaitUntilDeviceIsIdle(); void WaitUntilDeviceIsIdle();
void SubmitAndContinue();
const Handle HandleIndexBitCount = 16; const Handle HandleIndexBitCount = 16;
const Handle HandleIndexBitOffset = 0; const Handle HandleIndexBitOffset = 0;
const Handle HandleGenBitCount = 10; const Handle HandleGenBitCount = 10;

View file

@ -0,0 +1,81 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Quake 3 blend equations
#if !defined(DISABLE_PRAGMA_ONCE)
#pragma once
#endif
float4 BlendSource(float4 src, float4 dst, uint stateBits)
{
if(stateBits == GLS_SRCBLEND_ZERO)
return float4(0.0, 0.0, 0.0, 0.0);
else if(stateBits == GLS_SRCBLEND_ONE)
return src;
else if(stateBits == GLS_SRCBLEND_DST_COLOR)
return src * dst;
else if(stateBits == GLS_SRCBLEND_ONE_MINUS_DST_COLOR)
return src * (float4(1.0, 1.0, 1.0, 1.0) - dst);
else if(stateBits == GLS_SRCBLEND_SRC_ALPHA)
return src * float4(src.a, src.a, src.a, 1.0);
else if(stateBits == GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA)
return src * float4(1.0 - src.a, 1.0 - src.a, 1.0 - src.a, 1.0);
else if(stateBits == GLS_SRCBLEND_DST_ALPHA)
return src * float4(dst.a, dst.a, dst.a, 1.0);
else if(stateBits == GLS_SRCBLEND_ONE_MINUS_DST_ALPHA)
return src * float4(1.0 - dst.a, 1.0 - dst.a, 1.0 - dst.a, 1.0);
else if(stateBits == GLS_SRCBLEND_ALPHA_SATURATE)
return src * float4(src.a, src.a, src.a, 1.0);
else
return src;
}
float4 BlendDest(float4 src, float4 dst, uint stateBits)
{
if(stateBits == GLS_DSTBLEND_ZERO)
return float4(0.0, 0.0, 0.0, 0.0);
else if(stateBits == GLS_DSTBLEND_ONE)
return dst;
else if(stateBits == GLS_DSTBLEND_SRC_COLOR)
return dst * src;
else if(stateBits == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR)
return dst * float4(1.0 - src.r, 1.0 - src.g, 1.0 - src.b, 1.0 - src.a);
else if(stateBits == GLS_DSTBLEND_SRC_ALPHA)
return dst * float4(src.a, src.a, src.a, 1.0);
else if(stateBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA)
return dst * float4(1.0 - src.a, 1.0 - src.a, 1.0 - src.a, 0.0);
else if(stateBits == GLS_DSTBLEND_DST_ALPHA)
return dst * float4(dst.a, dst.a, dst.a, 1.0);
else if(stateBits == GLS_DSTBLEND_ONE_MINUS_DST_ALPHA)
return dst * float4(1.0 - dst.a, 1.0 - dst.a, 1.0 - dst.a, 1.0);
else
return float4(0.0, 0.0, 0.0, 0.0);
}
float4 Blend(float4 src, float4 dst, uint stateBits)
{
float4 srcOut = BlendSource(src, dst, stateBits & GLS_SRCBLEND_BITS);
float4 dstOut = BlendDest(src, dst, stateBits & GLS_DSTBLEND_BITS);
return srcOut + dstOut;
}

View file

@ -0,0 +1,96 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// mip-map generation
#pragma once
uint2 MipGen_FixCoords(int2 tc, int2 maxSize, uint clampCoords)
{
if(clampCoords > 0)
{
// clamp
return uint2(clamp(tc, int2(0, 0), maxSize));
}
// repeat
return uint2(tc & maxSize);
}
void MipGen_GammaToLinear(RWTexture2D<float4> dst, RWTexture2D<float4> src, uint3 dtid, float gamma)
{
uint w, h;
dst.GetDimensions(w, h);
if(any(dtid.xy >= uint2(w, h)))
{
return;
}
float4 v = src[dtid.xy];
dst[dtid.xy] = float4(pow(v.xyz, gamma), v.a);
}
void MipGen_LinearToGamma(RWTexture2D<float4> dst, RWTexture2D<float4> src, uint3 dtid, float4 blendColor, float intensity, float invGamma)
{
uint w, h;
dst.GetDimensions(w, h);
if(any(dtid.xy >= uint2(w, h)))
{
return;
}
// yes, intensity *should* be done in light-linear space
// but we keep the old behavior for consistency...
float4 in0 = src[dtid.xy];
float3 in1 = 0.5 * (in0.rgb + blendColor.rgb);
float3 inV = lerp(in0.rgb, in1.rgb, blendColor.a);
float3 out0 = pow(max(inV, 0.0), invGamma);
float3 out1 = out0 * intensity;
float4 outV = saturate(float4(out1, in0.a));
dst[dtid.xy] = outV;
}
void MipGen_DownSample(RWTexture2D<float4> dst, RWTexture2D<float4> src, uint3 dtid, int2 maxSize, uint clampCoords, int2 scale, int2 offset, float4 weights)
{
uint w, h;
dst.GetDimensions(w, h);
if(any(dtid.xy >= uint2(w, h)))
{
return;
}
#define FixCoords(tc) MipGen_FixCoords(tc, maxSize, clampCoords)
int2 base = int2(dtid.xy) * scale;
float4 r = float4(0, 0, 0, 0);
r += src[FixCoords(base - offset * 3)] * weights.x;
r += src[FixCoords(base - offset * 2)] * weights.y;
r += src[FixCoords(base - offset * 1)] * weights.z;
r += src[base] * weights.w;
r += src[base + offset] * weights.w;
r += src[FixCoords(base + offset * 2)] * weights.z;
r += src[FixCoords(base + offset * 3)] * weights.y;
r += src[FixCoords(base + offset * 4)] * weights.x;
dst[dtid.xy] = r;
#undef FixCoords
}

View file

@ -0,0 +1,65 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// shader stage state constants
#if !defined(DISABLE_PRAGMA_ONCE)
#pragma once
#endif
#define GLS_SRCBLEND_ZERO 0x00000001u
#define GLS_SRCBLEND_ONE 0x00000002u
#define GLS_SRCBLEND_DST_COLOR 0x00000003u
#define GLS_SRCBLEND_ONE_MINUS_DST_COLOR 0x00000004u
#define GLS_SRCBLEND_SRC_ALPHA 0x00000005u
#define GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA 0x00000006u
#define GLS_SRCBLEND_DST_ALPHA 0x00000007u
#define GLS_SRCBLEND_ONE_MINUS_DST_ALPHA 0x00000008u
#define GLS_SRCBLEND_ALPHA_SATURATE 0x00000009u
#define GLS_SRCBLEND_BITS 0x0000000fu
#define GLS_DSTBLEND_ZERO 0x00000010u
#define GLS_DSTBLEND_ONE 0x00000020u
#define GLS_DSTBLEND_SRC_COLOR 0x00000030u
#define GLS_DSTBLEND_ONE_MINUS_SRC_COLOR 0x00000040u
#define GLS_DSTBLEND_SRC_ALPHA 0x00000050u
#define GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA 0x00000060u
#define GLS_DSTBLEND_DST_ALPHA 0x00000070u
#define GLS_DSTBLEND_ONE_MINUS_DST_ALPHA 0x00000080u
#define GLS_DSTBLEND_BITS 0x000000f0u
#define GLS_BLEND_BITS 0x000000ffu
#define GLS_DEPTHMASK_TRUE 0x00000100u // enable depth writes
#define GLS_POLYMODE_LINE 0x00001000u // wireframe polygon filling, not line rendering
#define GLS_DEPTHTEST_DISABLE 0x00010000u // disable depth tests
#define GLS_DEPTHFUNC_EQUAL 0x00020000u
#define GLS_STAGEINDEX_BITS 0x00700000u
#define GLS_STAGEINDEX_SHIFT 20u
#define GLS_ATEST_GT_0 0x10000000u
#define GLS_ATEST_LT_80 0x20000000u
#define GLS_ATEST_GE_80 0x40000000u
#define GLS_ATEST_BITS 0x70000000u

View file

@ -0,0 +1,65 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// accumulation depth of field: accumulation pass
#include "common.hlsli"
cbuffer RootConstants
{
uint textureIndex;
};
struct VOut
{
float4 position : SV_Position;
};
#if VERTEX_SHADER
VOut vs(uint id : SV_VertexID)
{
VOut output;
output.position = FSTrianglePosFromVertexId(id);
return output;
}
#endif
#if PIXEL_SHADER
float4 ps(VOut input) : SV_Target
{
Texture2D texture0 = ResourceDescriptorHeap[textureIndex];
int2 tc = int2(input.position.xy);
float3 color = texture0.Load(int3(tc.x, tc.y, 0)).rgb;
float weight = 1.0 + Brightness(color);
float4 result = float4(color * weight, weight);
return result;
}
#endif

View file

@ -0,0 +1,99 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// accumulation depth of field: debug overlay
#include "common.hlsli"
#include "dof.hlsli"
cbuffer RootConstants
{
matrix mvp; // displaced view, to project to CS
matrix invMvp; // main view, to unproject to WS
uint colorTextureIndex;
uint depthTextureIndex;
uint debugMode; // 1: colorized coc, 2: constant intensity far field
int tcScale;
float focusDist;
float linearDepthA; // main view, to unproject to WS
float linearDepthB;
float maxNearCocCS;
float maxFarCocCS;
};
struct VOut
{
float4 position : SV_Position;
float2 texCoords : TEXCOORD0;
};
#if VERTEX_SHADER
VOut vs(uint id : SV_VertexID)
{
VOut output;
output.position = FSTrianglePosFromVertexId(id);
output.texCoords = FSTriangleTCFromVertexId(id);
return output;
}
#endif
#if PIXEL_SHADER
float4 ps(VOut input) : SV_Target
{
Texture2D colorTexture = ResourceDescriptorHeap[colorTextureIndex];
Texture2D<float> depthTexture = ResourceDescriptorHeap[depthTextureIndex];
int3 tcColor = int3(input.position.xy, 0);
int3 tcDepth = int3(input.position.xy / tcScale, 0);
float3 color = colorTexture.Load(tcColor).rgb;
float depthZW = depthTexture.Load(tcDepth);
float depth = LinearDepth(depthZW, linearDepthA, linearDepthB);
bool nearField = depth < focusDist;
float4 result;
if(debugMode == 1)
{
float quadPosXCS = input.texCoords.x * 2.0 - 1.0;
float quadPosYCS = (1.0 - input.texCoords.y) * 2.0 - 1.0;
float4 positionWS = mul(invMvp, float4(quadPosXCS, quadPosYCS, depthZW, 1));
float4 positionCS = mul(mvp, float4(positionWS.xyz / positionWS.w, 1));
float coc = distance(positionCS.xy / positionCS.w, float2(quadPosXCS, quadPosYCS));
result = DOF_DebugCoc(color, nearField, saturate(coc / maxNearCocCS), saturate(coc / maxFarCocCS));
}
else if(debugMode == 2)
{
result = DOF_DebugFocusPlane(color, nearField);
}
else
{
result = float4(color, 1);
}
return result;
}
#endif

View file

@ -0,0 +1,65 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// accumulation depth of field: normalization pass
#include "common.hlsli"
cbuffer RootConstants
{
uint textureIndex;
};
struct VOut
{
float4 position : SV_Position;
};
#if VERTEX_SHADER
VOut vs(uint id : SV_VertexID)
{
VOut output;
output.position = FSTrianglePosFromVertexId(id);
return output;
}
#endif
#if PIXEL_SHADER
float4 ps(VOut input) : SV_Target
{
Texture2D texture0 = ResourceDescriptorHeap[textureIndex];
int2 tc = int2(input.position.xy);
float4 sum = texture0.Load(int3(tc.x, tc.y, 0));
float3 color = saturate(sum.rgb / sum.a);
float4 result = float4(color, 1);
return result;
}
#endif

View file

@ -0,0 +1,69 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// blit shader - unlike texture copies, it doesn't care about the specific formats used
#include "common.hlsli"
cbuffer RootConstants : register(b0)
{
uint textureIndex;
uint samplerIndex;
float2 tcScale;
float2 tcBias;
};
struct VOut
{
float4 position : SV_Position;
float2 texCoords : TEXCOORD0;
};
#if VERTEX_SHADER
VOut vs(uint id : SV_VertexID)
{
VOut output;
output.position = FSTrianglePosFromVertexId(id);
output.texCoords = FSTriangleTCFromVertexId(id);
return output;
}
#endif
#if PIXEL_SHADER
float4 ps(VOut input) : SV_Target
{
Texture2D texture0 = ResourceDescriptorHeap[textureIndex];
SamplerState sampler0 = SamplerDescriptorHeap[samplerIndex];
float2 tc = input.texCoords * tcScale + tcBias;
float3 base = texture0.Sample(sampler0, tc).rgb;
float4 result = float4(base, 1.0);
return result;
}
#endif

View file

@ -0,0 +1,144 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// shared utilities
#pragma once
#include "../common/state_bits.h.hlsli"
#include "../common/blend.hlsli"
#define PI 3.1415926535897932384626433832795
#define PI_D2 (PI / 2.0)
#define PI_D4 (PI / 4.0)
#define PI_M2 (PI * 2.0)
float DegToRad(float deg)
{
return PI * (deg / 180.0);
}
float RadToDeg(float rad)
{
return 180.0 * (rad / PI);
}
float Brightness(float3 color)
{
float brightness = dot(color, float3(0.299, 0.587, 0.114));
return brightness;
}
float4 MakeGreyscale(float4 input, float amount)
{
float grey = dot(input.rgb, float3(0.299, 0.587, 0.114));
float4 result = lerp(input, float4(grey, grey, grey, input.a), amount);
return result;
}
/*
f = far clip plane distance
n = near clip plane distance
exp = exponential depth value (as stored in the Z-buffer)
2 * f * n B
linear(exp) = ----------------------- = -------
(f + n) - exp * (f - n) exp - A
f + n -2 * f * n
with A = ----- and B = ----------
f - n f - n
*/
float LinearDepth(float zwDepth, float A, float B)
{
return B / (zwDepth - A);
}
float4 FSTrianglePosFromVertexId(uint id)
{
return float4(
(float)(id / 2) * 4.0 - 1.0,
(float)(id % 2) * 4.0 - 1.0,
0.0,
1.0);
}
float2 FSTriangleTCFromVertexId(uint id)
{
return float2(
(float)(id / 2) * 2.0,
1.0 - (float)(id % 2) * 2.0);
}
uint PackColor(float4 c)
{
uint4 u = uint4(saturate(c) * 255.0);
uint r = u.r | (u.g << 8) | (u.b << 16) | (u.a << 24);
return r;
}
float4 UnpackColor(uint c)
{
uint4 u = uint4(c & 0xFFu, (c >> 8) & 0xFFu, (c >> 16) & 0xFFu, (c >> 24) & 0xFFu);
float4 r = float4(u) / 255.0;
return r;
}
float EaseInCubic(float x)
{
return x * x * x;
}
float EaseOutCubic(float x)
{
float y = 1.0 - x;
return 1.0 - y * y * y;
}
float EaseInOutCubic(float x)
{
if(x < 0.5)
{
return 4 * x * x * x;
}
float y = -2 * x + 2;
return 1 - 0.5 * y * y * y;
}
float EaseInQuad(float x)
{
return x * x;
}
float SmoothStep(float x)
{
return smoothstep(0.0, 1.0, x);
}

View file

@ -0,0 +1,54 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// depth of field: debug overlay support functions
#pragma once
float4 DOF_DebugCoc(float3 color, bool nearField, float nearCoc, float farCoc)
{
float blendFactor;
float3 target;
if(nearField)
{
blendFactor = 0.5 * nearCoc;
target = float3(0, 1, 0);
}
else
{
blendFactor = 0.5 * farCoc;
target = float3(0, 0, 1);
}
float4 result = float4(lerp(color, target, blendFactor), 1);
return result;
}
float4 DOF_DebugFocusPlane(float3 color, bool nearField)
{
float farFieldFactor = nearField ? 0.0 : 0.25;
float3 farFieldColor = float3(0.5, 0, 0.5);
float3 mixed = lerp(color, farFieldColor, farFieldFactor);
float4 result = float4(mixed, 1);
return result;
}

View file

@ -0,0 +1,58 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// fog volume (AABB) rendering - shared code
struct VOut
{
float4 position : SV_Position;
float depthVS : DEPTHVS;
};
cbuffer RootConstants
{
matrix modelViewMatrix;
matrix projectionMatrix;
float4 boxMin;
float4 boxMax;
float4 color;
float depth;
float linearDepthA;
float linearDepthB;
uint depthTextureIndex;
};
#ifdef VERTEX_SHADER
VOut vs(float3 positionOS : POSITION)
{
float3 positionWS = boxMin.xyz + positionOS * (boxMax.xyz - boxMin.xyz);
float4 positionVS = mul(modelViewMatrix, float4(positionWS, 1));
VOut output;
output.position = mul(projectionMatrix, positionVS);
output.depthVS = -positionVS.z;
return output;
}
#endif

View file

@ -0,0 +1,43 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// fog volume (AABB) seen from inside
#include "common.hlsli"
#include "fog.hlsli"
#if PIXEL_SHADER
float4 ps(VOut input) : SV_Target
{
Texture2D<float> depthTexture = ResourceDescriptorHeap[depthTextureIndex];
float depthZW = depthTexture.Load(int3(input.position.xy, 0));
float depthBuff = LinearDepth(depthZW, linearDepthA, linearDepthB);
float depthFrag = input.depthVS;
float depthMin = min(depthBuff, depthFrag);
float fogOpacity = saturate(depthMin / depth);
float4 result = float4(color.rgb, fogOpacity);
return result;
}
#endif

View file

@ -0,0 +1,47 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// fog volume (AABB) seen from outside
#include "common.hlsli"
#include "fog.hlsli"
#ifdef PIXEL_SHADER
float4 ps(VOut input) : SV_Target
{
Texture2D<float> depthTexture = ResourceDescriptorHeap[depthTextureIndex];
float depthZW = depthTexture.Load(int3(input.position.xy, 0));
float depthBuff = LinearDepth(depthZW, linearDepthA, linearDepthB);
float depthFrag = input.depthVS;
if(depthFrag > depthBuff)
{
discard;
}
float fogOpacity = saturate((depthBuff - depthFrag) / depth);
float4 result = float4(color.rgb, fogOpacity);
return result;
}
#endif

View file

@ -0,0 +1,61 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// gather depth of field: support functions
#pragma once
#include "common.hlsli"
#define MAX_BLUR_DIAMETER 32.0
#define MAX_COC 16.0
float CircleOfConfusion(float depth, float focusNearMin, float focusNearMax, float focusFarMin, float focusFarMax)
{
if(depth <= focusNearMin)
{
return -1.0;
}
if(depth > focusNearMin && depth < focusNearMax)
{
float t = 1.0 - (depth - focusNearMin) / (focusNearMax - focusNearMin);
return -t;
}
if(depth > focusFarMin && depth < focusFarMax)
{
float t = (depth - focusFarMin) / (focusFarMax - focusFarMin);
return t;
}
if(depth >= focusFarMax)
{
return 1.0;
}
return 0.0;
}

View file

@ -0,0 +1,207 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// gather depth of field: near-field and far-field blur
#include "common.hlsli"
#include "gatherdof.hlsli"
cbuffer RootConstants : register(b0)
{
uint colorTextureIndex;
uint nearColorTextureIndex;
uint nearMaxCocTextureIndex; // tile
uint nearCocTextureIndex; // blurry
uint nearOutputTextureIndex;
uint farColorTextureIndex;
uint farCocTextureIndex; // sharp
uint farOutputTextureIndex;
uint samplerIndex; // linear/clamp
float brightnessScale;
float bladeCount;
float bokehAngleRad;
};
// the input is in [0,1]^2, the output polygon is centered at the origin
float2 MapUnitSquareToPolygon(float2 square, float apertureBladeCount, float apertureAngleRad)
{
// needed to avoid inf/nan propagation through theta for samples
// that are exactly in the middle of the quad on either axis
// (i.e. square.x|y == 0.5 gets remapped to 0.0)
const float epsilon = 0.000001;
// morph into a square in [-1,1]^2
square = square * 2.0 - 1.0;
// morph the square into a disk
// "A Low Distortion Map Between Disk and Square" by Peter Shirley and Kenneth Chiu
float radius, angle;
float2 square2 = square * square;
if(square2.x > square2.y)
{
// left and right quadrants
radius = square.x;
angle = (square.y * PI_D4) / (square.x + epsilon);
}
else
{
// top and bottom quadrants
radius = square.y;
angle = PI_D2 - (square.x * PI_D4) / (square.y + epsilon);
}
if(radius < 0.0)
{
radius = -radius;
angle += PI;
}
// morph the disk into a polygon
// "Graphics Gems from CryENGINE 3" by Tiago Sousa
float edgeCount = apertureBladeCount;
if(edgeCount >= 3.0)
{
float num = cos(PI / edgeCount);
float den0 = PI_M2 / edgeCount;
float den1 = (angle * edgeCount + PI) / PI_M2;
float den = angle - (den0 * floor(den1));
radius *= num / cos(den);
angle += apertureAngleRad;
}
float2 disk;
sincos(angle, disk.y, disk.x);
disk *= radius;
return disk;
}
float4 BlurFarField(Texture2D inTexture, SamplerState samplerState, float coc, float2 tc01, float2 pixelSize)
{
const int TAP_COUNT_BLUR = 16;
float2 tcScale = 16.0 * coc * pixelSize;
float4 result = inTexture.SampleLevel(samplerState, tc01, 0);
for(int y = 0; y < TAP_COUNT_BLUR; ++y)
{
for(int x = 0; x < TAP_COUNT_BLUR; ++x)
{
float2 tcQuad = float2(x, y) / float(TAP_COUNT_BLUR - 1);
float2 tcOffset = MapUnitSquareToPolygon(tcQuad, bladeCount, bokehAngleRad) * tcScale;
float4 sampleValue = inTexture.SampleLevel(samplerState, tc01 + tcOffset, 0);
result += sampleValue;
}
}
result /= result.a;
return result;
}
float4 BlurNearField(Texture2D inTexture, SamplerState samplerState, float tileMaxCoc, float2 tc01, float2 pixelSize)
{
const int TAP_COUNT_BLUR = 15; // must be odd so we generate 1 sample at 0.5, 0.5 in the quad
float2 tcScale = 16.0 * tileMaxCoc * pixelSize;
float insideCount = 0.0;
float totalCount = 1.0 + float(TAP_COUNT_BLUR * TAP_COUNT_BLUR);
float4 result = float4(0, 0, 0, 0);
float weightSum = 0.0;
for(int y = 0; y < TAP_COUNT_BLUR; ++y)
{
for(int x = 0; x < TAP_COUNT_BLUR; ++x)
{
float2 tcQuad = float2(x, y) / float(TAP_COUNT_BLUR - 1);
float2 tcOffset = MapUnitSquareToPolygon(tcQuad, bladeCount, bokehAngleRad) * tcScale;
float4 sampleValue = inTexture.SampleLevel(samplerState, tc01 + tcOffset, 0);
float inside = sampleValue.a > 0.0 ? 1.0 : 0.0;
float brightnessWeight = 1.0 + brightnessScale * Brightness(sampleValue.rgb);
float colorWeight = (sampleValue.a / tileMaxCoc) * brightnessWeight;
insideCount += inside;
weightSum += inside * colorWeight;
result += inside * float4(colorWeight.xxx, 1) * sampleValue;
}
}
if(insideCount >= 1.0)
{
result.rgb /= weightSum;
result.a /= insideCount;
result.a *= EaseInOutCubic(saturate(2.0 * (insideCount / totalCount)));
}
else
{
result = float4(1, 1, 0, 0);
}
return result;
}
[numthreads(8, 8, 1)]
void cs(uint3 dtid : SV_DispatchThreadID)
{
uint2 tc = dtid.xy;
RWTexture2D<float4> nearOutputTexture = ResourceDescriptorHeap[nearOutputTextureIndex];
RWTexture2D<float4> farOutputTexture = ResourceDescriptorHeap[farOutputTextureIndex];
uint width, height;
farOutputTexture.GetDimensions(width, height);
if(any(dtid.xy >= uint2(width, height)))
{
return;
}
SamplerState samplerState = SamplerDescriptorHeap[samplerIndex];
Texture2D colorTexture = ResourceDescriptorHeap[colorTextureIndex];
Texture2D nearColorTexture = ResourceDescriptorHeap[nearColorTextureIndex];
Texture2D farColorTexture = ResourceDescriptorHeap[farColorTextureIndex];
Texture2D<float> nearCocTexture = ResourceDescriptorHeap[nearCocTextureIndex];
Texture2D<float> nearMaxCocTexture = ResourceDescriptorHeap[nearMaxCocTextureIndex];
Texture2D<float> farCocTexture = ResourceDescriptorHeap[farCocTextureIndex];
RWTexture2D<float4> nearOutTexture = ResourceDescriptorHeap[nearOutputTextureIndex];
RWTexture2D<float4> farOutTexture = ResourceDescriptorHeap[farOutputTextureIndex];
float2 tc01 = (float2(dtid.xy) + float2(0.5, 0.5)) / float2(width, height);
float2 pixelSize = float2(1, 1) / float2(width, height);
float nearCoc = nearCocTexture.SampleLevel(samplerState, tc01, 0);
float nearMaxCoc = nearMaxCocTexture.SampleLevel(samplerState, tc01, 0);
float farCoc = farCocTexture.SampleLevel(samplerState, tc01, 0);
float4 color = colorTexture.SampleLevel(samplerState, tc01, 0);
if(nearMaxCoc > 0.0)
{
nearOutTexture[tc] = BlurNearField(nearColorTexture, samplerState, nearMaxCoc, tc01, pixelSize);
}
else
{
// A must be 0 to disable the near field from being blended
nearOutTexture[tc] = float4(color.rgb, 0);
}
if(farCoc > 0.0)
{
farOutTexture[tc] = BlurFarField(farColorTexture, samplerState, farCoc, tc01, pixelSize);
}
else
{
// RGB must be 0 to not mess up the fill pass of neighbor pixels that are inside the near field
// A must be 0 to disable the far field from being blended
farOutTexture[tc] = float4(0, 0, 0, 0);
}
}

View file

@ -0,0 +1,65 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// gather depth of field: near-field circle of confusion tile generation
#include "common.hlsli"
#include "gatherdof.hlsli"
cbuffer RootConstants : register(b0)
{
uint inputTextureIndex;
uint outputTextureIndex;
};
[numthreads(8, 8, 1)]
void cs(uint3 dtid : SV_DispatchThreadID, uint3 gid : SV_GroupID, uint3 gtid : SV_GroupThreadID)
{
uint2 tcOut = dtid.xy;
RWTexture2D<float> outputTexture = ResourceDescriptorHeap[outputTextureIndex];
uint width, height;
outputTexture.GetDimensions(width, height);
if(any(dtid.xy >= uint2(width, height)))
{
return;
}
Texture2D<float> inputTexture = ResourceDescriptorHeap[inputTextureIndex];
// This loop can read out of bounds in the inputTexture.
// Each full-res pixel has a corresponding tile pixel, but the reverse isn't always true.
// Texture.Load is specced to return 0 on OOB accesses.
// Since we max() the values, zeroes have no effect on the final result.
uint2 tcInCorner = tcOut * uint2(16, 16);
float maxCoc = 0.0;
for(uint y = 0; y < 16; y++)
{
for(uint x = 0; x < 16; x++)
{
uint2 tcIn = tcInCorner + uint2(x, y);
float coc = inputTexture.Load(uint3(tcIn.x, tcIn.y, 0));
maxCoc = max(maxCoc, coc);
}
}
outputTexture[tcOut] = maxCoc;
}

View file

@ -0,0 +1,64 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// gather depth of field: near-field circle of confusion tile dilation
#include "common.hlsli"
#include "gatherdof.hlsli"
cbuffer RootConstants : register(b0)
{
uint inputTextureIndex;
uint outputTextureIndex;
uint samplerIndex; // point/clamp
};
[numthreads(8, 8, 1)]
void cs(uint3 dtid : SV_DispatchThreadID)
{
uint2 tc = dtid.xy;
RWTexture2D<float> outputTexture = ResourceDescriptorHeap[outputTextureIndex];
uint width, height;
outputTexture.GetDimensions(width, height);
if(any(dtid.xy >= uint2(width, height)))
{
return;
}
Texture2D<float> inputTexture = ResourceDescriptorHeap[inputTextureIndex];
SamplerState samplerState = SamplerDescriptorHeap[samplerIndex];
float2 tcShifted = float2(tc) + float2(0.5, 0.5);
float2 pixelSize = float2(1, 1) / float2(width, height);
float maxCoc = 0.0;
for(int y = -1; y <= 1; y++)
{
for(int x = -1; x <= 1; x++)
{
float2 tc01 = (tcShifted + float2(x, y)) * pixelSize;
float coc = inputTexture.SampleLevel(samplerState, tc01, 0);
maxCoc = max(maxCoc, coc);
}
}
outputTexture[tc] = maxCoc;
}

View file

@ -0,0 +1,84 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// gather depth of field: final blend pass
#include "common.hlsli"
#include "gatherdof.hlsli"
cbuffer RootConstants : register(b0)
{
uint nearTextureIndex;
uint farTextureIndex;
uint nearCocTextureIndex;
uint farCocTextureIndex;
uint sharpTextureIndex;
uint samplerIndex; // point/clamp
};
struct VOut
{
float4 position : SV_Position;
float2 texCoords : TEXCOORD0;
};
#if VERTEX_SHADER
VOut vs(uint id : SV_VertexID)
{
VOut output;
output.position = FSTrianglePosFromVertexId(id);
output.texCoords = FSTriangleTCFromVertexId(id);
return output;
}
#endif
#if PIXEL_SHADER
float4 ps(VOut input) : SV_Target
{
SamplerState samplerState = SamplerDescriptorHeap[samplerIndex];
Texture2D nearColorTexture = ResourceDescriptorHeap[nearTextureIndex];
Texture2D farColorTexture = ResourceDescriptorHeap[farTextureIndex];
Texture2D<float> nearCocTexture = ResourceDescriptorHeap[nearCocTextureIndex];
Texture2D<float> farCocTexture = ResourceDescriptorHeap[farCocTextureIndex];
Texture2D sharpTexture = ResourceDescriptorHeap[sharpTextureIndex];
float4 nearColor = nearColorTexture.Sample(samplerState, input.texCoords);
float4 farColor = farColorTexture.Sample(samplerState, input.texCoords);
//float nearCoc = nearCocTexture.Sample(samplerState, input.texCoords);
float nearCoc = saturate(nearColor.a);
float farCoc = farCocTexture.Sample(samplerState, input.texCoords);
float4 sharp = sharpTexture.Sample(samplerState, input.texCoords);
float3 color = lerp(sharp.rgb, farColor.rgb, farCoc);
color = lerp(color, nearColor.rgb, nearCoc);
float4 result = float4(color, 1.0);
return result;
}
#endif

View file

@ -0,0 +1,93 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// gather depth of field: debug overlay
#include "common.hlsli"
#include "gatherdof.hlsli"
#include "dof.hlsli"
cbuffer RootConstants : register(b0)
{
uint colorTextureIndex;
uint depthTextureIndex;
uint debugMode;
float linearDepthA;
float linearDepthB;
float focusNearMin;
float focusNearMax;
float focusFarMin;
float focusFarMax;
float focusDist;
};
struct VOut
{
float4 position : SV_Position;
float2 texCoords : TEXCOORD0;
};
#if VERTEX_SHADER
VOut vs(uint id : SV_VertexID)
{
VOut output;
output.position = FSTrianglePosFromVertexId(id);
output.texCoords = FSTriangleTCFromVertexId(id);
return output;
}
#endif
#if PIXEL_SHADER
float4 ps(VOut input) : SV_Target
{
Texture2D colorTexture = ResourceDescriptorHeap[colorTextureIndex];
Texture2D<float> depthTexture = ResourceDescriptorHeap[depthTextureIndex];
uint3 tc = uint3(input.position.x, input.position.y, 0);
float3 color = colorTexture.Load(tc).rgb;
float depthZW = depthTexture.Load(tc);
float depth = LinearDepth(depthZW, linearDepthA, linearDepthB);
float coc = CircleOfConfusion(depth, focusNearMin, focusNearMax, focusFarMin, focusFarMax);
float nearField = coc < 0.0;
float4 result;
if(debugMode == 1)
{
result = DOF_DebugCoc(color, nearField, saturate(-coc), saturate(coc));
}
else if(debugMode == 2)
{
result = DOF_DebugFocusPlane(color, nearField);
}
else
{
result = float4(color, 1);
}
return result;
}
#endif

View file

@ -0,0 +1,76 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// gather depth of field: max blur post-filter to combat undersampling
#include "common.hlsli"
#include "gatherdof.hlsli"
cbuffer RootConstants : register(b0)
{
uint nearInputTextureIndex;
uint nearOutputTextureIndex;
uint farInputTextureIndex;
uint farOutputTextureIndex;
uint samplerIndex; // point/clamp
};
[numthreads(8, 8, 1)]
void cs(uint3 dtid : SV_DispatchThreadID)
{
uint2 tc = dtid.xy;
RWTexture2D<float4> nearOutputTexture = ResourceDescriptorHeap[nearOutputTextureIndex];
RWTexture2D<float4> farOutputTexture = ResourceDescriptorHeap[farOutputTextureIndex];
uint width, height, levels;
nearOutputTexture.GetDimensions(width, height);
if(any(dtid.xy >= uint2(width, height)))
{
return;
}
SamplerState samplerState = SamplerDescriptorHeap[samplerIndex];
Texture2D nearInputTexture = ResourceDescriptorHeap[nearInputTextureIndex];
Texture2D farInputTexture = ResourceDescriptorHeap[farInputTextureIndex];
float2 tc01 = (float2(tc) + float2(0.5, 0.5)) / float2(width, height);
float2 pixelSize = float2(1, 1) / float2(width, height);
float4 nearFilled = float4(0, 0, 0, 0);
float4 farFilled = float4(0, 0, 0, 0);
for(int y = -1; y <= 1; y++)
{
for(int x = -1; x <= 1; x++)
{
float2 tcSample01 = tc01 + float2(x, y) * pixelSize;
float4 nearSample = nearInputTexture.SampleLevel(samplerState, tcSample01, 0);
float4 farSample = farInputTexture.SampleLevel(samplerState, tcSample01, 0);
nearFilled = max(nearFilled, nearSample);
farFilled = max(farFilled, farSample);
}
}
// make sure to keep the original blend factors
nearFilled.a = nearInputTexture.Load(uint3(tc.x, tc.y, 0)).a;
farFilled.a = farInputTexture.Load(uint3(tc.x, tc.y, 0)).a;
nearOutputTexture[tc] = nearFilled;
farOutputTexture[tc] = farFilled;
}

View file

@ -0,0 +1,78 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// gather depth of field: field split and CoC generation
#include "common.hlsli"
#include "gatherdof.hlsli"
cbuffer RootConstants : register(b0)
{
uint depthTextureIndex;
uint colorTextureIndex;
uint nearColorTextureIndex;
uint farColorTextureIndex;
uint nearCocTextureIndex;
uint farCocTextureIndex;
float linearDepthA;
float linearDepthB;
float focusNearMin;
float focusNearMax;
float focusFarMin;
float focusFarMax;
float brightnessScale;
};
[numthreads(8, 8, 1)]
void cs(uint3 dtid : SV_DispatchThreadID)
{
uint2 tc = dtid.xy;
Texture2D colorTexture = ResourceDescriptorHeap[colorTextureIndex];
uint width, height, levels;
colorTexture.GetDimensions(0, width, height, levels);
if(any(dtid.xy >= uint2(width, height)))
{
return;
}
Texture2D<float> depthTexture = ResourceDescriptorHeap[depthTextureIndex];
RWTexture2D<float4> nearColorTexture = ResourceDescriptorHeap[nearColorTextureIndex];
RWTexture2D<float4> farColorTexture = ResourceDescriptorHeap[farColorTextureIndex];
RWTexture2D<float> nearCocTexture = ResourceDescriptorHeap[nearCocTextureIndex];
RWTexture2D<float> farCocTexture = ResourceDescriptorHeap[farCocTextureIndex];
float4 color = colorTexture[tc];
float depthZW = depthTexture[tc];
float depth = LinearDepth(depthZW, linearDepthA, linearDepthB);
float coc = CircleOfConfusion(depth, focusNearMin, focusNearMax, focusFarMin, focusFarMax);
float nearCoc = max(-coc, 0.0);
float farCoc = max(coc, 0.0);
float brightnessWeight = 1.0 + brightnessScale * Brightness(color.rgb);
float farWeight = farCoc * brightnessWeight;
float4 nearColor = float4(color.rgb, nearCoc);
float4 farColor = float4(color.rgb * farWeight, farWeight);
nearColorTexture[tc] = nearColor;
farColorTexture[tc] = farColor;
nearCocTexture[tc] = nearCoc;
farCocTexture[tc] = farCoc;
}

View file

@ -0,0 +1,73 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Dear ImGui integration
struct VOut
{
float4 pos : SV_POSITION;
float4 col : COLOR0;
float2 uv : TEXCOORD0;
};
cbuffer RootConstants : register(b0)
{
float4x4 projectionMatrix;
uint textureIndex;
uint samplerIndex;
float mipIndex;
};
#if VERTEX_SHADER
struct VIn
{
float2 pos : POSITION;
float4 col : COLOR0;
float2 uv : TEXCOORD0;
};
VOut vs(VIn input)
{
VOut output;
output.pos = mul(projectionMatrix, float4(input.pos.xy, 0.0, 1.0));
output.col = input.col;
output.uv = input.uv;
return output;
}
#endif
#if PIXEL_SHADER
float4 ps(VOut input) : SV_Target
{
Texture2D texture0 = ResourceDescriptorHeap[textureIndex];
SamplerState sampler0 = SamplerDescriptorHeap[samplerIndex];
float4 result = input.col * texture0.SampleLevel(sampler0, input.uv, mipIndex);
return result;
}
#endif

View file

@ -0,0 +1,40 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// mip-map generation: gamma-space to linear-space transform
#include "../common/mip_gen.hlsli"
cbuffer RootConstants
{
float gamma;
uint srcTexture;
uint dstTexture;
}
[numthreads(8, 8, 1)]
void cs(uint3 id : SV_DispatchThreadID)
{
RWTexture2D<float4> src = ResourceDescriptorHeap[srcTexture];
RWTexture2D<float4> dst = ResourceDescriptorHeap[dstTexture];
MipGen_GammaToLinear(dst, src, id, gamma);
}

View file

@ -0,0 +1,46 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// mip-map generation: 8-tap 1D filter
#include "../common/mip_gen.hlsli"
cbuffer RootConstants
{
float4 weights;
int2 maxSize;
int2 scale;
int2 offset;
uint clampMode; // 0 = repeat
uint srcMip;
uint dstMip;
uint srcTexture;
uint dstTexture;
}
[numthreads(8, 8, 1)]
void cs(uint3 id : SV_DispatchThreadID)
{
RWTexture2D<float4> src = ResourceDescriptorHeap[srcTexture];
RWTexture2D<float4> dst = ResourceDescriptorHeap[dstTexture];
MipGen_DownSample(dst, src, id, maxSize, clampMode, scale, offset, weights);
}

View file

@ -0,0 +1,44 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// mip-map generation: linear-space to gamma-space transform
#include "../common/mip_gen.hlsli"
cbuffer RootConstants
{
float4 blendColor;
float intensity;
float invGamma; // 1.0 / gamma
uint srcMip;
uint dstMip;
uint srcTexture;
uint dstTexture;
}
[numthreads(8, 8, 1)]
void cs(uint3 id : SV_DispatchThreadID)
{
RWTexture2D<float4> src = ResourceDescriptorHeap[srcTexture];
RWTexture2D<float4> dst = ResourceDescriptorHeap[dstTexture];
MipGen_LinearToGamma(dst, src, id, blendColor, intensity, invGamma);
}

View file

@ -0,0 +1,72 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Nuklear integration
struct VOut
{
float4 pos : SV_POSITION;
float4 col : COLOR0;
float2 uv : TEXCOORD0;
};
cbuffer RootConstants
{
float4x4 projectionMatrix;
uint textureIndex;
uint samplerIndex;
};
#if VERTEX_SHADER
struct VIn
{
float2 pos : POSITION;
float4 col : COLOR0;
float2 uv : TEXCOORD0;
};
VOut vs(VIn input)
{
VOut output;
output.pos = mul(projectionMatrix, float4(input.pos.xy, 0.0, 1.0));
output.col = input.col;
output.uv = input.uv;
return output;
}
#endif
#if PIXEL_SHADER
float4 ps(VOut input) : SV_Target
{
Texture2D texture0 = ResourceDescriptorHeap[textureIndex];
SamplerState sampler0 = SamplerDescriptorHeap[samplerIndex];
float4 result = input.col * texture0.Sample(sampler0, input.uv);
return result;
}
#endif

View file

@ -0,0 +1,58 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// shared structures and constants used to implement order-independent transparency
#pragma once
#if defined(__cplusplus)
#pragma pack(push, 4)
typedef uint32_t uint;
#endif
#define OIT_MAX_FRAGMENTS_PER_PIXEL 32
#define OIT_AVG_FRAGMENTS_PER_PIXEL 16
struct OIT_Counter
{
uint fragmentCount;
uint maxFragmentCount;
uint overflowCount;
};
struct OIT_Fragment
{
uint color;
float depth; // higher is further away from the camera
uint stateBits; // GLS_* stage bits + stage index
uint next;
uint shaderTrace; // shader index: 14 - frame index: 2 - enable: 1
uint depthFadeDistOffset; // offset: fp16 - distance: fp16
uint depthFadeScaleBias; // enable: 1 - color bias: 4 - color scale: 4
// @TODO: move the 9 bits from depthFadeScaleBias into shaderTrace
};
#if defined(__cplusplus)
#pragma pack(pop)
static_assert(sizeof(OIT_Counter) == 12, "sizeof(OIT_Counter) is wrong");
static_assert(sizeof(OIT_Fragment) == 28, "sizeof(OIT_Fragment) is wrong");
#endif

View file

@ -0,0 +1,131 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// generic shader for opaque surfaces
#include "common.hlsli"
#include "world.h.hlsli"
#include "world.hlsli"
cbuffer RootConstants
{
// geometry
matrix modelViewMatrix;
matrix projectionMatrix;
float4 clipPlane;
// general
uint textureIndex;
uint samplerIndex;
uint shaderIndexBufferIndex;
uint alphaTest;
float greyscale;
// shader trace
uint shaderTrace; // shader index: 14 - frame index: 2 - enable: 1
uint centerPixel; // y: 16 - x: 16
// @TODO: dither
// @TODO: Voronoi tiling
};
#if VERTEX_SHADER
struct VIn
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texCoords : TEXCOORD0;
float4 color : COLOR0;
};
#endif
struct VOut
{
float4 position : SV_Position;
float3 normal : NORMAL;
float2 texCoords : TEXCOORD0;
float4 color : COLOR0;
float clipDist : SV_ClipDistance0;
float2 proj2232 : PROJ;
float depthVS : DEPTHVS;
};
#if VERTEX_SHADER
VOut vs(VIn input)
{
float4 positionVS = mul(modelViewMatrix, float4(input.position.xyz, 1));
VOut output;
output.position = mul(projectionMatrix, positionVS);
output.normal = input.normal;
output.texCoords = input.texCoords;
output.color = input.color;
output.clipDist = dot(positionVS, clipPlane);
output.proj2232 = float2(-projectionMatrix[2][2], projectionMatrix[2][3]);
output.depthVS = -positionVS.z;
return output;
}
#endif
#if PIXEL_SHADER
float4 ps(VOut input) : SV_Target
{
// @TODO: Voronoi tiling
Texture2D texture0 = ResourceDescriptorHeap[textureIndex];
SamplerState sampler0 = ResourceDescriptorHeap[samplerIndex];
float4 dst = texture0.Sample(sampler0, input.texCoords) * input.color;
if(FailsAlphaTest(dst.a, alphaTest))
{
discard;
}
dst = MakeGreyscale(dst, greyscale);
// @TODO: dithering (need to figure out the tone mapping function first)
if(shaderTrace & 1)
{
// we only store the shader index of 1 pixel
uint2 fragmentCoords = uint2(input.position.xy);
uint2 centerCoords = uint2(centerPixel & 0xFFFF, centerPixel >> 16);
if(all(fragmentCoords == centerCoords))
{
RWByteAddressBuffer shaderIndexBuffer = ResourceDescriptorHeap[shaderIndexBufferIndex];
uint frameIndex = (shaderTrace >> 1) & 3;
uint shaderIndex = shaderTrace >> 3;
shaderIndexBuffer.Store(frameIndex * 4, shaderIndex);
}
}
return dst;
}
#endif

View file

@ -0,0 +1,74 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// post-processing: moves from linear to gamma space
// applies r_gamma, r_brightness, r_greyscale
#include "common.hlsli"
struct VOut
{
float4 position : SV_Position;
float2 texCoords : TEXCOORD0;
};
cbuffer RootConstants
{
uint textureIndex;
uint samplerIndex;
float invGamma;
float brightness;
float greyscale;
};
#if VERTEX_SHADER
VOut vs(uint id : SV_VertexID)
{
VOut output;
output.position = FSTrianglePosFromVertexId(id);
output.texCoords = FSTriangleTCFromVertexId(id);
return output;
}
#endif
#if PIXEL_SHADER
// X3571: pow(f, e) won't work if f is negative
#pragma warning(disable : 3571)
float4 ps(VOut input) : SV_Target
{
Texture2D texture0 = ResourceDescriptorHeap[textureIndex];
SamplerState sampler0 = SamplerDescriptorHeap[samplerIndex];
float3 base = texture0.Sample(sampler0, input.texCoords).rgb;
float3 gc = pow(base, invGamma) * brightness;
float4 result = MakeGreyscale(float4(gc.rgb, 1.0), greyscale);
return result;
}
#endif

View file

@ -0,0 +1,72 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// post-processing: moves from gamma to linear space
#include "common.hlsli"
struct VOut
{
float4 position : SV_Position;
float2 texCoords : TEXCOORD0;
};
cbuffer RootConstants
{
uint textureIndex;
uint samplerIndex;
float gamma;
float invBrightness;
};
#if VERTEX_SHADER
VOut vs(uint id : SV_VertexID)
{
VOut output;
output.position = FSTrianglePosFromVertexId(id);
output.texCoords = FSTriangleTCFromVertexId(id);
return output;
}
#endif
#if PIXEL_SHADER
// X3571: pow(f, e) won't work if f is negative
#pragma warning(disable : 3571)
float4 ps(VOut input) : SV_Target
{
Texture2D texture0 = ResourceDescriptorHeap[textureIndex];
SamplerState sampler0 = SamplerDescriptorHeap[samplerIndex];
float3 base = texture0.Sample(sampler0, input.texCoords).rgb;
float3 linearSpace = pow(base * invBrightness, gamma);
float4 result = float4(linearSpace, 1.0);
return result;
}
#endif

View file

@ -0,0 +1,136 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// add fragments of transparent surfaces to per-pixel linked lists
#include "common.hlsli"
#include "world.h.hlsli"
#include "world.hlsli"
#include "oit.h.hlsli"
cbuffer RootConstants
{
matrix modelViewMatrix;
matrix projectionMatrix;
float4 clipPlane;
uint textureIndex;
uint samplerIndex;
uint alphaTest;
uint counterBuffer;
uint indexTexture;
uint fragmentBuffer;
float greyscale;
uint stateBits;
uint shaderTrace;
uint depthFadeDistOffset; // offset: fp16 - distance: fp16
uint depthFadeScaleBias; // enable: 1 - color bias: 4 - color scale: 4
};
#if VERTEX_SHADER
struct VIn
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texCoords : TEXCOORD0;
float4 color : COLOR0;
};
#endif
struct VOut
{
float4 position : SV_Position;
float3 normal : NORMAL;
float2 texCoords : TEXCOORD0;
float4 color : COLOR0;
float clipDist : SV_ClipDistance0;
float2 proj2232 : PROJ;
float depthVS : DEPTHVS;
};
#if VERTEX_SHADER
VOut vs(VIn input)
{
float4 positionVS = mul(modelViewMatrix, float4(input.position.xyz, 1));
VOut output;
output.position = mul(projectionMatrix, positionVS);
output.normal = input.normal;
output.texCoords = input.texCoords;
output.color = input.color;
output.clipDist = dot(positionVS, clipPlane);
output.proj2232 = float2(-projectionMatrix[2][2], projectionMatrix[2][3]);
output.depthVS = -positionVS.z;
return output;
}
#endif
#if PIXEL_SHADER
[earlydepthstencil]
void ps(VOut input)
{
Texture2D texture0 = ResourceDescriptorHeap[textureIndex];
SamplerState sampler0 = SamplerDescriptorHeap[samplerIndex];
float4 dst = texture0.Sample(sampler0, input.texCoords) * input.color;
if(FailsAlphaTest(dst.a, alphaTest))
{
return;
}
dst = MakeGreyscale(dst, greyscale);
RWStructuredBuffer<OIT_Counter> counter = ResourceDescriptorHeap[counterBuffer];
uint fragmentIndex;
InterlockedAdd(counter[0].fragmentCount, 1, fragmentIndex);
if(fragmentIndex < counter[0].maxFragmentCount)
{
RWTexture2D<uint> indexTex = ResourceDescriptorHeap[indexTexture];
RWStructuredBuffer<OIT_Fragment> fragments = ResourceDescriptorHeap[fragmentBuffer];
uint prevFragmentIndex;
InterlockedExchange(indexTex[int2(input.position.xy)], fragmentIndex, prevFragmentIndex);
OIT_Fragment fragment;
fragment.color = PackColor(dst);
fragment.depth = input.depthVS;
fragment.stateBits = stateBits;
fragment.next = prevFragmentIndex;
fragment.shaderTrace = shaderTrace;
fragment.depthFadeDistOffset = depthFadeDistOffset;
fragment.depthFadeScaleBias = depthFadeScaleBias;
fragments[fragmentIndex] = fragment;
}
else
{
uint garbage;
InterlockedAdd(counter[0].overflowCount, 1, garbage);
InterlockedAdd(counter[0].fragmentCount, -1, garbage);
}
}
#endif

View file

@ -0,0 +1,216 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// reads per-pixel fragment linked lists into arrays, sorts them and composites them
#include "common.hlsli"
#include "oit.h.hlsli"
#include "../common/state_bits.h.hlsli"
cbuffer RootConstants
{
uint renderTargetTexture;
uint shaderIndexBuffer;
uint indexTexture;
uint fragmentBuffer;
uint centerPixel; // y: 16 - x: 16
uint depthTexture;
float proj22;
float proj32;
float2 scissorRectMin;
float2 scissorRectMax;
};
struct VOut
{
float4 position : SV_Position;
float2 texCoords : TEXCOORD0;
};
#if VERTEX_SHADER
VOut vs(uint id : SV_VertexID)
{
VOut output;
output.position = FSTrianglePosFromVertexId(id);
output.texCoords = FSTriangleTCFromVertexId(id);
return output;
}
#endif
#if PIXEL_SHADER
uint GetShaderStage(uint stateBits)
{
return (stateBits & GLS_STAGEINDEX_BITS) >> GLS_STAGEINDEX_SHIFT;
}
bool IsBehind(float depthA, float depthB, uint stageA, uint stageB)
{
if(depthA > depthB)
{
return true;
}
if(depthA == depthB && stageA < stageB)
{
return true;
}
return false;
}
// from NVIDIA's 2007 "Soft Particles" whitepaper by Tristan Lorach
float Contrast(float d, float power)
{
bool aboveHalf = d > 0.5;
float base = saturate(2.0 * (aboveHalf ? (1.0 - d) : d));
float r = 0.5 * pow(base, power);
return aboveHalf ? (1.0 - r) : r;
}
float GetBitAsFloat(uint bits, uint bitIndex)
{
return (bits & (1u << bitIndex)) ? 1.0 : 0.0;
}
float2 UnpackHalf2(uint data)
{
return float2(f16tof32(data), f16tof32(data >> 16));
}
float4 DepthFadeFragmentColor(float4 color, OIT_Fragment fragment, float storedDepthZW)
{
if(((fragment.depthFadeScaleBias >> 8) & 1) == 0)
{
return color;
}
#define BIT(Index) GetBitAsFloat(fragment.depthFadeScaleBias, Index)
float4 dst = color;
float2 distOffset = UnpackHalf2(fragment.depthFadeDistOffset);
float4 fadeColorScale = float4(BIT(0), BIT(1), BIT(2), BIT(3));
float4 fadeColorBias = float4(BIT(4), BIT(5), BIT(6), BIT(7));
float zwDepth = storedDepthZW; // stored depth, z/w
float depthS = LinearDepth(zwDepth, proj22, proj32); // stored depth, linear
float depthP = fragment.depth - distOffset.y; // fragment depth, linear
float fadeScale = Contrast((depthS - depthP) * distOffset.x, 2.0);
dst = lerp(dst * fadeColorScale + fadeColorBias, dst, fadeScale);
#undef BIT
return dst;
}
float4 ps(VOut input) : SV_Target
{
Texture2D renderTarget = ResourceDescriptorHeap[renderTargetTexture];
int2 tc = int2(input.position.x, input.position.y);
float4 color = renderTarget.Load(int3(tc.x, tc.y, 0));
if(any(input.position.xy < scissorRectMin) ||
any(input.position.xy > scissorRectMax))
{
return color;
}
RWTexture2D<uint> index = ResourceDescriptorHeap[indexTexture];
RWStructuredBuffer<OIT_Fragment> fragments = ResourceDescriptorHeap[fragmentBuffer];
Texture2D depthTex = ResourceDescriptorHeap[depthTexture];
uint fragmentIndex = index[tc];
uint i;
OIT_Fragment sorted[OIT_MAX_FRAGMENTS_PER_PIXEL];
uint fragmentCount = 0;
// grab this pixel's fragments
while(fragmentIndex != 0 && fragmentCount < OIT_MAX_FRAGMENTS_PER_PIXEL)
{
sorted[fragmentCount] = fragments[fragmentIndex];
fragmentIndex = sorted[fragmentCount].next;
++fragmentCount;
}
// sort the fragments using an insertion sort
for(i = 1; i < fragmentCount; ++i)
{
OIT_Fragment insert = sorted[i];
uint stage = GetShaderStage(insert.stateBits);
uint j = i;
while(j > 0 && IsBehind(insert.depth, sorted[j - 1].depth, stage, GetShaderStage(sorted[j - 1].stateBits)))
{
sorted[j] = sorted[j - 1];
--j;
}
sorted[j] = insert;
}
// blend the results
float storedDepthZW = depthTex.Load(int3(input.position.xy, 0)).x; // stored depth, z/w
float dstDepth = 1.0;
for(i = 0; i < fragmentCount; ++i)
{
OIT_Fragment frag = sorted[i];
uint stateBits = frag.stateBits;
float fragDepth = frag.depth;
if((stateBits & (GLS_DEPTHFUNC_EQUAL | GLS_DEPTHTEST_DISABLE)) == GLS_DEPTHFUNC_EQUAL &&
fragDepth != dstDepth)
{
continue;
}
float4 fragColor = UnpackColor(frag.color);
fragColor = DepthFadeFragmentColor(fragColor, frag, storedDepthZW);
color = Blend(fragColor, color, frag.stateBits);
if((stateBits & GLS_DEPTHMASK_TRUE) != 0u &&
fragDepth < dstDepth)
{
dstDepth = fragDepth;
}
}
// write out the fragment shader ID of the closest fragment of the center pixel
if(fragmentCount > 0)
{
uint lastFragmentIndex = fragmentCount - 1;
OIT_Fragment closest = sorted[lastFragmentIndex];
uint shaderTrace = closest.shaderTrace;
if(shaderTrace & 1)
{
uint2 fragmentCoords = uint2(input.position.xy);
uint2 centerCoords = uint2(centerPixel & 0xFFFF, centerPixel >> 16);
if(all(fragmentCoords == centerCoords))
{
RWByteAddressBuffer shaderIdBuf = ResourceDescriptorHeap[shaderIndexBuffer];
uint frameIndex = (shaderTrace >> 1) & 3;
uint shaderId = shaderTrace >> 3;
shaderIdBuf.Store(frameIndex * 4, shaderId);
}
}
}
return color;
}
#endif

View file

@ -0,0 +1,73 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// UI rendering
struct VOut
{
float4 position : SV_Position;
float2 texCoords : TEXCOORD0;
float4 color : COLOR0;
};
cbuffer RootConstants : register(b0)
{
float2 scale;
uint textureIndex;
uint samplerIndex;
};
#if VERTEX_SHADER
struct VIn
{
float2 position : POSITION;
float2 texCoords : TEXCOORD0;
float4 color : COLOR0;
};
VOut vs(VIn input)
{
const float2 position = input.position * scale;
VOut output;
output.position = float4(position.x - 1.0, 1.0 - position.y, 0.0, 1.0);
output.texCoords = input.texCoords;
output.color = input.color;
return output;
}
#endif
#if PIXEL_SHADER
float4 ps(VOut input) : SV_Target
{
Texture2D texture0 = ResourceDescriptorHeap[textureIndex];
SamplerState sampler0 = SamplerDescriptorHeap[samplerIndex];
float4 result = input.color * texture0.Sample(sampler0, input.texCoords);
return result;
}
#endif

View file

@ -0,0 +1,30 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// shared world rendering constants
#pragma once
#define ATEST_NONE 0
#define ATEST_GT_0 1
#define ATEST_LT_HALF 2
#define ATEST_GE_HALF 3

View file

@ -0,0 +1,37 @@
/*
===========================================================================
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// shared world surface rendering functions
#pragma once
bool FailsAlphaTest(float alpha, uint alphaTest)
{
if(alphaTest == ATEST_GT_0)
return alpha == 0.0;
else if(alphaTest == ATEST_LT_HALF)
return alpha >= 0.5;
else if(alphaTest == ATEST_GE_HALF)
return alpha < 0.5;
else // ATEST_NONE
return false;
}

View file

@ -21,6 +21,9 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
// mip-map generation: gamma-space to linear-space transform // mip-map generation: gamma-space to linear-space transform
#include "../common/mip_gen.hlsli"
cbuffer RootConstants cbuffer RootConstants
{ {
float gamma; float gamma;
@ -32,14 +35,5 @@ RWTexture2D<float4> dst : register(u0);
[numthreads(8, 8, 1)] [numthreads(8, 8, 1)]
void cs(uint3 id : SV_DispatchThreadID) void cs(uint3 id : SV_DispatchThreadID)
{ {
// @TODO: is this actually required? MipGen_GammaToLinear(dst, src, id, gamma);
uint w, h;
dst.GetDimensions(w, h);
if(any(id.xy >= uint2(w, h)))
{
return;
}
float4 v = src[id.xy];
dst[id.xy] = float4(pow(v.xyz, gamma), v.a);
} }

View file

@ -21,6 +21,9 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
// mip-map generation: 8-tap 1D filter // mip-map generation: 8-tap 1D filter
#include "../common/mip_gen.hlsli"
cbuffer RootConstants cbuffer RootConstants
{ {
float4 weights; float4 weights;
@ -34,41 +37,10 @@ cbuffer RootConstants
RWTexture2D<float4> mips[2] : register(u0); RWTexture2D<float4> mips[2] : register(u0);
uint2 FixCoords(int2 c)
{
if(clampMode > 0)
{
// clamp
return uint2(clamp(c, int2(0, 0), maxSize));
}
// repeat
return uint2(c & maxSize);
}
[numthreads(8, 8, 1)] [numthreads(8, 8, 1)]
void cs(uint3 id : SV_DispatchThreadID) void cs(uint3 id : SV_DispatchThreadID)
{ {
RWTexture2D<float4> src = mips[srcMip]; RWTexture2D<float4> src = mips[srcMip];
RWTexture2D<float4> dst = mips[dstMip]; RWTexture2D<float4> dst = mips[dstMip];
MipGen_DownSample(dst, src, id, maxSize, clampMode, scale, offset, weights);
// @TODO: is this actually required?
uint w, h;
dst.GetDimensions(w, h);
if(any(id.xy >= uint2(w, h)))
{
return;
}
int2 base = int2(id.xy) * scale;
float4 r = float4(0, 0, 0, 0);
r += src[FixCoords(base - offset * 3)] * weights.x;
r += src[FixCoords(base - offset * 2)] * weights.y;
r += src[FixCoords(base - offset * 1)] * weights.z;
r += src[base] * weights.w;
r += src[base + offset] * weights.w;
r += src[FixCoords(base + offset * 2)] * weights.z;
r += src[FixCoords(base + offset * 3)] * weights.y;
r += src[FixCoords(base + offset * 4)] * weights.x;
dst[id.xy] = r;
} }

View file

@ -21,6 +21,9 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
// mip-map generation: linear-space to gamma-space transform // mip-map generation: linear-space to gamma-space transform
#include "../common/mip_gen.hlsli"
cbuffer RootConstants cbuffer RootConstants
{ {
float4 blendColor; float4 blendColor;
@ -37,22 +40,5 @@ void cs(uint3 id : SV_DispatchThreadID)
{ {
RWTexture2D<float4> src = mips[srcMip]; RWTexture2D<float4> src = mips[srcMip];
RWTexture2D<float4> dst = mips[dstMip]; RWTexture2D<float4> dst = mips[dstMip];
MipGen_LinearToGamma(dst, src, id, blendColor, intensity, invGamma);
// @TODO: is this actually required?
uint w, h;
dst.GetDimensions(w, h);
if(any(id.xy >= uint2(w, h)))
{
return;
}
// yes, intensity *should* be done in light-linear space
// but we keep the old behavior for consistency...
float4 in0 = src[id.xy];
float3 in1 = 0.5 * (in0.rgb + blendColor.rgb);
float3 inV = lerp(in0.rgb, in1.rgb, blendColor.a);
float3 out0 = pow(max(inV, 0.0), invGamma);
float3 out1 = out0 * intensity;
float4 outV = saturate(float4(out1, in0.a));
dst[id.xy] = outV;
} }

View file

@ -65,7 +65,7 @@ float4 ps(VOut input) : SV_Target
float3 base = texture0.Sample(sampler0, input.texCoords).rgb; float3 base = texture0.Sample(sampler0, input.texCoords).rgb;
float3 linearSpace = pow(base * invBrightness, gamma); float3 linearSpace = pow(base * invBrightness, gamma);
return float4(linearSpace, 1.0f); return float4(linearSpace, 1.0);
} }
#endif #endif

View file

@ -21,6 +21,11 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
// helper functions used by multiple shader files // helper functions used by multiple shader files
#if !defined(DISABLE_PRAGMA_ONCE)
#pragma once
#endif
float4 MakeGreyscale(float4 input, float amount) float4 MakeGreyscale(float4 input, float amount)
{ {
float grey = dot(input.rgb, float3(0.299, 0.587, 0.114)); float grey = dot(input.rgb, float3(0.299, 0.587, 0.114));

View file

@ -165,6 +165,8 @@ VOut vs(VIn input)
#if USE_INCLUDES #if USE_INCLUDES
#include "shared.hlsli" #include "shared.hlsli"
#include "../common/state_bits.h.hlsli"
#include "../common/blend.hlsli"
#endif #endif
cbuffer RootConstants cbuffer RootConstants
@ -191,86 +193,6 @@ Texture2D textures2D[4096] : register(t0);
SamplerState samplers[96] : register(s0); SamplerState samplers[96] : register(s0);
RWByteAddressBuffer shaderIndexBuffer : register(u0); RWByteAddressBuffer shaderIndexBuffer : register(u0);
#define GLS_SRCBLEND_ZERO 0x00000001
#define GLS_SRCBLEND_ONE 0x00000002
#define GLS_SRCBLEND_DST_COLOR 0x00000003
#define GLS_SRCBLEND_ONE_MINUS_DST_COLOR 0x00000004
#define GLS_SRCBLEND_SRC_ALPHA 0x00000005
#define GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA 0x00000006
#define GLS_SRCBLEND_DST_ALPHA 0x00000007
#define GLS_SRCBLEND_ONE_MINUS_DST_ALPHA 0x00000008
#define GLS_SRCBLEND_ALPHA_SATURATE 0x00000009
#define GLS_SRCBLEND_BITS 0x0000000f
#define GLS_DSTBLEND_ZERO 0x00000010
#define GLS_DSTBLEND_ONE 0x00000020
#define GLS_DSTBLEND_SRC_COLOR 0x00000030
#define GLS_DSTBLEND_ONE_MINUS_SRC_COLOR 0x00000040
#define GLS_DSTBLEND_SRC_ALPHA 0x00000050
#define GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA 0x00000060
#define GLS_DSTBLEND_DST_ALPHA 0x00000070
#define GLS_DSTBLEND_ONE_MINUS_DST_ALPHA 0x00000080
#define GLS_DSTBLEND_BITS 0x000000f0
#define GLS_ATEST_GT_0 0x10000000
#define GLS_ATEST_LT_80 0x20000000
#define GLS_ATEST_GE_80 0x40000000
#define GLS_ATEST_BITS 0x70000000
float4 BlendSource(float4 src, float4 dst, uint stateBits)
{
if(stateBits == GLS_SRCBLEND_ZERO)
return float4(0.0, 0.0, 0.0, 0.0);
else if(stateBits == GLS_SRCBLEND_ONE)
return src;
else if(stateBits == GLS_SRCBLEND_DST_COLOR)
return src * dst;
else if(stateBits == GLS_SRCBLEND_ONE_MINUS_DST_COLOR)
return src * (float4(1.0, 1.0, 1.0, 1.0) - dst);
else if(stateBits == GLS_SRCBLEND_SRC_ALPHA)
return src * float4(src.a, src.a, src.a, 1.0);
else if(stateBits == GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA)
return src * float4(1.0 - src.a, 1.0 - src.a, 1.0 - src.a, 1.0);
else if(stateBits == GLS_SRCBLEND_DST_ALPHA)
return src * float4(dst.a, dst.a, dst.a, 1.0);
else if(stateBits == GLS_SRCBLEND_ONE_MINUS_DST_ALPHA)
return src * float4(1.0 - dst.a, 1.0 - dst.a, 1.0 - dst.a, 1.0);
else if(stateBits == GLS_SRCBLEND_ALPHA_SATURATE)
return src * float4(src.a, src.a, src.a, 1.0); // ?????????
else
return src;
}
float4 BlendDest(float4 src, float4 dst, uint stateBits)
{
if(stateBits == GLS_DSTBLEND_ZERO)
return float4(0.0, 0.0, 0.0, 0.0);
else if(stateBits == GLS_DSTBLEND_ONE)
return dst;
else if(stateBits == GLS_DSTBLEND_SRC_COLOR)
return dst * src;
else if(stateBits == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR)
return dst * float4(1.0 - src.r, 1.0 - src.g, 1.0 - src.b, 1.0 - src.a);
else if(stateBits == GLS_DSTBLEND_SRC_ALPHA)
return dst * float4(src.a, src.a, src.a, 1.0);
else if(stateBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA)
return dst * float4(1.0 - src.a, 1.0 - src.a, 1.0 - src.a, 0.0);
else if(stateBits == GLS_DSTBLEND_DST_ALPHA)
return dst * float4(dst.a, dst.a, dst.a, 1.0);
else if(stateBits == GLS_DSTBLEND_ONE_MINUS_DST_ALPHA)
return dst * float4(1.0 - dst.a, 1.0 - dst.a, 1.0 - dst.a, 1.0);
else
return float4(0.0, 0.0, 0.0, 0.0);
}
float4 Blend(float4 src, float4 dst, uint stateBits)
{
float4 srcOut = BlendSource(src, dst, stateBits & GLS_SRCBLEND_BITS);
float4 dstOut = BlendDest(src, dst, stateBits & GLS_DSTBLEND_BITS);
return srcOut + dstOut;
}
bool FailsAlphaTest(float alpha, uint stateBits) bool FailsAlphaTest(float alpha, uint stateBits)
{ {
if(stateBits == GLS_ATEST_GT_0) if(stateBits == GLS_ATEST_GT_0)

View file

@ -1,6 +1,6 @@
/* /*
=========================================================================== ===========================================================================
Copyright (C) 2022-2023 Gian 'myT' Schellenbaum Copyright (C) 2022-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3). This file is part of Challenge Quake 3 (CNQ3).
@ -18,13 +18,11 @@ You should have received a copy of the GNU General Public License
along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>. along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
=========================================================================== ===========================================================================
*/ */
// Gameplay Rendering Pipeline - Dear ImGUI integration // Shared Rendering Pipeline - Dear ImGUI integration
#include "grp_local.h" #include "srp_local.h"
#include "../imgui/imgui.h" #include "../imgui/imgui.h"
#include "hlsl/imgui_vs.h"
#include "hlsl/imgui_ps.h"
#define MAX_VERTEX_COUNT (64 << 10) #define MAX_VERTEX_COUNT (64 << 10)
@ -46,13 +44,16 @@ struct PixelRC
#pragma pack(pop) #pragma pack(pop)
void ImGUI::Init() HTexture ImGUI::Init(bool ddhi_, const ShaderByteCode& vs, const ShaderByteCode& ps, TextureFormat::Id rtFormat, HDescriptorTable descTable, RootSignatureDesc* rootSigDesc)
{ {
ddhi = ddhi_;
descriptorTable = descTable;
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.DisplaySize.x = glConfig.vidWidth; io.DisplaySize.x = glConfig.vidWidth;
io.DisplaySize.y = glConfig.vidHeight; io.DisplaySize.y = glConfig.vidHeight;
if(grp.firstInit) if(srp.firstInit)
{ {
io.BackendRendererUserData = this; io.BackendRendererUserData = this;
io.BackendRendererName = "CNQ3 Direct3D 12"; io.BackendRendererName = "CNQ3 Direct3D 12";
@ -71,8 +72,13 @@ void ImGUI::Init()
fr->vertexBuffer = CreateBuffer(idx); fr->vertexBuffer = CreateBuffer(idx);
} }
if(ddhi)
{ {
RootSignatureDesc desc = grp.rootSignatureDesc; rootSignature = RHI_MAKE_NULL_HANDLE();
}
else
{
RootSignatureDesc desc = *rootSigDesc;
desc.name = "Dear ImGUI"; desc.name = "Dear ImGUI";
desc.constants[ShaderStage::Vertex].byteCount = sizeof(VertexRC); desc.constants[ShaderStage::Vertex].byteCount = sizeof(VertexRC);
desc.constants[ShaderStage::Pixel].byteCount = sizeof(PixelRC); desc.constants[ShaderStage::Pixel].byteCount = sizeof(PixelRC);
@ -100,8 +106,8 @@ void ImGUI::Init()
{ {
GraphicsPipelineDesc desc("Dear ImGUI", rootSignature); GraphicsPipelineDesc desc("Dear ImGUI", rootSignature);
desc.shortLifeTime = true; desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(g_vs); desc.vertexShader = vs;
desc.pixelShader = ShaderByteCode(g_ps); desc.pixelShader = ps;
desc.vertexLayout.bindingStrides[0] = sizeof(ImDrawVert); desc.vertexLayout.bindingStrides[0] = sizeof(ImDrawVert);
desc.vertexLayout.AddAttribute(0, ShaderSemantic::Position, desc.vertexLayout.AddAttribute(0, ShaderSemantic::Position,
DataType::Float32, 2, offsetof(ImDrawVert, pos)); DataType::Float32, 2, offsetof(ImDrawVert, pos));
@ -114,36 +120,39 @@ void ImGUI::Init()
desc.depthStencil.enableDepthTest = false; desc.depthStencil.enableDepthTest = false;
desc.depthStencil.enableDepthWrites = false; desc.depthStencil.enableDepthWrites = false;
desc.rasterizer.cullMode = CT_TWO_SIDED; desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA, grp.renderTargetFormat); desc.AddRenderTarget(GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA, rtFormat);
pipeline = CreateGraphicsPipeline(desc); pipeline = CreateGraphicsPipeline(desc);
} }
RegisterFontAtlas(); if(ddhi)
{
const uint32_t fontIndex = GetTextureIndexSRV(fontAtlas);
io.Fonts->SetTexID((ImTextureID)fontIndex);
}
return fontAtlas;
} }
void ImGUI::RegisterFontAtlas() void ImGUI::RegisterFontAtlas(uint32_t fontIndex)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGui::GetIO().Fonts->SetTexID((ImTextureID)fontIndex);
const uint32_t fontIndex = grp.RegisterTexture(fontAtlas);
io.Fonts->SetTexID((ImTextureID)fontIndex);
} }
void ImGUI::Draw() void ImGUI::Draw(HTexture renderTarget)
{ {
if(r_debugUI->integer == 0) if(r_debugUI->integer == 0)
{ {
SafeEndFrame(); EndFrame();
return; return;
} }
grp.renderMode = RenderMode::ImGui; srp.renderMode = RenderMode::ImGui;
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.DisplaySize.x = glConfig.vidWidth; io.DisplaySize.x = glConfig.vidWidth;
io.DisplaySize.y = glConfig.vidHeight; io.DisplaySize.y = glConfig.vidHeight;
SafeEndFrame(); EndFrame();
ImGui::Render(); ImGui::Render();
const ImDrawData* drawData = ImGui::GetDrawData(); const ImDrawData* drawData = ImGui::GetDrawData();
@ -151,7 +160,7 @@ void ImGUI::Draw()
// avoid rendering when minimized // avoid rendering when minimized
if(drawData->DisplaySize.x <= 0.0f || drawData->DisplaySize.y <= 0.0f) if(drawData->DisplaySize.x <= 0.0f || drawData->DisplaySize.y <= 0.0f)
{ {
grp.renderMode = RenderMode::None; srp.renderMode = RenderMode::None;
return; return;
} }
@ -201,14 +210,27 @@ void ImGUI::Draw()
const uint32_t vertexStride = sizeof(ImDrawVert); const uint32_t vertexStride = sizeof(ImDrawVert);
static_assert(sizeof(ImDrawIdx) == 4, "uint32 indices expected!"); static_assert(sizeof(ImDrawIdx) == 4, "uint32 indices expected!");
CmdBindRenderTargets(1, &grp.renderTarget, NULL); CmdBindRenderTargets(1, &renderTarget, NULL);
CmdBindRootSignature(rootSignature); if(!ddhi)
{
CmdBindRootSignature(rootSignature);
}
CmdBindPipeline(pipeline); CmdBindPipeline(pipeline);
CmdBindDescriptorTable(rootSignature, grp.descriptorTable); if(!ddhi)
{
CmdBindDescriptorTable(rootSignature, descriptorTable);
}
CmdBindVertexBuffers(1, &fr->vertexBuffer, &vertexStride, NULL); CmdBindVertexBuffers(1, &fr->vertexBuffer, &vertexStride, NULL);
CmdBindIndexBuffer(fr->indexBuffer, IndexType::UInt32, 0); CmdBindIndexBuffer(fr->indexBuffer, IndexType::UInt32, 0);
CmdSetViewport(0, 0, drawData->DisplaySize.x, drawData->DisplaySize.y); CmdSetViewport(0, 0, drawData->DisplaySize.x, drawData->DisplaySize.y);
CmdSetRootConstants(rootSignature, ShaderStage::Vertex, &vertexRC); if(ddhi)
{
CmdSetGraphicsRootConstants(0, sizeof(vertexRC), &vertexRC);
}
else
{
CmdSetRootConstants(rootSignature, ShaderStage::Vertex, &vertexRC);
}
// Render command lists // Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them) // (Because we merged all buffers into a single one, we maintain our own offset into them)
@ -234,7 +256,14 @@ void ImGUI::Draw()
pixelRC.texture = (uint32_t)cmd->TextureId & 0xFFFF; pixelRC.texture = (uint32_t)cmd->TextureId & 0xFFFF;
pixelRC.sampler = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear); pixelRC.sampler = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
pixelRC.mip = (float)(((uint32_t)cmd->TextureId >> 16) & 0xFFFF); pixelRC.mip = (float)(((uint32_t)cmd->TextureId >> 16) & 0xFFFF);
CmdSetRootConstants(rootSignature, ShaderStage::Pixel, &pixelRC); if(ddhi)
{
CmdSetGraphicsRootConstants(sizeof(vertexRC), sizeof(pixelRC), &pixelRC);
}
else
{
CmdSetRootConstants(rootSignature, ShaderStage::Pixel, &pixelRC);
}
// Apply Scissor/clipping rectangle, Draw // Apply Scissor/clipping rectangle, Draw
CmdSetScissor(clip_min.x, clip_min.y, clip_max.x - clip_min.x, clip_max.y - clip_min.y); CmdSetScissor(clip_min.x, clip_min.y, clip_max.x - clip_min.x, clip_max.y - clip_min.y);
@ -245,10 +274,10 @@ void ImGUI::Draw()
globalVtxOffset += cmdList->VtxBuffer.Size; globalVtxOffset += cmdList->VtxBuffer.Size;
} }
grp.renderMode = RenderMode::None; srp.renderMode = RenderMode::None;
} }
void ImGUI::SafeBeginFrame() void ImGUI::BeginFrame()
{ {
if(!frameStarted) if(!frameStarted)
{ {
@ -257,7 +286,7 @@ void ImGUI::SafeBeginFrame()
} }
} }
void ImGUI::SafeEndFrame() void ImGUI::EndFrame()
{ {
if(frameStarted) if(frameStarted)
{ {

435
code/renderer/srp_local.h Normal file
View file

@ -0,0 +1,435 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Shared Rendering Pipeline - private declarations
#pragma once
#include "tr_local.h"
#include "rhi_local.h"
using namespace RHI;
struct BufferBase
{
bool CanAdd(uint32_t count_)
{
return batchFirst + batchCount + count_ <= totalCount;
}
void EndBatch()
{
batchFirst += batchCount;
batchCount = 0;
}
void EndBatch(uint32_t size)
{
batchFirst += size;
batchCount = 0;
}
void Rewind()
{
batchFirst = 0;
batchCount = 0;
}
uint32_t totalCount = 0;
uint32_t batchFirst = 0;
uint32_t batchCount = 0;
};
struct IndexBuffer : BufferBase
{
void Create(const char* name, MemoryUsage::Id memoryUsage, uint32_t indexCount)
{
totalCount = indexCount;
BufferDesc desc = {};
desc.committedResource = true;
desc.initialState = ResourceStates::IndexBufferBit;
desc.memoryUsage = memoryUsage;
desc.name = va("%s index", name);
desc.byteCount = indexCount * sizeof(uint32_t);
buffer = CreateBuffer(desc);
}
void BeginUpload()
{
mapped = (uint32_t*)BeginBufferUpload(buffer);
}
void EndUpload()
{
EndBufferUpload(buffer);
mapped = NULL;
}
void Upload()
{
Q_assert(mapped != NULL);
uint32_t* const idx = mapped + batchFirst + batchCount;
memcpy(idx, &tess.indexes[0], tess.numIndexes * sizeof(uint32_t));
}
uint32_t* GetCurrentAddress()
{
return mapped + batchFirst + batchCount;
}
HBuffer buffer = RHI_MAKE_NULL_HANDLE();
uint32_t* mapped = NULL;
};
struct GeometryBuffer : BufferBase
{
void Init(uint32_t count_, uint32_t stride_)
{
buffer = RHI_MAKE_NULL_HANDLE();
byteCount = count_ * stride_;
stride = stride_;
totalCount = count_;
batchFirst = 0;
batchCount = 0;
}
void CreateVertexBuffer(const char* name, MemoryUsage::Id memoryUsage, uint32_t count, uint32_t stride_)
{
BufferDesc desc = {};
desc.committedResource = true;
desc.initialState = ResourceStates::VertexBufferBit;
desc.memoryUsage = memoryUsage;
desc.name = name;
desc.byteCount = count * stride_;
buffer = CreateBuffer(desc);
byteCount = count * stride_;
stride = stride_;
totalCount = count;
batchFirst = 0;
batchCount = 0;
}
void BeginUpload()
{
Q_assert(mapped == NULL);
mapped = BeginBufferUpload(buffer);
}
void EndUpload()
{
Q_assert(mapped != NULL);
EndBufferUpload(buffer);
mapped = NULL;
}
HBuffer buffer = RHI_MAKE_NULL_HANDLE();
uint32_t byteCount = 0;
uint32_t stride = 0;
uint8_t* mapped = NULL;
};
struct RenderMode
{
enum Id
{
None,
UI,
World,
ImGui,
Nuklear,
Count
};
};
struct RenderPassQueries
{
char name[64];
uint32_t gpuDurationUS;
uint32_t cpuDurationUS;
int64_t cpuStartUS;
uint32_t queryIndex;
};
enum
{
MaxRenderPasses = 64, // cg_draw3dIcons forces tons of 2D/3D transitions...
MaxStatsFrameCount = 64
};
struct RenderPassStats
{
void EndFrame(uint32_t cpu, uint32_t gpu);
uint32_t samplesCPU[MaxStatsFrameCount];
uint32_t samplesGPU[MaxStatsFrameCount];
stats_t statsCPU;
stats_t statsGPU;
uint32_t count;
uint32_t index;
};
struct RenderPassFrame
{
RenderPassQueries passes[MaxRenderPasses];
uint32_t count;
};
struct FrameStats
{
enum { MaxFrames = 1024 };
void EndFrame();
float temp[MaxFrames];
float p2pMS[MaxFrames];
stats_t p2pStats;
int frameCount;
int frameIndex;
int skippedFrames;
};
struct MipMapGenerator
{
void Init(bool ddhi, const ShaderByteCode& g2l, const ShaderByteCode& down, const ShaderByteCode& l2g);
void GenerateMipMaps(HTexture texture);
private:
struct Stage
{
enum Id
{
Start, // gamma to linear
DownSample, // down sample on 1 axis
End, // linear to gamma
Count
};
HRootSignature rootSignature;
HDescriptorTable descriptorTable;
HPipeline pipeline;
};
struct MipSlice
{
enum Id
{
Float16_0,
Float16_1,
Count
};
};
HTexture textures[MipSlice::Count];
Stage stages[3];
bool ddhi = false;
};
struct UI
{
void Init(bool ddhi_, const ShaderByteCode& vs, const ShaderByteCode& ps, TextureFormat::Id rtFormat,
HDescriptorTable descTable, RootSignatureDesc* rootSigDesc);
void BeginFrame();
void Begin(HTexture renderTarget);
void End();
void CmdSetColor(const uiSetColorCommand_t& cmd);
void CmdDrawQuad(const uiDrawQuadCommand_t& cmd);
void CmdDrawTriangle(const uiDrawTriangleCommand_t& cmd);
private:
void DrawBatch();
// 32-bit needed until the render logic is fixed!
typedef uint32_t Index;
const IndexType::Id indexType = IndexType::UInt32;
uint32_t renderPassIndex;
#pragma pack(push, 1)
struct Vertex
{
vec2_t position;
vec2_t texCoords;
uint32_t color;
};
#pragma pack(pop)
int maxIndexCount;
int maxVertexCount;
int firstIndex;
int firstVertex;
int indexCount;
int vertexCount;
HRootSignature rootSignature;
HDescriptorTable descriptorTable;
HPipeline pipeline;
HBuffer indexBuffer;
HBuffer vertexBuffer;
Index* indices;
Vertex* vertices;
uint32_t color;
const shader_t* shader;
bool ddhi; // direct descriptor heap indexing
};
struct ImGUI
{
HTexture Init(bool ddhi, const ShaderByteCode& vs, const ShaderByteCode& ps, TextureFormat::Id rtFormat, HDescriptorTable descTable, RootSignatureDesc* rootSigDesc);
void RegisterFontAtlas(uint32_t fontIndex);
void Draw(HTexture renderTarget);
void BeginFrame();
void EndFrame();
private:
struct FrameResources
{
HBuffer indexBuffer;
HBuffer vertexBuffer;
};
HRootSignature rootSignature;
HDescriptorTable descriptorTable;
HPipeline pipeline;
HTexture fontAtlas;
FrameResources frameResources[FrameCount];
bool frameStarted = false;
bool ddhi = false;
};
struct Nuklear
{
void Init(bool ddhi, const ShaderByteCode& vs, const ShaderByteCode& ps, TextureFormat::Id rtFormat, HDescriptorTable descTable, RootSignatureDesc* rootSigDesc);
void BeginFrame();
void Begin(HTexture renderTarget);
void End();
void Upload(const nuklearUploadCommand_t& cmd);
void Draw(const nuklearDrawCommand_t& cmd);
private:
struct FrameResources
{
HBuffer indexBuffer;
HBuffer vertexBuffer;
};
HRootSignature rootSignature;
HDescriptorTable descriptorTable;
HPipeline pipeline;
FrameResources frameResources[FrameCount];
uint32_t renderPassIndex;
int prevScissorRect[4];
// reset every frame
int firstVertex;
int firstIndex;
int numVertexes; // set in Upload
int numIndexes; // set in Upload
bool ddhi = false;
};
struct SRP
{
uint32_t BeginRenderPass(const char* name, float r, float g, float b);
void EndRenderPass(uint32_t index);
// @NOTE: SRP::BeginFrame doesn't call RHI::BeginFrame
// @NOTE: SRP::EndFrame calls RHI::EndFrame and Sys_V_EndFrame
void BeginFrame(); // call at the start of IRenderPipeline::BeginFrame
void EndFrame(); // call at the end of IRenderPipeline::EndFrame
void DrawGUI();
// call this in Init but only on srp.firstInit
// you need to register them in your own local descriptor table(s)
void CreateShaderTraceBuffers();
bool firstInit = true; // first RP init after a RHI init?
RenderMode::Id renderMode; // necessary for sampler selection, useful for debugging
// shader trace
HBuffer traceRenderBuffer;
HBuffer traceReadbackBuffer;
// data for frame breakdown and frame graph
RenderPassFrame renderPasses[FrameCount];
RenderPassFrame tempRenderPasses;
RenderPassStats renderPassStats[MaxRenderPasses];
RenderPassStats wholeFrameStats;
FrameStats frameStats;
bool enableRenderPassQueries = true;
// PSO stats
bool psoStatsValid = false;
int psoCount = 0;
int psoChangeCount = 0;
};
extern SRP srp;
struct ScopedRenderPass
{
ScopedRenderPass(const char* name, float r, float g, float b)
{
index = srp.BeginRenderPass(name, r, g, b);
}
~ScopedRenderPass()
{
srp.EndRenderPass(index);
}
uint32_t index;
};
#define SCOPED_RENDER_PASS(Name, R, G, B) ScopedRenderPass CONCAT(rp_, __LINE__)(Name, R, G, B)
#define BASE_SAMPLER_COUNT ((int)(TW_COUNT * TextureFilter::Count * MaxTextureMips))
const image_t* GetBundleImage(const textureBundle_t& bundle);
uint32_t GetBaseSamplerIndex(textureWrap_t wrap, TextureFilter::Id filter, uint32_t minLOD);
uint32_t GetSamplerIndex(textureWrap_t wrap, TextureFilter::Id filter, uint32_t minLOD = 0);
uint32_t GetSamplerIndex(const image_t* image);
void ReadTextureImage(void* outPixels, HTexture hreadback, int w, int h, int alignment, colorSpace_t colorSpace);
void UpdateEntityData(bool& depthHack, int entityNum, double originalTime);
cullType_t GetMirrorredCullType(cullType_t cullType);
uint32_t AlphaTestShaderConstFromStateBits(unsigned int stateBits);
inline void CmdSetViewportAndScissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
CmdSetViewport(x, y, w, h);
CmdSetScissor(x, y, w, h);
}
inline void CmdSetViewportAndScissor(const viewParms_t& vp)
{
CmdSetViewportAndScissor(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight);
}
inline bool IsDepthFadeEnabled(const shader_t& shader)
{
return
r_depthFade->integer != 0 &&
shader.dfType > DFT_NONE &&
shader.dfType < DFT_TBD;
}

539
code/renderer/srp_main.cpp Normal file
View file

@ -0,0 +1,539 @@
/*
===========================================================================
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Shared Rendering Pipeline - core functionality
#include "srp_local.h"
#include "../client/cl_imgui.h"
#include "shaders/crp/world.h.hlsli"
extern IRenderPipeline* grpp;
extern IRenderPipeline* crpp;
SRP srp;
IRenderPipeline* renderPipeline;
static ImPlotPoint FrameTimeGetter(int index, void*)
{
const FrameStats& fs = srp.frameStats;
const int realIndex = (fs.frameIndex + index) % fs.frameCount;
const float value = fs.p2pMS[realIndex];
ImPlotPoint p;
p.x = index;
p.y = value;
return p;
}
static void UpdateAnimatedImage(image_t* image, int w, int h, const byte* data, qbool dirty)
{
if(w != image->width || h != image->height)
{
// @TODO: ?
/*image->width = w;
image->height = h;
CreateTexture(&d3d.textures[image->texnum], image, 1, w, h);
GAL_UpdateTexture(image, 0, 0, 0, w, h, data);*/
}
else if(dirty)
{
// @TODO: ?
//GAL_UpdateTexture(image, 0, 0, 0, w, h, data);
}
}
const image_t* GetBundleImage(const textureBundle_t& bundle)
{
return R_UpdateAndGetBundleImage(&bundle, &UpdateAnimatedImage);
}
uint32_t GetBaseSamplerIndex(textureWrap_t wrap, TextureFilter::Id filter, uint32_t minLOD)
{
Q_assert((uint32_t)wrap < TW_COUNT);
Q_assert((uint32_t)filter < TextureFilter::Count);
const uint32_t baseIndex =
(uint32_t)filter +
(uint32_t)TextureFilter::Count * (uint32_t)wrap +
(uint32_t)TextureFilter::Count * (uint32_t)TW_COUNT * minLOD;
return baseIndex;
}
uint32_t GetSamplerIndex(textureWrap_t wrap, TextureFilter::Id filter, uint32_t minLOD)
{
const uint32_t baseIndex = GetBaseSamplerIndex(wrap, filter, minLOD);
const uint32_t descIndex = renderPipeline->GetSamplerDescriptorIndexFromBaseIndex(baseIndex);
return descIndex;
}
uint32_t GetSamplerIndex(const image_t* image)
{
TextureFilter::Id filter = TextureFilter::Anisotropic;
if(r_lego->integer &&
srp.renderMode == RenderMode::World &&
(image->flags & (IMG_LMATLAS | IMG_EXTLMATLAS | IMG_NOPICMIP)) == 0)
{
filter = TextureFilter::Point;
}
else if((image->flags & IMG_NOAF) != 0 ||
srp.renderMode != RenderMode::World)
{
filter = TextureFilter::Linear;
}
int minLOD = 0;
if(srp.renderMode == RenderMode::World &&
(image->flags & IMG_NOPICMIP) == 0)
{
minLOD = Com_ClampInt(0, MaxTextureMips - 1, r_picmip->integer);
}
return GetSamplerIndex(image->wrapClampMode, filter, (uint32_t)minLOD);
}
void ReadTextureImage(void* outPixels, HTexture hreadback, int w, int h, int alignment, colorSpace_t colorSpace)
{
MappedTexture mapped;
BeginTextureReadback(mapped, hreadback);
byte* const out0 = (byte*)outPixels;
const byte* const in0 = mapped.mappedData;
if(colorSpace == CS_RGBA)
{
const int dstRowSizeNoPadding = w * 4;
mapped.dstRowByteCount = AlignUp(dstRowSizeNoPadding, alignment);
for(int y = 0; y < mapped.rowCount; ++y)
{
byte* out = out0 + (mapped.rowCount - 1 - y) * mapped.dstRowByteCount;
const byte* in = in0 + y * mapped.srcRowByteCount;
memcpy(out, in, dstRowSizeNoPadding);
}
}
else if(colorSpace == CS_BGR)
{
mapped.dstRowByteCount = AlignUp(w * 3, alignment);
for(int y = 0; y < mapped.rowCount; ++y)
{
byte* out = out0 + (mapped.rowCount - 1 - y) * mapped.dstRowByteCount;
const byte* in = in0 + y * mapped.srcRowByteCount;
for(int x = 0; x < mapped.columnCount; ++x)
{
out[2] = in[0];
out[1] = in[1];
out[0] = in[2];
out += 3;
in += 4;
}
}
}
else
{
Q_assert(!"Unsupported color space");
}
EndTextureReadback();
}
void UpdateEntityData(bool& depthHack, int entityNum, double originalTime)
{
depthHack = false;
if(entityNum != ENTITYNUM_WORLD)
{
backEnd.currentEntity = &backEnd.refdef.entities[entityNum];
if(backEnd.currentEntity->intShaderTime)
{
backEnd.refdef.floatTime = originalTime - (double)backEnd.currentEntity->e.shaderTime.iShaderTime / 1000.0;
}
else
{
backEnd.refdef.floatTime = originalTime - backEnd.currentEntity->e.shaderTime.fShaderTime;
}
// we have to reset the shaderTime as well otherwise image animations start
// from the wrong frame
tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
// set up the transformation matrix
R_RotateForEntity(backEnd.currentEntity, &backEnd.viewParms, &backEnd.orient);
if(backEnd.currentEntity->e.renderfx & RF_DEPTHHACK)
{
depthHack = true;
}
}
else
{
backEnd.currentEntity = &tr.worldEntity;
backEnd.refdef.floatTime = originalTime;
backEnd.orient = backEnd.viewParms.world;
// we have to reset the shaderTime as well otherwise image animations on
// the world (like water) continue with the wrong frame
tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
}
}
cullType_t GetMirrorredCullType(cullType_t cullType)
{
switch(cullType)
{
case CT_BACK_SIDED: return CT_FRONT_SIDED;
case CT_FRONT_SIDED: return CT_BACK_SIDED;
default: return CT_TWO_SIDED;
}
}
uint32_t AlphaTestShaderConstFromStateBits(unsigned int stateBits)
{
stateBits &= GLS_ATEST_BITS;
switch(stateBits)
{
case GLS_ATEST_GE_80: return ATEST_GE_HALF;
case GLS_ATEST_GT_0: return ATEST_GT_0;
case GLS_ATEST_LT_80: return ATEST_LT_HALF;
default: return ATEST_NONE;
}
}
void R_SelectRenderPipeline()
{
if(r_pipeline->integer == 0)
{
renderPipeline = grpp;
}
else
{
renderPipeline = crpp;
}
}
void FrameStats::EndFrame()
{
frameCount = min(frameCount + 1, (int)MaxFrames);
frameIndex = (frameIndex + 1) % MaxFrames;
Com_StatsFromArray(p2pMS, frameCount, temp, &p2pStats);
}
void RenderPassStats::EndFrame(uint32_t cpu, uint32_t gpu)
{
static uint32_t tempSamples[MaxStatsFrameCount];
samplesCPU[index] = cpu;
samplesGPU[index] = gpu;
count = min(count + 1, (uint32_t)MaxStatsFrameCount);
index = (index + 1) % MaxStatsFrameCount;
Com_StatsFromArray((const int*)samplesCPU, count, (int*)tempSamples, &statsCPU);
Com_StatsFromArray((const int*)samplesGPU, count, (int*)tempSamples, &statsGPU);
}
uint32_t SRP::BeginRenderPass(const char* name, float r, float g, float b)
{
if(!enableRenderPassQueries)
{
CmdBeginDebugLabel(name, r, g, b);
return 0xDEADBEEF;
}
RenderPassFrame& f = renderPasses[tr.frameCount % FrameCount];
if(f.count >= ARRAY_LEN(f.passes))
{
Q_assert(0);
return UINT32_MAX;
}
CmdBeginDebugLabel(name, r, g, b);
const uint32_t index = f.count++;
RenderPassQueries& q = f.passes[index];
Q_strncpyz(q.name, name, sizeof(q.name));
q.cpuStartUS = Sys_Microseconds();
q.queryIndex = CmdBeginDurationQuery();
return index;
}
void SRP::EndRenderPass(uint32_t index)
{
if(!enableRenderPassQueries)
{
CmdEndDebugLabel();
return;
}
Q_assert(index != 0xDEADBEEF); // enableRenderPassQueries should still be false!
RenderPassFrame& f = renderPasses[tr.frameCount % FrameCount];
if(index >= f.count)
{
Q_assert(0);
return;
}
CmdEndDebugLabel();
RenderPassQueries& q = f.passes[index];
q.cpuDurationUS = (uint32_t)(Sys_Microseconds() - q.cpuStartUS);
CmdEndDurationQuery(q.queryIndex);
}
void SRP::DrawGUI()
{
uint32_t durations[MaxDurationQueries];
GetDurations(durations);
wholeFrameStats.EndFrame(rhie.renderToPresentUS, durations[0]);
const RenderPassFrame& currFrame = renderPasses[(tr.frameCount % FrameCount) ^ 1];
RenderPassFrame& tempFrame = tempRenderPasses;
// see if the render pass list is the same as the previous frame's
bool sameRenderPass = true;
if(currFrame.count == tempRenderPasses.count)
{
for(uint32_t p = 0; p < currFrame.count; ++p)
{
if(Q_stricmp(currFrame.passes[p].name, tempRenderPasses.passes[p].name) != 0)
{
sameRenderPass = false;
break;
}
}
}
else
{
sameRenderPass = false;
}
// write out the displayed timings into the temp buffer
tempFrame.count = currFrame.count;
if(sameRenderPass)
{
for(uint32_t p = 0; p < currFrame.count; ++p)
{
const uint32_t index = currFrame.passes[p].queryIndex;
if(index < MaxDurationQueries)
{
renderPassStats[p].EndFrame(currFrame.passes[p].cpuDurationUS, durations[index]);
tempFrame.passes[p].gpuDurationUS = renderPassStats[p].statsGPU.median;
tempFrame.passes[p].cpuDurationUS = renderPassStats[p].statsCPU.median;
}
}
}
else
{
for(uint32_t p = 0; p < currFrame.count; ++p)
{
const uint32_t index = currFrame.passes[p].queryIndex;
if(index < MaxDurationQueries)
{
tempFrame.passes[p].gpuDurationUS = durations[index];
tempFrame.passes[p].cpuDurationUS = currFrame.passes[p].cpuDurationUS;
}
}
}
static bool breakdownActive = false;
ToggleBooleanWithShortcut(breakdownActive, ImGuiKey_F);
GUI_AddMainMenuItem(GUI_MainMenu::Perf, "Frame breakdown", "Ctrl+F", &breakdownActive);
if(breakdownActive)
{
if(ImGui::Begin("Frame breakdown", &breakdownActive, ImGuiWindowFlags_AlwaysAutoResize))
{
if(BeginTable("Frame breakdown", 3))
{
TableHeader(3, "Pass", "GPU [us]", "CPU [us]");
TableRow(3, "Whole frame",
va("%d", (int)wholeFrameStats.statsGPU.median),
va("%d", (int)wholeFrameStats.statsCPU.median));
for(uint32_t p = 0; p < currFrame.count; ++p)
{
const RenderPassQueries& rp = tempFrame.passes[p];
if(rp.queryIndex < MaxDurationQueries)
{
TableRow(3, rp.name,
va("%d", (int)rp.gpuDurationUS),
va("%d", (int)rp.cpuDurationUS));
}
}
ImGui::EndTable();
}
if(psoStatsValid)
{
ImGui::Text("PSO count: %d", (int)psoCount);
ImGui::Text("PSO changes: %d", (int)psoChangeCount);
}
}
ImGui::End();
}
// save the current render pass list in the temp buffer
memcpy(&tempFrame, &currFrame, sizeof(tempFrame));
static bool frameTimeActive = false;
GUI_AddMainMenuItem(GUI_MainMenu::Perf, "Frame stats", NULL, &frameTimeActive);
if(frameTimeActive)
{
if(ImGui::Begin("Frame stats", &frameTimeActive, ImGuiWindowFlags_AlwaysAutoResize))
{
if(BeginTable("Frame stats", 2))
{
const FrameStats& fs = frameStats;
const stats_t& s = fs.p2pStats;
TableRow2("Skipped frames", fs.skippedFrames);
TableRow2("Frame time target", rhie.targetFrameDurationMS);
TableRow2("Frame time average", s.average);
TableRow2("Frame time std dev.", s.stdDev);
TableRow2("Input to render", (float)rhie.inputToRenderUS / 1000.0f);
TableRow2("Input to present", (float)rhie.inputToPresentUS / 1000.0f);
ImGui::EndTable();
}
}
ImGui::End();
}
static bool graphsActive = false;
ToggleBooleanWithShortcut(graphsActive, ImGuiKey_G);
GUI_AddMainMenuItem(GUI_MainMenu::Perf, "Frame time graphs", "Ctrl+G", &graphsActive);
if(graphsActive)
{
const int windowFlags =
ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoMove;
ImGui::SetNextWindowSize(ImVec2(glConfig.vidWidth, glConfig.vidHeight / 2), ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(0, glConfig.vidHeight / 2), ImGuiCond_Always);
if(ImGui::Begin("Frame time graphs", &graphsActive, windowFlags))
{
const FrameStats& fs = frameStats;
const double target = (double)rhie.targetFrameDurationMS;
static bool autoFit = false;
ImGui::Checkbox("Auto-fit", &autoFit);
if(ImPlot::BeginPlot("Frame Times", ImVec2(-1, -1), ImPlotFlags_NoInputs))
{
const int axisFlags = 0; // ImPlotAxisFlags_NoTickLabels
const int axisFlagsY = axisFlags | (autoFit ? ImPlotAxisFlags_AutoFit : 0);
ImPlot::SetupAxes(NULL, NULL, axisFlags, axisFlagsY);
ImPlot::SetupAxisLimits(ImAxis_X1, 0, FrameStats::MaxFrames, ImGuiCond_Always);
if(!autoFit)
{
ImPlot::SetupAxisLimits(ImAxis_Y1, max(target - 2.0, 0.0), target + 2.0, ImGuiCond_Always);
}
ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 1.0f);
ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL, 1.0f);
ImPlot::PlotInfLines("Target", &target, 1, ImPlotInfLinesFlags_Horizontal);
ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 1.0f);
ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL, 1.0f);
ImPlot::PlotLineG("Frame Time", &FrameTimeGetter, NULL, fs.frameCount, ImPlotLineFlags_None);
ImPlot::EndPlot();
}
}
ImGui::End();
}
GUI_DrawMainMenu();
R_DrawGUI();
}
void SRP::BeginFrame()
{
srp.renderPasses[tr.frameCount % FrameCount].count = 0;
R_SetColorMappings();
// nothing is bound to the command list yet!
srp.renderMode = RenderMode::None;
}
void SRP::EndFrame()
{
tr.tracedWorldShaderIndex = -1;
if(tr.traceWorldShader && tr.world != NULL)
{
// schedule a GPU -> CPU transfer
{
BufferBarrier barrier(traceRenderBuffer, ResourceStates::CopySourceBit);
CmdBarrier(0, NULL, 1, &barrier);
}
CmdCopyBuffer(traceReadbackBuffer, traceRenderBuffer);
{
BufferBarrier barrier(traceRenderBuffer, ResourceStates::UnorderedAccessBit);
CmdBarrier(0, NULL, 1, &barrier);
}
// grab last frame's result
uint32_t* shaderIndices = (uint32_t*)MapBuffer(traceReadbackBuffer);
const uint32_t shaderIndex = shaderIndices[RHI::GetFrameIndex() ^ 1];
UnmapBuffer(traceReadbackBuffer);
if(shaderIndex < (uint32_t)tr.numShaders)
{
tr.tracedWorldShaderIndex = (int)shaderIndex;
}
}
RHI::EndFrame();
if(rhie.presentToPresentUS > 0)
{
frameStats.p2pMS[frameStats.frameIndex] = (float)rhie.presentToPresentUS / 1000.0f;
frameStats.EndFrame();
}
else
{
frameStats.skippedFrames++;
}
if(backEnd.renderFrame)
{
Sys_V_EndFrame();
}
}
void SRP::CreateShaderTraceBuffers()
{
{
BufferDesc desc("shader trace opaque", 2 * sizeof(uint32_t), ResourceStates::UnorderedAccessBit);
traceRenderBuffer = CreateBuffer(desc);
}
{
BufferDesc desc("shader trace opaque readback", 2 * sizeof(uint32_t), ResourceStates::Common);
desc.memoryUsage = MemoryUsage::Readback;
traceReadbackBuffer = CreateBuffer(desc);
}
}

View file

@ -1,6 +1,6 @@
/* /*
=========================================================================== ===========================================================================
Copyright (C) 2023 Gian 'myT' Schellenbaum Copyright (C) 2023-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3). This file is part of Challenge Quake 3 (CNQ3).
@ -18,26 +18,10 @@ You should have received a copy of the GNU General Public License
along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>. along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
=========================================================================== ===========================================================================
*/ */
// Gameplay Rendering Pipeline - texture mip-map generation // Shared Rendering Pipeline - texture mip-map generation
// @TODO: test for OOB accesses in the shaders and return #include "srp_local.h"
// (also, is it needed with feature level 12.0 and HLSL 5.1/6.0 ?)
#include "grp_local.h"
namespace start
{
#include "hlsl/mip_1_cs.h"
}
namespace down
{
#include "hlsl/mip_2_cs.h"
}
namespace end
{
#include "hlsl/mip_3_cs.h"
}
#pragma pack(push, 4) #pragma pack(push, 4)
@ -45,6 +29,8 @@ namespace end
struct StartConstants struct StartConstants
{ {
float gamma; float gamma;
uint32_t srcTexture;
uint32_t dstTexture;
}; };
struct DownConstants struct DownConstants
@ -56,6 +42,8 @@ struct DownConstants
uint32_t clampMode; // 0 = repeat uint32_t clampMode; // 0 = repeat
uint32_t srcMip; uint32_t srcMip;
uint32_t dstMip; uint32_t dstMip;
uint32_t srcTexture;
uint32_t dstTexture;
}; };
struct EndConstants struct EndConstants
@ -65,18 +53,22 @@ struct EndConstants
float invGamma; // 1.0 / gamma float invGamma; // 1.0 / gamma
uint32_t srcMip; uint32_t srcMip;
uint32_t dstMip; uint32_t dstMip;
uint32_t srcTexture;
uint32_t dstTexture;
}; };
#pragma pack(pop) #pragma pack(pop)
void MipMapGenerator::Init() void MipMapGenerator::Init(bool ddhi_, const ShaderByteCode& g2l, const ShaderByteCode& down, const ShaderByteCode& l2g)
{ {
if(!grp.firstInit) if(!srp.firstInit)
{ {
return; return;
} }
ddhi = ddhi_;
for(int t = 0; t < 2; ++t) for(int t = 0; t < 2; ++t)
{ {
TextureDesc desc(va("mip-map generation #%d", t + 1), MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE); TextureDesc desc(va("mip-map generation #%d", t + 1), MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE);
@ -88,26 +80,33 @@ void MipMapGenerator::Init()
const char* const stageNames[] = { "start", "down", "end" }; const char* const stageNames[] = { "start", "down", "end" };
const uint32_t stageRCByteCount[] = { sizeof(StartConstants), sizeof(DownConstants), sizeof(EndConstants) }; const uint32_t stageRCByteCount[] = { sizeof(StartConstants), sizeof(DownConstants), sizeof(EndConstants) };
const ShaderByteCode stageShaders[] = { ShaderByteCode(start::g_cs), ShaderByteCode(down::g_cs), ShaderByteCode(end::g_cs) };
const uint32_t stageExtraMips[] = { 1, 0, MaxTextureMips }; const uint32_t stageExtraMips[] = { 1, 0, MaxTextureMips };
const ShaderByteCode stageShaders[] = { g2l, down, l2g };
for(int s = 0; s < 3; ++s) for(int s = 0; s < 3; ++s)
{ {
Stage& stage = stages[s]; Stage& stage = stages[s];
if(ddhi)
{ {
RootSignatureDesc desc(va("mip-map %s", stageNames[s])); stage.rootSignature = RHI_MAKE_NULL_HANDLE();
desc.pipelineType = PipelineType::Compute;
desc.constants[ShaderStage::Compute].byteCount = stageRCByteCount[s];
desc.AddRange(DescriptorType::RWTexture, 0, MipSlice::Count + stageExtraMips[s]);
stage.rootSignature = CreateRootSignature(desc);
} }
else
{ {
const DescriptorTableDesc desc(DescriptorTableDesc(va("mip-map %s", stageNames[s]), stage.rootSignature)); {
stage.descriptorTable = CreateDescriptorTable(desc); RootSignatureDesc desc(va("mip-map %s", stageNames[s]));
desc.pipelineType = PipelineType::Compute;
desc.constants[ShaderStage::Compute].byteCount = stageRCByteCount[s];
desc.AddRange(DescriptorType::RWTexture, 0, MipSlice::Count + stageExtraMips[s]);
stage.rootSignature = CreateRootSignature(desc);
}
{
const DescriptorTableDesc desc(DescriptorTableDesc(va("mip-map %s", stageNames[s]), stage.rootSignature));
stage.descriptorTable = CreateDescriptorTable(desc);
DescriptorTableUpdate update; DescriptorTableUpdate update;
update.SetRWTexturesSlice(ARRAY_LEN(textures), textures, 0, 0); update.SetRWTexturesSlice(ARRAY_LEN(textures), textures, 0, 0);
UpdateDescriptorTable(stage.descriptorTable, update); UpdateDescriptorTable(stage.descriptorTable, update);
}
} }
{ {
ComputePipelineDesc desc(va("mip-map %s", stageNames[s]), stage.rootSignature); ComputePipelineDesc desc(va("mip-map %s", stageNames[s]), stage.rootSignature);
@ -119,7 +118,7 @@ void MipMapGenerator::Init()
void MipMapGenerator::GenerateMipMaps(HTexture texture) void MipMapGenerator::GenerateMipMaps(HTexture texture)
{ {
// @FIXME: // @TODO: better look-up
image_t* image = NULL; image_t* image = NULL;
for(int i = 0; i < tr.numImages; ++i) for(int i = 0; i < tr.numImages; ++i)
{ {
@ -153,19 +152,22 @@ void MipMapGenerator::GenerateMipMaps(HTexture texture)
CmdBarrier(ARRAY_LEN(allBarriers), allBarriers); CmdBarrier(ARRAY_LEN(allBarriers), allBarriers);
} }
// this must happen after the BeginTempCommandList call because if(!ddhi)
// it has a CPU wait that guarantees it's safe to update the descriptor tables
{ {
Stage& stage = stages[Stage::Start]; // this must happen after the BeginTempCommandList call because
DescriptorTableUpdate update; // it has a CPU wait that guarantees it's safe to update the descriptor tables
update.SetRWTexturesSlice(1, &texture, MipSlice::Count, 0); {
UpdateDescriptorTable(stage.descriptorTable, update); Stage& stage = stages[Stage::Start];
} DescriptorTableUpdate update;
{ update.SetRWTexturesSlice(1, &texture, MipSlice::Count, 0);
Stage& stage = stages[Stage::End]; UpdateDescriptorTable(stage.descriptorTable, update);
DescriptorTableUpdate update; }
update.SetRWTexturesChain(1, &texture, MipSlice::Count); {
UpdateDescriptorTable(stage.descriptorTable, update); Stage& stage = stages[Stage::End];
DescriptorTableUpdate update;
update.SetRWTexturesChain(1, &texture, MipSlice::Count);
UpdateDescriptorTable(stage.descriptorTable, update);
}
} }
int w = image->width; int w = image->width;
@ -180,10 +182,22 @@ void MipMapGenerator::GenerateMipMaps(HTexture texture)
StartConstants rc = {}; StartConstants rc = {};
rc.gamma = r_mipGenGamma->value; rc.gamma = r_mipGenGamma->value;
CmdBindRootSignature(stage.rootSignature); if(!ddhi)
{
CmdBindRootSignature(stage.rootSignature);
}
CmdBindPipeline(stage.pipeline); CmdBindPipeline(stage.pipeline);
CmdBindDescriptorTable(stage.rootSignature, stage.descriptorTable); if(ddhi)
CmdSetRootConstants(stage.rootSignature, ShaderStage::Compute, &rc); {
rc.srcTexture = GetTextureIndexUAV(texture, 0);
rc.dstTexture = GetTextureIndexUAV(textures[MipSlice::Float16_0], 0);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
}
else
{
CmdBindDescriptorTable(stage.rootSignature, stage.descriptorTable);
CmdSetRootConstants(stage.rootSignature, ShaderStage::Compute, &rc);
}
Dispatch(w, h); Dispatch(w, h);
} }
@ -207,10 +221,22 @@ void MipMapGenerator::GenerateMipMaps(HTexture texture)
rc.srcMip = MipSlice::Float16_0; rc.srcMip = MipSlice::Float16_0;
rc.dstMip = MipSlice::Count + destMip; rc.dstMip = MipSlice::Count + destMip;
CmdBindRootSignature(stage.rootSignature); if(!ddhi)
{
CmdBindRootSignature(stage.rootSignature);
}
CmdBindPipeline(stage.pipeline); CmdBindPipeline(stage.pipeline);
CmdBindDescriptorTable(stage.rootSignature, stage.descriptorTable); if(ddhi)
CmdSetRootConstants(stage.rootSignature, ShaderStage::Compute, &rc); {
rc.srcTexture = GetTextureIndexUAV(textures[MipSlice::Float16_0], 0);
rc.dstTexture = GetTextureIndexUAV(texture, destMip);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
}
else
{
CmdBindDescriptorTable(stage.rootSignature, stage.descriptorTable);
CmdSetRootConstants(stage.rootSignature, ShaderStage::Compute, &rc);
}
CmdBarrier(ARRAY_LEN(tempBarriers), tempBarriers); CmdBarrier(ARRAY_LEN(tempBarriers), tempBarriers);
Dispatch(w, h); Dispatch(w, h);
} }
@ -229,9 +255,15 @@ void MipMapGenerator::GenerateMipMaps(HTexture texture)
rc.clampMode = image->wrapClampMode == TW_REPEAT ? 0 : 1; rc.clampMode = image->wrapClampMode == TW_REPEAT ? 0 : 1;
memcpy(rc.weights, tr.mipFilter, sizeof(rc.weights)); memcpy(rc.weights, tr.mipFilter, sizeof(rc.weights));
CmdBindRootSignature(stage.rootSignature); if(!ddhi)
{
CmdBindRootSignature(stage.rootSignature);
}
CmdBindPipeline(stage.pipeline); CmdBindPipeline(stage.pipeline);
CmdBindDescriptorTable(stage.rootSignature, stage.descriptorTable); if(!ddhi)
{
CmdBindDescriptorTable(stage.rootSignature, stage.descriptorTable);
}
// down-sample on the X-axis // down-sample on the X-axis
rc.srcMip = MipSlice::Float16_0; rc.srcMip = MipSlice::Float16_0;
@ -242,7 +274,16 @@ void MipMapGenerator::GenerateMipMaps(HTexture texture)
rc.maxSize[1] = hs - 1; rc.maxSize[1] = hs - 1;
rc.offset[0] = 1; rc.offset[0] = 1;
rc.offset[1] = 0; rc.offset[1] = 0;
CmdSetRootConstants(stage.rootSignature, ShaderStage::Compute, &rc); if(ddhi)
{
rc.srcTexture = GetTextureIndexUAV(textures[MipSlice::Float16_0], 0);
rc.dstTexture = GetTextureIndexUAV(textures[MipSlice::Float16_1], 0);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
}
else
{
CmdSetRootConstants(stage.rootSignature, ShaderStage::Compute, &rc);
}
CmdBarrier(ARRAY_LEN(tempBarriers), tempBarriers); CmdBarrier(ARRAY_LEN(tempBarriers), tempBarriers);
Dispatch(w, hs); Dispatch(w, hs);
@ -255,7 +296,16 @@ void MipMapGenerator::GenerateMipMaps(HTexture texture)
rc.maxSize[1] = hs - 1; rc.maxSize[1] = hs - 1;
rc.offset[0] = 0; rc.offset[0] = 0;
rc.offset[1] = 1; rc.offset[1] = 1;
CmdSetRootConstants(stage.rootSignature, ShaderStage::Compute, &rc); if(ddhi)
{
rc.srcTexture = GetTextureIndexUAV(textures[MipSlice::Float16_1], 0);
rc.dstTexture = GetTextureIndexUAV(textures[MipSlice::Float16_0], 0);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
}
else
{
CmdSetRootConstants(stage.rootSignature, ShaderStage::Compute, &rc);
}
CmdBarrier(ARRAY_LEN(tempBarriers), tempBarriers); CmdBarrier(ARRAY_LEN(tempBarriers), tempBarriers);
Dispatch(w, h); Dispatch(w, h);
} }
@ -272,10 +322,22 @@ void MipMapGenerator::GenerateMipMaps(HTexture texture)
rc.srcMip = MipSlice::Float16_0; rc.srcMip = MipSlice::Float16_0;
rc.dstMip = MipSlice::Count + destMip; rc.dstMip = MipSlice::Count + destMip;
CmdBindRootSignature(stage.rootSignature); if(!ddhi)
{
CmdBindRootSignature(stage.rootSignature);
}
CmdBindPipeline(stage.pipeline); CmdBindPipeline(stage.pipeline);
CmdBindDescriptorTable(stage.rootSignature, stage.descriptorTable); if(ddhi)
CmdSetRootConstants(stage.rootSignature, ShaderStage::Compute, &rc); {
rc.srcTexture = GetTextureIndexUAV(textures[MipSlice::Float16_0], 0);
rc.dstTexture = GetTextureIndexUAV(texture, destMip);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
}
else
{
CmdBindDescriptorTable(stage.rootSignature, stage.descriptorTable);
CmdSetRootConstants(stage.rootSignature, ShaderStage::Compute, &rc);
}
CmdBarrier(ARRAY_LEN(tempBarriers), tempBarriers); CmdBarrier(ARRAY_LEN(tempBarriers), tempBarriers);
Dispatch(w, h); Dispatch(w, h);
} }

View file

@ -1,6 +1,6 @@
/* /*
=========================================================================== ===========================================================================
Copyright (C) 2023 Gian 'myT' Schellenbaum Copyright (C) 2023-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3). This file is part of Challenge Quake 3 (CNQ3).
@ -21,9 +21,7 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
// Gameplay Rendering Pipeline - Nuklear integration // Gameplay Rendering Pipeline - Nuklear integration
#include "grp_local.h" #include "srp_local.h"
#include "hlsl/nuklear_vs.h"
#include "hlsl/nuklear_ps.h"
#define MAX_NUKLEAR_VERTEX_COUNT (1024 * 1024) #define MAX_NUKLEAR_VERTEX_COUNT (1024 * 1024)
@ -53,9 +51,12 @@ struct NuklearVertex
#pragma pack(pop) #pragma pack(pop)
void Nuklear::Init() void Nuklear::Init(bool ddhi_, const ShaderByteCode& vs, const ShaderByteCode& ps, TextureFormat::Id rtFormat, HDescriptorTable descTable, RootSignatureDesc* rootSigDesc)
{ {
if(grp.firstInit) ddhi = ddhi_;
descriptorTable = descTable;
if(srp.firstInit)
{ {
for(int i = 0; i < FrameCount; i++) for(int i = 0; i < FrameCount; i++)
{ {
@ -70,8 +71,9 @@ void Nuklear::Init()
fr->vertexBuffer = CreateBuffer(idx); fr->vertexBuffer = CreateBuffer(idx);
} }
if(!ddhi)
{ {
RootSignatureDesc desc = grp.rootSignatureDesc; RootSignatureDesc desc = *rootSigDesc;
desc.name = "Nuklear"; desc.name = "Nuklear";
desc.constants[ShaderStage::Vertex].byteCount = sizeof(VertexRC); desc.constants[ShaderStage::Vertex].byteCount = sizeof(VertexRC);
desc.constants[ShaderStage::Pixel].byteCount = sizeof(PixelRC); desc.constants[ShaderStage::Pixel].byteCount = sizeof(PixelRC);
@ -82,8 +84,8 @@ void Nuklear::Init()
{ {
GraphicsPipelineDesc desc("Nuklear", rootSignature); GraphicsPipelineDesc desc("Nuklear", rootSignature);
desc.shortLifeTime = true; desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(g_vs); desc.vertexShader = vs;
desc.pixelShader = ShaderByteCode(g_ps); desc.pixelShader = ps;
desc.vertexLayout.bindingStrides[0] = sizeof(NuklearVertex); desc.vertexLayout.bindingStrides[0] = sizeof(NuklearVertex);
desc.vertexLayout.AddAttribute(0, ShaderSemantic::Position, desc.vertexLayout.AddAttribute(0, ShaderSemantic::Position,
DataType::Float32, 2, offsetof(NuklearVertex, position)); DataType::Float32, 2, offsetof(NuklearVertex, position));
@ -96,7 +98,7 @@ void Nuklear::Init()
desc.depthStencil.enableDepthTest = false; desc.depthStencil.enableDepthTest = false;
desc.depthStencil.enableDepthWrites = false; desc.depthStencil.enableDepthWrites = false;
desc.rasterizer.cullMode = CT_TWO_SIDED; desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA, grp.renderTargetFormat); desc.AddRenderTarget(GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA, rtFormat);
pipeline = CreateGraphicsPipeline(desc); pipeline = CreateGraphicsPipeline(desc);
} }
} }
@ -109,24 +111,30 @@ void Nuklear::BeginFrame()
numIndexes = 0; numIndexes = 0;
} }
void Nuklear::Begin() void Nuklear::Begin(HTexture renderTarget)
{ {
if(grp.renderMode == RenderMode::Nuklear) if(srp.renderMode == RenderMode::Nuklear)
{ {
return; return;
} }
grp.renderMode = RenderMode::Nuklear; srp.renderMode = RenderMode::Nuklear;
renderPassIndex = grp.BeginRenderPass("Nuklear", 0.75f, 0.75f, 1.0f); renderPassIndex = srp.BeginRenderPass("Nuklear", 0.75f, 0.75f, 1.0f);
FrameResources* const fr = &frameResources[GetFrameIndex()]; FrameResources* const fr = &frameResources[GetFrameIndex()];
const uint32_t vertexStride = sizeof(NuklearVertex); const uint32_t vertexStride = sizeof(NuklearVertex);
CmdBindRenderTargets(1, &grp.renderTarget, NULL); CmdBindRenderTargets(1, &renderTarget, NULL);
CmdBindRootSignature(rootSignature); if(!ddhi)
{
CmdBindRootSignature(rootSignature);
}
CmdBindPipeline(pipeline); CmdBindPipeline(pipeline);
CmdBindDescriptorTable(rootSignature, grp.descriptorTable); if(!ddhi)
{
CmdBindDescriptorTable(rootSignature, descriptorTable);
}
CmdBindVertexBuffers(1, &fr->vertexBuffer, &vertexStride, NULL); CmdBindVertexBuffers(1, &fr->vertexBuffer, &vertexStride, NULL);
CmdBindIndexBuffer(fr->indexBuffer, IndexType::UInt32, 0); CmdBindIndexBuffer(fr->indexBuffer, IndexType::UInt32, 0);
CmdSetViewport(0, 0, glConfig.vidWidth, glConfig.vidHeight); CmdSetViewport(0, 0, glConfig.vidWidth, glConfig.vidHeight);
@ -142,7 +150,14 @@ void Nuklear::Begin()
0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f,
(R + L) / (L - R), (T + B) / (B - T), 0.5f, 1.0f (R + L) / (L - R), (T + B) / (B - T), 0.5f, 1.0f
}; };
CmdSetRootConstants(rootSignature, ShaderStage::Vertex, &vertexRC); if(ddhi)
{
CmdSetGraphicsRootConstants(0, sizeof(vertexRC), &vertexRC);
}
else
{
CmdSetRootConstants(rootSignature, ShaderStage::Vertex, &vertexRC);
}
for(int i = 0; i < 4; ++i) for(int i = 0; i < 4; ++i)
{ {
@ -152,9 +167,9 @@ void Nuklear::Begin()
void Nuklear::End() void Nuklear::End()
{ {
grp.EndRenderPass(renderPassIndex); srp.EndRenderPass(renderPassIndex);
grp.renderMode = RenderMode::None; srp.renderMode = RenderMode::None;
} }
void Nuklear::Upload(const nuklearUploadCommand_t& cmd) void Nuklear::Upload(const nuklearUploadCommand_t& cmd)
@ -196,7 +211,14 @@ void Nuklear::Draw(const nuklearDrawCommand_t& cmd)
PixelRC pixelRC = {}; PixelRC pixelRC = {};
pixelRC.texture = (uint32_t)image->textureIndex; pixelRC.texture = (uint32_t)image->textureIndex;
pixelRC.sampler = GetSamplerIndex(image->wrapClampMode, TextureFilter::Linear); pixelRC.sampler = GetSamplerIndex(image->wrapClampMode, TextureFilter::Linear);
CmdSetRootConstants(rootSignature, ShaderStage::Pixel, &pixelRC); if(ddhi)
{
CmdSetGraphicsRootConstants(sizeof(VertexRC), sizeof(pixelRC), &pixelRC);
}
else
{
CmdSetRootConstants(rootSignature, ShaderStage::Pixel, &pixelRC);
}
if(memcmp(cmd.scissorRect, prevScissorRect, sizeof(prevScissorRect)) != 0) if(memcmp(cmd.scissorRect, prevScissorRect, sizeof(prevScissorRect)) != 0)
{ {

View file

@ -1,6 +1,6 @@
/* /*
=========================================================================== ===========================================================================
Copyright (C) 2022-2023 Gian 'myT' Schellenbaum Copyright (C) 2022-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3). This file is part of Challenge Quake 3 (CNQ3).
@ -18,16 +18,13 @@ You should have received a copy of the GNU General Public License
along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>. along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
=========================================================================== ===========================================================================
*/ */
// Gameplay Rendering Pipeline - UI/2D rendering // Shared Rendering Pipeline - UI/2D rendering
#include "grp_local.h" #include "srp_local.h"
#include "hlsl/ui_vs.h"
#include "hlsl/ui_ps.h"
#pragma pack(push, 4) #pragma pack(push, 4)
struct VertexRC struct VertexRC
{ {
float scale[2]; float scale[2];
@ -38,16 +35,23 @@ struct PixelRC
uint32_t texture; uint32_t texture;
uint32_t sampler; uint32_t sampler;
}; };
#pragma pack(pop) #pragma pack(pop)
void UI::Init() void UI::Init(bool ddhi_, const ShaderByteCode& vs, const ShaderByteCode& ps, TextureFormat::Id rtFormat, HDescriptorTable descTable, RootSignatureDesc* rootSigDesc)
{ {
if(grp.firstInit) ddhi = ddhi_;
descriptorTable = descTable;
if(srp.firstInit)
{ {
if(ddhi)
{ {
RootSignatureDesc desc = grp.rootSignatureDesc; rootSignature = RHI_MAKE_NULL_HANDLE();
}
else
{
RootSignatureDesc desc = *rootSigDesc;
desc.name = "UI"; desc.name = "UI";
desc.constants[ShaderStage::Vertex].byteCount = 8; desc.constants[ShaderStage::Vertex].byteCount = 8;
desc.constants[ShaderStage::Pixel].byteCount = 8; desc.constants[ShaderStage::Pixel].byteCount = 8;
@ -71,8 +75,8 @@ void UI::Init()
{ {
GraphicsPipelineDesc desc("UI", rootSignature); GraphicsPipelineDesc desc("UI", rootSignature);
desc.shortLifeTime = true; desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(g_vs); desc.vertexShader = vs;
desc.pixelShader = ShaderByteCode(g_ps); desc.pixelShader = ps;
desc.vertexLayout.bindingStrides[0] = sizeof(UI::Vertex); desc.vertexLayout.bindingStrides[0] = sizeof(UI::Vertex);
desc.vertexLayout.AddAttribute(0, ShaderSemantic::Position, desc.vertexLayout.AddAttribute(0, ShaderSemantic::Position,
DataType::Float32, 2, offsetof(UI::Vertex, position)); DataType::Float32, 2, offsetof(UI::Vertex, position));
@ -85,7 +89,7 @@ void UI::Init()
desc.depthStencil.enableDepthTest = false; desc.depthStencil.enableDepthTest = false;
desc.depthStencil.enableDepthWrites = false; desc.depthStencil.enableDepthWrites = false;
desc.rasterizer.cullMode = CT_TWO_SIDED; desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA, grp.renderTargetFormat); desc.AddRenderTarget(GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA, rtFormat);
pipeline = CreateGraphicsPipeline(desc); pipeline = CreateGraphicsPipeline(desc);
} }
} }
@ -99,20 +103,29 @@ void UI::BeginFrame()
renderPassIndex = UINT32_MAX; renderPassIndex = UINT32_MAX;
} }
void UI::Begin() void UI::Begin(HTexture renderTarget)
{ {
grp.renderMode = RenderMode::UI; srp.renderMode = RenderMode::UI;
renderPassIndex = grp.BeginRenderPass("UI", 0.0f, 0.85f, 1.0f); renderPassIndex = srp.BeginRenderPass("UI", 0.0f, 0.85f, 1.0f);
CmdBindRenderTargets(1, &grp.renderTarget, NULL); const TextureBarrier tb(renderTarget, ResourceStates::RenderTargetBit);
CmdBarrier(1, &tb);
CmdBindRenderTargets(1, &renderTarget, NULL);
// UI always uses the entire render surface // UI always uses the entire render surface
CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight); CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight);
CmdBindRootSignature(rootSignature); if(!ddhi)
{
CmdBindRootSignature(rootSignature);
}
CmdBindPipeline(pipeline); CmdBindPipeline(pipeline);
CmdBindDescriptorTable(rootSignature, grp.descriptorTable); if(!ddhi)
{
CmdBindDescriptorTable(rootSignature, descriptorTable);
}
const uint32_t stride = sizeof(UI::Vertex); const uint32_t stride = sizeof(UI::Vertex);
CmdBindVertexBuffers(1, &vertexBuffer, &stride, NULL); CmdBindVertexBuffers(1, &vertexBuffer, &stride, NULL);
CmdBindIndexBuffer(indexBuffer, indexType, 0); CmdBindIndexBuffer(indexBuffer, indexType, 0);
@ -120,16 +133,23 @@ void UI::Begin()
VertexRC vertexRC = {}; VertexRC vertexRC = {};
vertexRC.scale[0] = 2.0f / glConfig.vidWidth; vertexRC.scale[0] = 2.0f / glConfig.vidWidth;
vertexRC.scale[1] = 2.0f / glConfig.vidHeight; vertexRC.scale[1] = 2.0f / glConfig.vidHeight;
CmdSetRootConstants(rootSignature, ShaderStage::Vertex, &vertexRC); if(ddhi)
{
CmdSetGraphicsRootConstants(0, sizeof(vertexRC), &vertexRC);
}
else
{
CmdSetRootConstants(rootSignature, ShaderStage::Vertex, &vertexRC);
}
} }
void UI::End() void UI::End()
{ {
DrawBatch(); DrawBatch();
grp.EndRenderPass(renderPassIndex); srp.EndRenderPass(renderPassIndex);
grp.renderMode = RenderMode::None; srp.renderMode = RenderMode::None;
} }
void UI::DrawBatch() void UI::DrawBatch()
@ -146,7 +166,14 @@ void UI::DrawBatch()
PixelRC pixelRC = {}; PixelRC pixelRC = {};
pixelRC.texture = GetBundleImage(bundle)->textureIndex; pixelRC.texture = GetBundleImage(bundle)->textureIndex;
pixelRC.sampler = GetSamplerIndex(wrapMode, TextureFilter::Linear); pixelRC.sampler = GetSamplerIndex(wrapMode, TextureFilter::Linear);
CmdSetRootConstants(rootSignature, ShaderStage::Pixel, &pixelRC); if(ddhi)
{
CmdSetGraphicsRootConstants(sizeof(VertexRC), sizeof(PixelRC), &pixelRC);
}
else
{
CmdSetRootConstants(rootSignature, ShaderStage::Pixel, &pixelRC);
}
CmdDrawIndexed(indexCount, firstIndex, 0); CmdDrawIndexed(indexCount, firstIndex, 0);
firstIndex += indexCount; firstIndex += indexCount;
@ -155,7 +182,7 @@ void UI::DrawBatch()
vertexCount = 0; vertexCount = 0;
} }
void UI::UISetColor(const uiSetColorCommand_t& cmd) void UI::CmdSetColor(const uiSetColorCommand_t& cmd)
{ {
const float rgbScale = tr.identityLight * 255.0f; const float rgbScale = tr.identityLight * 255.0f;
byte* const colors = (byte*)&color; byte* const colors = (byte*)&color;
@ -165,7 +192,7 @@ void UI::UISetColor(const uiSetColorCommand_t& cmd)
colors[3] = (byte)(cmd.color[3] * 255.0f); colors[3] = (byte)(cmd.color[3] * 255.0f);
} }
void UI::UIDrawQuad(const uiDrawQuadCommand_t& cmd) void UI::CmdDrawQuad(const uiDrawQuadCommand_t& cmd)
{ {
if(vertexCount + 4 > maxVertexCount || if(vertexCount + 4 > maxVertexCount ||
indexCount + 6 > maxIndexCount) indexCount + 6 > maxIndexCount)
@ -217,7 +244,7 @@ void UI::UIDrawQuad(const uiDrawQuadCommand_t& cmd)
vertices[v + 3].color = color; vertices[v + 3].color = color;
} }
void UI::UIDrawTriangle(const uiDrawTriangleCommand_t& cmd) void UI::CmdDrawTriangle(const uiDrawTriangleCommand_t& cmd)
{ {
if(vertexCount + 3 > maxVertexCount || if(vertexCount + 3 > maxVertexCount ||
indexCount + 3 > maxIndexCount) indexCount + 3 > maxIndexCount)

View file

@ -90,6 +90,7 @@ cvar_t *r_picmip;
cvar_t *r_clear; cvar_t *r_clear;
cvar_t *r_vsync; cvar_t *r_vsync;
cvar_t *r_lego; cvar_t *r_lego;
cvar_t *r_pipeline;
cvar_t *r_lockpvs; cvar_t *r_lockpvs;
cvar_t *r_noportals; cvar_t *r_noportals;
cvar_t *r_portalOnly; cvar_t *r_portalOnly;
@ -445,10 +446,16 @@ static const cvarTableItem_t r_cvars[] =
}, },
{ {
&r_vsync, "r_vsync", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, "enables v-sync", &r_vsync, "r_vsync", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, "enables v-sync",
"V-Sync", CVARCAT_DISPLAY | CVARCAT_PERFORMANCE, "Enabling locks the framerate to the monitor's refresh rate", "" "V-Sync", CVARCAT_DISPLAY | CVARCAT_PERFORMANCE, "Enabling locks the framerate to the monitor's refresh rate", "",
CVAR_GUI_VALUE("0", "Frame cap", "The framerate is capped by CNQ3's own limiter") CVAR_GUI_VALUE("0", "Frame cap", "The framerate is capped by CNQ3's own limiter")
CVAR_GUI_VALUE("1", "V-Sync", "The framerate matches the monitor's refresh rate") CVAR_GUI_VALUE("1", "V-Sync", "The framerate matches the monitor's refresh rate")
}, },
{
&r_pipeline, "r_pipeline", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, "rendering pipeline",
"Rendering pipeline", CVARCAT_GRAPHICS, "", "",
CVAR_GUI_VALUE("0", "Gameplay", "Use to play the game")
CVAR_GUI_VALUE("1", "Cinematic", "Use for screenshots and movies")
},
// //
// latched variables that can only change over a restart // latched variables that can only change over a restart
@ -751,6 +758,7 @@ void R_Init()
R_InitMipFilter(); R_InitMipFilter();
R_SelectRenderPipeline();
renderPipeline->Init(); renderPipeline->Init();
R_InitImages(); R_InitImages();
@ -773,6 +781,9 @@ static void RE_Shutdown( qbool destroyWindow )
if ( tr.registered ) { if ( tr.registered ) {
ri.Cmd_UnregisterModule(); ri.Cmd_UnregisterModule();
if ( !destroyWindow && r_pipeline->latchedString != NULL ) {
destroyWindow = qtrue;
}
renderPipeline->ShutDown( destroyWindow ); renderPipeline->ShutDown( destroyWindow );
} }

View file

@ -28,6 +28,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "../qcommon/qfiles.h" #include "../qcommon/qfiles.h"
#include "../qcommon/qcommon.h" #include "../qcommon/qcommon.h"
#include "tr_public.h" #include "tr_public.h"
#include "shaders/common/state_bits.h.hlsli" // contains all the shared GLS_* macros
#define GLS_DEFAULT GLS_DEPTHMASK_TRUE
#define GLS_DEFAULT_2D (GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA)
extern const float s_flipMatrix[16]; extern const float s_flipMatrix[16];
@ -427,6 +432,9 @@ struct shader_t {
pipeline_t pipelines[MAX_SHADER_STAGES]; pipeline_t pipelines[MAX_SHADER_STAGES];
int numPipelines; int numPipelines;
pipeline_t transpPipelines[MAX_SHADER_STAGES];
int numTranspPipelines;
shader_t* next; shader_t* next;
}; };
@ -473,6 +481,7 @@ typedef struct {
float projectionMatrix[16]; float projectionMatrix[16];
cplane_t frustum[4]; cplane_t frustum[4];
vec3_t visBounds[2]; vec3_t visBounds[2];
float zNear;
float zFar; float zFar;
} viewParms_t; } viewParms_t;
@ -1061,6 +1070,7 @@ extern cvar_t *r_picmip; // controls picmip values
extern cvar_t *r_vsync; extern cvar_t *r_vsync;
extern cvar_t *r_lego; extern cvar_t *r_lego;
extern cvar_t *r_pipeline;
extern cvar_t *r_vertexLight; // vertex lighting mode for better performance extern cvar_t *r_vertexLight; // vertex lighting mode for better performance
extern cvar_t *r_uiFullScreen; // ui is running fullscreen extern cvar_t *r_uiFullScreen; // ui is running fullscreen
@ -1112,49 +1122,11 @@ int R_CullPointAndRadius( const vec3_t origin, float radius );
int R_CullLocalPointAndRadius( const vec3_t origin, float radius ); int R_CullLocalPointAndRadius( const vec3_t origin, float radius );
void R_RotateForEntity( const trRefEntity_t* ent, const viewParms_t* viewParms, orientationr_t* orient ); void R_RotateForEntity( const trRefEntity_t* ent, const viewParms_t* viewParms, orientationr_t* orient );
void R_CreateWorldModelMatrix( const vec3_t origin, const vec3_t axis[3], float* viewMatrix );
typedef void (*updateAnimatedImage_t)( image_t* image, int w, int h, const byte* data, qbool dirty ); typedef void (*updateAnimatedImage_t)( image_t* image, int w, int h, const byte* data, qbool dirty );
const image_t* R_UpdateAndGetBundleImage( const textureBundle_t* bundle, updateAnimatedImage_t updateImage ); const image_t* R_UpdateAndGetBundleImage( const textureBundle_t* bundle, updateAnimatedImage_t updateImage );
#define GLS_SRCBLEND_ZERO 0x00000001
#define GLS_SRCBLEND_ONE 0x00000002
#define GLS_SRCBLEND_DST_COLOR 0x00000003
#define GLS_SRCBLEND_ONE_MINUS_DST_COLOR 0x00000004
#define GLS_SRCBLEND_SRC_ALPHA 0x00000005
#define GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA 0x00000006
#define GLS_SRCBLEND_DST_ALPHA 0x00000007
#define GLS_SRCBLEND_ONE_MINUS_DST_ALPHA 0x00000008
#define GLS_SRCBLEND_ALPHA_SATURATE 0x00000009
#define GLS_SRCBLEND_BITS 0x0000000f
#define GLS_DSTBLEND_ZERO 0x00000010
#define GLS_DSTBLEND_ONE 0x00000020
#define GLS_DSTBLEND_SRC_COLOR 0x00000030
#define GLS_DSTBLEND_ONE_MINUS_SRC_COLOR 0x00000040
#define GLS_DSTBLEND_SRC_ALPHA 0x00000050
#define GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA 0x00000060
#define GLS_DSTBLEND_DST_ALPHA 0x00000070
#define GLS_DSTBLEND_ONE_MINUS_DST_ALPHA 0x00000080
#define GLS_DSTBLEND_BITS 0x000000f0
#define GLS_BLEND_BITS 0x000000ff
#define GLS_DEPTHMASK_TRUE 0x00000100 // enable depth writes
#define GLS_POLYMODE_LINE 0x00001000 // wireframe polygon filling, not line rendering
#define GLS_DEPTHTEST_DISABLE 0x00010000 // disable depth tests
#define GLS_DEPTHFUNC_EQUAL 0x00020000
#define GLS_ATEST_GT_0 0x10000000
#define GLS_ATEST_LT_80 0x20000000
#define GLS_ATEST_GE_80 0x40000000
#define GLS_ATEST_BITS 0x70000000
#define GLS_DEFAULT GLS_DEPTHMASK_TRUE
#define GLS_DEFAULT_2D (GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA)
void RE_LoadWorldMap( const char *mapname ); void RE_LoadWorldMap( const char *mapname );
void RE_SetWorldVisData( const byte *vis ); void RE_SetWorldVisData( const byte *vis );
qhandle_t RE_RegisterModel( const char *name ); qhandle_t RE_RegisterModel( const char *name );
@ -1309,6 +1281,9 @@ struct shaderCommands_t
// how to process the colors of the current batch // how to process the colors of the current batch
float greyscale; float greyscale;
// identifier of the code currently tessellating geometry
int tessellator;
}; };
extern shaderCommands_t tess; extern shaderCommands_t tess;
@ -1490,7 +1465,6 @@ struct drawSceneViewCommand_t : renderCommandBase_t {
struct endSceneCommand_t : renderCommandBase_t { struct endSceneCommand_t : renderCommandBase_t {
viewParms_t viewParms; viewParms_t viewParms;
uint32_t padding2;
}; };
struct nuklearUploadCommand_t : renderCommandBase_t { struct nuklearUploadCommand_t : renderCommandBase_t {
@ -1631,6 +1605,11 @@ void R_CameraAxisVectorsFromMatrix( const float* modelView, vec3_t axisX, vec3_t
void R_MakeIdentityMatrix( float* m ); void R_MakeIdentityMatrix( float* m );
void R_MakeOrthoProjectionMatrix( float* m, float w, float h ); void R_MakeOrthoProjectionMatrix( float* m, float w, float h );
// LinearDepth(depthZW, A, B) -> B / (depthZW - A)
void R_LinearDepthConstantsFromProjectionMatrix( const float* projMatrix, float* A, float* B );
void R_LinearDepthConstantsFromClipPlanes( float zNear, float zFar, float* A, float* B );
void RB_LinearDepthConstants( float* A, float* B );
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
@ -1693,20 +1672,18 @@ struct IRenderPipeline
virtual void EndTextureUpload() = 0; virtual void EndTextureUpload() = 0;
virtual void ExecuteRenderCommands(const byte* data, bool readbackRequested) = 0; virtual void ExecuteRenderCommands(const byte* data, bool readbackRequested) = 0;
virtual void UISetColor(const uiSetColorCommand_t& cmd) = 0;
virtual void UIDrawQuad(const uiDrawQuadCommand_t& cmd) = 0;
virtual void UIDrawTriangle(const uiDrawTriangleCommand_t& cmd) = 0;
virtual void DrawSceneView(const drawSceneViewCommand_t& cmd) = 0;
virtual void TessellationOverflow() = 0; virtual void TessellationOverflow() = 0;
virtual void DrawSkyBox() = 0; virtual void DrawSkyBox() = 0;
virtual void DrawClouds() = 0; virtual void DrawClouds() = 0;
virtual void ReadPixels(int w, int h, int alignment, colorSpace_t colorSpace, void* out) = 0; virtual void ReadPixels(int w, int h, int alignment, colorSpace_t colorSpace, void* out) = 0;
virtual uint32_t GetSamplerDescriptorIndexFromBaseIndex(uint32_t baseIndex) = 0;
}; };
extern IRenderPipeline* renderPipeline; extern IRenderPipeline* renderPipeline;
void R_SelectRenderPipeline();
struct RHIExport struct RHIExport
{ {
uint32_t renderToPresentUS; uint32_t renderToPresentUS;

View file

@ -464,6 +464,26 @@ void R_MakeOrthoProjectionMatrix( float* m, float w, float h )
} }
void R_LinearDepthConstantsFromProjectionMatrix( const float* projMatrix, float* A, float* B )
{
*A = -projMatrix[2 * 4 + 2];
*B = projMatrix[3 * 4 + 2];
}
void R_LinearDepthConstantsFromClipPlanes( float n, float f, float* A, float* B )
{
*A = -n / (f - n);
*B = f * (n / (f - n));
}
void RB_LinearDepthConstants( float* A, float* B )
{
R_LinearDepthConstantsFromProjectionMatrix( backEnd.viewParms.projectionMatrix, A, B );
}
/* /*
================= =================
R_RotateForEntity R_RotateForEntity
@ -534,13 +554,40 @@ void R_RotateForEntity( const trRefEntity_t* ent, const viewParms_t* viewParms,
} }
void R_CreateWorldModelMatrix( const vec3_t origin, const vec3_t axis[3], float* viewMatrix )
{
float viewerMatrix[16];
viewerMatrix[0] = axis[0][0];
viewerMatrix[4] = axis[0][1];
viewerMatrix[8] = axis[0][2];
viewerMatrix[12] = -origin[0] * viewerMatrix[0] + -origin[1] * viewerMatrix[4] + -origin[2] * viewerMatrix[8];
viewerMatrix[1] = axis[1][0];
viewerMatrix[5] = axis[1][1];
viewerMatrix[9] = axis[1][2];
viewerMatrix[13] = -origin[0] * viewerMatrix[1] + -origin[1] * viewerMatrix[5] + -origin[2] * viewerMatrix[9];
viewerMatrix[2] = axis[2][0];
viewerMatrix[6] = axis[2][1];
viewerMatrix[10] = axis[2][2];
viewerMatrix[14] = -origin[0] * viewerMatrix[2] + -origin[1] * viewerMatrix[6] + -origin[2] * viewerMatrix[10];
viewerMatrix[3] = 0.0f;
viewerMatrix[7] = 0.0f;
viewerMatrix[11] = 0.0f;
viewerMatrix[15] = 1.0f;
// convert from our coordinate system (looking down X)
// to the back-end's coordinate system (looking down -Z)
R_MultMatrix( viewerMatrix, s_flipMatrix, viewMatrix );
}
// sets up the modelview matrix for a given viewParm // sets up the modelview matrix for a given viewParm
static void R_RotateForViewer() static void R_RotateForViewer()
{ {
float viewerMatrix[16];
vec3_t origin;
Com_Memset( &tr.orient, 0, sizeof(tr.orient) ); Com_Memset( &tr.orient, 0, sizeof(tr.orient) );
tr.orient.axis[0][0] = 1; tr.orient.axis[0][0] = 1;
tr.orient.axis[1][1] = 1; tr.orient.axis[1][1] = 1;
@ -548,42 +595,20 @@ static void R_RotateForViewer()
VectorCopy( tr.viewParms.orient.origin, tr.orient.viewOrigin ); VectorCopy( tr.viewParms.orient.origin, tr.orient.viewOrigin );
// transform by the camera placement // transform by the camera placement
VectorCopy( tr.viewParms.orient.origin, origin ); R_CreateWorldModelMatrix( tr.viewParms.orient.origin, tr.viewParms.orient.axis, tr.orient.modelMatrix );
viewerMatrix[0] = tr.viewParms.orient.axis[0][0];
viewerMatrix[4] = tr.viewParms.orient.axis[0][1];
viewerMatrix[8] = tr.viewParms.orient.axis[0][2];
viewerMatrix[12] = -origin[0] * viewerMatrix[0] + -origin[1] * viewerMatrix[4] + -origin[2] * viewerMatrix[8];
viewerMatrix[1] = tr.viewParms.orient.axis[1][0];
viewerMatrix[5] = tr.viewParms.orient.axis[1][1];
viewerMatrix[9] = tr.viewParms.orient.axis[1][2];
viewerMatrix[13] = -origin[0] * viewerMatrix[1] + -origin[1] * viewerMatrix[5] + -origin[2] * viewerMatrix[9];
viewerMatrix[2] = tr.viewParms.orient.axis[2][0];
viewerMatrix[6] = tr.viewParms.orient.axis[2][1];
viewerMatrix[10] = tr.viewParms.orient.axis[2][2];
viewerMatrix[14] = -origin[0] * viewerMatrix[2] + -origin[1] * viewerMatrix[6] + -origin[2] * viewerMatrix[10];
viewerMatrix[3] = 0;
viewerMatrix[7] = 0;
viewerMatrix[11] = 0;
viewerMatrix[15] = 1;
// convert from our coordinate system (looking down X)
// to the back-end's coordinate system (looking down -Z)
R_MultMatrix( viewerMatrix, s_flipMatrix, tr.orient.modelMatrix );
tr.viewParms.world = tr.orient; tr.viewParms.world = tr.orient;
} }
static void SetFarClip() static void SetClipPlanes()
{ {
tr.viewParms.zNear = 1.0f;
// if not rendering the world (icons, menus, etc) // if not rendering the world (icons, menus, etc)
// set a 2k far clip plane // set a 2k far clip plane
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
tr.viewParms.zFar = 2048; tr.viewParms.zFar = 2048.0f;
return; return;
} }
@ -614,37 +639,37 @@ static void R_SetupProjection()
float zNear, zFar; float zNear, zFar;
// dynamically compute far clip plane distance // dynamically compute far clip plane distance
SetFarClip(); SetClipPlanes();
// //
// set up projection matrix // set up projection matrix
// //
zNear = 1.0f; zNear = tr.viewParms.zNear;
zFar = tr.viewParms.zFar; zFar = tr.viewParms.zFar;
height = 2.0f * zNear * tan( tr.refdef.fov_y * M_PI / 360.0f ); height = 2.0f * zNear * tan( tr.refdef.fov_y * M_PI / 360.0f );
width = 2.0f * zNear * tan( tr.refdef.fov_x * M_PI / 360.0f ); width = 2.0f * zNear * tan( tr.refdef.fov_x * M_PI / 360.0f );
depth = zFar - zNear; depth = zFar - zNear;
tr.viewParms.projectionMatrix[0] = 2 * zNear / width; tr.viewParms.projectionMatrix[ 0] = 2.0f * zNear / width;
tr.viewParms.projectionMatrix[4] = 0; tr.viewParms.projectionMatrix[ 4] = 0.0f;
tr.viewParms.projectionMatrix[8] = 0; tr.viewParms.projectionMatrix[ 8] = 0.0f;
tr.viewParms.projectionMatrix[12] = 0; tr.viewParms.projectionMatrix[12] = 0.0f;
tr.viewParms.projectionMatrix[1] = 0; tr.viewParms.projectionMatrix[ 1] = 0.0f;
tr.viewParms.projectionMatrix[5] = 2 * zNear / height; tr.viewParms.projectionMatrix[ 5] = 2.0f * zNear / height;
tr.viewParms.projectionMatrix[9] = 0; tr.viewParms.projectionMatrix[ 9] = 0.0f;
tr.viewParms.projectionMatrix[13] = 0; tr.viewParms.projectionMatrix[13] = 0.0f;
tr.viewParms.projectionMatrix[2] = 0; tr.viewParms.projectionMatrix[ 2] = 0.0f;
tr.viewParms.projectionMatrix[6] = 0; tr.viewParms.projectionMatrix[ 6] = 0.0f;
tr.viewParms.projectionMatrix[10] = zNear / depth; tr.viewParms.projectionMatrix[10] = zNear / depth;
tr.viewParms.projectionMatrix[14] = zFar * zNear / depth; tr.viewParms.projectionMatrix[14] = zFar * zNear / depth;
tr.viewParms.projectionMatrix[3] = 0; tr.viewParms.projectionMatrix[ 3] = 0.0f;
tr.viewParms.projectionMatrix[7] = 0; tr.viewParms.projectionMatrix[ 7] = 0.0f;
tr.viewParms.projectionMatrix[11] = -1; tr.viewParms.projectionMatrix[11] = -1.0f;
tr.viewParms.projectionMatrix[15] = 0; tr.viewParms.projectionMatrix[15] = 0.0f;
} }

View file

@ -1,9 +1,44 @@
/*
===========================================================================
Copyright (C) 2022-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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// compiles core shaders as byte code to be embedded into the CNQ3 client
#include <Windows.h> #include <Windows.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include <shlwapi.h>
#include <sal.h>
#include "../renderer/uber_shaders.h" #include "../renderer/grp_uber_shaders.h"
char repoPath[MAX_PATH];
char outputPath[MAX_PATH];
char bin2headerPath[MAX_PATH];
char dxcPath[MAX_PATH];
const char* targetVS = "vs_6_0";
const char* targetPS = "ps_6_0";
const char* targetCS = "cs_6_0";
#define PS(Data) #Data, #define PS(Data) #Data,
@ -22,7 +57,7 @@ const char* uberShaderPixelStates[] =
// -Wno-warning disables the warning // -Wno-warning disables the warning
const char* va(const char* format, ...) const char* va(_Printf_format_string_ const char* format, ...)
{ {
static char string[64][32000]; static char string[64][32000];
static int index = 0; static int index = 0;
@ -49,12 +84,14 @@ void CompileShader(const ShaderArgs& args, int extraCount = 0, const char** extr
{ {
static char temp[4096]; static char temp[4096];
const char* headerPath = va("%s\\%s", outputPath, args.headerPath);
// -Ges: Enable strict mode // -Ges: Enable strict mode
// -Gis: Force IEEE strictness // -Gis: Force IEEE strictness
// -Zi: Embed debug info // -Zi: Embed debug info
// -Qembed_debug: Embed debug info in shader container // -Qembed_debug: Embed debug info in shader container
strcpy(temp, va("dxc.exe -Fh %s -E %s -T %s -WX -Ges -Gis -Zi -Qembed_debug", strcpy(temp, va("%s -Fh %s -E %s -T %s -WX -Ges -Gis -Zi -Qembed_debug",
args.headerPath, args.entryPoint, args.targetProfile)); dxcPath, headerPath, args.entryPoint, args.targetProfile));
for(int i = 0; i < extraCount; ++i) for(int i = 0; i < extraCount; ++i)
{ {
@ -93,7 +130,7 @@ void CompileSMAAShader(const SMAAArgs& smaaArgs)
args.entryPoint = smaaArgs.vertexShader ? "vs" : "ps"; args.entryPoint = smaaArgs.vertexShader ? "vs" : "ps";
args.headerPath = smaaArgs.headerPath; args.headerPath = smaaArgs.headerPath;
args.shaderPath = smaaArgs.shaderPath; args.shaderPath = smaaArgs.shaderPath;
args.targetProfile = smaaArgs.vertexShader ? "vs_6_0" : "ps_6_0"; args.targetProfile = smaaArgs.vertexShader ? targetVS : targetPS;
CompileShader(args, _countof(extras), extras); CompileShader(args, _countof(extras), extras);
} }
@ -130,7 +167,7 @@ void CompileVS(const char* headerPath, const char* shaderPath)
args.entryPoint = "vs"; args.entryPoint = "vs";
args.headerPath = headerPath; args.headerPath = headerPath;
args.shaderPath = shaderPath; args.shaderPath = shaderPath;
args.targetProfile = "vs_6_0"; args.targetProfile = targetVS;
CompileShader(args, _countof(extras), extras); CompileShader(args, _countof(extras), extras);
} }
@ -142,7 +179,7 @@ void CompilePS(const char* headerPath, const char* shaderPath)
args.entryPoint = "ps"; args.entryPoint = "ps";
args.headerPath = headerPath; args.headerPath = headerPath;
args.shaderPath = shaderPath; args.shaderPath = shaderPath;
args.targetProfile = "ps_6_0"; args.targetProfile = targetPS;
CompileShader(args, _countof(extras), extras); CompileShader(args, _countof(extras), extras);
} }
@ -154,7 +191,7 @@ void CompileCS(const char* headerPath, const char* shaderPath)
args.entryPoint = "cs"; args.entryPoint = "cs";
args.headerPath = headerPath; args.headerPath = headerPath;
args.shaderPath = shaderPath; args.shaderPath = shaderPath;
args.targetProfile = "cs_6_0"; args.targetProfile = targetCS;
CompileShader(args, _countof(extras), extras); CompileShader(args, _countof(extras), extras);
} }
@ -177,7 +214,7 @@ void CompileUberVS(const char* headerPath, const char* shaderPath, int stageCoun
args.entryPoint = "vs"; args.entryPoint = "vs";
args.headerPath = headerPath; args.headerPath = headerPath;
args.shaderPath = shaderPath; args.shaderPath = shaderPath;
args.targetProfile = "vs_6_0"; args.targetProfile = targetVS;
CompileShader(args, _countof(extras), extras); CompileShader(args, _countof(extras), extras);
} }
@ -213,26 +250,38 @@ void CompileUberPS(const char* stateString)
args.entryPoint = "ps"; args.entryPoint = "ps";
args.headerPath = va("uber_shader_ps_%s.h", stateString); args.headerPath = va("uber_shader_ps_%s.h", stateString);
args.shaderPath = "uber_shader.hlsl"; args.shaderPath = "uber_shader.hlsl";
args.targetProfile = "ps_6_0"; args.targetProfile = targetPS;
CompileShader(args, extraCount, extras); CompileShader(args, extraCount, extras);
} }
int main(int /*argc*/, const char** argv) const char* Canonicalize(const char* path)
{ {
char dirPath[MAX_PATH]; static char canonPath[MAX_PATH];
strcpy(dirPath, argv[0]);
int l = strlen(dirPath);
while(l-- > 0)
{
if(dirPath[l] == '/' || dirPath[l] == '\\')
{
dirPath[l] = '\0';
break;
}
}
SetCurrentDirectoryA(dirPath);
system("del *.h"); PathCanonicalizeA(canonPath, path);
return canonPath;
}
void InitDirectory(const char* dirName)
{
const char* rendererPath = va("%s\\code\\renderer", repoPath);
const char* cd = Canonicalize(va("%s\\shaders\\%s", rendererPath, dirName));
SetCurrentDirectoryA(cd);
const char* out = Canonicalize(va("%s\\compshaders\\%s", rendererPath, dirName));
strcpy(outputPath, out);
CreateDirectoryA(outputPath, NULL);
system(va("del %s\\*.h", outputPath));
system(va("del %s\\*.temp", outputPath));
}
void ProcessGRP()
{
InitDirectory("grp");
targetVS = "vs_6_0";
targetPS = "ps_6_0";
targetCS = "cs_6_0";
CompileVSAndPS("post_gamma", "post_gamma.hlsl"); CompileVSAndPS("post_gamma", "post_gamma.hlsl");
CompileVSAndPS("post_inverse_gamma", "post_inverse_gamma.hlsl"); CompileVSAndPS("post_inverse_gamma", "post_inverse_gamma.hlsl");
@ -249,25 +298,91 @@ int main(int /*argc*/, const char** argv)
CompileCS("mip_3_cs.h", "mip_3.hlsl"); CompileCS("mip_3_cs.h", "mip_3.hlsl");
CompileSMAAShaders(); CompileSMAAShaders();
system("type smaa*.h > complete_smaa.h"); system(va("type %s\\smaa*.h > %s\\complete_smaa.h", outputPath, outputPath));
system("type shared.hlsli uber_shader.hlsl > uber_shader.temp"); // combines both files into one // type combines all files into one
system("..\\..\\..\\tools\\bin2header.exe --output uber_shader.h --hname uber_shader_string uber_shader.temp"); system(va("type ..\\common\\state_bits.h.hlsli ..\\common\\blend.hlsli shared.hlsli uber_shader.hlsl > %s\\uber_shader.temp", outputPath));
system("del uber_shader.temp"); system(va("%s --output %s\\uber_shader.h --hname uber_shader_string %s\\uber_shader.temp", bin2headerPath, outputPath, outputPath));
system(va("del %s\\uber_shader.temp", outputPath));
for(int i = 0; i < 8; ++i) for(int i = 0; i < 8; ++i)
{ {
CompileUberVS(va("uber_shader_vs_%i.h", i + 1), "uber_shader.hlsl", i + 1); CompileUberVS(va("uber_shader_vs_%i.h", i + 1), "uber_shader.hlsl", i + 1);
} }
system("type uber_shader_vs_*.h > complete_uber_vs.h"); system(va("type %s\\uber_shader_vs_*.h > %s\\complete_uber_vs.h", outputPath, outputPath));
system("del uber_shader_vs_*.h"); system(va("del %s\\uber_shader_vs_*.h", outputPath));
for(int i = 0; i < _countof(uberShaderPixelStates); ++i) for(int i = 0; i < _countof(uberShaderPixelStates); ++i)
{ {
CompileUberPS(uberShaderPixelStates[i]); CompileUberPS(uberShaderPixelStates[i]);
} }
system("type uber_shader_ps_*.h > complete_uber_ps.h"); system(va("type %s\\uber_shader_ps_*.h > %s\\complete_uber_ps.h", outputPath, outputPath));
system("del uber_shader_ps_*.h"); system(va("del %s\\uber_shader_ps_*.h", outputPath));
}
void ProcessCRP()
{
InitDirectory("crp");
targetVS = "vs_6_6";
targetPS = "ps_6_6";
targetCS = "cs_6_6";
CompileVSAndPS("blit", "blit.hlsl");
CompileVSAndPS("ui", "ui.hlsl");
CompileVSAndPS("imgui", "imgui.hlsl");
CompileVSAndPS("nuklear", "nuklear.hlsl");
CompileCS("mip_1_cs.h", "mip_1.hlsl");
CompileCS("mip_2_cs.h", "mip_2.hlsl");
CompileCS("mip_3_cs.h", "mip_3.hlsl");
CompileVSAndPS("opaque", "opaque.hlsl");
CompileVSAndPS("transp_draw", "transp_draw.hlsl");
CompileVSAndPS("transp_resolve", "transp_resolve.hlsl");
CompileVSAndPS("tone_map", "tone_map.hlsl");
CompileVSAndPS("tone_map_inverse", "tone_map_inverse.hlsl");
CompileVSAndPS("accumdof_accum", "accumdof_accum.hlsl");
CompileVSAndPS("accumdof_norm", "accumdof_norm.hlsl");
CompileVSAndPS("accumdof_debug", "accumdof_debug.hlsl");
CompileCS("gatherdof_split.h", "gatherdof_split.hlsl");
CompileCS("gatherdof_coc_tile_gen.h", "gatherdof_coc_tile_gen.hlsl");
CompileCS("gatherdof_coc_tile_max.h", "gatherdof_coc_tile_max.hlsl");
CompileCS("gatherdof_blur.h", "gatherdof_blur.hlsl");
CompileCS("gatherdof_fill.h", "gatherdof_fill.hlsl");
CompileVSAndPS("gatherdof_combine", "gatherdof_combine.hlsl");
CompileVSAndPS("gatherdof_debug", "gatherdof_debug.hlsl");
CompileVSAndPS("fog_inside", "fog_inside.hlsl");
CompileVSAndPS("fog_outside", "fog_outside.hlsl");
}
int main(int /*argc*/, const char** argv)
{
char dirPath[MAX_PATH];
strcpy(dirPath, argv[0]);
int l = strlen(dirPath);
while(l-- > 0)
{
if(dirPath[l] == '/' || dirPath[l] == '\\')
{
dirPath[l] = '\0';
break;
}
}
strcpy(repoPath, Canonicalize(va("%s\\..\\..", dirPath)));
strcpy(bin2headerPath, Canonicalize(va("%s\\tools\\bin2header.exe", repoPath)));
char* path = getenv("DXCPATH");
if(path != NULL)
{
strcpy(dxcPath, path);
}
else
{
strcpy(dxcPath, "dxc.exe");
}
system(va("%s --version", dxcPath));
ProcessGRP();
ProcessCRP();
return 0; return 0;
} }

View file

@ -1,5 +0,0 @@
@echo off
set fxc="%FXCPATH%"
if "%FXCPATH%"=="" if not "%DXSDK_DIR%"=="" set fxc="%DXSDK_DIR%Utilities\\bin\\x86\\fxc.exe"
if "%FXCPATH%"=="" if "%DXSDK_DIR%"=="" set fxc="C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.20348.0\\x64\\fxc.exe"
%fxc% %*

View file

@ -640,17 +640,9 @@ solution "cnq3"
kind "ConsoleApp" kind "ConsoleApp"
language "C++" language "C++"
AddSourcesAndHeaders("shadercomp") AddSourcesAndHeaders("shadercomp")
postbuildcommands { string.format("{copyfile} \"%%{cfg.buildtarget.directory}/%%{cfg.buildtarget.basename}.exe\" \"%s/renderer/hlsl\"", make_path_src) } postbuildcommands { "$(TargetPath)" }
postbuildcommands { string.format("{copyfile} \"%%{cfg.buildtarget.directory}/%%{cfg.buildtarget.basename}.pdb\" \"%s/renderer/hlsl\"", make_path_src) }
postbuildcommands { string.format("\"%s/renderer/hlsl/%%{cfg.buildtarget.name}\"", make_path_src) }
ApplyProjectSettings(true) ApplyProjectSettings(true)
--[[ links { "Shlwapi" }
VC++ STILL requires absolute paths for these... maybe it will be fixed a few decades after I'm in the grave
local debug_path_dir = string.format("%s/renderer/hlsl", make_path_src)
local debug_path_exe = string.format("%s/%%{cfg.buildtarget.name}", debug_path_dir)
debugdir(debug_path_dir)
debugcommand(debug_path_exe)
--]]
project "renderer" project "renderer"
@ -662,8 +654,8 @@ solution "cnq3"
includedirs { "/usr/local/include" } includedirs { "/usr/local/include" }
end end
if os.istarget("windows") then if os.istarget("windows") then
files { string.format("%s/renderer/hlsl/*.hlsl", path_src) } files { string.format("%s/renderer/shaders/**.hlsl", path_src) }
files { string.format("%s/renderer/hlsl/*.hlsli", path_src) } files { string.format("%s/renderer/shaders/**.hlsli", path_src) }
filter "files:**.hlsl" filter "files:**.hlsl"
flags { "ExcludeFromBuild" } flags { "ExcludeFromBuild" }
filter { } filter { }
@ -686,6 +678,8 @@ solution "cnq3"
if os.istarget("windows") then if os.istarget("windows") then
includedirs { path_src.."/imgui" } includedirs { path_src.."/imgui" }
libdirs { path_src.."/nvapi" } libdirs { path_src.."/nvapi" }
files { string.format("premake5.lua", path_make) }
vpaths { ["*"] = "../code/" } -- don't allow "code" to be the parent filter
end end
if os.istarget("bsd") then if os.istarget("bsd") then
includedirs { "/usr/local/include" } includedirs { "/usr/local/include" }

View file

@ -199,16 +199,18 @@ copy "..\..\.bin\release\cnq3.pdb" "$(QUAKE3DIR)"</Command>
<ClInclude Include="..\..\code\qcommon\vm_local.h" /> <ClInclude Include="..\..\code\qcommon\vm_local.h" />
<ClInclude Include="..\..\code\qcommon\vm_shim.h" /> <ClInclude Include="..\..\code\qcommon\vm_shim.h" />
<ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h" /> <ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h" />
<ClInclude Include="..\..\code\renderer\crp_local.h" />
<ClInclude Include="..\..\code\renderer\grp_local.h" /> <ClInclude Include="..\..\code\renderer\grp_local.h" />
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
<ClInclude Include="..\..\code\renderer\rhi_local.h" /> <ClInclude Include="..\..\code\renderer\rhi_local.h" />
<ClInclude Include="..\..\code\renderer\rhi_public.h" /> <ClInclude Include="..\..\code\renderer\rhi_public.h" />
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" /> <ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" /> <ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
<ClInclude Include="..\..\code\renderer\srp_local.h" />
<ClInclude Include="..\..\code\renderer\stb_image.h" /> <ClInclude Include="..\..\code\renderer\stb_image.h" />
<ClInclude Include="..\..\code\renderer\tr_help.h" /> <ClInclude Include="..\..\code\renderer\tr_help.h" />
<ClInclude Include="..\..\code\renderer\tr_local.h" /> <ClInclude Include="..\..\code\renderer\tr_local.h" />
<ClInclude Include="..\..\code\renderer\tr_public.h" /> <ClInclude Include="..\..\code\renderer\tr_public.h" />
<ClInclude Include="..\..\code\renderer\uber_shaders.h" />
<ClInclude Include="..\..\code\server\server.h" /> <ClInclude Include="..\..\code\server\server.h" />
<ClInclude Include="..\..\code\win32\resource.h" /> <ClInclude Include="..\..\code\win32\resource.h" />
<ClInclude Include="..\..\code\win32\win_help.h" /> <ClInclude Include="..\..\code\win32\win_help.h" />
@ -288,6 +290,9 @@ copy "..\..\.bin\release\cnq3.pdb" "$(QUAKE3DIR)"</Command>
<ClCompile Include="..\..\code\win32\win_syscon.cpp" /> <ClCompile Include="..\..\code\win32\win_syscon.cpp" />
<ClCompile Include="..\..\code\win32\win_wndproc.cpp" /> <ClCompile Include="..\..\code\win32\win_wndproc.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="..\premake5.lua" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="botlib.vcxproj"> <ProjectReference Include="botlib.vcxproj">
<Project>{A1A792F4-8D49-BDCA-7604-D11E6245441B}</Project> <Project>{A1A792F4-8D49-BDCA-7604-D11E6245441B}</Project>

View file

@ -243,9 +243,15 @@
<ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h"> <ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h">
<Filter>renderer</Filter> <Filter>renderer</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\code\renderer\crp_local.h">
<Filter>renderer</Filter>
</ClInclude>
<ClInclude Include="..\..\code\renderer\grp_local.h"> <ClInclude Include="..\..\code\renderer\grp_local.h">
<Filter>renderer</Filter> <Filter>renderer</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h">
<Filter>renderer</Filter>
</ClInclude>
<ClInclude Include="..\..\code\renderer\rhi_local.h"> <ClInclude Include="..\..\code\renderer\rhi_local.h">
<Filter>renderer</Filter> <Filter>renderer</Filter>
</ClInclude> </ClInclude>
@ -258,6 +264,9 @@
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h"> <ClInclude Include="..\..\code\renderer\smaa_search_texture.h">
<Filter>renderer</Filter> <Filter>renderer</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\code\renderer\srp_local.h">
<Filter>renderer</Filter>
</ClInclude>
<ClInclude Include="..\..\code\renderer\stb_image.h"> <ClInclude Include="..\..\code\renderer\stb_image.h">
<Filter>renderer</Filter> <Filter>renderer</Filter>
</ClInclude> </ClInclude>
@ -270,9 +279,6 @@
<ClInclude Include="..\..\code\renderer\tr_public.h"> <ClInclude Include="..\..\code\renderer\tr_public.h">
<Filter>renderer</Filter> <Filter>renderer</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\code\renderer\uber_shaders.h">
<Filter>renderer</Filter>
</ClInclude>
<ClInclude Include="..\..\code\server\server.h"> <ClInclude Include="..\..\code\server\server.h">
<Filter>server</Filter> <Filter>server</Filter>
</ClInclude> </ClInclude>
@ -504,4 +510,7 @@
<Filter>win32</Filter> <Filter>win32</Filter>
</ClCompile> </ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="..\premake5.lua" />
</ItemGroup>
</Project> </Project>

View file

@ -112,28 +112,41 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h" /> <ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h" />
<ClInclude Include="..\..\code\renderer\crp_local.h" />
<ClInclude Include="..\..\code\renderer\grp_local.h" /> <ClInclude Include="..\..\code\renderer\grp_local.h" />
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
<ClInclude Include="..\..\code\renderer\rhi_local.h" /> <ClInclude Include="..\..\code\renderer\rhi_local.h" />
<ClInclude Include="..\..\code\renderer\rhi_public.h" /> <ClInclude Include="..\..\code\renderer\rhi_public.h" />
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" /> <ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" /> <ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
<ClInclude Include="..\..\code\renderer\srp_local.h" />
<ClInclude Include="..\..\code\renderer\stb_image.h" /> <ClInclude Include="..\..\code\renderer\stb_image.h" />
<ClInclude Include="..\..\code\renderer\tr_help.h" /> <ClInclude Include="..\..\code\renderer\tr_help.h" />
<ClInclude Include="..\..\code\renderer\tr_local.h" /> <ClInclude Include="..\..\code\renderer\tr_local.h" />
<ClInclude Include="..\..\code\renderer\tr_public.h" /> <ClInclude Include="..\..\code\renderer\tr_public.h" />
<ClInclude Include="..\..\code\renderer\uber_shaders.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\code\renderer\D3D12MemAlloc.cpp" /> <ClCompile Include="..\..\code\renderer\D3D12MemAlloc.cpp" />
<ClCompile Include="..\..\code\renderer\grp_imgui.cpp" /> <ClCompile Include="..\..\code\renderer\crp_dof_accum.cpp" />
<ClCompile Include="..\..\code\renderer\crp_dof_gather.cpp" />
<ClCompile Include="..\..\code\renderer\crp_fog.cpp" />
<ClCompile Include="..\..\code\renderer\crp_geometry.cpp" />
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
<ClCompile Include="..\..\code\renderer\crp_tone_map.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_draw.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_resolve.cpp" />
<ClCompile Include="..\..\code\renderer\grp_geometry.cpp" />
<ClCompile Include="..\..\code\renderer\grp_main.cpp" /> <ClCompile Include="..\..\code\renderer\grp_main.cpp" />
<ClCompile Include="..\..\code\renderer\grp_mip_gen.cpp" />
<ClCompile Include="..\..\code\renderer\grp_nuklear.cpp" />
<ClCompile Include="..\..\code\renderer\grp_post.cpp" /> <ClCompile Include="..\..\code\renderer\grp_post.cpp" />
<ClCompile Include="..\..\code\renderer\grp_smaa.cpp" /> <ClCompile Include="..\..\code\renderer\grp_smaa.cpp" />
<ClCompile Include="..\..\code\renderer\grp_ui.cpp" />
<ClCompile Include="..\..\code\renderer\grp_world.cpp" /> <ClCompile Include="..\..\code\renderer\grp_world.cpp" />
<ClCompile Include="..\..\code\renderer\rhi_d3d12.cpp" /> <ClCompile Include="..\..\code\renderer\rhi_d3d12.cpp" />
<ClCompile Include="..\..\code\renderer\srp_imgui.cpp" />
<ClCompile Include="..\..\code\renderer\srp_main.cpp" />
<ClCompile Include="..\..\code\renderer\srp_mip_gen.cpp" />
<ClCompile Include="..\..\code\renderer\srp_nuklear.cpp" />
<ClCompile Include="..\..\code\renderer\srp_ui.cpp" />
<ClCompile Include="..\..\code\renderer\stb_image.cpp" /> <ClCompile Include="..\..\code\renderer\stb_image.cpp" />
<ClCompile Include="..\..\code\renderer\tr_backend.cpp" /> <ClCompile Include="..\..\code\renderer\tr_backend.cpp" />
<ClCompile Include="..\..\code\renderer\tr_bsp.cpp" /> <ClCompile Include="..\..\code\renderer\tr_bsp.cpp" />
@ -159,59 +172,141 @@
<ClCompile Include="..\..\code\renderer\tr_world.cpp" /> <ClCompile Include="..\..\code\renderer\tr_world.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<FxCompile Include="..\..\code\renderer\hlsl\depth_pre_pass.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\accumdof_accum.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\dynamic_light.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\accumdof_debug.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\fog_inside.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\accumdof_norm.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\fog_outside.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\blit.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\imgui.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\fog_inside.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\mip_1.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\fog_outside.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\mip_2.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_blur.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\mip_3.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_coc_tile_gen.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\nuklear.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_coc_tile_max.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\post_gamma.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_combine.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\post_inverse_gamma.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_debug.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\smaa_1.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_fill.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\smaa_2.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_split.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\smaa_3.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\imgui.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\uber_shader.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\mip_1.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\ui.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\mip_2.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mip_3.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\nuklear.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map_inverse.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\transp_draw.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\transp_resolve.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\ui.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\depth_pre_pass.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\dynamic_light.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\fog_inside.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\fog_outside.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\imgui.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\mip_1.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\mip_2.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\mip_3.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\nuklear.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\post_gamma.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\post_inverse_gamma.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\smaa_1.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\smaa_2.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\smaa_3.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\uber_shader.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\ui.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\code\renderer\hlsl\fog.hlsli" /> <None Include="..\..\code\renderer\shaders\common\blend.hlsli" />
<None Include="..\..\code\renderer\hlsl\shared.hlsli" /> <None Include="..\..\code\renderer\shaders\common\mip_gen.hlsli" />
<None Include="..\..\code\renderer\hlsl\smaa.hlsli" /> <None Include="..\..\code\renderer\shaders\common\state_bits.h.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\common.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\dof.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\fog.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\gatherdof.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\oit.h.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\world.h.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\world.hlsli" />
<None Include="..\..\code\renderer\shaders\grp\fog.hlsli" />
<None Include="..\..\code\renderer\shaders\grp\shared.hlsli" />
<None Include="..\..\code\renderer\shaders\grp\smaa.hlsli" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View file

@ -1,34 +1,56 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<Filter Include="hlsl"> <Filter Include="shaders">
<UniqueIdentifier>{98F1977C-8428-990D-2D15-7F10192B150F}</UniqueIdentifier> <UniqueIdentifier>{0F45D591-7B24-542D-843C-DF03F09ABA8E}</UniqueIdentifier>
</Filter>
<Filter Include="shaders\common">
<UniqueIdentifier>{873F6737-730D-5B3D-5CA1-CB63480E37A2}</UniqueIdentifier>
</Filter>
<Filter Include="shaders\crp">
<UniqueIdentifier>{A3AE1A56-0F64-934B-9858-8D22040D8A4F}</UniqueIdentifier>
</Filter>
<Filter Include="shaders\grp">
<UniqueIdentifier>{A7BF1A56-1375-934B-9C69-8D22081E8A4F}</UniqueIdentifier>
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h" /> <ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h" />
<ClInclude Include="..\..\code\renderer\crp_local.h" />
<ClInclude Include="..\..\code\renderer\grp_local.h" /> <ClInclude Include="..\..\code\renderer\grp_local.h" />
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
<ClInclude Include="..\..\code\renderer\rhi_local.h" /> <ClInclude Include="..\..\code\renderer\rhi_local.h" />
<ClInclude Include="..\..\code\renderer\rhi_public.h" /> <ClInclude Include="..\..\code\renderer\rhi_public.h" />
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" /> <ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" /> <ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
<ClInclude Include="..\..\code\renderer\srp_local.h" />
<ClInclude Include="..\..\code\renderer\stb_image.h" /> <ClInclude Include="..\..\code\renderer\stb_image.h" />
<ClInclude Include="..\..\code\renderer\tr_help.h" /> <ClInclude Include="..\..\code\renderer\tr_help.h" />
<ClInclude Include="..\..\code\renderer\tr_local.h" /> <ClInclude Include="..\..\code\renderer\tr_local.h" />
<ClInclude Include="..\..\code\renderer\tr_public.h" /> <ClInclude Include="..\..\code\renderer\tr_public.h" />
<ClInclude Include="..\..\code\renderer\uber_shaders.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\code\renderer\D3D12MemAlloc.cpp" /> <ClCompile Include="..\..\code\renderer\D3D12MemAlloc.cpp" />
<ClCompile Include="..\..\code\renderer\grp_imgui.cpp" /> <ClCompile Include="..\..\code\renderer\crp_dof_accum.cpp" />
<ClCompile Include="..\..\code\renderer\crp_dof_gather.cpp" />
<ClCompile Include="..\..\code\renderer\crp_fog.cpp" />
<ClCompile Include="..\..\code\renderer\crp_geometry.cpp" />
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
<ClCompile Include="..\..\code\renderer\crp_tone_map.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_draw.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_resolve.cpp" />
<ClCompile Include="..\..\code\renderer\grp_geometry.cpp" />
<ClCompile Include="..\..\code\renderer\grp_main.cpp" /> <ClCompile Include="..\..\code\renderer\grp_main.cpp" />
<ClCompile Include="..\..\code\renderer\grp_mip_gen.cpp" />
<ClCompile Include="..\..\code\renderer\grp_nuklear.cpp" />
<ClCompile Include="..\..\code\renderer\grp_post.cpp" /> <ClCompile Include="..\..\code\renderer\grp_post.cpp" />
<ClCompile Include="..\..\code\renderer\grp_smaa.cpp" /> <ClCompile Include="..\..\code\renderer\grp_smaa.cpp" />
<ClCompile Include="..\..\code\renderer\grp_ui.cpp" />
<ClCompile Include="..\..\code\renderer\grp_world.cpp" /> <ClCompile Include="..\..\code\renderer\grp_world.cpp" />
<ClCompile Include="..\..\code\renderer\rhi_d3d12.cpp" /> <ClCompile Include="..\..\code\renderer\rhi_d3d12.cpp" />
<ClCompile Include="..\..\code\renderer\srp_imgui.cpp" />
<ClCompile Include="..\..\code\renderer\srp_main.cpp" />
<ClCompile Include="..\..\code\renderer\srp_mip_gen.cpp" />
<ClCompile Include="..\..\code\renderer\srp_nuklear.cpp" />
<ClCompile Include="..\..\code\renderer\srp_ui.cpp" />
<ClCompile Include="..\..\code\renderer\stb_image.cpp" /> <ClCompile Include="..\..\code\renderer\stb_image.cpp" />
<ClCompile Include="..\..\code\renderer\tr_backend.cpp" /> <ClCompile Include="..\..\code\renderer\tr_backend.cpp" />
<ClCompile Include="..\..\code\renderer\tr_bsp.cpp" /> <ClCompile Include="..\..\code\renderer\tr_bsp.cpp" />
@ -54,64 +76,166 @@
<ClCompile Include="..\..\code\renderer\tr_world.cpp" /> <ClCompile Include="..\..\code\renderer\tr_world.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<FxCompile Include="..\..\code\renderer\hlsl\depth_pre_pass.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\accumdof_accum.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\dynamic_light.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\accumdof_debug.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\fog_inside.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\accumdof_norm.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\fog_outside.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\blit.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\imgui.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\fog_inside.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\mip_1.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\fog_outside.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\mip_2.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_blur.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\mip_3.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_coc_tile_gen.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\nuklear.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_coc_tile_max.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\post_gamma.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_combine.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\post_inverse_gamma.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_debug.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\smaa_1.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_fill.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\smaa_2.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_split.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\smaa_3.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\imgui.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\uber_shader.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\mip_1.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\ui.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\mip_2.hlsl">
<Filter>hlsl</Filter> <Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mip_3.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\nuklear.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map_inverse.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\transp_draw.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\transp_resolve.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\ui.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\depth_pre_pass.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\dynamic_light.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\fog_inside.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\fog_outside.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\imgui.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\mip_1.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\mip_2.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\mip_3.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\nuklear.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\post_gamma.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\post_inverse_gamma.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\smaa_1.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\smaa_2.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\smaa_3.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\uber_shader.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\ui.hlsl">
<Filter>shaders\grp</Filter>
</FxCompile> </FxCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\code\renderer\hlsl\fog.hlsli"> <None Include="..\..\code\renderer\shaders\common\blend.hlsli">
<Filter>hlsl</Filter> <Filter>shaders\common</Filter>
</None> </None>
<None Include="..\..\code\renderer\hlsl\shared.hlsli"> <None Include="..\..\code\renderer\shaders\common\mip_gen.hlsli">
<Filter>hlsl</Filter> <Filter>shaders\common</Filter>
</None> </None>
<None Include="..\..\code\renderer\hlsl\smaa.hlsli"> <None Include="..\..\code\renderer\shaders\common\state_bits.h.hlsli">
<Filter>hlsl</Filter> <Filter>shaders\common</Filter>
</None>
<None Include="..\..\code\renderer\shaders\crp\common.hlsli">
<Filter>shaders\crp</Filter>
</None>
<None Include="..\..\code\renderer\shaders\crp\dof.hlsli">
<Filter>shaders\crp</Filter>
</None>
<None Include="..\..\code\renderer\shaders\crp\fog.hlsli">
<Filter>shaders\crp</Filter>
</None>
<None Include="..\..\code\renderer\shaders\crp\gatherdof.hlsli">
<Filter>shaders\crp</Filter>
</None>
<None Include="..\..\code\renderer\shaders\crp\oit.h.hlsli">
<Filter>shaders\crp</Filter>
</None>
<None Include="..\..\code\renderer\shaders\crp\world.h.hlsli">
<Filter>shaders\crp</Filter>
</None>
<None Include="..\..\code\renderer\shaders\crp\world.hlsli">
<Filter>shaders\crp</Filter>
</None>
<None Include="..\..\code\renderer\shaders\grp\fog.hlsli">
<Filter>shaders\grp</Filter>
</None>
<None Include="..\..\code\renderer\shaders\grp\shared.hlsli">
<Filter>shaders\grp</Filter>
</None>
<None Include="..\..\code\renderer\shaders\grp\smaa.hlsli">
<Filter>shaders\grp</Filter>
</None> </None>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -72,13 +72,12 @@
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\.build\debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>..\..\.build\debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions> %(AdditionalOptions)</AdditionalOptions> <AdditionalOptions> %(AdditionalOptions)</AdditionalOptions>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command>copy /B /Y "$(TargetDir)\$(TargetName).exe" "..\..\code\renderer\hlsl" <Command>$(TargetPath)</Command>
copy /B /Y "$(TargetDir)\$(TargetName).pdb" "..\..\code\renderer\hlsl"
"../../code/renderer/hlsl/$(TargetFileName)"</Command>
</PostBuildEvent> </PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release|x64'">
@ -107,13 +106,12 @@ copy /B /Y "$(TargetDir)\$(TargetName).pdb" "..\..\code\renderer\hlsl"
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\.build\release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>..\..\.build\release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions> %(AdditionalOptions)</AdditionalOptions> <AdditionalOptions> %(AdditionalOptions)</AdditionalOptions>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command>copy /B /Y "$(TargetDir)\$(TargetName).exe" "..\..\code\renderer\hlsl" <Command>$(TargetPath)</Command>
copy /B /Y "$(TargetDir)\$(TargetName).pdb" "..\..\code\renderer\hlsl"
"../../code/renderer/hlsl/$(TargetFileName)"</Command>
</PostBuildEvent> </PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>

View file

@ -201,16 +201,18 @@ copy "..\..\.bin\release\cnq3.pdb" "$(QUAKE3DIR)"</Command>
<ClInclude Include="..\..\code\qcommon\vm_local.h" /> <ClInclude Include="..\..\code\qcommon\vm_local.h" />
<ClInclude Include="..\..\code\qcommon\vm_shim.h" /> <ClInclude Include="..\..\code\qcommon\vm_shim.h" />
<ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h" /> <ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h" />
<ClInclude Include="..\..\code\renderer\crp_local.h" />
<ClInclude Include="..\..\code\renderer\grp_local.h" /> <ClInclude Include="..\..\code\renderer\grp_local.h" />
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
<ClInclude Include="..\..\code\renderer\rhi_local.h" /> <ClInclude Include="..\..\code\renderer\rhi_local.h" />
<ClInclude Include="..\..\code\renderer\rhi_public.h" /> <ClInclude Include="..\..\code\renderer\rhi_public.h" />
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" /> <ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" /> <ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
<ClInclude Include="..\..\code\renderer\srp_local.h" />
<ClInclude Include="..\..\code\renderer\stb_image.h" /> <ClInclude Include="..\..\code\renderer\stb_image.h" />
<ClInclude Include="..\..\code\renderer\tr_help.h" /> <ClInclude Include="..\..\code\renderer\tr_help.h" />
<ClInclude Include="..\..\code\renderer\tr_local.h" /> <ClInclude Include="..\..\code\renderer\tr_local.h" />
<ClInclude Include="..\..\code\renderer\tr_public.h" /> <ClInclude Include="..\..\code\renderer\tr_public.h" />
<ClInclude Include="..\..\code\renderer\uber_shaders.h" />
<ClInclude Include="..\..\code\server\server.h" /> <ClInclude Include="..\..\code\server\server.h" />
<ClInclude Include="..\..\code\win32\resource.h" /> <ClInclude Include="..\..\code\win32\resource.h" />
<ClInclude Include="..\..\code\win32\win_help.h" /> <ClInclude Include="..\..\code\win32\win_help.h" />
@ -290,6 +292,9 @@ copy "..\..\.bin\release\cnq3.pdb" "$(QUAKE3DIR)"</Command>
<ClCompile Include="..\..\code\win32\win_syscon.cpp" /> <ClCompile Include="..\..\code\win32\win_syscon.cpp" />
<ClCompile Include="..\..\code\win32\win_wndproc.cpp" /> <ClCompile Include="..\..\code\win32\win_wndproc.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="..\premake5.lua" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="botlib.vcxproj"> <ProjectReference Include="botlib.vcxproj">
<Project>{A1A792F4-8D49-BDCA-7604-D11E6245441B}</Project> <Project>{A1A792F4-8D49-BDCA-7604-D11E6245441B}</Project>

View file

@ -243,9 +243,15 @@
<ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h"> <ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h">
<Filter>renderer</Filter> <Filter>renderer</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\code\renderer\crp_local.h">
<Filter>renderer</Filter>
</ClInclude>
<ClInclude Include="..\..\code\renderer\grp_local.h"> <ClInclude Include="..\..\code\renderer\grp_local.h">
<Filter>renderer</Filter> <Filter>renderer</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h">
<Filter>renderer</Filter>
</ClInclude>
<ClInclude Include="..\..\code\renderer\rhi_local.h"> <ClInclude Include="..\..\code\renderer\rhi_local.h">
<Filter>renderer</Filter> <Filter>renderer</Filter>
</ClInclude> </ClInclude>
@ -258,6 +264,9 @@
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h"> <ClInclude Include="..\..\code\renderer\smaa_search_texture.h">
<Filter>renderer</Filter> <Filter>renderer</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\code\renderer\srp_local.h">
<Filter>renderer</Filter>
</ClInclude>
<ClInclude Include="..\..\code\renderer\stb_image.h"> <ClInclude Include="..\..\code\renderer\stb_image.h">
<Filter>renderer</Filter> <Filter>renderer</Filter>
</ClInclude> </ClInclude>
@ -270,9 +279,6 @@
<ClInclude Include="..\..\code\renderer\tr_public.h"> <ClInclude Include="..\..\code\renderer\tr_public.h">
<Filter>renderer</Filter> <Filter>renderer</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\code\renderer\uber_shaders.h">
<Filter>renderer</Filter>
</ClInclude>
<ClInclude Include="..\..\code\server\server.h"> <ClInclude Include="..\..\code\server\server.h">
<Filter>server</Filter> <Filter>server</Filter>
</ClInclude> </ClInclude>
@ -504,4 +510,7 @@
<Filter>win32</Filter> <Filter>win32</Filter>
</ClCompile> </ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="..\premake5.lua" />
</ItemGroup>
</Project> </Project>

View file

@ -114,28 +114,41 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h" /> <ClInclude Include="..\..\code\renderer\D3D12MemAlloc.h" />
<ClInclude Include="..\..\code\renderer\crp_local.h" />
<ClInclude Include="..\..\code\renderer\grp_local.h" /> <ClInclude Include="..\..\code\renderer\grp_local.h" />
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
<ClInclude Include="..\..\code\renderer\rhi_local.h" /> <ClInclude Include="..\..\code\renderer\rhi_local.h" />
<ClInclude Include="..\..\code\renderer\rhi_public.h" /> <ClInclude Include="..\..\code\renderer\rhi_public.h" />
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" /> <ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" /> <ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
<ClInclude Include="..\..\code\renderer\srp_local.h" />
<ClInclude Include="..\..\code\renderer\stb_image.h" /> <ClInclude Include="..\..\code\renderer\stb_image.h" />
<ClInclude Include="..\..\code\renderer\tr_help.h" /> <ClInclude Include="..\..\code\renderer\tr_help.h" />
<ClInclude Include="..\..\code\renderer\tr_local.h" /> <ClInclude Include="..\..\code\renderer\tr_local.h" />
<ClInclude Include="..\..\code\renderer\tr_public.h" /> <ClInclude Include="..\..\code\renderer\tr_public.h" />
<ClInclude Include="..\..\code\renderer\uber_shaders.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\code\renderer\D3D12MemAlloc.cpp" /> <ClCompile Include="..\..\code\renderer\D3D12MemAlloc.cpp" />
<ClCompile Include="..\..\code\renderer\grp_imgui.cpp" /> <ClCompile Include="..\..\code\renderer\crp_dof_accum.cpp" />
<ClCompile Include="..\..\code\renderer\crp_dof_gather.cpp" />
<ClCompile Include="..\..\code\renderer\crp_fog.cpp" />
<ClCompile Include="..\..\code\renderer\crp_geometry.cpp" />
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
<ClCompile Include="..\..\code\renderer\crp_tone_map.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_draw.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_resolve.cpp" />
<ClCompile Include="..\..\code\renderer\grp_geometry.cpp" />
<ClCompile Include="..\..\code\renderer\grp_main.cpp" /> <ClCompile Include="..\..\code\renderer\grp_main.cpp" />
<ClCompile Include="..\..\code\renderer\grp_mip_gen.cpp" />
<ClCompile Include="..\..\code\renderer\grp_nuklear.cpp" />
<ClCompile Include="..\..\code\renderer\grp_post.cpp" /> <ClCompile Include="..\..\code\renderer\grp_post.cpp" />
<ClCompile Include="..\..\code\renderer\grp_smaa.cpp" /> <ClCompile Include="..\..\code\renderer\grp_smaa.cpp" />
<ClCompile Include="..\..\code\renderer\grp_ui.cpp" />
<ClCompile Include="..\..\code\renderer\grp_world.cpp" /> <ClCompile Include="..\..\code\renderer\grp_world.cpp" />
<ClCompile Include="..\..\code\renderer\rhi_d3d12.cpp" /> <ClCompile Include="..\..\code\renderer\rhi_d3d12.cpp" />
<ClCompile Include="..\..\code\renderer\srp_imgui.cpp" />
<ClCompile Include="..\..\code\renderer\srp_main.cpp" />
<ClCompile Include="..\..\code\renderer\srp_mip_gen.cpp" />
<ClCompile Include="..\..\code\renderer\srp_nuklear.cpp" />
<ClCompile Include="..\..\code\renderer\srp_ui.cpp" />
<ClCompile Include="..\..\code\renderer\stb_image.cpp" /> <ClCompile Include="..\..\code\renderer\stb_image.cpp" />
<ClCompile Include="..\..\code\renderer\tr_backend.cpp" /> <ClCompile Include="..\..\code\renderer\tr_backend.cpp" />
<ClCompile Include="..\..\code\renderer\tr_bsp.cpp" /> <ClCompile Include="..\..\code\renderer\tr_bsp.cpp" />
@ -161,59 +174,141 @@
<ClCompile Include="..\..\code\renderer\tr_world.cpp" /> <ClCompile Include="..\..\code\renderer\tr_world.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<FxCompile Include="..\..\code\renderer\hlsl\depth_pre_pass.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\accumdof_accum.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\dynamic_light.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\accumdof_debug.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\fog_inside.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\accumdof_norm.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\fog_outside.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\blit.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\imgui.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\fog_inside.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\mip_1.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\fog_outside.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\mip_2.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_blur.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\mip_3.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_coc_tile_gen.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\nuklear.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_coc_tile_max.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\post_gamma.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_combine.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\post_inverse_gamma.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_debug.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\smaa_1.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_fill.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\smaa_2.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\gatherdof_split.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\smaa_3.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\imgui.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\uber_shader.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\mip_1.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
<FxCompile Include="..\..\code\renderer\hlsl\ui.hlsl"> <FxCompile Include="..\..\code\renderer\shaders\crp\mip_2.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mip_3.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\nuklear.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map_inverse.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\transp_draw.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\transp_resolve.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\ui.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\depth_pre_pass.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\dynamic_light.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\fog_inside.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\fog_outside.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\imgui.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\mip_1.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\mip_2.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\mip_3.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\nuklear.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\post_gamma.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\post_inverse_gamma.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\smaa_1.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\smaa_2.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\smaa_3.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\uber_shader.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\grp\ui.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild> <ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile> </FxCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\code\renderer\hlsl\fog.hlsli" /> <None Include="..\..\code\renderer\shaders\common\blend.hlsli" />
<None Include="..\..\code\renderer\hlsl\shared.hlsli" /> <None Include="..\..\code\renderer\shaders\common\mip_gen.hlsli" />
<None Include="..\..\code\renderer\hlsl\smaa.hlsli" /> <None Include="..\..\code\renderer\shaders\common\state_bits.h.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\common.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\dof.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\fog.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\gatherdof.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\oit.h.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\world.h.hlsli" />
<None Include="..\..\code\renderer\shaders\crp\world.hlsli" />
<None Include="..\..\code\renderer\shaders\grp\fog.hlsli" />
<None Include="..\..\code\renderer\shaders\grp\shared.hlsli" />
<None Include="..\..\code\renderer\shaders\grp\smaa.hlsli" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

Some files were not shown because too many files have changed in this diff Show more