From 9e88365c59692732cdcc4fc41be52a2c8b91cdf9 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Tue, 26 Dec 2023 22:08:04 +0100 Subject: [PATCH 01/37] Added Commodore 64 post processing effect --- .../menus/MenuScreen_Shell_SystemOptions.cpp | 10 +- neo/renderer/RenderBackend.cpp | 13 +- neo/renderer/RenderCommon.h | 2 +- neo/renderer/RenderProgs.cpp | 1 + neo/renderer/RenderProgs.h | 6 + neo/renderer/RenderSystem_init.cpp | 2 +- neo/shaders/builtin/post/retro_c64.ps.hlsl | 157 ++++++++++++++++++ neo/shaders/builtin/post/retro_c64.vs.hlsl | 55 ++++++ neo/shaders/global_inc.hlsl | 44 ++++- neo/shaders/shaders.cfg | 2 + 10 files changed, 275 insertions(+), 17 deletions(-) create mode 100644 neo/shaders/builtin/post/retro_c64.ps.hlsl create mode 100644 neo/shaders/builtin/post/retro_c64.vs.hlsl diff --git a/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp b/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp index aac9eb91..bfc3bfa1 100755 --- a/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp +++ b/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp @@ -34,7 +34,7 @@ const static int NUM_SYSTEM_OPTIONS_OPTIONS = 8; extern idCVar r_graphicsAPI; extern idCVar r_antiAliasing; -extern idCVar r_useFilmicPostProcessing; +extern idCVar r_usePostProcessing; extern idCVar r_swapInterval; extern idCVar s_volume_dB; extern idCVar r_exposure; // RB: use this to control HDR exposure or brightness in LDR mode @@ -421,7 +421,7 @@ void idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings::LoadData //originalShadowMapping = r_useShadowMapping.GetInteger(); originalSSAO = r_useSSAO.GetInteger(); originalAmbientBrightness = r_forceAmbient.GetFloat(); - originalPostProcessing = r_useFilmicPostProcessing.GetInteger(); + originalPostProcessing = r_usePostProcessing.GetInteger(); // RB end const int fullscreen = r_fullscreen.GetInteger(); @@ -597,7 +597,7 @@ void idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings::AdjustFi { static const int numValues = 2; static const int values[numValues] = { 0, 1 }; - r_useFilmicPostProcessing.SetInteger( AdjustOption( r_useFilmicPostProcessing.GetInteger(), values, numValues, adjustAmount ) ); + r_usePostProcessing.SetInteger( AdjustOption( r_usePostProcessing.GetInteger(), values, numValues, adjustAmount ) ); break; } /* @@ -760,7 +760,7 @@ idSWFScriptVar idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings return values[ r_antiAliasing.GetInteger() ]; } case SYSTEM_FIELD_POSTFX: - if( r_useFilmicPostProcessing.GetInteger() > 0 ) + if( r_usePostProcessing.GetInteger() > 0 ) { return "#str_swf_enabled"; } @@ -848,7 +848,7 @@ bool idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings::IsDataCh return true; } - if( originalPostProcessing != r_useFilmicPostProcessing.GetInteger() ) + if( originalPostProcessing != r_usePostProcessing.GetInteger() ) { return true; } diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 60951145..28c66840 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -5979,7 +5979,7 @@ void idRenderBackend::PostProcess( const void* data ) // only do the post process step if resolution scaling is enabled. Prevents the unnecessary copying of the framebuffer and // corresponding full screen quad pass. /* - if( rs_enable.GetInteger() == 0 && !r_useFilmicPostProcessing.GetBool() && r_antiAliasing.GetInteger() == 0 ) + if( rs_enable.GetInteger() == 0 && !r_usePostProcessing.GetBool() && r_antiAliasing.GetInteger() == 0 ) { return; } @@ -6102,7 +6102,7 @@ void idRenderBackend::PostProcess( const void* data ) } #endif - if( r_useFilmicPostProcessing.GetBool() ) + if( r_usePostProcessing.GetInteger() > 0 ) { BlitParameters blitParms; blitParms.sourceTexture = ( nvrhi::ITexture* )globalImages->ldrImage->GetTextureID(); @@ -6119,7 +6119,14 @@ void idRenderBackend::PostProcess( const void* data ) GL_SelectTexture( 1 ); globalImages->blueNoiseImage256->Bind(); - renderProgManager.BindShader_PostProcess(); + if( r_usePostProcessing.GetInteger() == 2 ) + { + renderProgManager.BindShader_PostProcess_RetroC64(); + } + else + { + renderProgManager.BindShader_PostProcess(); + } float jitterTexOffset[4]; jitterTexOffset[0] = 1.0f / globalImages->blueNoiseImage256->GetUploadWidth(); diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index 3a9dbf21..ec2762fd 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -1259,7 +1259,7 @@ extern idCVar r_hdrDebug; extern idCVar r_ldrContrastThreshold; extern idCVar r_ldrContrastOffset; -extern idCVar r_useFilmicPostProcessing; +extern idCVar r_usePostProcessing; extern idCVar r_forceAmbient; extern idCVar r_useSSAO; diff --git a/neo/renderer/RenderProgs.cpp b/neo/renderer/RenderProgs.cpp index 2a70f606..5640debd 100644 --- a/neo/renderer/RenderProgs.cpp +++ b/neo/renderer/RenderProgs.cpp @@ -521,6 +521,7 @@ void idRenderProgManager::Init( nvrhi::IDevice* device ) { BUILTIN_SKYBOX, "builtin/legacy/skybox", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, { BUILTIN_WOBBLESKY, "builtin/legacy/wobblesky", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, { BUILTIN_POSTPROCESS, "builtin/post/postprocess", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, + { BUILTIN_POSTPROCESS_RETRO_C64, "builtin/post/retro_c64", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_SCREEN, "builtin/post/screen", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, { BUILTIN_TONEMAP, "builtin/post/tonemap", "", { { "BRIGHTPASS", "0" }, { "HDR_DEBUG", "0"} }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, diff --git a/neo/renderer/RenderProgs.h b/neo/renderer/RenderProgs.h index 4d94efa7..3df7bc87 100644 --- a/neo/renderer/RenderProgs.h +++ b/neo/renderer/RenderProgs.h @@ -365,6 +365,7 @@ enum BUILTIN_SKYBOX, BUILTIN_WOBBLESKY, BUILTIN_POSTPROCESS, + BUILTIN_POSTPROCESS_RETRO_C64, // RB begin BUILTIN_SCREEN, BUILTIN_TONEMAP, @@ -817,6 +818,11 @@ public: BindShader_Builtin( BUILTIN_POSTPROCESS ); } + void BindShader_PostProcess_RetroC64() + { + BindShader_Builtin( BUILTIN_POSTPROCESS_RETRO_C64 ); + } + void BindShader_Screen() { BindShader_Builtin( BUILTIN_SCREEN ); diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 0c2d7d9f..8c31c004 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -276,7 +276,7 @@ idCVar r_hdrDebug( "r_hdrDebug", "0", CVAR_RENDERER | CVAR_FLOAT, "show scene lu idCVar r_ldrContrastThreshold( "r_ldrContrastThreshold", "1.1", CVAR_RENDERER | CVAR_FLOAT, "" ); idCVar r_ldrContrastOffset( "r_ldrContrastOffset", "3", CVAR_RENDERER | CVAR_FLOAT, "" ); -idCVar r_useFilmicPostProcessing( "r_useFilmicPostProcessing", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "apply several post process effects to mimic a filmic look" ); +idCVar r_usePostProcessing( "r_usePostProcessing", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "1 = Filmic Look, 2 = Retro C64", 0, 2 ); idCVar r_forceAmbient( "r_forceAmbient", "0.5", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_FLOAT, "render additional ambient pass to make the game less dark", 0.0f, 1.0f ); diff --git a/neo/shaders/builtin/post/retro_c64.ps.hlsl b/neo/shaders/builtin/post/retro_c64.ps.hlsl new file mode 100644 index 00000000..c3d85271 --- /dev/null +++ b/neo/shaders/builtin/post/retro_c64.ps.hlsl @@ -0,0 +1,157 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Robert Beckebans + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code 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 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "global_inc.hlsl" + + +// *INDENT-OFF* +Texture2D t_BaseColor : register( t0 VK_DESCRIPTOR_SET( 0 ) ); +Texture2D t_BlueNoise : register( t1 VK_DESCRIPTOR_SET( 0 ) ); + +SamplerState samp0 : register(s0 VK_DESCRIPTOR_SET( 1 ) ); +SamplerState samp1 : register(s1 VK_DESCRIPTOR_SET( 1 ) ); // blue noise 256 + +struct PS_IN +{ + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; + +struct PS_OUT +{ + float4 color : SV_Target0; +}; +// *INDENT-ON* + + +float3 BlueNoise3( float2 n, float x ) +{ + float2 uv = n.xy * rpJitterTexOffset.xy; + + float3 noise = t_BlueNoise.Sample( samp1, uv ).rgb; + + noise = frac( noise + c_goldenRatioConjugate * rpJitterTexOffset.w * x ); + + return noise; +} + +#define RESOLUTION_DIVISOR 4.0 +#define NUM_COLORS 16 + +// find nearest palette color using Euclidean distance +float4 EuclidDist( float3 c, float3 pal[NUM_COLORS] ) +{ + int idx = 0; + float nd = distance( c, pal[0] ); + + for( int i = 1; i < NUM_COLORS; i++ ) + { + float d = distance( c, pal[i] ); + + if( d < nd ) + { + nd = d; + idx = i; + } + } + + return float4( pal[idx], 1.0 ); +} + + +#define RGB(r, g, b) float3(float(r)/255.0, float(g)/255.0, float(b)/255.0) + +void main( PS_IN fragment, out PS_OUT result ) +{ + float2 uv = ( fragment.texcoord0 ); + float2 uvPixellated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; + //float2 uvPixellated = fragment.position.xy; + + float4 color = t_BaseColor.Sample( samp0, uv ); + + float3 quantizationPeriod = _float3( 1.0 / NUM_COLORS ); + + // get pixellated base color + float3 dc = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; + color.rgb = dc; + + // add Bayer 8x8 dithering + float dither = DitherArray8x8( uvPixellated ); + color.rgb += ( float3( dither, dither, dither ) * quantizationPeriod ); + + + // C64 colors http://unusedino.de/ec64/technical/misc/vic656x/colors/ +#if 0 + const float3 palette[NUM_COLORS] = + { + RGB( 0, 0, 0 ), + RGB( 255, 255, 255 ), + RGB( 116, 67, 53 ), + RGB( 124, 172, 186 ), + RGB( 123, 72, 144 ), + RGB( 100, 151, 79 ), + RGB( 64, 50, 133 ), + RGB( 191, 205, 122 ), + RGB( 123, 91, 47 ), + RGB( 79, 69, 0 ), + RGB( 163, 114, 101 ), + RGB( 80, 80, 80 ), + RGB( 120, 120, 120 ), + RGB( 164, 215, 142 ), + RGB( 120, 106, 189 ), + RGB( 159, 159, 150 ), + }; +#else + // gamma corrected version + const float3 palette[NUM_COLORS] = + { + RGB( 0, 0, 0 ), // black + RGB( 255, 255, 255 ), // white + RGB( 104, 55, 43 ), // red + RGB( 112, 164, 178 ), // cyan + RGB( 111, 61, 134 ), // purple + RGB( 88, 141, 67 ), // green + RGB( 53, 40, 121 ), // blue + RGB( 184, 199, 111 ), // yellow + RGB( 111, 79, 37 ), // orange + RGB( 67, 57, 0 ), // brown + RGB( 154, 103, 89 ), // light red + RGB( 68, 68, 68 ), // dark grey + RGB( 108, 108, 108 ), // grey + RGB( 154, 210, 132 ), // light green + RGB( 108, 94, 181 ), // light blue + RGB( 149, 149, 149 ), // light grey + }; +#endif + + // find closest color match from C64 color palette + color = EuclidDist( color.rgb, palette ); + + result.color = color; +} diff --git a/neo/shaders/builtin/post/retro_c64.vs.hlsl b/neo/shaders/builtin/post/retro_c64.vs.hlsl new file mode 100644 index 00000000..75519fe8 --- /dev/null +++ b/neo/shaders/builtin/post/retro_c64.vs.hlsl @@ -0,0 +1,55 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code 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 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "global_inc.hlsl" + + +// *INDENT-OFF* +struct VS_IN +{ + float4 position : POSITION; + float2 texcoord : TEXCOORD0; + float4 normal : NORMAL; + float4 tangent : TANGENT; + float4 color : COLOR0; + float4 color2 : COLOR1; +}; + +struct VS_OUT { + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; +// *INDENT-ON* + +void main( VS_IN vertex, out VS_OUT result ) +{ + result.position = vertex.position; + result.position.y = -result.position.y; + + result.texcoord0 = vertex.texcoord; +} \ No newline at end of file diff --git a/neo/shaders/global_inc.hlsl b/neo/shaders/global_inc.hlsl index ca4cf161..c7712c92 100644 --- a/neo/shaders/global_inc.hlsl +++ b/neo/shaders/global_inc.hlsl @@ -488,14 +488,44 @@ float InterleavedGradientNoiseAnim( float2 uv, float frameIndex ) return rnd; } -// RB: very efficient white noise without sine https://www.shadertoy.com/view/4djSRW -#define HASHSCALE3 float3(443.897, 441.423, 437.195) - -float3 Hash33( float3 p3 ) +float R2Noise( float2 uv ) { - p3 = frac( p3 * HASHSCALE3 ); - p3 += dot( p3, p3.yxz + 19.19 ); - return frac( ( p3.xxy + p3.yxx ) * p3.zyx ); + const float a1 = 0.75487766624669276; + const float a2 = 0.569840290998; + + return frac( a1 * float( uv.x ) + a2 * float( uv.y ) ); } +// array/table version from http://www.anisopteragames.com/how-to-fix-color-banding-with-dithering/ +static const uint ArrayDitherArray8x8[] = +{ + 0, 32, 8, 40, 2, 34, 10, 42, /* 8x8 Bayer ordered dithering */ + 48, 16, 56, 24, 50, 18, 58, 26, /* pattern. Each input pixel */ + 12, 44, 4, 36, 14, 46, 6, 38, /* is scaled to the 0..63 range */ + 60, 28, 52, 20, 62, 30, 54, 22, /* before looking in this table */ + 3, 35, 11, 43, 1, 33, 9, 41, /* to determine the action. */ + 51, 19, 59, 27, 49, 17, 57, 25, + 15, 47, 7, 39, 13, 45, 5, 37, + 63, 31, 55, 23, 61, 29, 53, 21 +}; + +float DitherArray8x8( float2 pos ) +{ + uint stippleOffset = ( ( uint )pos.y % 8 ) * 8 + ( ( uint )pos.x % 8 ); + uint byte = ArrayDitherArray8x8[stippleOffset]; + float stippleThreshold = byte / 64.0f; + return stippleThreshold; +} + +float DitherArray8x8Anim( float2 pos, int frameIndexMod4 ) +{ + pos += int2( frameIndexMod4 % 2, frameIndexMod4 / 2 ) * uint2( 5, 5 ); + + uint stippleOffset = ( ( uint )pos.y % 8 ) * 8 + ( ( uint )pos.x % 8 ); + uint byte = ArrayDitherArray8x8[stippleOffset]; + float stippleThreshold = byte / 64.0f; + return stippleThreshold; +} + + #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) \ No newline at end of file diff --git a/neo/shaders/shaders.cfg b/neo/shaders/shaders.cfg index 2e74f58d..5d3ebdd2 100644 --- a/neo/shaders/shaders.cfg +++ b/neo/shaders/shaders.cfg @@ -52,6 +52,8 @@ builtin/lighting/interactionSM.ps.hlsl -T ps_5_0 -D USE_GPU_SKINNING={0,1} -D LI builtin/post/postprocess.vs.hlsl -T vs_5_0 builtin/post/postprocess.ps.hlsl -T ps_5_0 +builtin/post/retro_c64.vs.hlsl -T vs_5_0 +builtin/post/retro_c64.ps.hlsl -T ps_5_0 builtin/post/screen.vs.hlsl -T vs_5_0 builtin/post/screen.ps.hlsl -T ps_5_0 builtin/post/tonemap.vs.hlsl -T vs_5_0 -D BRIGHTPASS={0,1} -D HDR_DEBUG={0,1} From a589f3e6f02162a30390f6013e800026b282dc76 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Wed, 27 Dec 2023 15:14:58 +0100 Subject: [PATCH 02/37] Implemented binary search for colors in HLSL --- neo/shaders/builtin/post/retro_c64.ps.hlsl | 82 ++++++++++++++++++---- 1 file changed, 69 insertions(+), 13 deletions(-) diff --git a/neo/shaders/builtin/post/retro_c64.ps.hlsl b/neo/shaders/builtin/post/retro_c64.ps.hlsl index c3d85271..757c63fa 100644 --- a/neo/shaders/builtin/post/retro_c64.ps.hlsl +++ b/neo/shaders/builtin/post/retro_c64.ps.hlsl @@ -50,22 +50,12 @@ struct PS_OUT // *INDENT-ON* -float3 BlueNoise3( float2 n, float x ) -{ - float2 uv = n.xy * rpJitterTexOffset.xy; - - float3 noise = t_BlueNoise.Sample( samp1, uv ).rgb; - - noise = frac( noise + c_goldenRatioConjugate * rpJitterTexOffset.w * x ); - - return noise; -} - #define RESOLUTION_DIVISOR 4.0 #define NUM_COLORS 16 + // find nearest palette color using Euclidean distance -float4 EuclidDist( float3 c, float3 pal[NUM_COLORS] ) +float4 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) { int idx = 0; float nd = distance( c, pal[0] ); @@ -84,6 +74,71 @@ float4 EuclidDist( float3 c, float3 pal[NUM_COLORS] ) return float4( pal[idx], 1.0 ); } +float3 GetClosest( float3 val1, float3 val2, float3 target ) +{ + if( distance( target, val1 ) >= distance( val2, target ) ) + { + return val2; + } + else + { + return val1; + } +} + +// find nearest palette color using Euclidean disntance and binary search +// this requires an already sorted palette as input +float3 BinarySearch( float3 target, float3 pal[NUM_COLORS] ) +{ + float targetY = PhotoLuma( target ); + + // left-side case + if( targetY <= PhotoLuma( pal[0] ) ) + { + return pal[0]; + } + + // right-side case + if( targetY >= PhotoLuma( pal[NUM_COLORS - 1] ) ) + { + return pal[NUM_COLORS - 1]; + } + + int i = 0, j = NUM_COLORS, mid = 0; + while( i < j ) + { + mid = ( i + j ) / 2; + + if( distance( pal[mid], target ) < 0.01 ) + { + return pal[mid]; + } + + // if target is less than array element, then search in left + if( targetY < PhotoLuma( pal[mid] ) ) + { + // if target is greater than previous + // to mid, return closest of two + if( mid > 0 && targetY > PhotoLuma( pal[mid - 1] ) ) + { + return GetClosest( pal[mid - 1], pal[mid], target ); + } + j = mid; + } + else + { + if( mid < ( NUM_COLORS - 1 ) && targetY < PhotoLuma( pal[mid + 1] ) ) + { + return GetClosest( pal[mid], pal[mid + 1], target ); + } + i = mid + 1; + } + } + + // only single element left after search + return pal[mid]; +} + #define RGB(r, g, b) float3(float(r)/255.0, float(g)/255.0, float(b)/255.0) @@ -151,7 +206,8 @@ void main( PS_IN fragment, out PS_OUT result ) #endif // find closest color match from C64 color palette - color = EuclidDist( color.rgb, palette ); + color = LinearSearch( color.rgb, palette ); + //color = float4( BinarySearch( color.rgb, palette ), 1.0 ); result.color = color; } From 49130533c689721f2407a2470c02c0af4dda6e78 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Thu, 28 Dec 2023 13:06:42 +0100 Subject: [PATCH 03/37] Further tweaks on the C64 shader --- neo/shaders/builtin/post/retro_c64.ps.hlsl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/neo/shaders/builtin/post/retro_c64.ps.hlsl b/neo/shaders/builtin/post/retro_c64.ps.hlsl index 757c63fa..1c094d86 100644 --- a/neo/shaders/builtin/post/retro_c64.ps.hlsl +++ b/neo/shaders/builtin/post/retro_c64.ps.hlsl @@ -146,7 +146,6 @@ void main( PS_IN fragment, out PS_OUT result ) { float2 uv = ( fragment.texcoord0 ); float2 uvPixellated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; - //float2 uvPixellated = fragment.position.xy; float4 color = t_BaseColor.Sample( samp0, uv ); @@ -157,8 +156,9 @@ void main( PS_IN fragment, out PS_OUT result ) color.rgb = dc; // add Bayer 8x8 dithering - float dither = DitherArray8x8( uvPixellated ); - color.rgb += ( float3( dither, dither, dither ) * quantizationPeriod ); + //float2 psxDitherScale = fragment.position.xy / ( RESOLUTION_DIVISOR / 2.0 ); + float dither = DitherArray8x8( uvPixellated ) - 0.5; + color.rgb += float3( dither, dither, dither ) * quantizationPeriod; // C64 colors http://unusedino.de/ec64/technical/misc/vic656x/colors/ @@ -205,6 +205,9 @@ void main( PS_IN fragment, out PS_OUT result ) }; #endif + // PSX color quantization + //color = floor( color * 32.0 ) / 32.0; + // find closest color match from C64 color palette color = LinearSearch( color.rgb, palette ); //color = float4( BinarySearch( color.rgb, palette ), 1.0 ); From ed6b61a9f93071a81ee6f06170c90ca0cb9f0cc6 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Thu, 28 Dec 2023 14:39:59 +0100 Subject: [PATCH 04/37] Hooked in the ability to do postfx after everything else is rendered --- neo/cmake-vs2019-win64-no-vulkan.bat | 6 +++ neo/d3xp/PlayerView.cpp | 1 - neo/framework/common_frame.cpp | 2 + neo/renderer/RenderBackend.cpp | 75 ++++++++++++++++++++++++++++ neo/renderer/RenderBackend.h | 2 + neo/renderer/RenderCommon.h | 18 +++++-- neo/renderer/RenderProgs.cpp | 1 + neo/renderer/RenderProgs.h | 8 ++- neo/renderer/RenderSystem.cpp | 64 ++++++------------------ neo/renderer/RenderSystem.h | 5 +- neo/renderer/RenderSystem_init.cpp | 2 + neo/shaders/shaders.cfg | 2 + 12 files changed, 129 insertions(+), 57 deletions(-) create mode 100644 neo/cmake-vs2019-win64-no-vulkan.bat diff --git a/neo/cmake-vs2019-win64-no-vulkan.bat b/neo/cmake-vs2019-win64-no-vulkan.bat new file mode 100644 index 00000000..1df467f8 --- /dev/null +++ b/neo/cmake-vs2019-win64-no-vulkan.bat @@ -0,0 +1,6 @@ +cd .. +del /s /q build +mkdir build +cd build +cmake -G "Visual Studio 16" -A x64 -DFFMPEG=OFF -DBINKDEC=ON -DUSE_VULKAN=OFF ../neo +pause \ No newline at end of file diff --git a/neo/d3xp/PlayerView.cpp b/neo/d3xp/PlayerView.cpp index 0c895480..cf62dbdd 100644 --- a/neo/d3xp/PlayerView.cpp +++ b/neo/d3xp/PlayerView.cpp @@ -429,7 +429,6 @@ idPlayerView::SingleView */ void idPlayerView::SingleView( const renderView_t* view, idMenuHandler_HUD* hudManager ) { - // normal rendering if( !view ) { diff --git a/neo/framework/common_frame.cpp b/neo/framework/common_frame.cpp index 1591458c..953ffe67 100644 --- a/neo/framework/common_frame.cpp +++ b/neo/framework/common_frame.cpp @@ -404,6 +404,8 @@ void idCommonLocal::Draw() // draw the half console / notify console on top of everything console->Draw( false ); + + renderSystem->DrawCRTPostFX(); } } diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 28c66840..614d4b05 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -5374,6 +5374,10 @@ void idRenderBackend::ExecuteBackEndCommands( const emptyCommand_t* cmds ) break; } + case RC_CRT_POST_PROCESS: + CRTPostProcess(); + break; + default: common->Error( "RB_ExecuteBackEndCommands: bad commandId" ); break; @@ -6171,3 +6175,74 @@ void idRenderBackend::PostProcess( const void* data ) renderLog.CloseBlock(); renderLog.CloseMainBlock(); } + +void idRenderBackend::CRTPostProcess() +{ + //renderLog.OpenMainBlock( MRB_POSTPROCESS ); + renderLog.OpenBlock( "Render_CRTPostFX", colorBlue ); + + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK | GLS_DEPTHFUNC_ALWAYS | GLS_CULL_TWOSIDED ); + + int screenWidth = renderSystem->GetWidth(); + int screenHeight = renderSystem->GetHeight(); + + // set the window clipping + GL_Viewport( 0, 0, screenWidth, screenHeight ); + GL_Scissor( 0, 0, screenWidth, screenHeight ); + + if( r_useCRTPostFX.GetInteger() > 0 ) + { + BlitParameters blitParms; + blitParms.sourceTexture = ( nvrhi::ITexture* )globalImages->ldrImage->GetTextureID(); + blitParms.targetFramebuffer = globalFramebuffers.smaaBlendFBO->GetApiObject(); + + blitParms.targetViewport = nvrhi::Viewport( renderSystem->GetWidth(), renderSystem->GetHeight() ); + commonPasses.BlitTexture( commandList, blitParms, &bindingCache ); + + GL_SelectTexture( 0 ); + globalImages->smaaBlendImage->Bind(); + + globalFramebuffers.ldrFBO->Bind(); + + GL_SelectTexture( 1 ); + globalImages->blueNoiseImage256->Bind(); + + renderProgManager.BindShader_CrtNewPixie(); + + float jitterTexOffset[4]; + jitterTexOffset[0] = 1.0f / globalImages->blueNoiseImage256->GetUploadWidth(); + jitterTexOffset[1] = 1.0f / globalImages->blueNoiseImage256->GetUploadHeight(); + + if( r_shadowMapRandomizeJitter.GetBool() ) + { + jitterTexOffset[2] = Sys_Milliseconds() / 1000.0f; + jitterTexOffset[3] = tr.frameCount % 64; + } + else + { + jitterTexOffset[2] = 0.0f; + jitterTexOffset[3] = 0.0f; + } + + SetFragmentParm( RENDERPARM_JITTERTEXOFFSET, jitterTexOffset ); // rpJitterTexOffset + + // Draw + DrawElementsWithCounters( &unitSquareSurface ); + } + + GL_SelectTexture( 0 ); + renderProgManager.Unbind(); + + // copy LDR result to DX12 / Vulkan swapchain image + BlitParameters blitParms; + blitParms.sourceTexture = ( nvrhi::ITexture* )globalImages->ldrImage->GetTextureID(); + blitParms.targetFramebuffer = deviceManager->GetCurrentFramebuffer(); + blitParms.targetViewport = nvrhi::Viewport( renderSystem->GetWidth(), renderSystem->GetHeight() ); + commonPasses.BlitTexture( commandList, blitParms, &bindingCache ); + + GL_SelectTexture( 0 ); + globalImages->currentRenderImage->Bind(); + + renderLog.CloseBlock(); + //renderLog.CloseMainBlock(); +} diff --git a/neo/renderer/RenderBackend.h b/neo/renderer/RenderBackend.h index 5e49d74c..87cbc575 100644 --- a/neo/renderer/RenderBackend.h +++ b/neo/renderer/RenderBackend.h @@ -208,7 +208,9 @@ private: // Experimental feature void MotionBlur(); + void PostProcess( const void* data ); + void CRTPostProcess(); private: void GL_StartFrame(); diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index ec2762fd..f3f4f7fa 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -699,11 +699,12 @@ TR_CMDS enum renderCommand_t { RC_NOP, - RC_DRAW_VIEW_3D, // may be at a reduced resolution, will be upsampled before 2D GUIs - RC_DRAW_VIEW_GUI, // not resolution scaled + RC_DRAW_VIEW_3D, // may be at a reduced resolution, will be upsampled before 2D GUIs + RC_DRAW_VIEW_GUI, // not resolution scaled RC_SET_BUFFER, RC_COPY_RENDER, - RC_POST_PROCESS, + RC_POST_PROCESS, // postfx after scene rendering is done but before GUI rendering + RC_CRT_POST_PROCESS, // CRT simulation after everything has been rendered on the final swapchain image }; struct emptyCommand_t @@ -746,6 +747,12 @@ struct postProcessCommand_t viewDef_t* viewDef; }; +struct crtPostProcessCommand_t +{ + renderCommand_t commandId; + renderCommand_t* next; +}; + //======================================================================= // this is the inital allocation for max number of drawsurfs @@ -932,6 +939,8 @@ public: virtual void DrawBigChar( int x, int y, int ch ); virtual void DrawBigStringExt( int x, int y, const char* string, const idVec4& setColor, bool forceColor ); + virtual void DrawCRTPostFX(); // RB + virtual void WriteDemoPics(); virtual void WriteEndFrame(); virtual void DrawDemoPics(); @@ -950,7 +959,6 @@ public: virtual void CropRenderSize( int width, int height ); virtual void CropRenderSize( int x, int y, int width, int height, bool topLeftAncor ); virtual void CaptureRenderToImage( const char* imageName, bool clearColorAfterCopy = false ); - virtual void CaptureRenderToFile( const char* fileName, bool fixAlpha ); virtual void UnCrop(); virtual bool UploadImage( const char* imageName, const byte* data, int width, int height ); @@ -1283,6 +1291,8 @@ extern idCVar r_taaClampingFactor; extern idCVar r_taaNewFrameWeight; extern idCVar r_taaMaxRadiance; extern idCVar r_taaMotionVectors; + +extern idCVar r_useCRTPostFX; // RB end /* diff --git a/neo/renderer/RenderProgs.cpp b/neo/renderer/RenderProgs.cpp index 5640debd..90cae8a7 100644 --- a/neo/renderer/RenderProgs.cpp +++ b/neo/renderer/RenderProgs.cpp @@ -522,6 +522,7 @@ void idRenderProgManager::Init( nvrhi::IDevice* device ) { BUILTIN_WOBBLESKY, "builtin/legacy/wobblesky", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, { BUILTIN_POSTPROCESS, "builtin/post/postprocess", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_POSTPROCESS_RETRO_C64, "builtin/post/retro_c64", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, + { BUILTIN_CRT_NUPIXIE, "builtin/post/crt_newpixie", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_SCREEN, "builtin/post/screen", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, { BUILTIN_TONEMAP, "builtin/post/tonemap", "", { { "BRIGHTPASS", "0" }, { "HDR_DEBUG", "0"} }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, diff --git a/neo/renderer/RenderProgs.h b/neo/renderer/RenderProgs.h index 3df7bc87..b1ec53bd 100644 --- a/neo/renderer/RenderProgs.h +++ b/neo/renderer/RenderProgs.h @@ -365,8 +365,9 @@ enum BUILTIN_SKYBOX, BUILTIN_WOBBLESKY, BUILTIN_POSTPROCESS, - BUILTIN_POSTPROCESS_RETRO_C64, // RB begin + BUILTIN_POSTPROCESS_RETRO_C64, + BUILTIN_CRT_NUPIXIE, BUILTIN_SCREEN, BUILTIN_TONEMAP, BUILTIN_BRIGHTPASS, @@ -823,6 +824,11 @@ public: BindShader_Builtin( BUILTIN_POSTPROCESS_RETRO_C64 ); } + void BindShader_CrtNewPixie() + { + BindShader_Builtin( BUILTIN_CRT_NUPIXIE ); + } + void BindShader_Screen() { BindShader_Builtin( BUILTIN_SCREEN ); diff --git a/neo/renderer/RenderSystem.cpp b/neo/renderer/RenderSystem.cpp index ef73194a..fb8e24bb 100644 --- a/neo/renderer/RenderSystem.cpp +++ b/neo/renderer/RenderSystem.cpp @@ -782,7 +782,6 @@ const emptyCommand_t* idRenderSystemLocal::SwapCommandBuffers_FinishCommandBuffe // set the time for shader effects in 2D rendering frameShaderTime = Sys_Milliseconds() * 0.001; - // RB: TODO RC_SET_BUFFER is not handled in OpenGL setBufferCommand_t* cmd2 = ( setBufferCommand_t* )R_GetCommandBuffer( sizeof( *cmd2 ) ); cmd2->commandId = RC_SET_BUFFER; cmd2->buffer = 0; @@ -1038,53 +1037,6 @@ void idRenderSystemLocal::CaptureRenderToImage( const char* imageName, bool clea guiModel->Clear(); } -/* -============== -idRenderSystemLocal::CaptureRenderToFile -============== -*/ -void idRenderSystemLocal::CaptureRenderToFile( const char* fileName, bool fixAlpha ) -{ - if( !IsInitialized() ) - { - return; - } - - idScreenRect& rc = renderCrops[currentRenderCrop]; - - guiModel->EmitFullScreen(); - guiModel->Clear(); - - RenderCommandBuffers( frameData->cmdHead ); - - // TODO implement for NVRHI - -#if !defined( USE_VULKAN ) && !defined( USE_NVRHI ) - glReadBuffer( GL_BACK ); - - // include extra space for OpenGL padding to word boundaries - int c = ( rc.GetWidth() + 3 ) * rc.GetHeight(); - byte* data = ( byte* )R_StaticAlloc( c * 3 ); - - glReadPixels( rc.x1, rc.y1, rc.GetWidth(), rc.GetHeight(), GL_RGB, GL_UNSIGNED_BYTE, data ); - - byte* data2 = ( byte* )R_StaticAlloc( c * 4 ); - - for( int i = 0 ; i < c ; i++ ) - { - data2[ i * 4 ] = data[ i * 3 ]; - data2[ i * 4 + 1 ] = data[ i * 3 + 1 ]; - data2[ i * 4 + 2 ] = data[ i * 3 + 2 ]; - data2[ i * 4 + 3 ] = 0xff; - } - - R_WriteTGA( fileName, data2, rc.GetWidth(), rc.GetHeight(), true ); - - R_StaticFree( data ); - R_StaticFree( data2 ); -#endif -} - /* ============== @@ -1153,3 +1105,19 @@ bool idRenderSystemLocal::UploadImage( const char* imageName, const byte* data, return true; } + + +// RB +void idRenderSystemLocal::DrawCRTPostFX() +{ + if( !IsInitialized() ) + { + return; + } + + guiModel->EmitFullScreen(); + guiModel->Clear(); + + crtPostProcessCommand_t* cmd = ( crtPostProcessCommand_t* )R_GetCommandBuffer( sizeof( *cmd ) ); + cmd->commandId = RC_CRT_POST_PROCESS; +} \ No newline at end of file diff --git a/neo/renderer/RenderSystem.h b/neo/renderer/RenderSystem.h index 383e2490..62b02a4e 100644 --- a/neo/renderer/RenderSystem.h +++ b/neo/renderer/RenderSystem.h @@ -306,6 +306,8 @@ public: virtual void DrawBigChar( int x, int y, int ch ) = 0; virtual void DrawBigStringExt( int x, int y, const char* string, const idVec4& setColor, bool forceColor ) = 0; + virtual void DrawCRTPostFX() = 0; // RB + // dump all 2D drawing so far this frame to the demo file virtual void WriteDemoPics() = 0; virtual void WriteEndFrame() = 0; @@ -359,9 +361,6 @@ public: virtual void CropRenderSize( int width, int height ) = 0; virtual void CropRenderSize( int x, int y, int width, int height, bool topLeftAncor ) = 0; virtual void CaptureRenderToImage( const char* imageName, bool clearColorAfterCopy = false ) = 0; - // fixAlpha will set all the alpha channel values to 0xff, which allows screen captures - // to use the default tga loading code without having dimmed down areas in many places - virtual void CaptureRenderToFile( const char* fileName, bool fixAlpha = false ) = 0; virtual void UnCrop() = 0; // the image has to be already loaded ( most straightforward way would be through a FindMaterial ) diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 8c31c004..41c3e3a9 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -302,6 +302,8 @@ idCVar r_taaClampingFactor( "r_taaClampingFactor", "1.0", CVAR_RENDERER | CVAR_F idCVar r_taaNewFrameWeight( "r_taaNewFrameWeight", "0.1", CVAR_RENDERER | CVAR_FLOAT, "" ); idCVar r_taaMaxRadiance( "r_taaMaxRadiance", "10000", CVAR_RENDERER | CVAR_FLOAT, "" ); idCVar r_taaMotionVectors( "r_taaMotionVectors", "1", CVAR_RENDERER | CVAR_BOOL, "" ); + +idCVar r_useCRTPostFX( "r_useCRTPostFX", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "CRT simulation: 1 = New Pixie", 0, 1 ); // RB end const char* fileExten[4] = { "tga", "png", "jpg", "exr" }; diff --git a/neo/shaders/shaders.cfg b/neo/shaders/shaders.cfg index 5d3ebdd2..61f0e4b0 100644 --- a/neo/shaders/shaders.cfg +++ b/neo/shaders/shaders.cfg @@ -54,6 +54,8 @@ builtin/post/postprocess.vs.hlsl -T vs_5_0 builtin/post/postprocess.ps.hlsl -T ps_5_0 builtin/post/retro_c64.vs.hlsl -T vs_5_0 builtin/post/retro_c64.ps.hlsl -T ps_5_0 +builtin/post/crt_newpixie.vs.hlsl -T vs_5_0 +builtin/post/crt_newpixie.ps.hlsl -T ps_5_0 builtin/post/screen.vs.hlsl -T vs_5_0 builtin/post/screen.ps.hlsl -T ps_5_0 builtin/post/tonemap.vs.hlsl -T vs_5_0 -D BRIGHTPASS={0,1} -D HDR_DEBUG={0,1} From 13f9ee8cfb73ffc58b78216ac3e846b0c3c9b7b1 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Thu, 28 Dec 2023 15:29:50 +0100 Subject: [PATCH 05/37] Added old MatthiasCRT shader to crt_newpixie.*.hlsl --- neo/renderer/RenderBackend.cpp | 11 +- neo/renderer/RenderCommon.h | 1 + neo/shaders/builtin/post/crt_newpixie.ps.hlsl | 123 ++++++++++++++++++ neo/shaders/builtin/post/crt_newpixie.vs.hlsl | 55 ++++++++ 4 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 neo/shaders/builtin/post/crt_newpixie.ps.hlsl create mode 100644 neo/shaders/builtin/post/crt_newpixie.vs.hlsl diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 614d4b05..d8fed322 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -6178,6 +6178,7 @@ void idRenderBackend::PostProcess( const void* data ) void idRenderBackend::CRTPostProcess() { +#if 1 //renderLog.OpenMainBlock( MRB_POSTPROCESS ); renderLog.OpenBlock( "Render_CRTPostFX", colorBlue ); @@ -6209,6 +6210,13 @@ void idRenderBackend::CRTPostProcess() renderProgManager.BindShader_CrtNewPixie(); + float windowCoordParm[4]; + windowCoordParm[0] = 1.0f / screenWidth; + windowCoordParm[1] = 1.0f / screenHeight; + windowCoordParm[2] = screenWidth; + windowCoordParm[3] = screenHeight; + SetFragmentParm( RENDERPARM_WINDOWCOORD, windowCoordParm ); // rpWindowCoord + float jitterTexOffset[4]; jitterTexOffset[0] = 1.0f / globalImages->blueNoiseImage256->GetUploadWidth(); jitterTexOffset[1] = 1.0f / globalImages->blueNoiseImage256->GetUploadHeight(); @@ -6231,7 +6239,7 @@ void idRenderBackend::CRTPostProcess() } GL_SelectTexture( 0 ); - renderProgManager.Unbind(); + //renderProgManager.Unbind(); // copy LDR result to DX12 / Vulkan swapchain image BlitParameters blitParms; @@ -6245,4 +6253,5 @@ void idRenderBackend::CRTPostProcess() renderLog.CloseBlock(); //renderLog.CloseMainBlock(); +#endif } diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index f3f4f7fa..8cc17f0c 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -751,6 +751,7 @@ struct crtPostProcessCommand_t { renderCommand_t commandId; renderCommand_t* next; + int padding; }; //======================================================================= diff --git a/neo/shaders/builtin/post/crt_newpixie.ps.hlsl b/neo/shaders/builtin/post/crt_newpixie.ps.hlsl new file mode 100644 index 00000000..e5815c07 --- /dev/null +++ b/neo/shaders/builtin/post/crt_newpixie.ps.hlsl @@ -0,0 +1,123 @@ +/* +=========================================================================== + +newpixie CRT + +Copyright (c) 2016 Mattias Gustavsson +Copyright (c) 2023 Robert Beckebans + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +=========================================================================== +*/ + +#include "global_inc.hlsl" + + +// *INDENT-OFF* +Texture2D t_CurrentRender : register( t0 VK_DESCRIPTOR_SET( 0 ) ); +Texture2D t_BlueNoise : register( t1 VK_DESCRIPTOR_SET( 0 ) ); + +SamplerState LinearSampler : register(s0 VK_DESCRIPTOR_SET( 1 ) ); +SamplerState samp1 : register(s1 VK_DESCRIPTOR_SET( 1 ) ); // blue noise 256 + +struct PS_IN +{ + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; + +struct PS_OUT +{ + float4 color : SV_Target0; +}; +// *INDENT-ON* + + +float2 curve( float2 uv ) +{ + uv = ( uv - 0.5 ) * 2.0; + uv *= 1.1; + uv.x *= 1.0 + pow( ( abs( uv.y ) / 5.0 ), 2.0 ); + uv.y *= 1.0 + pow( ( abs( uv.x ) / 4.0 ), 2.0 ); + uv = ( uv / 2.0 ) + 0.5; + uv = uv * 0.92 + 0.04; + return uv; +} + +void main( PS_IN fragment, out PS_OUT result ) +{ + //float2 uv = ( fragment.texcoord0 ); + + //float4 color = t_Screen.Sample( samp0, uv ); + + float2 iResolution = rpWindowCoord.zw; + float iTime = rpJitterTexOffset.x; + + float2 q = fragment.texcoord0.xy; + q = saturate( q ); + + float2 uv = q; + uv = curve( uv ); + float3 oricol = t_CurrentRender.Sample( LinearSampler, float2( q.x, q.y ) ).xyz; + float3 col; + float x = sin( 0.3 * iTime + uv.y * 21.0 ) * sin( 0.7 * iTime + uv.y * 29.0 ) * sin( 0.3 + 0.33 * iTime + uv.y * 31.0 ) * 0.0017; + + col.r = t_CurrentRender.Sample( LinearSampler, float2( x + uv.x + 0.001, uv.y + 0.001 ) ).x + 0.05; + col.g = t_CurrentRender.Sample( LinearSampler, float2( x + uv.x + 0.000, uv.y - 0.002 ) ).y + 0.05; + col.b = t_CurrentRender.Sample( LinearSampler, float2( x + uv.x - 0.002, uv.y + 0.000 ) ).z + 0.05; + col.r += 0.08 * t_CurrentRender.Sample( LinearSampler, 0.75 * float2( x + 0.025, -0.027 ) + float2( uv.x + 0.001, uv.y + 0.001 ) ).x; + col.g += 0.05 * t_CurrentRender.Sample( LinearSampler, 0.75 * float2( x + -0.022, -0.02 ) + float2( uv.x + 0.000, uv.y - 0.002 ) ).y; + col.b += 0.08 * t_CurrentRender.Sample( LinearSampler, 0.75 * float2( x + -0.02, -0.018 ) + float2( uv.x - 0.002, uv.y + 0.000 ) ).z; + + col = clamp( col * 0.6 + 0.4 * col * col * 1.0, 0.0, 1.0 ); + + float vig = ( 0.0 + 1.0 * 16.0 * uv.x * uv.y * ( 1.0 - uv.x ) * ( 1.0 - uv.y ) ); + col *= _float3( pow( vig, 0.3 ) ); + + col *= float3( 0.95, 1.05, 0.95 ); + col *= 2.8; + + float scans = clamp( 0.35 + 0.35 * sin( 3.5 * iTime + uv.y * iResolution.y * 1.5 ), 0.0, 1.0 ); + + float s = pow( scans, 1.7 ); + col = col * _float3( 0.4 + 0.7 * s ) ; + + col *= 1.0 + 0.01 * sin( 110.0 * iTime ); + if( uv.x < 0.0 || uv.x > 1.0 ) + { + col *= 0.0; + } + if( uv.y < 0.0 || uv.y > 1.0 ) + { + col *= 0.0; + } + + col *= 1.0 - 0.65 * _float3( clamp( ( fmod( fragment.texcoord0.x, 2.0 ) - 1.0 ) * 2.0, 0.0, 1.0 ) ); + + float comp = smoothstep( 0.1, 0.9, sin( iTime ) ); + + // Remove the next line to stop cross-fade between original and postprocess + //col = mix( col, oricol, comp ); + + result.color = float4( col.x, col.y, col.z, 1.0 ); + + //result.color = tex2D(samp0, fragment.texcoord0.xy ); + //result.color = float4(1.0f, 1.0f, 1.0f, 1.0f); +} diff --git a/neo/shaders/builtin/post/crt_newpixie.vs.hlsl b/neo/shaders/builtin/post/crt_newpixie.vs.hlsl new file mode 100644 index 00000000..75519fe8 --- /dev/null +++ b/neo/shaders/builtin/post/crt_newpixie.vs.hlsl @@ -0,0 +1,55 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code 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 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "global_inc.hlsl" + + +// *INDENT-OFF* +struct VS_IN +{ + float4 position : POSITION; + float2 texcoord : TEXCOORD0; + float4 normal : NORMAL; + float4 tangent : TANGENT; + float4 color : COLOR0; + float4 color2 : COLOR1; +}; + +struct VS_OUT { + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; +// *INDENT-ON* + +void main( VS_IN vertex, out VS_OUT result ) +{ + result.position = vertex.position; + result.position.y = -result.position.y; + + result.texcoord0 = vertex.texcoord; +} \ No newline at end of file From 1e9bc961913b35af9f0ebf4c918f0fb3827e2c52 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Thu, 28 Dec 2023 16:16:15 +0100 Subject: [PATCH 06/37] Going back to a single renderprog volatile constant buffer to avoid crashes --- neo/renderer/NVRHI/BufferObject_NVRHI.cpp | 8 ++++++-- neo/renderer/NVRHI/RenderProgs_NVRHI.cpp | 2 +- neo/renderer/Passes/SsaoPass.cpp | 2 +- neo/renderer/RenderBackend.cpp | 2 +- neo/renderer/RenderProgs.cpp | 10 +++++----- neo/renderer/RenderProgs.h | 5 +++-- neo/shaders/builtin/post/crt_newpixie.ps.hlsl | 2 +- 7 files changed, 18 insertions(+), 13 deletions(-) diff --git a/neo/renderer/NVRHI/BufferObject_NVRHI.cpp b/neo/renderer/NVRHI/BufferObject_NVRHI.cpp index 2b13f2f8..fdc9269f 100644 --- a/neo/renderer/NVRHI/BufferObject_NVRHI.cpp +++ b/neo/renderer/NVRHI/BufferObject_NVRHI.cpp @@ -729,9 +729,13 @@ bool idUniformBuffer::AllocBufferObject( const void* data, int allocSize, buffer // This buffer is a shader resource as opposed to a constant buffer due to // constant buffers not being able to be sub-ranged. nvrhi::BufferDesc bufferDesc; + //bufferDesc.initialState = nvrhi::ResourceStates::ConstantBuffer; // SRS - shouldn't this be initialized to CopyDest? + bufferDesc.initialState = nvrhi::ResourceStates::CopyDest; + bufferDesc.canHaveTypedViews = true; + bufferDesc.canHaveRawViews = true; bufferDesc.byteSize = numBytes; - bufferDesc.structStride = sizeof( idVec4 ); // SRS - this defines a structured storage buffer vs. a constant buffer - bufferDesc.initialState = nvrhi::ResourceStates::Common; + bufferDesc.structStride = sizeof( idVec4 ); + bufferDesc.isConstantBuffer = true; if( usage == BU_DYNAMIC ) { diff --git a/neo/renderer/NVRHI/RenderProgs_NVRHI.cpp b/neo/renderer/NVRHI/RenderProgs_NVRHI.cpp index facf8069..f2f1fbb9 100644 --- a/neo/renderer/NVRHI/RenderProgs_NVRHI.cpp +++ b/neo/renderer/NVRHI/RenderProgs_NVRHI.cpp @@ -316,7 +316,7 @@ bool idRenderProgManager::CommitConstantBuffer( nvrhi::ICommandList* commandList // The vkDoom3 backend even didn't bother with this and always fired the uniforms for each draw call. if( uniformsChanged || bindingLayoutTypeChanged ) { - commandList->writeBuffer( constantBuffer[BindingLayoutType()], uniforms.Ptr(), uniforms.Allocated() ); + commandList->writeBuffer( constantBuffer /*[BindingLayoutType()]*/, uniforms.Ptr(), uniforms.Allocated() ); uniformsChanged = false; diff --git a/neo/renderer/Passes/SsaoPass.cpp b/neo/renderer/Passes/SsaoPass.cpp index 25b127e2..187b7f32 100644 --- a/neo/renderer/Passes/SsaoPass.cpp +++ b/neo/renderer/Passes/SsaoPass.cpp @@ -254,8 +254,8 @@ void SsaoPass::Render( quarterResExtent.maxY = ( quarterResExtent.maxY + 3 ) / 4; // TODO required and remove this by fixing the shaders + // because they include #include renderProgManager.BindShader_TextureVertexColor(); - renderProgManager.CommitConstantBuffer( commandList, true ); SsaoConstants ssaoConstants = {}; diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index d8fed322..e53088e1 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -6238,7 +6238,7 @@ void idRenderBackend::CRTPostProcess() DrawElementsWithCounters( &unitSquareSurface ); } - GL_SelectTexture( 0 ); + //GL_SelectTexture( 0 ); //renderProgManager.Unbind(); // copy LDR result to DX12 / Vulkan swapchain image diff --git a/neo/renderer/RenderProgs.cpp b/neo/renderer/RenderProgs.cpp index 90cae8a7..0fdeb30a 100644 --- a/neo/renderer/RenderProgs.cpp +++ b/neo/renderer/RenderProgs.cpp @@ -88,10 +88,10 @@ void idRenderProgManager::Init( nvrhi::IDevice* device ) uniforms.SetNum( RENDERPARM_TOTAL, vec4_zero ); uniformsChanged = false; - for( int i = 0; i < NUM_BINDING_LAYOUTS; i++ ) + //for( int i = 0; i < NUM_BINDING_LAYOUTS; i++ ) { - auto constantBufferDesc = nvrhi::utils::CreateVolatileConstantBufferDesc( uniforms.Allocated(), va( "RenderParams_%d", i ), 16384 ); - constantBuffer[i] = device->createBuffer( constantBufferDesc ); + auto constantBufferDesc = nvrhi::utils::CreateVolatileConstantBufferDesc( uniforms.Allocated(), va( "RenderParams_%d", 1 ), 16384 ); + constantBuffer = device->createBuffer( constantBufferDesc ); } // === Main draw vertex layout === @@ -722,9 +722,9 @@ void idRenderProgManager::Shutdown() } // SRS - Unmap buffer memory using overloaded = operator - for( int i = 0; i < constantBuffer.Num(); i++ ) + //for( int i = 0; i < constantBuffer.Num(); i++ ) { - constantBuffer[i] = nullptr; + constantBuffer = nullptr; } } diff --git a/neo/renderer/RenderProgs.h b/neo/renderer/RenderProgs.h index b1ec53bd..8a445e00 100644 --- a/neo/renderer/RenderProgs.h +++ b/neo/renderer/RenderProgs.h @@ -976,7 +976,7 @@ public: ID_INLINE nvrhi::IBuffer* ConstantBuffer() { - return constantBuffer[BindingLayoutType()]; + return constantBuffer;//[BindingLayoutType()]; } ID_INLINE idUniformBuffer& BindingParamUbo() { @@ -1082,7 +1082,8 @@ private: idStaticList< idStaticList, NUM_BINDING_LAYOUTS > bindingLayouts; - idArray constantBuffer; + //idArray constantBuffer; + nvrhi::BufferHandle constantBuffer; }; extern idRenderProgManager renderProgManager; diff --git a/neo/shaders/builtin/post/crt_newpixie.ps.hlsl b/neo/shaders/builtin/post/crt_newpixie.ps.hlsl index e5815c07..59afea4f 100644 --- a/neo/shaders/builtin/post/crt_newpixie.ps.hlsl +++ b/neo/shaders/builtin/post/crt_newpixie.ps.hlsl @@ -27,7 +27,7 @@ SOFTWARE. =========================================================================== */ -#include "global_inc.hlsl" +#include // *INDENT-OFF* From df7d0b8d28721adda33d83aa37a59d3d9dc09b74 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Thu, 28 Dec 2023 22:35:58 +0100 Subject: [PATCH 07/37] Tweaked CRT shaders --- neo/shaders/builtin/post/crt_mattias.ps.hlsl | 127 ++++++++++++ neo/shaders/builtin/post/crt_mattias.vs.hlsl | 55 +++++ neo/shaders/builtin/post/crt_newpixie.ps.hlsl | 195 +++++++++++++++--- 3 files changed, 345 insertions(+), 32 deletions(-) create mode 100644 neo/shaders/builtin/post/crt_mattias.ps.hlsl create mode 100644 neo/shaders/builtin/post/crt_mattias.vs.hlsl diff --git a/neo/shaders/builtin/post/crt_mattias.ps.hlsl b/neo/shaders/builtin/post/crt_mattias.ps.hlsl new file mode 100644 index 00000000..7a375e10 --- /dev/null +++ b/neo/shaders/builtin/post/crt_mattias.ps.hlsl @@ -0,0 +1,127 @@ +/* +=========================================================================== + +newpixie CRT + +Copyright (c) 2016 Mattias Gustavsson +Copyright (c) 2023 Robert Beckebans + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +=========================================================================== +*/ + +#include + + +// *INDENT-OFF* +Texture2D t_CurrentRender : register( t0 VK_DESCRIPTOR_SET( 0 ) ); +Texture2D t_BlueNoise : register( t1 VK_DESCRIPTOR_SET( 0 ) ); + +SamplerState LinearSampler : register(s0 VK_DESCRIPTOR_SET( 1 ) ); +SamplerState samp1 : register(s1 VK_DESCRIPTOR_SET( 1 ) ); // blue noise 256 + +struct PS_IN +{ + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; + +struct PS_OUT +{ + float4 color : SV_Target0; +}; +// *INDENT-ON* + + +float2 curve( float2 uv, float curvature ) +{ + uv = ( uv - 0.5 ) * 2.0; + uv *= 1.1; + uv.x *= 1.0 + pow( ( abs( uv.y ) / 5.0 ), 2.0 ); + uv.y *= 1.0 + pow( ( abs( uv.x ) / 4.0 ), 2.0 ); + uv = ( uv / curvature ) + 0.5; + uv = uv * 0.92 + 0.04; + return uv; +} + +void main( PS_IN fragment, out PS_OUT result ) +{ + float2 iResolution = rpWindowCoord.zw; + float iTime = rpJitterTexOffset.x; + + float2 q = fragment.texcoord0.xy; + q = saturate( q ); + + float2 uv = q; + if( rpWindowCoord.x > 0.0 ) + { + uv = curve( uv, 2.0 ); + } + + // wiggle + float x = 0.0; //sin( 0.3 * iTime + uv.y * 21.0 ) * sin( 0.7 * iTime + uv.y * 29.0 ) * sin( 0.3 + 0.33 * iTime + uv.y * 31.0 ) * 0.0017; + + float3 col; + col.r = t_CurrentRender.Sample( LinearSampler, float2( x + uv.x + 0.001, uv.y + 0.001 ) ).x + 0.05; + col.g = t_CurrentRender.Sample( LinearSampler, float2( x + uv.x + 0.000, uv.y - 0.002 ) ).y + 0.05; + col.b = t_CurrentRender.Sample( LinearSampler, float2( x + uv.x - 0.002, uv.y + 0.000 ) ).z + 0.05; + + /* Ghosting */ +#if 1 + col.r += 0.08 * t_CurrentRender.Sample( LinearSampler, 0.75 * float2( x + 0.025, -0.027 ) + float2( uv.x + 0.001, uv.y + 0.001 ) ).x; + col.g += 0.05 * t_CurrentRender.Sample( LinearSampler, 0.75 * float2( x + -0.022, -0.02 ) + float2( uv.x + 0.000, uv.y - 0.002 ) ).y; + col.b += 0.08 * t_CurrentRender.Sample( LinearSampler, 0.75 * float2( x + -0.02, -0.018 ) + float2( uv.x - 0.002, uv.y + 0.000 ) ).z; +#endif + + /* Level adjustment (curves) */ + col = clamp( col * 0.6 + 0.4 * col * col * 1.0, 0.0, 1.0 ); + + /* Vignette */ + float vig = ( 0.0 + 1.0 * 16.0 * uv.x * uv.y * ( 1.0 - uv.x ) * ( 1.0 - uv.y ) ); + col *= _float3( pow( vig, 0.3 ) ); + + col *= float3( 0.95, 1.05, 0.95 ); + col *= 2.8; + + /* Scanlines */ + float scans = clamp( 0.35 + 0.35 * sin( 3.5 * iTime + uv.y * iResolution.y * 1.5 ), 0.0, 1.0 ); + + float s = pow( scans, 1.7 ); + col = col * _float3( 0.4 + 0.7 * s ) ; + + col *= 1.0 + 0.01 * sin( 110.0 * iTime ); + if( uv.x < 0.0 || uv.x > 1.0 ) + { + col *= 0.0; + } + if( uv.y < 0.0 || uv.y > 1.0 ) + { + col *= 0.0; + } + + col *= 1.0 - 0.65 * _float3( clamp( ( fmod( fragment.texcoord0.x, 2.0 ) - 1.0 ) * 2.0, 0.0, 1.0 ) ); + + float comp = smoothstep( 0.1, 0.9, sin( iTime ) ); + + // Remove the next line to stop cross-fade between original and postprocess + //col = mix( col, oricol, comp ); + + result.color = float4( col.x, col.y, col.z, 1.0 ); +} diff --git a/neo/shaders/builtin/post/crt_mattias.vs.hlsl b/neo/shaders/builtin/post/crt_mattias.vs.hlsl new file mode 100644 index 00000000..75519fe8 --- /dev/null +++ b/neo/shaders/builtin/post/crt_mattias.vs.hlsl @@ -0,0 +1,55 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code 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 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "global_inc.hlsl" + + +// *INDENT-OFF* +struct VS_IN +{ + float4 position : POSITION; + float2 texcoord : TEXCOORD0; + float4 normal : NORMAL; + float4 tangent : TANGENT; + float4 color : COLOR0; + float4 color2 : COLOR1; +}; + +struct VS_OUT { + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; +// *INDENT-ON* + +void main( VS_IN vertex, out VS_OUT result ) +{ + result.position = vertex.position; + result.position.y = -result.position.y; + + result.texcoord0 = vertex.texcoord; +} \ No newline at end of file diff --git a/neo/shaders/builtin/post/crt_newpixie.ps.hlsl b/neo/shaders/builtin/post/crt_newpixie.ps.hlsl index 59afea4f..285f9b6a 100644 --- a/neo/shaders/builtin/post/crt_newpixie.ps.hlsl +++ b/neo/shaders/builtin/post/crt_newpixie.ps.hlsl @@ -50,74 +50,205 @@ struct PS_OUT // *INDENT-ON* -float2 curve( float2 uv ) +float3 tsample( Texture2D tex, vec2 tc, float offs, vec2 resolution ) +{ +#if 1 + //tc = tc * vec2( 1.025, 0.92 ) + vec2( -0.0125, 0.04 ); + float3 s = pow( abs( tex.Sample( LinearSampler, vec2( tc.x, 1.0 - tc.y ) ).rgb ), _float3( 2.2 ) ); + return s * _float3( 1.25 ); +#else + float3 s = tex.Sample( LinearSampler, vec2( tc.x, 1.0 - tc.y ) ).rgb; + return s; +#endif +} + +float3 filmic( float3 LinearColor ) +{ + float3 x = max( _float3( 0.0 ), LinearColor - _float3( 0.004 ) ); + return ( x * ( 6.2 * x + 0.5 ) ) / ( x * ( 6.2 * x + 1.7 ) + 0.06 ); +} + +float2 curve( float2 uv, float curvature ) { uv = ( uv - 0.5 ) * 2.0; uv *= 1.1; uv.x *= 1.0 + pow( ( abs( uv.y ) / 5.0 ), 2.0 ); uv.y *= 1.0 + pow( ( abs( uv.x ) / 4.0 ), 2.0 ); - uv = ( uv / 2.0 ) + 0.5; + uv = ( uv / curvature ) + 0.5; + uv = uv * 0.92 + 0.04; + return uv; +} + +float2 curve2( float2 uv, float curvature ) +{ + uv = ( uv - 0.5 ) * 2.0; + uv *= 1.1; + uv.x *= 1.0 + pow( ( abs( uv.y ) / 4.0 ), 2.0 ); + uv.y *= 1.0 + pow( ( abs( uv.x ) / 3.0 ), 2.0 ); + uv = ( uv / curvature ) + 0.5; uv = uv * 0.92 + 0.04; return uv; } void main( PS_IN fragment, out PS_OUT result ) { - //float2 uv = ( fragment.texcoord0 ); + // revised version from RetroArch - //float4 color = t_Screen.Sample( samp0, uv ); + struct Params + { + float curvature; + float ghosting; + float scanroll; + float vignette; + float wiggle_toggle; + int FrameCount; + }; - float2 iResolution = rpWindowCoord.zw; - float iTime = rpJitterTexOffset.x; + Params params; + params.curvature = rpWindowCoord.x; + params.ghosting = 0.0; + params.scanroll = 1.0; + params.wiggle_toggle = 0.0; + params.vignette = rpWindowCoord.y; + params.FrameCount = int( rpJitterTexOffset.w ); - float2 q = fragment.texcoord0.xy; - q = saturate( q ); + // stop time variable so the screen doesn't wiggle + float time = params.FrameCount % 849 * 36.0; + float2 uv = fragment.texcoord0.xy; + uv.y = 1.0 - uv.y; + uv = saturate( uv ); - float2 uv = q; - uv = curve( uv ); - float3 oricol = t_CurrentRender.Sample( LinearSampler, float2( q.x, q.y ) ).xyz; + /* Curve */ + //float2 curved_uv = lerp( curve2( uv, params.curvature ), uv, 0.4 ); + //float scale = -0.101; + //float2 scuv = curved_uv * ( 1.0 - scale ) + scale / 2.0 + float2( 0.003, -0.001 ); + + //uv = scuv; + + float2 curved_uv = uv; + + if( params.curvature > 0.0 ) + { + curved_uv = curve( uv, 2.0 ); + } + float2 scuv = curved_uv; + + float2 resolution = rpWindowCoord.zw; + + /* Main color, Bleed */ float3 col; - float x = sin( 0.3 * iTime + uv.y * 21.0 ) * sin( 0.7 * iTime + uv.y * 29.0 ) * sin( 0.3 + 0.33 * iTime + uv.y * 31.0 ) * 0.0017; + float x = params.wiggle_toggle * sin( 0.1 * time + curved_uv.y * 13.0 ) * sin( 0.23 * time + curved_uv.y * 19.0 ) * sin( 0.3 + 0.11 * time + curved_uv.y * 23.0 ) * 0.0012; + // make time do something again + time = float( params.FrameCount % 640 * 1 ); + + Texture2D backbuffer = t_CurrentRender; +#if 1 + col.r = tsample( backbuffer, vec2( x + scuv.x + 0.0009, scuv.y + 0.0009 ), resolution.y / 800.0, resolution ).x + 0.02; + col.g = tsample( backbuffer, vec2( x + scuv.x + 0.0000, scuv.y - 0.0011 ), resolution.y / 800.0, resolution ).y + 0.02; + col.b = tsample( backbuffer, vec2( x + scuv.x - 0.0015, scuv.y + 0.0000 ), resolution.y / 800.0, resolution ).z + 0.02; +#else col.r = t_CurrentRender.Sample( LinearSampler, float2( x + uv.x + 0.001, uv.y + 0.001 ) ).x + 0.05; col.g = t_CurrentRender.Sample( LinearSampler, float2( x + uv.x + 0.000, uv.y - 0.002 ) ).y + 0.05; col.b = t_CurrentRender.Sample( LinearSampler, float2( x + uv.x - 0.002, uv.y + 0.000 ) ).z + 0.05; - col.r += 0.08 * t_CurrentRender.Sample( LinearSampler, 0.75 * float2( x + 0.025, -0.027 ) + float2( uv.x + 0.001, uv.y + 0.001 ) ).x; - col.g += 0.05 * t_CurrentRender.Sample( LinearSampler, 0.75 * float2( x + -0.022, -0.02 ) + float2( uv.x + 0.000, uv.y - 0.002 ) ).y; - col.b += 0.08 * t_CurrentRender.Sample( LinearSampler, 0.75 * float2( x + -0.02, -0.018 ) + float2( uv.x - 0.002, uv.y + 0.000 ) ).z; +#endif - col = clamp( col * 0.6 + 0.4 * col * col * 1.0, 0.0, 1.0 ); - float vig = ( 0.0 + 1.0 * 16.0 * uv.x * uv.y * ( 1.0 - uv.x ) * ( 1.0 - uv.y ) ); - col *= _float3( pow( vig, 0.3 ) ); + /* Ghosting */ +#if 1 + { + float o = sin( -fragment.position.y * 1.5 ) / resolution.x; + x += o * 0.25; + + float ghs = 0.15 * params.ghosting; + Texture2D blurbuffer = t_CurrentRender; // FIXME + float3 r = tsample( blurbuffer, float2( x - 0.014 * 1.0, -0.027 ) * 0.85 + 0.007 * float2( 0.35 * sin( 1.0 / 7.0 + 15.0 * curved_uv.y + 0.9 * time ), + 0.35 * sin( 2.0 / 7.0 + 10.0 * curved_uv.y + 1.37 * time ) ) + float2( scuv.x + 0.001, scuv.y + 0.001 ), + 5.5 + 1.3 * sin( 3.0 / 9.0 + 31.0 * curved_uv.x + 1.70 * time ), resolution ).xyz * float3( 0.5, 0.25, 0.25 ); + float3 g = tsample( blurbuffer, float2( x - 0.019 * 1.0, -0.020 ) * 0.85 + 0.007 * float2( 0.35 * cos( 1.0 / 9.0 + 15.0 * curved_uv.y + 0.5 * time ), + 0.35 * sin( 2.0 / 9.0 + 10.0 * curved_uv.y + 1.50 * time ) ) + float2( scuv.x + 0.000, scuv.y - 0.002 ), + 5.4 + 1.3 * sin( 3.0 / 3.0 + 71.0 * curved_uv.x + 1.90 * time ), resolution ).xyz * float3( 0.25, 0.5, 0.25 ); + float3 b = tsample( blurbuffer, float2( x - 0.017 * 1.0, -0.003 ) * 0.85 + 0.007 * float2( 0.35 * sin( 2.0 / 3.0 + 15.0 * curved_uv.y + 0.7 * time ), + 0.35 * cos( 2.0 / 3.0 + 10.0 * curved_uv.y + 1.63 * time ) ) + float2( scuv.x - 0.002, scuv.y + 0.000 ), + 5.3 + 1.3 * sin( 3.0 / 7.0 + 91.0 * curved_uv.x + 1.65 * time ), resolution ).xyz * float3( 0.25, 0.25, 0.5 ); + + float i = clamp( col.r * 0.299 + col.g * 0.587 + col.b * 0.114, 0.0, 1.0 ); + i = pow( 1.0 - pow( i, 2.0 ), 1.0 ); + i = ( 1.0 - i ) * 0.85 + 0.15; + + col += _float3( ghs * ( 1.0 - 0.299 ) ) * pow( clamp( _float3( 3.0 ) * r, _float3( 0.0 ), _float3( 1.0 ) ), _float3( 2.0 ) ) * _float3( i ); + col += _float3( ghs * ( 1.0 - 0.587 ) ) * pow( clamp( _float3( 3.0 ) * g, _float3( 0.0 ), _float3( 1.0 ) ), _float3( 2.0 ) ) * _float3( i ); + col += _float3( ghs * ( 1.0 - 0.114 ) ) * pow( clamp( _float3( 3.0 ) * b, _float3( 0.0 ), _float3( 1.0 ) ), _float3( 2.0 ) ) * _float3( i ); + } +#endif + + /* Level adjustment (curves) */ +#if 1 col *= float3( 0.95, 1.05, 0.95 ); - col *= 2.8; + col = clamp( col * 1.3 + 0.75 * col * col + 1.25 * col * col * col * col * col, _float3( 0.0 ), _float3( 10.0 ) ); +#endif - float scans = clamp( 0.35 + 0.35 * sin( 3.5 * iTime + uv.y * iResolution.y * 1.5 ), 0.0, 1.0 ); + /* Vignette */ +#if 1 + float vig = ( ( 1.0 - 0.99 * params.vignette ) + 1.0 * 16.0 * curved_uv.x * curved_uv.y * ( 1.0 - curved_uv.x ) * ( 1.0 - curved_uv.y ) ); + vig = 1.3 * pow( vig, 0.5 ); + col *= vig; +#endif - float s = pow( scans, 1.7 ); - col = col * _float3( 0.4 + 0.7 * s ) ; + time *= params.scanroll; - col *= 1.0 + 0.01 * sin( 110.0 * iTime ); - if( uv.x < 0.0 || uv.x > 1.0 ) + /* Scanlines */ + float scans = clamp( 0.35 + 0.18 * sin( 6.0 * time - curved_uv.y * resolution.y * 1.5 ), 0.0, 1.0 ); + float s = pow( scans, 0.9 ); + col = col * _float3( s ); + + /* Vertical lines (shadow mask) */ + col *= 1.0 - 0.23 * ( clamp( ( fragment.position.xy.x % 3.0 ) / 2.0, 0.0, 1.0 ) ); + + /* Tone map */ + col = filmic( col ); + + /* Noise */ +#if 1 + /*float2 seed = floor(curved_uv*resolution.xy*float2(0.5))/resolution.xy;*/ + float2 seed = curved_uv * resolution.xy; + /* seed = curved_uv; */ + col -= 0.015 * pow( float3( rand( seed + time ), rand( seed + time * 2.0 ), rand( seed + time * 3.0 ) ), _float3( 1.5 ) ); +#endif + + /* Flicker */ + col *= ( 1.0 - 0.004 * ( sin( 50.0 * time + curved_uv.y * 2.0 ) * 0.5 + 0.5 ) ); + + /* Clamp */ +#if 1 + if( curved_uv.x < 0.0 || curved_uv.x > 1.0 ) { col *= 0.0; } - if( uv.y < 0.0 || uv.y > 1.0 ) + if( curved_uv.y < 0.0 || curved_uv.y > 1.0 ) { col *= 0.0; } +#endif - col *= 1.0 - 0.65 * _float3( clamp( ( fmod( fragment.texcoord0.x, 2.0 ) - 1.0 ) * 2.0, 0.0, 1.0 ) ); + uv = curved_uv; - float comp = smoothstep( 0.1, 0.9, sin( iTime ) ); +#if 1 + /* Frame */ + float2 fscale = float2( 0.026, -0.018 ); //float2( -0.018, -0.013 ); + //uv = float2( uv.x, 1.0 - uv.y ); - // Remove the next line to stop cross-fade between original and postprocess - //col = mix( col, oricol, comp ); + //float4 f = texture( frametexture, vTexCoord.xy ); //*((1.0)+2.0*fscale)-fscale-float2(-0.0, 0.005)); + //f.xyz = mix( f.xyz, float3( 0.5, 0.5, 0.5 ), 0.5 ); + float4 f = _float4( 0.5 ); + float fvig = clamp( -0.00 + 512.0 * uv.x * uv.y * ( 1.0 - uv.x ) * ( 1.0 - uv.y ), 0.2, 0.8 ); + //col = lerp( col, lerp( max( col, 0.0 ), pow( abs( f.xyz ), _float3( 1.4 ) ) * fvig, f.w * f.w ), _float3( use_frame ) ); + //col = lerp( col, lerp( max( col, 0.0 ), pow( abs( f.xyz ), _float3( 1.4 ) ) * fvig, f.w * f.w ), _float3( 1.0 ) ); - result.color = float4( col.x, col.y, col.z, 1.0 ); + // Gamma correction since we are not rendering to an sRGB render target. + col = pow( col, _float3( 1.0 / 1.1 ) ); +#endif - //result.color = tex2D(samp0, fragment.texcoord0.xy ); - //result.color = float4(1.0f, 1.0f, 1.0f, 1.0f); + result.color = float4( col, 1.0 ); } From e51b41cc6630646a0a5341ecd91237b6522b8bb2 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Fri, 29 Dec 2023 12:31:43 +0100 Subject: [PATCH 08/37] Added CRT options as cvars --- neo/renderer/RenderBackend.cpp | 13 ++++++++++--- neo/renderer/RenderCommon.h | 2 ++ neo/renderer/RenderProgs.cpp | 1 + neo/renderer/RenderProgs.h | 6 ++++++ neo/renderer/RenderSystem_init.cpp | 4 +++- neo/shaders/shaders.cfg | 2 ++ 6 files changed, 24 insertions(+), 4 deletions(-) diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index e53088e1..0d41fd95 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -6208,11 +6208,18 @@ void idRenderBackend::CRTPostProcess() GL_SelectTexture( 1 ); globalImages->blueNoiseImage256->Bind(); - renderProgManager.BindShader_CrtNewPixie(); + if( r_useCRTPostFX.GetInteger() == 1 ) + { + renderProgManager.BindShader_CrtMattias(); + } + else + { + renderProgManager.BindShader_CrtNewPixie(); + } float windowCoordParm[4]; - windowCoordParm[0] = 1.0f / screenWidth; - windowCoordParm[1] = 1.0f / screenHeight; + windowCoordParm[0] = r_crtCurvature.GetFloat(); + windowCoordParm[1] = r_crtVignette.GetFloat(); windowCoordParm[2] = screenWidth; windowCoordParm[3] = screenHeight; SetFragmentParm( RENDERPARM_WINDOWCOORD, windowCoordParm ); // rpWindowCoord diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index 8cc17f0c..a53c5a78 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -1294,6 +1294,8 @@ extern idCVar r_taaMaxRadiance; extern idCVar r_taaMotionVectors; extern idCVar r_useCRTPostFX; +extern idCVar r_crtCurvature; +extern idCVar r_crtVignette; // RB end /* diff --git a/neo/renderer/RenderProgs.cpp b/neo/renderer/RenderProgs.cpp index 0fdeb30a..cee09c51 100644 --- a/neo/renderer/RenderProgs.cpp +++ b/neo/renderer/RenderProgs.cpp @@ -522,6 +522,7 @@ void idRenderProgManager::Init( nvrhi::IDevice* device ) { BUILTIN_WOBBLESKY, "builtin/legacy/wobblesky", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, { BUILTIN_POSTPROCESS, "builtin/post/postprocess", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_POSTPROCESS_RETRO_C64, "builtin/post/retro_c64", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, + { BUILTIN_CRT_MATTIAS, "builtin/post/crt_mattias", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_CRT_NUPIXIE, "builtin/post/crt_newpixie", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_SCREEN, "builtin/post/screen", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, diff --git a/neo/renderer/RenderProgs.h b/neo/renderer/RenderProgs.h index 8a445e00..775e2448 100644 --- a/neo/renderer/RenderProgs.h +++ b/neo/renderer/RenderProgs.h @@ -367,6 +367,7 @@ enum BUILTIN_POSTPROCESS, // RB begin BUILTIN_POSTPROCESS_RETRO_C64, + BUILTIN_CRT_MATTIAS, BUILTIN_CRT_NUPIXIE, BUILTIN_SCREEN, BUILTIN_TONEMAP, @@ -824,6 +825,11 @@ public: BindShader_Builtin( BUILTIN_POSTPROCESS_RETRO_C64 ); } + void BindShader_CrtMattias() + { + BindShader_Builtin( BUILTIN_CRT_MATTIAS ); + } + void BindShader_CrtNewPixie() { BindShader_Builtin( BUILTIN_CRT_NUPIXIE ); diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 41c3e3a9..030c36e1 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -303,7 +303,9 @@ idCVar r_taaNewFrameWeight( "r_taaNewFrameWeight", "0.1", CVAR_RENDERER | CVAR_F idCVar r_taaMaxRadiance( "r_taaMaxRadiance", "10000", CVAR_RENDERER | CVAR_FLOAT, "" ); idCVar r_taaMotionVectors( "r_taaMotionVectors", "1", CVAR_RENDERER | CVAR_BOOL, "" ); -idCVar r_useCRTPostFX( "r_useCRTPostFX", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "CRT simulation: 1 = New Pixie", 0, 1 ); +idCVar r_useCRTPostFX( "r_useCRTPostFX", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "RetroArch CRT shader: 1 = Matthias CRT, 1 = New Pixie", 0, 2 ); +idCVar r_crtCurvature( "r_crtCurvature", "2", CVAR_RENDERER | CVAR_FLOAT, "rounded borders" ); +idCVar r_crtVignette( "r_crtVignette", "0.8", CVAR_RENDERER | CVAR_FLOAT, "fading into the borders" ); // RB end const char* fileExten[4] = { "tga", "png", "jpg", "exr" }; diff --git a/neo/shaders/shaders.cfg b/neo/shaders/shaders.cfg index 61f0e4b0..3b1521d9 100644 --- a/neo/shaders/shaders.cfg +++ b/neo/shaders/shaders.cfg @@ -54,6 +54,8 @@ builtin/post/postprocess.vs.hlsl -T vs_5_0 builtin/post/postprocess.ps.hlsl -T ps_5_0 builtin/post/retro_c64.vs.hlsl -T vs_5_0 builtin/post/retro_c64.ps.hlsl -T ps_5_0 +builtin/post/crt_mattias.vs.hlsl -T vs_5_0 +builtin/post/crt_mattias.ps.hlsl -T ps_5_0 builtin/post/crt_newpixie.vs.hlsl -T vs_5_0 builtin/post/crt_newpixie.ps.hlsl -T ps_5_0 builtin/post/screen.vs.hlsl -T vs_5_0 From cd6c8045087402e94522cdc160d438472b39616d Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Fri, 29 Dec 2023 17:16:54 +0100 Subject: [PATCH 09/37] Moved makeImageHeader to Image_intrinsic.cpp --- neo/renderer/Image_intrinsic.cpp | 90 +++++++++++++++++++++++++- neo/renderer/RenderWorld_envprobes.cpp | 85 ------------------------ 2 files changed, 89 insertions(+), 86 deletions(-) diff --git a/neo/renderer/Image_intrinsic.cpp b/neo/renderer/Image_intrinsic.cpp index 7ce5f4d8..2a27d08c 100644 --- a/neo/renderer/Image_intrinsic.cpp +++ b/neo/renderer/Image_intrinsic.cpp @@ -3,7 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. -Copyright (C) 2013-2021 Robert Beckebans +Copyright (C) 2013-2023 Robert Beckebans Copyright (C) 2022 Stephen Pridham This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -1148,3 +1148,91 @@ void idImageManager::CreateIntrinsicImages() release_assert( loadingIconImage->referencedOutsideLevelLoad ); release_assert( hellLoadingIconImage->referencedOutsideLevelLoad ); } + + +CONSOLE_COMMAND( makeImageHeader, "load an image and turn it into a .h file", NULL ) +{ + byte* buffer; + int width = 0, height = 0; + + if( args.Argc() < 2 ) + { + common->Printf( "USAGE: makeImageHeader filename [exportname]\n" ); + return; + } + + idStr filename = args.Argv( 1 ); + + R_LoadImage( filename, &buffer, &width, &height, NULL, true, NULL ); + if( !buffer ) + { + common->Printf( "loading %s failed.\n", filename.c_str() ); + return; + } + + filename.StripFileExtension(); + + idStr exportname; + + if( args.Argc() == 3 ) + { + exportname.Format( "Image_%s.h", args.Argv( 2 ) ); + } + else + { + exportname.Format( "Image_%s.h", filename.c_str() ); + } + + for( int i = 0; i < exportname.Length(); i++ ) + { + if( exportname[ i ] == '/' ) + { + exportname[ i ] = '_'; + } + } + + idFileLocal headerFile( fileSystem->OpenFileWrite( exportname, "fs_basepath" ) ); + + idStr uppername = exportname; + uppername.ToUpper(); + + for( int i = 0; i < uppername.Length(); i++ ) + { + if( uppername[ i ] == '.' ) + { + uppername[ i ] = '_'; + } + } + + headerFile->Printf( "#ifndef %s_TEX_H\n", uppername.c_str() ); + headerFile->Printf( "#define %s_TEX_H\n\n", uppername.c_str() ); + + headerFile->Printf( "#define %s_TEX_WIDTH %i\n", uppername.c_str(), width ); + headerFile->Printf( "#define %s_TEX_HEIGHT %i\n\n", uppername.c_str(), height ); + + headerFile->Printf( "static const unsigned char %s_Bytes[] = {\n", uppername.c_str() ); + + int bufferSize = width * height * 4; + + for( int i = 0; i < bufferSize; i++ ) + { + byte b = buffer[i]; + + if( i < ( bufferSize - 1 ) ) + { + headerFile->Printf( "0x%02hhx, ", b ); + } + else + { + headerFile->Printf( "0x%02hhx", b ); + } + + if( i % 12 == 0 ) + { + headerFile->Printf( "\n" ); + } + } + headerFile->Printf( "\n};\n#endif\n" ); + + Mem_Free( buffer ); +} \ No newline at end of file diff --git a/neo/renderer/RenderWorld_envprobes.cpp b/neo/renderer/RenderWorld_envprobes.cpp index cea914d5..287b0c23 100644 --- a/neo/renderer/RenderWorld_envprobes.cpp +++ b/neo/renderer/RenderWorld_envprobes.cpp @@ -1264,89 +1264,4 @@ static const unsigned char brfLutTexBytes[] = Mem_Free( hdrBuffer ); } -CONSOLE_COMMAND( makeImageHeader, "load an image and turn it into a .h file", NULL ) -{ - byte* buffer; - int width = 0, height = 0; - if( args.Argc() < 2 ) - { - common->Printf( "USAGE: makeImageHeader filename [exportname]\n" ); - return; - } - - idStr filename = args.Argv( 1 ); - - R_LoadImage( filename, &buffer, &width, &height, NULL, true, NULL ); - if( !buffer ) - { - common->Printf( "loading %s failed.\n", filename.c_str() ); - return; - } - - filename.StripFileExtension(); - - idStr exportname; - - if( args.Argc() == 3 ) - { - exportname.Format( "Image_%s.h", args.Argv( 2 ) ); - } - else - { - exportname.Format( "Image_%s.h", filename.c_str() ); - } - - for( int i = 0; i < exportname.Length(); i++ ) - { - if( exportname[ i ] == '/' ) - { - exportname[ i ] = '_'; - } - } - - idFileLocal headerFile( fileSystem->OpenFileWrite( exportname, "fs_basepath" ) ); - - idStr uppername = exportname; - uppername.ToUpper(); - - for( int i = 0; i < uppername.Length(); i++ ) - { - if( uppername[ i ] == '.' ) - { - uppername[ i ] = '_'; - } - } - - headerFile->Printf( "#ifndef %s_TEX_H\n", uppername.c_str() ); - headerFile->Printf( "#define %s_TEX_H\n\n", uppername.c_str() ); - - headerFile->Printf( "#define %s_TEX_WIDTH %i\n", uppername.c_str(), width ); - headerFile->Printf( "#define %s_TEX_HEIGHT %i\n\n", uppername.c_str(), height ); - - headerFile->Printf( "static const unsigned char %s_Bytes[] = {\n", uppername.c_str() ); - - int bufferSize = width * height * 4; - - for( int i = 0; i < bufferSize; i++ ) - { - byte b = buffer[i]; - - if( i < ( bufferSize - 1 ) ) - { - headerFile->Printf( "0x%02hhx, ", b ); - } - else - { - headerFile->Printf( "0x%02hhx", b ); - } - - if( i % 12 == 0 ) - { - headerFile->Printf( "\n" ); - } - } - headerFile->Printf( "\n};\n#endif\n" ); - - Mem_Free( buffer ); -} From 567b95a6422fedcba147ce3a5ad9471f60431013 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Sat, 30 Dec 2023 14:10:07 +0100 Subject: [PATCH 10/37] Added r_renderMode cvar for Retro rendering modes: C64, Sega, PSX --- base/devtools.cfg | 12 +- .../menus/MenuScreen_Shell_SystemOptions.cpp | 10 +- neo/renderer/Image_intrinsic.cpp | 87 +++++++++++++ neo/renderer/RenderBackend.cpp | 21 ++-- neo/renderer/RenderCommon.h | 4 +- neo/renderer/RenderProgs.cpp | 2 + neo/renderer/RenderProgs.h | 14 ++- neo/renderer/RenderSystem_init.cpp | 4 +- neo/shaders/builtin/post/retro_c64.ps.hlsl | 16 +-- .../builtin/post/retro_genesis.ps.hlsl | 117 ++++++++++++++++++ .../builtin/post/retro_genesis.vs.hlsl | 55 ++++++++ neo/shaders/builtin/post/retro_ps1.ps.hlsl | 88 +++++++++++++ neo/shaders/builtin/post/retro_ps1.vs.hlsl | 55 ++++++++ neo/shaders/shaders.cfg | 4 + 14 files changed, 453 insertions(+), 36 deletions(-) create mode 100644 neo/shaders/builtin/post/retro_genesis.ps.hlsl create mode 100644 neo/shaders/builtin/post/retro_genesis.vs.hlsl create mode 100644 neo/shaders/builtin/post/retro_ps1.ps.hlsl create mode 100644 neo/shaders/builtin/post/retro_ps1.vs.hlsl diff --git a/base/devtools.cfg b/base/devtools.cfg index 9ce803c1..c3e4b73f 100644 --- a/base/devtools.cfg +++ b/base/devtools.cfg @@ -3,14 +3,14 @@ bind "I" "toggle r_showSurfaceInfo" bind "N" "noclip" bind "M" "spawn moveable_macbethchart" -bind "F1" "toggle r_showViewEnvprobes 1 2 3 0" -bind "F2" "toggle r_showTris 1 2 3 0" +bind "F1" "toggle editLights" +bind "F2" "toggle r_showTris 1 2 0" bind "F3" "toggle r_forceAmbient 0.5 1.0 0" bind "F4" "toggle r_skipInteractions" bind "F5" "savegame quick" bind "F6" "toggle r_showLightGrid 1 3 4 0" -bind "F7" "toggle r_useSSAO" -bind "F8" "toggle r_useFilmicPostProcessing" +bind "F7" "toggle r_renderMode 0 1 2 3" +bind "F8" "toggle r_useCRTPostFX 0 1 2" bind "F9" "loadgame quick" -bind "F10" "toggle com_fixedTic" -bind "F11" "toggle r_pbrDebug" +//bind "F10" "toggle com_fixedTic" +bind "F11" "toggle r_useFilmicPostFX" diff --git a/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp b/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp index bfc3bfa1..3bb0259e 100755 --- a/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp +++ b/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp @@ -34,7 +34,7 @@ const static int NUM_SYSTEM_OPTIONS_OPTIONS = 8; extern idCVar r_graphicsAPI; extern idCVar r_antiAliasing; -extern idCVar r_usePostProcessing; +extern idCVar r_useFilmicPostFX; extern idCVar r_swapInterval; extern idCVar s_volume_dB; extern idCVar r_exposure; // RB: use this to control HDR exposure or brightness in LDR mode @@ -421,7 +421,7 @@ void idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings::LoadData //originalShadowMapping = r_useShadowMapping.GetInteger(); originalSSAO = r_useSSAO.GetInteger(); originalAmbientBrightness = r_forceAmbient.GetFloat(); - originalPostProcessing = r_usePostProcessing.GetInteger(); + originalPostProcessing = r_useFilmicPostFX.GetInteger(); // RB end const int fullscreen = r_fullscreen.GetInteger(); @@ -597,7 +597,7 @@ void idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings::AdjustFi { static const int numValues = 2; static const int values[numValues] = { 0, 1 }; - r_usePostProcessing.SetInteger( AdjustOption( r_usePostProcessing.GetInteger(), values, numValues, adjustAmount ) ); + r_useFilmicPostFX.SetInteger( AdjustOption( r_useFilmicPostFX.GetInteger(), values, numValues, adjustAmount ) ); break; } /* @@ -760,7 +760,7 @@ idSWFScriptVar idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings return values[ r_antiAliasing.GetInteger() ]; } case SYSTEM_FIELD_POSTFX: - if( r_usePostProcessing.GetInteger() > 0 ) + if( r_useFilmicPostFX.GetInteger() > 0 ) { return "#str_swf_enabled"; } @@ -848,7 +848,7 @@ bool idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings::IsDataCh return true; } - if( originalPostProcessing != r_usePostProcessing.GetInteger() ) + if( originalPostProcessing != r_useFilmicPostFX.GetInteger() ) { return true; } diff --git a/neo/renderer/Image_intrinsic.cpp b/neo/renderer/Image_intrinsic.cpp index 2a27d08c..1642d444 100644 --- a/neo/renderer/Image_intrinsic.cpp +++ b/neo/renderer/Image_intrinsic.cpp @@ -1234,5 +1234,92 @@ CONSOLE_COMMAND( makeImageHeader, "load an image and turn it into a .h file", NU } headerFile->Printf( "\n};\n#endif\n" ); + Mem_Free( buffer ); +} + +CONSOLE_COMMAND( makePaletteHeader, "load a .pal palette, build an image from it and turn it into a .h file", NULL ) +{ + byte* buffer; + int width = 0, height = 0; + + if( args.Argc() < 2 ) + { + common->Printf( "USAGE: makePaletteHeader filename [exportname]\n" ); + return; + } + + idStr filename = args.Argv( 1 ); + + R_LoadImage( filename, &buffer, &width, &height, NULL, true, NULL ); + if( !buffer ) + { + common->Printf( "loading %s failed.\n", filename.c_str() ); + return; + } + + filename.StripFileExtension(); + + idStr exportname; + + if( args.Argc() == 3 ) + { + exportname.Format( "Image_%s.h", args.Argv( 2 ) ); + } + else + { + exportname.Format( "Image_%s.h", filename.c_str() ); + } + + for( int i = 0; i < exportname.Length(); i++ ) + { + if( exportname[ i ] == '/' ) + { + exportname[ i ] = '_'; + } + } + + idFileLocal headerFile( fileSystem->OpenFileWrite( exportname, "fs_basepath" ) ); + + idStr uppername = exportname; + uppername.ToUpper(); + + for( int i = 0; i < uppername.Length(); i++ ) + { + if( uppername[ i ] == '.' ) + { + uppername[ i ] = '_'; + } + } + + headerFile->Printf( "#ifndef %s_TEX_H\n", uppername.c_str() ); + headerFile->Printf( "#define %s_TEX_H\n\n", uppername.c_str() ); + + headerFile->Printf( "#define %s_TEX_WIDTH %i\n", uppername.c_str(), width ); + headerFile->Printf( "#define %s_TEX_HEIGHT %i\n\n", uppername.c_str(), height ); + + headerFile->Printf( "static const unsigned char %s_Bytes[] = {\n", uppername.c_str() ); + + int bufferSize = width * height * 4; + + for( int i = 0; i < bufferSize; i++ ) + { + byte b = buffer[i]; + + if( i < ( bufferSize - 1 ) ) + { + headerFile->Printf( "0x%02hhx, ", b ); + } + else + { + headerFile->Printf( "0x%02hhx", b ); + } + + if( i % 12 == 0 ) + { + headerFile->Printf( "\n" ); + } + } + headerFile->Printf( "\n};\n#endif\n" ); + Mem_Free( buffer ); } \ No newline at end of file diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 0d41fd95..2d7a41ce 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -5980,15 +5980,6 @@ idRenderBackend::PostProcess extern idCVar rs_enable; void idRenderBackend::PostProcess( const void* data ) { - // only do the post process step if resolution scaling is enabled. Prevents the unnecessary copying of the framebuffer and - // corresponding full screen quad pass. - /* - if( rs_enable.GetInteger() == 0 && !r_usePostProcessing.GetBool() && r_antiAliasing.GetInteger() == 0 ) - { - return; - } - */ - if( viewDef->renderView.rdflags & RDF_IRRADIANCE ) { #if defined( USE_NVRHI ) @@ -6106,7 +6097,7 @@ void idRenderBackend::PostProcess( const void* data ) } #endif - if( r_usePostProcessing.GetInteger() > 0 ) + if( r_useFilmicPostFX.GetBool() || r_renderMode.GetInteger() > 0 ) { BlitParameters blitParms; blitParms.sourceTexture = ( nvrhi::ITexture* )globalImages->ldrImage->GetTextureID(); @@ -6123,10 +6114,18 @@ void idRenderBackend::PostProcess( const void* data ) GL_SelectTexture( 1 ); globalImages->blueNoiseImage256->Bind(); - if( r_usePostProcessing.GetInteger() == 2 ) + if( r_renderMode.GetInteger() == 1 ) { renderProgManager.BindShader_PostProcess_RetroC64(); } + else if( r_renderMode.GetInteger() == 2 ) + { + renderProgManager.BindShader_PostProcess_RetroGenesis(); + } + else if( r_renderMode.GetInteger() == 3 ) + { + renderProgManager.BindShader_PostProcess_RetroPSX(); + } else { renderProgManager.BindShader_PostProcess(); diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index a53c5a78..4aacb17f 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -1268,7 +1268,7 @@ extern idCVar r_hdrDebug; extern idCVar r_ldrContrastThreshold; extern idCVar r_ldrContrastOffset; -extern idCVar r_usePostProcessing; +extern idCVar r_useFilmicPostFX; extern idCVar r_forceAmbient; extern idCVar r_useSSAO; @@ -1296,6 +1296,8 @@ extern idCVar r_taaMotionVectors; extern idCVar r_useCRTPostFX; extern idCVar r_crtCurvature; extern idCVar r_crtVignette; + +extern idCVar r_renderMode; // RB end /* diff --git a/neo/renderer/RenderProgs.cpp b/neo/renderer/RenderProgs.cpp index cee09c51..c95dcc03 100644 --- a/neo/renderer/RenderProgs.cpp +++ b/neo/renderer/RenderProgs.cpp @@ -522,6 +522,8 @@ void idRenderProgManager::Init( nvrhi::IDevice* device ) { BUILTIN_WOBBLESKY, "builtin/legacy/wobblesky", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, { BUILTIN_POSTPROCESS, "builtin/post/postprocess", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_POSTPROCESS_RETRO_C64, "builtin/post/retro_c64", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, + { BUILTIN_POSTPROCESS_RETRO_GENESIS, "builtin/post/retro_genesis", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, + { BUILTIN_POSTPROCESS_RETRO_PSX, "builtin/post/retro_ps1", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_CRT_MATTIAS, "builtin/post/crt_mattias", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_CRT_NUPIXIE, "builtin/post/crt_newpixie", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, diff --git a/neo/renderer/RenderProgs.h b/neo/renderer/RenderProgs.h index 775e2448..72497da2 100644 --- a/neo/renderer/RenderProgs.h +++ b/neo/renderer/RenderProgs.h @@ -366,7 +366,9 @@ enum BUILTIN_WOBBLESKY, BUILTIN_POSTPROCESS, // RB begin - BUILTIN_POSTPROCESS_RETRO_C64, + BUILTIN_POSTPROCESS_RETRO_C64, // Commodore 64 + BUILTIN_POSTPROCESS_RETRO_GENESIS, // Sega Genesis / Megadrive + BUILTIN_POSTPROCESS_RETRO_PSX, // Sony Playstation 1 BUILTIN_CRT_MATTIAS, BUILTIN_CRT_NUPIXIE, BUILTIN_SCREEN, @@ -825,6 +827,16 @@ public: BindShader_Builtin( BUILTIN_POSTPROCESS_RETRO_C64 ); } + void BindShader_PostProcess_RetroGenesis() + { + BindShader_Builtin( BUILTIN_POSTPROCESS_RETRO_GENESIS ); + } + + void BindShader_PostProcess_RetroPSX() + { + BindShader_Builtin( BUILTIN_POSTPROCESS_RETRO_PSX ); + } + void BindShader_CrtMattias() { BindShader_Builtin( BUILTIN_CRT_MATTIAS ); diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 030c36e1..fdb4e832 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -276,7 +276,7 @@ idCVar r_hdrDebug( "r_hdrDebug", "0", CVAR_RENDERER | CVAR_FLOAT, "show scene lu idCVar r_ldrContrastThreshold( "r_ldrContrastThreshold", "1.1", CVAR_RENDERER | CVAR_FLOAT, "" ); idCVar r_ldrContrastOffset( "r_ldrContrastOffset", "3", CVAR_RENDERER | CVAR_FLOAT, "" ); -idCVar r_usePostProcessing( "r_usePostProcessing", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "1 = Filmic Look, 2 = Retro C64", 0, 2 ); +idCVar r_useFilmicPostFX( "r_useFilmicPostFX", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "filmic look with chromatic abberation and film grain" ); idCVar r_forceAmbient( "r_forceAmbient", "0.5", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_FLOAT, "render additional ambient pass to make the game less dark", 0.0f, 1.0f ); @@ -306,6 +306,8 @@ idCVar r_taaMotionVectors( "r_taaMotionVectors", "1", CVAR_RENDERER | CVAR_BOOL, idCVar r_useCRTPostFX( "r_useCRTPostFX", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "RetroArch CRT shader: 1 = Matthias CRT, 1 = New Pixie", 0, 2 ); idCVar r_crtCurvature( "r_crtCurvature", "2", CVAR_RENDERER | CVAR_FLOAT, "rounded borders" ); idCVar r_crtVignette( "r_crtVignette", "0.8", CVAR_RENDERER | CVAR_FLOAT, "fading into the borders" ); + +idCVar r_renderMode( "r_renderMode", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "0 = Doom, 1 = Commodore 64, 3 = Sega Genesis, 4 = Sony PSX", 0, 4 ); // RB end const char* fileExten[4] = { "tga", "png", "jpg", "exr" }; diff --git a/neo/shaders/builtin/post/retro_c64.ps.hlsl b/neo/shaders/builtin/post/retro_c64.ps.hlsl index 1c094d86..de622bcc 100644 --- a/neo/shaders/builtin/post/retro_c64.ps.hlsl +++ b/neo/shaders/builtin/post/retro_c64.ps.hlsl @@ -55,7 +55,7 @@ struct PS_OUT // find nearest palette color using Euclidean distance -float4 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) +float3 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) { int idx = 0; float nd = distance( c, pal[0] ); @@ -71,7 +71,7 @@ float4 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) } } - return float4( pal[idx], 1.0 ); + return pal[idx]; } float3 GetClosest( float3 val1, float3 val2, float3 target ) @@ -147,16 +147,13 @@ void main( PS_IN fragment, out PS_OUT result ) float2 uv = ( fragment.texcoord0 ); float2 uvPixellated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; - float4 color = t_BaseColor.Sample( samp0, uv ); - float3 quantizationPeriod = _float3( 1.0 / NUM_COLORS ); // get pixellated base color - float3 dc = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; - color.rgb = dc; + float3 color = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; // add Bayer 8x8 dithering - //float2 psxDitherScale = fragment.position.xy / ( RESOLUTION_DIVISOR / 2.0 ); + //float2 uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / 2.0 ); float dither = DitherArray8x8( uvPixellated ) - 0.5; color.rgb += float3( dither, dither, dither ) * quantizationPeriod; @@ -205,12 +202,9 @@ void main( PS_IN fragment, out PS_OUT result ) }; #endif - // PSX color quantization - //color = floor( color * 32.0 ) / 32.0; - // find closest color match from C64 color palette color = LinearSearch( color.rgb, palette ); //color = float4( BinarySearch( color.rgb, palette ), 1.0 ); - result.color = color; + result.color = float4( color, 1.0 ); } diff --git a/neo/shaders/builtin/post/retro_genesis.ps.hlsl b/neo/shaders/builtin/post/retro_genesis.ps.hlsl new file mode 100644 index 00000000..9e3c4db1 --- /dev/null +++ b/neo/shaders/builtin/post/retro_genesis.ps.hlsl @@ -0,0 +1,117 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Robert Beckebans + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code 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 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "global_inc.hlsl" + + +// *INDENT-OFF* +Texture2D t_BaseColor : register( t0 VK_DESCRIPTOR_SET( 0 ) ); +Texture2D t_BlueNoise : register( t1 VK_DESCRIPTOR_SET( 0 ) ); + +SamplerState samp0 : register(s0 VK_DESCRIPTOR_SET( 1 ) ); +SamplerState samp1 : register(s1 VK_DESCRIPTOR_SET( 1 ) ); // blue noise 256 + +struct PS_IN +{ + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; + +struct PS_OUT +{ + float4 color : SV_Target0; +}; +// *INDENT-ON* + + +#define RESOLUTION_DIVISOR 4.0 +#define NUM_COLORS 64 + + +// find nearest palette color using Euclidean distance +float3 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) +{ + int idx = 0; + float nd = distance( c, pal[0] ); + + for( int i = 1; i < NUM_COLORS; i++ ) + { + float d = distance( c, pal[i] ); + + if( d < nd ) + { + nd = d; + idx = i; + } + } + + return pal[idx]; +} + +float Quantize( float inp, float period ) +{ + return floor( ( inp + period / 2.0 ) / period ) * period; +} + +#define RGB(r, g, b) float3(float(r)/255.0, float(g)/255.0, float(b)/255.0) + +void main( PS_IN fragment, out PS_OUT result ) +{ + float2 uv = ( fragment.texcoord0 ); + float2 uvPixellated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; + + // the Sega Mega Drive has a 9 bit HW palette making a total of 512 available colors + // that is 3 bit per RGB channel + // 2^3 = 8 + // 8 * 8 * 8 = 512 colors + // although only 61 colors were available on the screen at the same time but we ignore this for now + + const int quantizationSteps = 8; + float3 quantizationPeriod = _float3( 1.0 / ( quantizationSteps - 1 ) ); + + // get pixellated base color + float3 color = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; + + // add Bayer 8x8 dithering + float2 uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / 1.0 ); + float dither = DitherArray8x8( uvPixellated ) - 0.5; + color.rgb += float3( dither, dither, dither ) * quantizationPeriod; + + // find closest color match from Sega Mega Drive color palette + color = float3( + Quantize( color.r, quantizationPeriod.r ), + Quantize( color.g, quantizationPeriod.g ), + Quantize( color.b, quantizationPeriod.b ) + ); + + //color = LinearSearch( color.rgb, palette ); + //color = float4( BinarySearch( color.rgb, palette ), 1.0 ); + + result.color = float4( color, 1.0 ); +} diff --git a/neo/shaders/builtin/post/retro_genesis.vs.hlsl b/neo/shaders/builtin/post/retro_genesis.vs.hlsl new file mode 100644 index 00000000..75519fe8 --- /dev/null +++ b/neo/shaders/builtin/post/retro_genesis.vs.hlsl @@ -0,0 +1,55 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code 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 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "global_inc.hlsl" + + +// *INDENT-OFF* +struct VS_IN +{ + float4 position : POSITION; + float2 texcoord : TEXCOORD0; + float4 normal : NORMAL; + float4 tangent : TANGENT; + float4 color : COLOR0; + float4 color2 : COLOR1; +}; + +struct VS_OUT { + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; +// *INDENT-ON* + +void main( VS_IN vertex, out VS_OUT result ) +{ + result.position = vertex.position; + result.position.y = -result.position.y; + + result.texcoord0 = vertex.texcoord; +} \ No newline at end of file diff --git a/neo/shaders/builtin/post/retro_ps1.ps.hlsl b/neo/shaders/builtin/post/retro_ps1.ps.hlsl new file mode 100644 index 00000000..745c8df5 --- /dev/null +++ b/neo/shaders/builtin/post/retro_ps1.ps.hlsl @@ -0,0 +1,88 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Robert Beckebans + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code 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 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "global_inc.hlsl" + + +// *INDENT-OFF* +Texture2D t_BaseColor : register( t0 VK_DESCRIPTOR_SET( 0 ) ); +Texture2D t_BlueNoise : register( t1 VK_DESCRIPTOR_SET( 0 ) ); + +SamplerState samp0 : register(s0 VK_DESCRIPTOR_SET( 1 ) ); +SamplerState samp1 : register(s1 VK_DESCRIPTOR_SET( 1 ) ); // blue noise 256 + +struct PS_IN +{ + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; + +struct PS_OUT +{ + float4 color : SV_Target0; +}; +// *INDENT-ON* + + +#define RESOLUTION_DIVISOR 4.0 + + +float Quantize( float inp, float period ) +{ + return floor( ( inp + period / 2.0 ) / period ) * period; +} +void main( PS_IN fragment, out PS_OUT result ) +{ + float2 uv = ( fragment.texcoord0 ); + float2 uvPixellated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; + + // most Sony Playstation 1 titles used 5 bit per RGB channel + // 2^5 = 32 + // 32 * 32 * 32 = 32768 colors + + const int quantizationSteps = 32; + float3 quantizationPeriod = _float3( 1.0 / ( quantizationSteps - 1 ) ); + + // get pixellated base color + float3 color = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; + + // add Bayer 8x8 dithering + float2 uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / 1.0 ); + float dither = DitherArray8x8( uvDither ) - 0.5; + color.rgb += float3( dither, dither, dither ) * quantizationPeriod; + + // PSX color quantization + color = float3( + Quantize( color.r, quantizationPeriod.r ), + Quantize( color.g, quantizationPeriod.g ), + Quantize( color.b, quantizationPeriod.b ) + ); + + result.color = float4( color, 1.0 ); +} diff --git a/neo/shaders/builtin/post/retro_ps1.vs.hlsl b/neo/shaders/builtin/post/retro_ps1.vs.hlsl new file mode 100644 index 00000000..75519fe8 --- /dev/null +++ b/neo/shaders/builtin/post/retro_ps1.vs.hlsl @@ -0,0 +1,55 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code 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 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "global_inc.hlsl" + + +// *INDENT-OFF* +struct VS_IN +{ + float4 position : POSITION; + float2 texcoord : TEXCOORD0; + float4 normal : NORMAL; + float4 tangent : TANGENT; + float4 color : COLOR0; + float4 color2 : COLOR1; +}; + +struct VS_OUT { + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; +// *INDENT-ON* + +void main( VS_IN vertex, out VS_OUT result ) +{ + result.position = vertex.position; + result.position.y = -result.position.y; + + result.texcoord0 = vertex.texcoord; +} \ No newline at end of file diff --git a/neo/shaders/shaders.cfg b/neo/shaders/shaders.cfg index 3b1521d9..7b52b129 100644 --- a/neo/shaders/shaders.cfg +++ b/neo/shaders/shaders.cfg @@ -54,6 +54,10 @@ builtin/post/postprocess.vs.hlsl -T vs_5_0 builtin/post/postprocess.ps.hlsl -T ps_5_0 builtin/post/retro_c64.vs.hlsl -T vs_5_0 builtin/post/retro_c64.ps.hlsl -T ps_5_0 +builtin/post/retro_genesis.vs.hlsl -T vs_5_0 +builtin/post/retro_genesis.ps.hlsl -T ps_5_0 +builtin/post/retro_ps1.vs.hlsl -T vs_5_0 +builtin/post/retro_ps1.ps.hlsl -T ps_5_0 builtin/post/crt_mattias.vs.hlsl -T vs_5_0 builtin/post/crt_mattias.ps.hlsl -T ps_5_0 builtin/post/crt_newpixie.vs.hlsl -T vs_5_0 From fd5b25dc594c38a2e44a7ee2377b86fe7594fede Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Sat, 30 Dec 2023 15:01:31 +0100 Subject: [PATCH 11/37] Added nearest filtering for PSX render mode. Closes #613 --- neo/renderer/Image.h | 10 +- neo/renderer/NVRHI/Image_NVRHI.cpp | 32 +- neo/renderer/NVRHI/RenderBackend_NVRHI.cpp | 396 ++++++++++++++------- neo/renderer/RenderCommon.h | 8 + neo/shaders/builtin/post/retro_ps1.ps.hlsl | 2 + 5 files changed, 316 insertions(+), 132 deletions(-) diff --git a/neo/renderer/Image.h b/neo/renderer/Image.h index fce19a08..dba4dee1 100644 --- a/neo/renderer/Image.h +++ b/neo/renderer/Image.h @@ -459,15 +459,7 @@ public: return ( void* )texture.Get(); } - void* GetSampler( SamplerCache& samplerCache ) - { - if( !sampler ) - { - sampler = samplerCache.GetOrCreateSampler( samplerDesc ); - } - - return ( void* )sampler.Get(); - } + void* GetSampler( SamplerCache& samplerCache ); void* GetSampler( nvrhi::IDevice* device ) { diff --git a/neo/renderer/NVRHI/Image_NVRHI.cpp b/neo/renderer/NVRHI/Image_NVRHI.cpp index be615b60..df2454a9 100644 --- a/neo/renderer/NVRHI/Image_NVRHI.cpp +++ b/neo/renderer/NVRHI/Image_NVRHI.cpp @@ -3,7 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. -Copyright (C) 2013-2022 Robert Beckebans +Copyright (C) 2013-2023 Robert Beckebans Copyright (C) 2022 Stephen Pridham This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -296,6 +296,36 @@ void idImage::SetTexParameters() { } +/* +======================== +idImage::GetSampler +======================== +*/ +void* idImage::GetSampler( SamplerCache& samplerCache ) +{ + if( r_renderMode.GetInteger() == RENDERMODE_PSX ) + { + if( !sampler ) + { + nvrhi::SamplerDesc sampDesc = samplerDesc; + + // turn off linear filtering + sampDesc.setAllFilters( false ); + + sampler = samplerCache.GetOrCreateSampler( samplerDesc ); + } + } + else + { + if( !sampler ) + { + sampler = samplerCache.GetOrCreateSampler( samplerDesc ); + } + } + + return ( void* )sampler.Get(); +} + /* ======================== idImage::AllocImage diff --git a/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp b/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp index baf70f90..e077960d 100644 --- a/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp +++ b/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp @@ -733,18 +733,37 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) desc[2].bindings[5].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 10 )->GetTextureID(); } - if( desc[3].bindings.empty() ) + if( r_renderMode.GetInteger() == RENDERMODE_PSX ) { - desc[3].bindings = + if( desc[3].bindings.empty() ) { - nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ), - nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearClampSampler ) - }; + desc[3].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_PointWrapSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearClampSampler ) + }; + } + else + { + desc[3].bindings[0].resourceHandle = commonPasses.m_PointWrapSampler; + desc[3].bindings[1].resourceHandle = commonPasses.m_LinearClampSampler; + } } else { - desc[3].bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler; - desc[3].bindings[1].resourceHandle = commonPasses.m_LinearClampSampler; + if( desc[3].bindings.empty() ) + { + desc[3].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearClampSampler ) + }; + } + else + { + desc[3].bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler; + desc[3].bindings[1].resourceHandle = commonPasses.m_LinearClampSampler; + } } } else if( type == BINDING_LAYOUT_AMBIENT_LIGHTING_IBL_SKINNED ) @@ -804,18 +823,37 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) desc[2].bindings[5].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 10 )->GetTextureID(); } - if( desc[3].bindings.empty() ) + if( r_renderMode.GetInteger() == RENDERMODE_PSX ) { - desc[3].bindings = + if( desc[3].bindings.empty() ) { - nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ), - nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearClampSampler ) - }; + desc[3].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_PointWrapSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearClampSampler ) + }; + } + else + { + desc[3].bindings[0].resourceHandle = commonPasses.m_PointWrapSampler; + desc[3].bindings[1].resourceHandle = commonPasses.m_LinearClampSampler; + } } else { - desc[3].bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler; - desc[3].bindings[1].resourceHandle = commonPasses.m_LinearClampSampler; + if( desc[3].bindings.empty() ) + { + desc[3].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearClampSampler ) + }; + } + else + { + desc[3].bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler; + desc[3].bindings[1].resourceHandle = commonPasses.m_LinearClampSampler; + } } } else if( type == BINDING_LAYOUT_DRAW_AO ) @@ -900,18 +938,37 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) } // samplers: 3 - if( desc[3].bindings.empty() ) + if( r_renderMode.GetInteger() == RENDERMODE_PSX ) { - desc[3].bindings = + if( desc[3].bindings.empty() ) { - nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ), - nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler ) - }; + desc[3].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_PointWrapSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler ) + }; + } + else + { + desc[3].bindings[0].resourceHandle = commonPasses.m_PointWrapSampler; + desc[3].bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler; + } } else { - desc[3].bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler; - desc[3].bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler; + if( desc[3].bindings.empty() ) + { + desc[3].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler ) + }; + } + else + { + desc[3].bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler; + desc[3].bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler; + } } } else if( type == BINDING_LAYOUT_DRAW_INTERACTION_SKINNED ) @@ -967,18 +1024,37 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) } // samplers: 3 - if( desc[3].bindings.empty() ) + if( r_renderMode.GetInteger() == RENDERMODE_PSX ) { - desc[3].bindings = + if( desc[3].bindings.empty() ) { - nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ), - nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler ) - }; + desc[3].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_PointWrapSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler ) + }; + } + else + { + desc[3].bindings[0].resourceHandle = commonPasses.m_PointWrapSampler; + desc[3].bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler; + } } else { - desc[3].bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler; - desc[3].bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler; + if( desc[3].bindings.empty() ) + { + desc[3].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler ) + }; + } + else + { + desc[3].bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler; + desc[3].bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler; + } } } else if( type == BINDING_LAYOUT_DRAW_INTERACTION_SM ) @@ -1037,24 +1113,49 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) } // samplers: 3 - if( desc[3].bindings.empty() ) + if( r_renderMode.GetInteger() == RENDERMODE_PSX ) { - auto& bindings = desc[3].bindings; - bindings = + if( desc[3].bindings.empty() ) { - nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ), - nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler ), - nvrhi::BindingSetItem::Sampler( 2, commonPasses.m_LinearClampCompareSampler ), - nvrhi::BindingSetItem::Sampler( 3, commonPasses.m_PointWrapSampler ) // blue noise - }; + auto& bindings = desc[3].bindings; + bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_PointWrapSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler ), + nvrhi::BindingSetItem::Sampler( 2, commonPasses.m_LinearClampCompareSampler ), + nvrhi::BindingSetItem::Sampler( 3, commonPasses.m_PointWrapSampler ) // blue noise + }; + } + else + { + auto& bindings = desc[3].bindings; + bindings[0].resourceHandle = commonPasses.m_PointWrapSampler; + bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler; + bindings[2].resourceHandle = commonPasses.m_LinearClampCompareSampler; + bindings[3].resourceHandle = commonPasses.m_PointWrapSampler; + } } else { - auto& bindings = desc[3].bindings; - bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler; - bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler; - bindings[2].resourceHandle = commonPasses.m_LinearClampCompareSampler; - bindings[3].resourceHandle = commonPasses.m_PointWrapSampler; + if( desc[3].bindings.empty() ) + { + auto& bindings = desc[3].bindings; + bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler ), + nvrhi::BindingSetItem::Sampler( 2, commonPasses.m_LinearClampCompareSampler ), + nvrhi::BindingSetItem::Sampler( 3, commonPasses.m_PointWrapSampler ) // blue noise + }; + } + else + { + auto& bindings = desc[3].bindings; + bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler; + bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler; + bindings[2].resourceHandle = commonPasses.m_LinearClampCompareSampler; + bindings[3].resourceHandle = commonPasses.m_PointWrapSampler; + } } } else if( type == BINDING_LAYOUT_DRAW_INTERACTION_SM_SKINNED ) @@ -1117,24 +1218,49 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) } // samplers: 3 - if( desc[3].bindings.empty() ) + if( r_renderMode.GetInteger() == RENDERMODE_PSX ) { - auto& bindings = desc[3].bindings; - bindings = + if( desc[3].bindings.empty() ) { - nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ), - nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler ), - nvrhi::BindingSetItem::Sampler( 2, commonPasses.m_LinearClampCompareSampler ), - nvrhi::BindingSetItem::Sampler( 3, commonPasses.m_PointWrapSampler ) // blue noise - }; + auto& bindings = desc[3].bindings; + bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_PointWrapSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler ), + nvrhi::BindingSetItem::Sampler( 2, commonPasses.m_LinearClampCompareSampler ), + nvrhi::BindingSetItem::Sampler( 3, commonPasses.m_PointWrapSampler ) // blue noise + }; + } + else + { + auto& bindings = desc[3].bindings; + bindings[0].resourceHandle = commonPasses.m_PointWrapSampler; + bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler; + bindings[2].resourceHandle = commonPasses.m_LinearClampCompareSampler; + bindings[3].resourceHandle = commonPasses.m_PointWrapSampler; + } } else { - auto& bindings = desc[3].bindings; - bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler; - bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler; - bindings[2].resourceHandle = commonPasses.m_LinearClampCompareSampler; - bindings[3].resourceHandle = commonPasses.m_PointWrapSampler; + if( desc[3].bindings.empty() ) + { + auto& bindings = desc[3].bindings; + bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler ), + nvrhi::BindingSetItem::Sampler( 2, commonPasses.m_LinearClampCompareSampler ), + nvrhi::BindingSetItem::Sampler( 3, commonPasses.m_PointWrapSampler ) // blue noise + }; + } + else + { + auto& bindings = desc[3].bindings; + bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler; + bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler; + bindings[2].resourceHandle = commonPasses.m_LinearClampCompareSampler; + bindings[3].resourceHandle = commonPasses.m_PointWrapSampler; + } } } else if( type == BINDING_LAYOUT_FOG ) @@ -1340,16 +1466,33 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) desc[0].bindings[3].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 2 )->GetTextureID(); } - if( desc[1].bindings.empty() ) + if( r_renderMode.GetInteger() == RENDERMODE_PSX ) { - desc[1].bindings = + if( desc[1].bindings.empty() ) { - nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_LinearClampSampler ) - }; + desc[1].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_PointClampSampler ) + }; + } + else + { + desc[1].bindings[0].resourceHandle = commonPasses.m_PointClampSampler; + } } else { - desc[1].bindings[0].resourceHandle = commonPasses.m_LinearClampSampler; + if( desc[1].bindings.empty() ) + { + desc[1].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_LinearClampSampler ) + }; + } + else + { + desc[1].bindings[0].resourceHandle = commonPasses.m_LinearClampSampler; + } } } else if( type == BINDING_LAYOUT_POST_PROCESS_FINAL ) @@ -1415,16 +1558,33 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) bindings[1].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 1 )->GetTextureID(); } - if( desc[2].bindings.empty() ) + if( r_renderMode.GetInteger() == RENDERMODE_PSX ) { - desc[2].bindings = + if( desc[2].bindings.empty() ) { - nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_LinearWrapSampler ) - }; + desc[2].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_PointWrapSampler ) + }; + } + else + { + desc[2].bindings[0].resourceHandle = commonPasses.m_PointWrapSampler; + } } else { - desc[2].bindings[0].resourceHandle = commonPasses.m_LinearWrapSampler; + if( desc[2].bindings.empty() ) + { + desc[2].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_LinearWrapSampler ) + }; + } + else + { + desc[2].bindings[0].resourceHandle = commonPasses.m_LinearWrapSampler; + } } } else if( type == BINDING_LAYOUT_NORMAL_CUBE_SKINNED ) @@ -1462,16 +1622,33 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) bindings[1].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 1 )->GetTextureID(); } - if( desc[2].bindings.empty() ) + if( r_renderMode.GetInteger() == RENDERMODE_PSX ) { - desc[2].bindings = + if( desc[2].bindings.empty() ) { - nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_LinearWrapSampler ) - }; + desc[2].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_PointWrapSampler ) + }; + } + else + { + desc[2].bindings[0].resourceHandle = commonPasses.m_PointWrapSampler; + } } else { - desc[2].bindings[0].resourceHandle = commonPasses.m_LinearWrapSampler; + if( desc[2].bindings.empty() ) + { + desc[2].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_LinearWrapSampler ) + }; + } + else + { + desc[2].bindings[0].resourceHandle = commonPasses.m_LinearWrapSampler; + } } } else if( type == BINDING_LAYOUT_BINK_VIDEO ) @@ -1495,16 +1672,33 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) desc[0].bindings[3].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 2 )->GetTextureID(); } - if( desc[1].bindings.empty() ) + if( r_renderMode.GetInteger() == RENDERMODE_PSX ) { - desc[1].bindings = + if( desc[1].bindings.empty() ) { - nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_LinearWrapSampler ) - }; + desc[1].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_PointWrapSampler ) + }; + } + else + { + desc[1].bindings[0].resourceHandle = commonPasses.m_PointWrapSampler; + } } else { - desc[1].bindings[0].resourceHandle = commonPasses.m_LinearWrapSampler; + if( desc[1].bindings.empty() ) + { + desc[1].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_LinearWrapSampler ) + }; + } + else + { + desc[1].bindings[0].resourceHandle = commonPasses.m_LinearWrapSampler; + } } } else if( type == BINDING_LAYOUT_TAA_MOTION_VECTORS ) @@ -1830,7 +2024,7 @@ See if some cvars that we watch have changed */ void idRenderBackend::CheckCVars() { - // gamma stuff + // TODO remove, gamma stuff doesn't work and isn't used using the latest Nvidia drivers if( r_gamma.IsModified() || r_brightness.IsModified() ) { r_gamma.ClearModified(); @@ -1845,56 +2039,14 @@ void idRenderBackend::CheckCVars() deviceManager->SetVsyncEnabled( r_swapInterval.GetInteger() ); } - // filtering - /*if( r_maxAnisotropicFiltering.IsModified() || r_useTrilinearFiltering.IsModified() || r_lodBias.IsModified() ) + // retro rendering + if( r_renderMode.IsModified() ) { - idLib::Printf( "Updating texture filter parameters.\n" ); - r_maxAnisotropicFiltering.ClearModified(); - r_useTrilinearFiltering.ClearModified(); - r_lodBias.ClearModified(); + r_renderMode.ClearModified(); - for( int i = 0; i < globalImages->images.Num(); i++ ) - { - if( globalImages->images[i] ) - { - globalImages->images[i]->Bind(); - globalImages->images[i]->SetTexParameters(); - } - } - }*/ - -#if 0 - if( r_antiAliasing.IsModified() ) - { - switch( r_antiAliasing.GetInteger() ) - { - case ANTI_ALIASING_MSAA_2X: - case ANTI_ALIASING_MSAA_4X: - if( r_antiAliasing.GetInteger() > 0 ) - { - //glEnable( GL_MULTISAMPLE ); - } - break; - - default: - //glDisable( GL_MULTISAMPLE ); - break; - } - - if( tr.IsInitialized() ) - { - Framebuffer::ResizeFramebuffers(); - } - - if( taaPass ) - { - delete taaPass; - taaPass = NULL; - } - - r_antiAliasing.ClearModified(); + // clear caches because PSX rendering will use nearest texture filtering instead of linear + ClearCaches(); } -#endif } /* diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index 4aacb17f..94b7c10a 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -1297,6 +1297,14 @@ extern idCVar r_useCRTPostFX; extern idCVar r_crtCurvature; extern idCVar r_crtVignette; +enum RenderMode +{ + RENDERMODE_DOOM, + RENDERMODE_C64, + RENDERMODE_MEGADRIVE, + RENDERMODE_PSX, +}; + extern idCVar r_renderMode; // RB end diff --git a/neo/shaders/builtin/post/retro_ps1.ps.hlsl b/neo/shaders/builtin/post/retro_ps1.ps.hlsl index 745c8df5..d475f3dc 100644 --- a/neo/shaders/builtin/post/retro_ps1.ps.hlsl +++ b/neo/shaders/builtin/post/retro_ps1.ps.hlsl @@ -84,5 +84,7 @@ void main( PS_IN fragment, out PS_OUT result ) Quantize( color.b, quantizationPeriod.b ) ); + //color = t_BaseColor.Sample( samp0, uv ).rgb; + result.color = float4( color, 1.0 ); } From 779f38d84c23db158b448456f55338e98a283c7b Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Sat, 30 Dec 2023 18:43:09 +0100 Subject: [PATCH 12/37] Tweaked PSX dithering --- neo/shaders/builtin/post/postprocess.ps.hlsl | 138 +----------------- .../builtin/post/retro_genesis.ps.hlsl | 27 ++-- neo/shaders/builtin/post/retro_ps1.ps.hlsl | 63 ++++++-- neo/shaders/global_inc.hlsl | 1 + 4 files changed, 69 insertions(+), 160 deletions(-) diff --git a/neo/shaders/builtin/post/postprocess.ps.hlsl b/neo/shaders/builtin/post/postprocess.ps.hlsl index 8684da9f..d0499027 100644 --- a/neo/shaders/builtin/post/postprocess.ps.hlsl +++ b/neo/shaders/builtin/post/postprocess.ps.hlsl @@ -72,8 +72,6 @@ struct PS_OUT #define Dithering_QuantizationSteps 16.0 // 8.0 = 2 ^ 3 quantization bits #define Dithering_NoiseBoost 1.0 #define Dithering_Wide 1.0 -#define DITHER_IN_LINEAR_SPACE 0 -#define DITHER_GENERATE_NOISE 0 float3 overlay( float3 a, float3 b ) { @@ -284,70 +282,9 @@ float3 BlueNoise3( float2 n, float x ) } -float Noise( float2 n, float x ) -{ - n += x; - -#if 1 - return frac( sin( dot( n.xy, float2( 12.9898, 78.233 ) ) ) * 43758.5453 ) * 2.0 - 1.0; -#else - //return BlueNoise( n, 55.0 ); - return BlueNoise( n, 1.0 ); - - //return InterleavedGradientNoise( n ) * 2.0 - 1.0; -#endif -} - -// Step 1 in generation of the dither source texture. -float Step1( float2 uv, float n ) -{ - float a = 1.0, b = 2.0, c = -12.0, t = 1.0; - - return ( 1.0 / ( a * 4.0 + b * 4.0 - c ) ) * ( - Noise( uv + float2( -1.0, -1.0 ) * t, n ) * a + - Noise( uv + float2( 0.0, -1.0 ) * t, n ) * b + - Noise( uv + float2( 1.0, -1.0 ) * t, n ) * a + - Noise( uv + float2( -1.0, 0.0 ) * t, n ) * b + - Noise( uv + float2( 0.0, 0.0 ) * t, n ) * c + - Noise( uv + float2( 1.0, 0.0 ) * t, n ) * b + - Noise( uv + float2( -1.0, 1.0 ) * t, n ) * a + - Noise( uv + float2( 0.0, 1.0 ) * t, n ) * b + - Noise( uv + float2( 1.0, 1.0 ) * t, n ) * a + - 0.0 ); -} - -// Step 2 in generation of the dither source texture. -float Step2( float2 uv, float n ) -{ - float a = 1.0, b = 2.0, c = -2.0, t = 1.0; - -#if DITHER_IN_LINEAR_SPACE - return ( 1.0 / ( a * 4.0 + b * 4.0 - c ) ) * ( -#else - return ( 4.0 / ( a * 4.0 + b * 4.0 - c ) ) * ( -#endif - Step1( uv + float2( -1.0, -1.0 ) * t, n ) * a + - Step1( uv + float2( 0.0, -1.0 ) * t, n ) * b + - Step1( uv + float2( 1.0, -1.0 ) * t, n ) * a + - Step1( uv + float2( -1.0, 0.0 ) * t, n ) * b + - Step1( uv + float2( 0.0, 0.0 ) * t, n ) * c + - Step1( uv + float2( 1.0, 0.0 ) * t, n ) * b + - Step1( uv + float2( -1.0, 1.0 ) * t, n ) * a + - Step1( uv + float2( 0.0, 1.0 ) * t, n ) * b + - Step1( uv + float2( 1.0, 1.0 ) * t, n ) * a + - 0.0 ); -} - // Used for stills. float3 Step3( float2 uv ) { -#if DITHER_GENERATE_NOISE - float a = Step2( uv, 0.07 ); - float b = Step2( uv, 0.11 ); - float c = Step2( uv, 0.13 ); - - return float3( a, b, c ); -#else float3 noise = BlueNoise3( uv, 0.0 ); #if 1 @@ -359,19 +296,11 @@ float3 Step3( float2 uv ) #endif return noise; -#endif } // Used for temporal dither. float3 Step3T( float2 uv ) { -#if DITHER_GENERATE_NOISE - float a = Step2( uv, 0.07 * fract( rpJitterTexOffset.z ) ); - float b = Step2( uv, 0.11 * fract( rpJitterTexOffset.z ) ); - float c = Step2( uv, 0.13 * fract( rpJitterTexOffset.z ) ); - - return float3( a, b, c ); -#else float3 noise = BlueNoise3( uv, 1.0 ); #if 1 @@ -383,7 +312,6 @@ float3 Step3T( float2 uv ) #endif return noise; -#endif } @@ -395,86 +323,32 @@ void DitheringPass( inout float4 fragColor, PS_IN fragment ) float3 color = fragColor.rgb; #if 0 -// BOTTOM: Show bands. if( uv2.y >= 0.975 ) { - // quantized signal - color = float3( uv2.x ); + // BOTTOM: Show bands. + color = _float3( uv2.x ); - // dithered still - //color = floor( color * Dithering_QuantizationSteps + Step3( uv ) * Dithering_NoiseBoost ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); } else if( uv2.y >= 0.95 ) { // quantized signal - color = float3( uv2.x ); + color = _float3( uv2.x ); color = floor( color * Dithering_QuantizationSteps ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); } else if( uv2.y >= 0.925 ) { // quantized signal dithered temporally - color = float3( uv2.x ); + color = _float3( uv2.x ); color = floor( color * Dithering_QuantizationSteps + Step3( uv ) * Dithering_NoiseBoost ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); } - // TOP: Show dither texture. else if( uv2.y >= 0.9 ) { + // TOP: Show dither texture. color = Step3( uv ) * ( 0.25 * Dithering_NoiseBoost ) + 0.5; } else #endif { - -#if DITHER_IN_LINEAR_SPACE - - color = Linear3( color ); - - // Add grain in linear space. -#if 0 - // Slow more correct solutions. -#if 1 - // Too expensive. - // Helps understand the fast solutions. - float3 amount = Linear3( Srgb3( color ) + ( Dithering_NoiseBoost / Dithering_QuantizationSteps ) ) - color; -#else - // Less too expensive. - float luma = PhotoLuma( color ); - - // Implement this as a texture lookup table. - float amount = Linear1( Srgb1( luma ) + ( Dithering_NoiseBoost / Dithering_QuantizationSteps ) ) - luma; -#endif - -#else - // Fast solutions. -#if 1 - // Hack 1 (fastest). - // For HDR need saturate() around luma. - float luma = PhotoLuma( color ); - float amount = mix( - Linear1( Dithering_NoiseBoost / Dithering_QuantizationSteps ), - Linear1( ( Dithering_NoiseBoost / Dithering_QuantizationSteps ) + 1.0 ) - 1.0, - luma ); -#else - // Hack 2 (slower?). - // For HDR need saturate() around color in mix(). - float3 amount = mix( - float3( Linear1( Dithering_NoiseBoost / Dithering_QuantizationSteps ) ), - float3( Linear1( ( Dithering_NoiseBoost / Dithering_QuantizationSteps ) + 1.0 ) - 1.0 ), - color ); -#endif - -#endif - color += Step3T( uv ) * amount;// * Dithering_NoiseBoost; - - // The following represents hardware linear->sRGB xform - // which happens on sRGB formatted render targets, - // except using a lot less bits/pixel. - color = max( float3( 0.0 ), color ); - color = Srgb3( color ); - color = floor( color * Dithering_QuantizationSteps ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); - -#else - #if 0 if( uv2.x <= 0.5 ) { @@ -486,8 +360,6 @@ void DitheringPass( inout float4 fragColor, PS_IN fragment ) { color = floor( 0.5 + color * ( Dithering_QuantizationSteps + Dithering_Wide - 1.0 ) + ( -Dithering_Wide * 0.5 ) + Step3T( uv ) * ( Dithering_Wide ) ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); } -#endif - } fragColor.rgb = color; diff --git a/neo/shaders/builtin/post/retro_genesis.ps.hlsl b/neo/shaders/builtin/post/retro_genesis.ps.hlsl index 9e3c4db1..fcab69be 100644 --- a/neo/shaders/builtin/post/retro_genesis.ps.hlsl +++ b/neo/shaders/builtin/post/retro_genesis.ps.hlsl @@ -50,8 +50,16 @@ struct PS_OUT // *INDENT-ON* -#define RESOLUTION_DIVISOR 4.0 -#define NUM_COLORS 64 +#define RESOLUTION_DIVISOR 4.0 +#define NUM_COLORS 64 +#define Dithering_QuantizationSteps 8.0 // 8.0 = 2 ^ 3 quantization bits + +float3 Quantize( float3 color, float3 period ) +{ + return floor( color * Dithering_QuantizationSteps ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); + + //return floor( ( color + period / 2.0 ) / period ) * period; +} // find nearest palette color using Euclidean distance @@ -74,11 +82,6 @@ float3 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) return pal[idx]; } -float Quantize( float inp, float period ) -{ - return floor( ( inp + period / 2.0 ) / period ) * period; -} - #define RGB(r, g, b) float3(float(r)/255.0, float(g)/255.0, float(b)/255.0) void main( PS_IN fragment, out PS_OUT result ) @@ -92,23 +95,19 @@ void main( PS_IN fragment, out PS_OUT result ) // 8 * 8 * 8 = 512 colors // although only 61 colors were available on the screen at the same time but we ignore this for now - const int quantizationSteps = 8; + const int quantizationSteps = Dithering_QuantizationSteps; float3 quantizationPeriod = _float3( 1.0 / ( quantizationSteps - 1 ) ); // get pixellated base color float3 color = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; // add Bayer 8x8 dithering - float2 uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / 1.0 ); + float2 uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / 2.0 ); float dither = DitherArray8x8( uvPixellated ) - 0.5; color.rgb += float3( dither, dither, dither ) * quantizationPeriod; // find closest color match from Sega Mega Drive color palette - color = float3( - Quantize( color.r, quantizationPeriod.r ), - Quantize( color.g, quantizationPeriod.g ), - Quantize( color.b, quantizationPeriod.b ) - ); + color = Quantize( color, quantizationPeriod ); //color = LinearSearch( color.rgb, palette ); //color = float4( BinarySearch( color.rgb, palette ), 1.0 ); diff --git a/neo/shaders/builtin/post/retro_ps1.ps.hlsl b/neo/shaders/builtin/post/retro_ps1.ps.hlsl index d475f3dc..5684521e 100644 --- a/neo/shaders/builtin/post/retro_ps1.ps.hlsl +++ b/neo/shaders/builtin/post/retro_ps1.ps.hlsl @@ -51,12 +51,16 @@ struct PS_OUT #define RESOLUTION_DIVISOR 4.0 +#define Dithering_QuantizationSteps 32.0 // 8.0 = 2 ^ 3 quantization bits - -float Quantize( float inp, float period ) +float3 Quantize( float3 color, float3 period ) { - return floor( ( inp + period / 2.0 ) / period ) * period; + return floor( color * Dithering_QuantizationSteps ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); + + //return floor( ( color + period / 2.0 ) / period ) * period; } + + void main( PS_IN fragment, out PS_OUT result ) { float2 uv = ( fragment.texcoord0 ); @@ -66,23 +70,56 @@ void main( PS_IN fragment, out PS_OUT result ) // 2^5 = 32 // 32 * 32 * 32 = 32768 colors - const int quantizationSteps = 32; + const int quantizationSteps = Dithering_QuantizationSteps; float3 quantizationPeriod = _float3( 1.0 / ( quantizationSteps - 1 ) ); // get pixellated base color float3 color = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; // add Bayer 8x8 dithering - float2 uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / 1.0 ); - float dither = DitherArray8x8( uvDither ) - 0.5; - color.rgb += float3( dither, dither, dither ) * quantizationPeriod; +#if 0 + // this looks awesome with 3 bits per color channel + float2 uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / 2.0 ); +#else + // more faithful to the PSX look + float2 uvDither = fragment.position.xy / RESOLUTION_DIVISOR; +#endif + + float dither = DitherArray8x8( uvDither ) - 0.5; + +#if 0 + if( uv.y < 0.05 ) + { + color = _float3( uv.x ); + } + else if( uv.y < 0.1 ) + { + // quantized signal + color = _float3( uv.x ); + color = Quantize( color, quantizationPeriod ); + } + else if( uv.y < 0.15 ) + { + // quantized signal dithered + color = _float3( uv.x ); + color = Quantize( color, quantizationPeriod ); + + color.rgb += float3( dither, dither, dither ) * quantizationPeriod; + } + else if( uv.y < 0.2 ) + { + color.rgb = float3( dither, dither, dither ) * quantizationPeriod; + } + else +#endif + { + color.rgb += float3( dither, dither, dither ) * quantizationPeriod; + + // PSX color quantization + color = Quantize( color, quantizationPeriod ); + } + - // PSX color quantization - color = float3( - Quantize( color.r, quantizationPeriod.r ), - Quantize( color.g, quantizationPeriod.g ), - Quantize( color.b, quantizationPeriod.b ) - ); //color = t_BaseColor.Sample( samp0, uv ).rgb; diff --git a/neo/shaders/global_inc.hlsl b/neo/shaders/global_inc.hlsl index c7712c92..70a09fec 100644 --- a/neo/shaders/global_inc.hlsl +++ b/neo/shaders/global_inc.hlsl @@ -366,6 +366,7 @@ float rand( float2 co ) #define _float3( x ) float3( x, x, x ) #define _float4( x ) float4( x, x, x, x ) #define _int2( x ) int2( x, x ) +#define _int3( x ) int3( x, x, x ) #define vec2 float2 #define vec3 float3 #define vec4 float4 From cbe6147f034a64884bc369f47468d82591316169 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Mon, 1 Jan 2024 16:45:17 +0100 Subject: [PATCH 13/37] Tweaked dithering and added more modes --- base/devtools.cfg | 2 +- neo/renderer/RenderBackend.cpp | 14 +++++++-- neo/renderer/RenderCommon.h | 4 ++- neo/renderer/RenderSystem_init.cpp | 2 +- neo/shaders/builtin/post/retro_c64.ps.hlsl | 9 ++++-- .../builtin/post/retro_genesis.ps.hlsl | 29 +++++++++++++++++-- neo/shaders/builtin/post/retro_ps1.ps.hlsl | 6 ---- 7 files changed, 50 insertions(+), 16 deletions(-) diff --git a/base/devtools.cfg b/base/devtools.cfg index c3e4b73f..1388508f 100644 --- a/base/devtools.cfg +++ b/base/devtools.cfg @@ -9,7 +9,7 @@ bind "F3" "toggle r_forceAmbient 0.5 1.0 0" bind "F4" "toggle r_skipInteractions" bind "F5" "savegame quick" bind "F6" "toggle r_showLightGrid 1 3 4 0" -bind "F7" "toggle r_renderMode 0 1 2 3" +bind "F7" "toggle r_renderMode 0 1 2 3 4 5" bind "F8" "toggle r_useCRTPostFX 0 1 2" bind "F9" "loadgame quick" //bind "F10" "toggle com_fixedTic" diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 2d7a41ce..6a55e4b9 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -6114,15 +6114,21 @@ void idRenderBackend::PostProcess( const void* data ) GL_SelectTexture( 1 ); globalImages->blueNoiseImage256->Bind(); - if( r_renderMode.GetInteger() == 1 ) + float jitterTexScale[4] = {}; + + if( r_renderMode.GetInteger() == RENDERMODE_C64 || r_renderMode.GetInteger() == RENDERMODE_C64_HIGHRES ) { + jitterTexScale[0] = r_renderMode.GetInteger() == RENDERMODE_C64_HIGHRES ? 2.0 : 1.0; + renderProgManager.BindShader_PostProcess_RetroC64(); } - else if( r_renderMode.GetInteger() == 2 ) + else if( r_renderMode.GetInteger() == RENDERMODE_GENESIS || r_renderMode.GetInteger() == RENDERMODE_GENESIS_HIGHRES ) { + jitterTexScale[0] = r_renderMode.GetInteger() == RENDERMODE_GENESIS_HIGHRES ? 2.0 : 1.0; + renderProgManager.BindShader_PostProcess_RetroGenesis(); } - else if( r_renderMode.GetInteger() == 3 ) + else if( r_renderMode.GetInteger() == RENDERMODE_PSX ) { renderProgManager.BindShader_PostProcess_RetroPSX(); } @@ -6131,6 +6137,8 @@ void idRenderBackend::PostProcess( const void* data ) renderProgManager.BindShader_PostProcess(); } + SetFragmentParm( RENDERPARM_JITTERTEXSCALE, jitterTexScale ); // rpWindowCoord + float jitterTexOffset[4]; jitterTexOffset[0] = 1.0f / globalImages->blueNoiseImage256->GetUploadWidth(); jitterTexOffset[1] = 1.0f / globalImages->blueNoiseImage256->GetUploadHeight(); diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index 94b7c10a..e58eb3b4 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -1301,7 +1301,9 @@ enum RenderMode { RENDERMODE_DOOM, RENDERMODE_C64, - RENDERMODE_MEGADRIVE, + RENDERMODE_C64_HIGHRES, + RENDERMODE_GENESIS, + RENDERMODE_GENESIS_HIGHRES, RENDERMODE_PSX, }; diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index fdb4e832..12522503 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -307,7 +307,7 @@ idCVar r_useCRTPostFX( "r_useCRTPostFX", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVA idCVar r_crtCurvature( "r_crtCurvature", "2", CVAR_RENDERER | CVAR_FLOAT, "rounded borders" ); idCVar r_crtVignette( "r_crtVignette", "0.8", CVAR_RENDERER | CVAR_FLOAT, "fading into the borders" ); -idCVar r_renderMode( "r_renderMode", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "0 = Doom, 1 = Commodore 64, 3 = Sega Genesis, 4 = Sony PSX", 0, 4 ); +idCVar r_renderMode( "r_renderMode", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "0 = Doom, 1 = Commodore 64 240p, 2 = Commodore 64 480i, 3 = Sega Genesis 240p, 4 = Sega Genesis 480i, 5 = Sony PSX 240p", 0, 5 ); // RB end const char* fileExten[4] = { "tga", "png", "jpg", "exr" }; diff --git a/neo/shaders/builtin/post/retro_c64.ps.hlsl b/neo/shaders/builtin/post/retro_c64.ps.hlsl index de622bcc..52a616d1 100644 --- a/neo/shaders/builtin/post/retro_c64.ps.hlsl +++ b/neo/shaders/builtin/post/retro_c64.ps.hlsl @@ -153,8 +153,13 @@ void main( PS_IN fragment, out PS_OUT result ) float3 color = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; // add Bayer 8x8 dithering - //float2 uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / 2.0 ); - float dither = DitherArray8x8( uvPixellated ) - 0.5; + float2 uvDither = uvPixellated; + //if( rpJitterTexScale.x > 1.0 ) + { + uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / rpJitterTexScale.x ); + } + float dither = DitherArray8x8( uvDither ) - 0.5; + color.rgb += float3( dither, dither, dither ) * quantizationPeriod; diff --git a/neo/shaders/builtin/post/retro_genesis.ps.hlsl b/neo/shaders/builtin/post/retro_genesis.ps.hlsl index fcab69be..3b5f8817 100644 --- a/neo/shaders/builtin/post/retro_genesis.ps.hlsl +++ b/neo/shaders/builtin/post/retro_genesis.ps.hlsl @@ -62,6 +62,23 @@ float3 Quantize( float3 color, float3 period ) } +float3 BlueNoise3( float2 n, float x ) +{ + float2 uv = n.xy * rpJitterTexOffset.xy; + + float3 noise = t_BlueNoise.Sample( samp1, uv ).rgb; + + noise = frac( noise + c_goldenRatioConjugate * rpJitterTexOffset.w * x ); + + noise.x = RemapNoiseTriErp( noise.x ); + noise.y = RemapNoiseTriErp( noise.y ); + noise.z = RemapNoiseTriErp( noise.z ); + + noise = noise * 2.0 - 1.0; + + return noise; +} + // find nearest palette color using Euclidean distance float3 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) { @@ -101,9 +118,17 @@ void main( PS_IN fragment, out PS_OUT result ) // get pixellated base color float3 color = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; + //color = _float3( uv.x ); + //color = Quantize( color, quantizationPeriod ); + // add Bayer 8x8 dithering - float2 uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / 2.0 ); - float dither = DitherArray8x8( uvPixellated ) - 0.5; + float2 uvDither = uvPixellated; + //if( rpJitterTexScale.x > 1.0 ) + { + uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / rpJitterTexScale.x ); + } + float dither = DitherArray8x8( uvDither ) - 0.5; + color.rgb += float3( dither, dither, dither ) * quantizationPeriod; // find closest color match from Sega Mega Drive color palette diff --git a/neo/shaders/builtin/post/retro_ps1.ps.hlsl b/neo/shaders/builtin/post/retro_ps1.ps.hlsl index 5684521e..897c4880 100644 --- a/neo/shaders/builtin/post/retro_ps1.ps.hlsl +++ b/neo/shaders/builtin/post/retro_ps1.ps.hlsl @@ -77,13 +77,7 @@ void main( PS_IN fragment, out PS_OUT result ) float3 color = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; // add Bayer 8x8 dithering -#if 0 - // this looks awesome with 3 bits per color channel - float2 uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / 2.0 ); -#else - // more faithful to the PSX look float2 uvDither = fragment.position.xy / RESOLUTION_DIVISOR; -#endif float dither = DitherArray8x8( uvDither ) - 0.5; From 610157fe8f2bbfee0b5f32d82767ccd8d845a136 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Wed, 3 Jan 2024 15:54:58 +0100 Subject: [PATCH 14/37] Added menu options for Retro modes --- README.md | 4 +- neo/d3xp/menus/MenuScreen.h | 6 +- .../menus/MenuScreen_Shell_SystemOptions.cpp | 93 ++++++++++++++----- neo/renderer/BinaryImage.cpp | 1 - neo/renderer/Material.cpp | 11 +-- neo/renderer/NVRHI/Image_NVRHI.cpp | 8 +- neo/renderer/NVRHI/RenderBackend_NVRHI.cpp | 20 ++-- neo/renderer/RenderBackend.cpp | 12 +-- neo/renderer/RenderCommon.h | 6 +- neo/renderer/RenderProgs.cpp | 1 - neo/renderer/RenderSystem_init.cpp | 10 +- neo/shaders/builtin/post/retro_c64.ps.hlsl | 6 +- .../builtin/post/retro_genesis.ps.hlsl | 6 +- neo/shaders/builtin/post/retro_ps1.ps.hlsl | 4 +- 14 files changed, 112 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index f093db43..b4af8fec 100644 --- a/README.md +++ b/README.md @@ -639,8 +639,10 @@ r_graphicsAPI | Default DX12, can be either DX12 or Vul r_antiAliasing | Different Anti-Aliasing modes r_exposure [0 .. 1] | Default 0.5, controls brightness and affects HDR -> sRGB Rec. 709 exposure key. This is what you change in the video brightness options r_useSSAO [0 .. 1] | Use Screen Space Ambient Occlusion to darken the corners in the scene and give it more depth -r_useFilmicPostProcessing [0, 1] | Apply several post process effects to mimic a filmic look r_forceAmbient | Default 0.5, controls additional brightness by Global Illumination +r_useFilmicPostFX [0, 1] | Apply several post process effects to mimic a filmic look +r_useCRTPostFX [0, 1] | CRT monitor/TV filter +r_renderMode [0 .. 5] | Default 0 = Doom 3, 1 = Commodore 64, 2 = Commodore 64 Highres, 3 = Sega Genesis, 4 = Sega Genesis Highres, 5 = Sony PSX ## Modding Support Name | Description diff --git a/neo/d3xp/menus/MenuScreen.h b/neo/d3xp/menus/MenuScreen.h index f5f1b0a3..7cc58154 100755 --- a/neo/d3xp/menus/MenuScreen.h +++ b/neo/d3xp/menus/MenuScreen.h @@ -1368,9 +1368,11 @@ public: SYSTEM_FIELD_VSYNC, SYSTEM_FIELD_ANTIALIASING, // RB begin - SYSTEM_FIELD_POSTFX, - SYSTEM_FIELD_SSAO, + SYSTEM_FIELD_RENDERMODE, SYSTEM_FIELD_AMBIENT_BRIGHTNESS, + SYSTEM_FIELD_SSAO, + SYSTEM_FIELD_FILMIC_POSTFX, + SYSTEM_FIELD_CRT_POSTFX, // RB end SYSTEM_FIELD_BRIGHTNESS, SYSTEM_FIELD_VOLUME, diff --git a/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp b/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp index 3bb0259e..ad9d113b 100755 --- a/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp +++ b/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp @@ -119,10 +119,19 @@ void idMenuScreen_Shell_SystemOptions::Initialize( idMenuHandler* data ) // RB begin control = new( TAG_SWF ) idMenuWidget_ControlButton(); control->SetOptionType( OPTION_SLIDER_TEXT ); - control->SetLabel( "Filmic VFX" ); - control->SetDataSource( &systemData, idMenuDataSource_SystemSettings::SYSTEM_FIELD_POSTFX ); + control->SetLabel( "Render Mode" ); + control->SetDataSource( &systemData, idMenuDataSource_SystemSettings::SYSTEM_FIELD_RENDERMODE ); control->SetupEvents( DEFAULT_REPEAT_TIME, options->GetChildren().Num() ); - control->AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, idMenuDataSource_SystemSettings::SYSTEM_FIELD_POSTFX ); + control->AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, idMenuDataSource_SystemSettings::SYSTEM_FIELD_RENDERMODE ); + options->AddChild( control ); + + control = new( TAG_SWF ) idMenuWidget_ControlButton(); + control->SetOptionType( OPTION_SLIDER_BAR ); + control->SetLabel( "Ambient Lighting" ); + control->SetDescription( "Sets the amount of indirect lighting. Needed for modern PBR reflections" ); + control->SetDataSource( &systemData, idMenuDataSource_SystemSettings::SYSTEM_FIELD_AMBIENT_BRIGHTNESS ); + control->SetupEvents( 2, options->GetChildren().Num() ); + control->AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, idMenuDataSource_SystemSettings::SYSTEM_FIELD_AMBIENT_BRIGHTNESS ); options->AddChild( control ); control = new( TAG_SWF ) idMenuWidget_ControlButton(); @@ -142,12 +151,19 @@ void idMenuScreen_Shell_SystemOptions::Initialize( idMenuHandler* data ) options->AddChild( control );*/ control = new( TAG_SWF ) idMenuWidget_ControlButton(); - control->SetOptionType( OPTION_SLIDER_BAR ); - control->SetLabel( "Ambient Lighting" ); - control->SetDescription( "Sets the amount of indirect lighting. Needed for modern PBR reflections" ); - control->SetDataSource( &systemData, idMenuDataSource_SystemSettings::SYSTEM_FIELD_AMBIENT_BRIGHTNESS ); - control->SetupEvents( 2, options->GetChildren().Num() ); - control->AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, idMenuDataSource_SystemSettings::SYSTEM_FIELD_SSAO ); + control->SetOptionType( OPTION_SLIDER_TEXT ); + control->SetLabel( "Filmic Post FX" ); + control->SetDataSource( &systemData, idMenuDataSource_SystemSettings::SYSTEM_FIELD_FILMIC_POSTFX ); + control->SetupEvents( DEFAULT_REPEAT_TIME, options->GetChildren().Num() ); + control->AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, idMenuDataSource_SystemSettings::SYSTEM_FIELD_FILMIC_POSTFX ); + options->AddChild( control ); + + control = new( TAG_SWF ) idMenuWidget_ControlButton(); + control->SetOptionType( OPTION_SLIDER_TEXT ); + control->SetLabel( "CRT Filter" ); + control->SetDataSource( &systemData, idMenuDataSource_SystemSettings::SYSTEM_FIELD_CRT_POSTFX ); + control->SetupEvents( DEFAULT_REPEAT_TIME, options->GetChildren().Num() ); + control->AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_COMMAND, idMenuDataSource_SystemSettings::SYSTEM_FIELD_CRT_POSTFX ); options->AddChild( control ); // RB end @@ -593,13 +609,27 @@ void idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings::AdjustFi break; } // RB begin - case SYSTEM_FIELD_POSTFX: + case SYSTEM_FIELD_RENDERMODE: + { + static const int numValues = 6; + static const int values[numValues] = { 0, 1, 2, 3, 4, 5 }; + r_renderMode.SetInteger( AdjustOption( r_renderMode.GetInteger(), values, numValues, adjustAmount ) ); + break; + } + case SYSTEM_FIELD_FILMIC_POSTFX: { static const int numValues = 2; static const int values[numValues] = { 0, 1 }; r_useFilmicPostFX.SetInteger( AdjustOption( r_useFilmicPostFX.GetInteger(), values, numValues, adjustAmount ) ); break; } + case SYSTEM_FIELD_CRT_POSTFX: + { + static const int numValues = 3; + static const int values[numValues] = { 0, 1, 2 }; + r_useCRTPostFX.SetInteger( AdjustOption( r_useCRTPostFX.GetInteger(), values, numValues, adjustAmount ) ); + break; + } /* case SYSTEM_FIELD_SHADOWMAPPING: { @@ -759,7 +789,24 @@ idSWFScriptVar idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings return values[ r_antiAliasing.GetInteger() ]; } - case SYSTEM_FIELD_POSTFX: + case SYSTEM_FIELD_RENDERMODE: + { + static const int numValues = 6; + static const char* values[numValues] = + { + "Doom 3", + "Commodore 64", + "Commodore 64 Highres", + "Sega Genesis", + "Sega Genesis Highres", + "Sony PSX", + }; + + compile_time_assert( numValues == ( RENDERMODE_PSX + 1 ) ); + + return values[ r_renderMode.GetInteger() ]; + } + case SYSTEM_FIELD_FILMIC_POSTFX: if( r_useFilmicPostFX.GetInteger() > 0 ) { return "#str_swf_enabled"; @@ -768,20 +815,19 @@ idSWFScriptVar idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings { return "#str_swf_disabled"; } - //return va( "%dx", idMath::IPow( 2, r_motionBlur.GetInteger() ) ); - // RB begin - /* - case SYSTEM_FIELD_SHADOWMAPPING: - if( r_useShadowMapping.GetInteger() == 1 ) + case SYSTEM_FIELD_CRT_POSTFX: + { + static const int numValues = 3; + static const char* values[numValues] = { - return "#str_swf_enabled"; - } - else - { - return "#str_swf_disabled"; - } - */ + "#str_swf_disabled", + "Mattias CRT", + "Newpixie CRT", + }; + + return values[ r_useCRTPostFX.GetInteger() ]; + } //case SYSTEM_FIELD_LODBIAS: // return LinearAdjust( r_lodBias.GetFloat(), -1.0f, 1.0f, 0.0f, 100.0f ); @@ -798,7 +844,6 @@ idSWFScriptVar idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings case SYSTEM_FIELD_AMBIENT_BRIGHTNESS: return LinearAdjust( r_forceAmbient.GetFloat(), 0.0f, 1.0f, 0.0f, 100.0f ); - // RB end case SYSTEM_FIELD_BRIGHTNESS: return LinearAdjust( r_exposure.GetFloat(), 0.0f, 1.0f, 0.0f, 100.0f ); diff --git a/neo/renderer/BinaryImage.cpp b/neo/renderer/BinaryImage.cpp index a1a50c7f..ddfd7271 100644 --- a/neo/renderer/BinaryImage.cpp +++ b/neo/renderer/BinaryImage.cpp @@ -46,7 +46,6 @@ If you have questions concerning this license or the applicable additional terms #include "../libs/mesa/format_r11g11b10f.h" idCVar image_highQualityCompression( "image_highQualityCompression", "0", CVAR_BOOL, "Use high quality (slow) compression" ); -idCVar r_useHighQualitySky( "r_useHighQualitySky", "1", CVAR_BOOL | CVAR_ARCHIVE, "Use high quality skyboxes" ); /* ======================== diff --git a/neo/renderer/Material.cpp b/neo/renderer/Material.cpp index 2584eb33..18d4a790 100644 --- a/neo/renderer/Material.cpp +++ b/neo/renderer/Material.cpp @@ -73,8 +73,6 @@ typedef struct mtrParsingData_s bool forceOverlays; } mtrParsingData_t; -extern idCVar r_useHighQualitySky; - idCVar r_forceSoundOpAmplitude( "r_forceSoundOpAmplitude", "0", CVAR_FLOAT, "Don't call into the sound system for amplitudes" ); /* @@ -1870,14 +1868,7 @@ void idMaterial::ParseStage( idLexer& src, const textureRepeat_t trpDefault ) } if( !token.Icmp( "uncompressedCubeMap" ) ) { - if( r_useHighQualitySky.GetBool() ) - { - td = TD_HIGHQUALITY_CUBE; // motorsep 05-17-2015; token to mark cubemap/skybox to be uncompressed texture - } - else - { - td = TD_LOWQUALITY_CUBE; - } + td = TD_HIGHQUALITY_CUBE; // motorsep 05-17-2015; token to mark cubemap/skybox to be uncompressed texture continue; } if( !token.Icmp( "nopicmip" ) ) diff --git a/neo/renderer/NVRHI/Image_NVRHI.cpp b/neo/renderer/NVRHI/Image_NVRHI.cpp index df2454a9..38c62fa4 100644 --- a/neo/renderer/NVRHI/Image_NVRHI.cpp +++ b/neo/renderer/NVRHI/Image_NVRHI.cpp @@ -30,11 +30,7 @@ If you have questions concerning this license or the applicable additional terms #include "precompiled.h" #pragma hdrstop -/* -================================================================================================ -Contains the Image implementation for OpenGL. -================================================================================================ -*/ +idCVar image_pixelLook( "image_pixelLook", "0", CVAR_BOOL | CVAR_ARCHIVE, "Turn off linear filtering on most textures to achieve the 90s software renderer look" ); #include "../RenderCommon.h" @@ -303,7 +299,7 @@ idImage::GetSampler */ void* idImage::GetSampler( SamplerCache& samplerCache ) { - if( r_renderMode.GetInteger() == RENDERMODE_PSX ) + if( R_UsePixelatedLook() ) { if( !sampler ) { diff --git a/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp b/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp index e077960d..67e13769 100644 --- a/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp +++ b/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp @@ -733,7 +733,7 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) desc[2].bindings[5].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 10 )->GetTextureID(); } - if( r_renderMode.GetInteger() == RENDERMODE_PSX ) + if( R_UsePixelatedLook() ) { if( desc[3].bindings.empty() ) { @@ -823,7 +823,7 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) desc[2].bindings[5].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 10 )->GetTextureID(); } - if( r_renderMode.GetInteger() == RENDERMODE_PSX ) + if( R_UsePixelatedLook() ) { if( desc[3].bindings.empty() ) { @@ -938,7 +938,7 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) } // samplers: 3 - if( r_renderMode.GetInteger() == RENDERMODE_PSX ) + if( R_UsePixelatedLook() ) { if( desc[3].bindings.empty() ) { @@ -1024,7 +1024,7 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) } // samplers: 3 - if( r_renderMode.GetInteger() == RENDERMODE_PSX ) + if( R_UsePixelatedLook() ) { if( desc[3].bindings.empty() ) { @@ -1113,7 +1113,7 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) } // samplers: 3 - if( r_renderMode.GetInteger() == RENDERMODE_PSX ) + if( R_UsePixelatedLook() ) { if( desc[3].bindings.empty() ) { @@ -1218,7 +1218,7 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) } // samplers: 3 - if( r_renderMode.GetInteger() == RENDERMODE_PSX ) + if( R_UsePixelatedLook() ) { if( desc[3].bindings.empty() ) { @@ -1466,7 +1466,7 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) desc[0].bindings[3].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 2 )->GetTextureID(); } - if( r_renderMode.GetInteger() == RENDERMODE_PSX ) + if( R_UsePixelatedLook() ) { if( desc[1].bindings.empty() ) { @@ -1558,7 +1558,7 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) bindings[1].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 1 )->GetTextureID(); } - if( r_renderMode.GetInteger() == RENDERMODE_PSX ) + if( R_UsePixelatedLook() ) { if( desc[2].bindings.empty() ) { @@ -1622,7 +1622,7 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) bindings[1].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 1 )->GetTextureID(); } - if( r_renderMode.GetInteger() == RENDERMODE_PSX ) + if( R_UsePixelatedLook() ) { if( desc[2].bindings.empty() ) { @@ -1672,7 +1672,7 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) desc[0].bindings[3].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 2 )->GetTextureID(); } - if( r_renderMode.GetInteger() == RENDERMODE_PSX ) + if( R_UsePixelatedLook() ) { if( desc[1].bindings.empty() ) { diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 6a55e4b9..6eea515a 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -2172,11 +2172,11 @@ void idRenderBackend::AmbientPass( const drawSurf_t* const* drawSurfs, int numDr specularColor = lightColor;// * 0.5f; float ambientBoost = 1.0f; - if( !r_usePBR.GetBool() ) - { - ambientBoost += r_useSSAO.GetBool() ? 0.2f : 0.0f; - ambientBoost *= 1.1f; - } + //if( !r_usePBR.GetBool() ) + //{ + // ambientBoost += r_useSSAO.GetBool() ? 0.2f : 0.0f; + // ambientBoost *= 1.1f; + //} ambientColor.x = r_forceAmbient.GetFloat() * ambientBoost; ambientColor.y = r_forceAmbient.GetFloat() * ambientBoost; @@ -2186,7 +2186,7 @@ void idRenderBackend::AmbientPass( const drawSurf_t* const* drawSurfs, int numDr renderProgManager.SetRenderParm( RENDERPARM_AMBIENT_COLOR, ambientColor.ToFloatPtr() ); - bool useIBL = r_usePBR.GetBool() && !fillGbuffer; + bool useIBL = !fillGbuffer; // setup renderparms assuming we will be drawing trivial surfaces first RB_SetupForFastPathInteractions( diffuseColor, specularColor ); diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index e58eb3b4..6cdf801c 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -1268,7 +1268,6 @@ extern idCVar r_hdrDebug; extern idCVar r_ldrContrastThreshold; extern idCVar r_ldrContrastOffset; -extern idCVar r_useFilmicPostFX; extern idCVar r_forceAmbient; extern idCVar r_useSSAO; @@ -1276,7 +1275,6 @@ extern idCVar r_ssaoDebug; extern idCVar r_ssaoFiltering; extern idCVar r_useHierarchicalDepthBuffer; -extern idCVar r_usePBR; extern idCVar r_pbrDebug; extern idCVar r_showViewEnvprobes; extern idCVar r_showLightGrid; // show Quake 3 style light grid points @@ -1293,6 +1291,7 @@ extern idCVar r_taaNewFrameWeight; extern idCVar r_taaMaxRadiance; extern idCVar r_taaMotionVectors; +extern idCVar r_useFilmicPostFX; extern idCVar r_useCRTPostFX; extern idCVar r_crtCurvature; extern idCVar r_crtVignette; @@ -1308,6 +1307,7 @@ enum RenderMode }; extern idCVar r_renderMode; +extern idCVar image_pixelLook; // RB end /* @@ -1318,6 +1318,8 @@ INITIALIZATION ==================================================================== */ +bool R_UsePixelatedLook(); + bool R_UseTemporalAA(); uint R_GetMSAASamples(); diff --git a/neo/renderer/RenderProgs.cpp b/neo/renderer/RenderProgs.cpp index c95dcc03..ceb9ed20 100644 --- a/neo/renderer/RenderProgs.cpp +++ b/neo/renderer/RenderProgs.cpp @@ -623,7 +623,6 @@ void idRenderProgManager::Init( nvrhi::IDevice* device ) } } - r_usePBR.ClearModified(); r_pbrDebug.ClearModified(); uniforms.SetNum( RENDERPARM_TOTAL, vec4_zero ); diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 12522503..8dfd2bfe 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -171,9 +171,6 @@ idCVar r_flareSize( "r_flareSize", "1", CVAR_RENDERER | CVAR_FLOAT, "scale the f idCVar r_useScissor( "r_useScissor", "1", CVAR_RENDERER | CVAR_BOOL, "scissor clip as portals and lights are processed" ); idCVar r_useLightDepthBounds( "r_useLightDepthBounds", "1", CVAR_RENDERER | CVAR_BOOL, "use depth bounds test on lights to reduce both shadow and interaction fill" ); idCVar r_useShadowDepthBounds( "r_useShadowDepthBounds", "1", CVAR_RENDERER | CVAR_BOOL, "use depth bounds test on individual shadow volumes to reduce shadow fill" ); -// RB begin -idCVar r_useHalfLambertLighting( "r_useHalfLambertLighting", "0", CVAR_RENDERER | CVAR_BOOL | CVAR_ARCHIVE, "use Half-Lambert lighting instead of classic Lambert, requires reloadShaders" ); -// RB end idCVar r_screenFraction( "r_screenFraction", "100", CVAR_RENDERER | CVAR_INTEGER, "for testing fill rate, the resolution of the entire screen can be changed" ); idCVar r_usePortals( "r_usePortals", "1", CVAR_RENDERER | CVAR_BOOL, " 1 = use portals to perform area culling, otherwise draw everything" ); @@ -285,8 +282,6 @@ idCVar r_ssaoDebug( "r_ssaoDebug", "0", CVAR_RENDERER | CVAR_INTEGER, "" ); idCVar r_ssaoFiltering( "r_ssaoFiltering", "0", CVAR_RENDERER | CVAR_BOOL, "" ); idCVar r_useHierarchicalDepthBuffer( "r_useHierarchicalDepthBuffer", "1", CVAR_RENDERER | CVAR_BOOL, "" ); -// RB: only change r_usePBR if you are a developer -idCVar r_usePBR( "r_usePBR", "1", CVAR_RENDERER | CVAR_ROM | CVAR_STATIC | CVAR_BOOL, "use PBR and Image Based Lighting instead of old Quake 4 style ambient lighting" ); idCVar r_pbrDebug( "r_pbrDebug", "0", CVAR_RENDERER | CVAR_INTEGER, "show which materials have PBR support (green = PBR, red = oldschool D3)" ); idCVar r_showViewEnvprobes( "r_showViewEnvprobes", "0", CVAR_RENDERER | CVAR_INTEGER, "1 = displays the bounding boxes of all view environment probes, 2 = show irradiance" ); idCVar r_showLightGrid( "r_showLightGrid", "0", CVAR_RENDERER | CVAR_INTEGER, "show Quake 3 style light grid points" ); @@ -317,6 +312,11 @@ const char* skyDirection[6] = { "_forward", "_back", "_left", "_right", "_up", " DeviceManager* deviceManager = NULL; +bool R_UsePixelatedLook() +{ + return ( r_renderMode.GetInteger() == RENDERMODE_PSX ) || image_pixelLook.GetBool(); +} + bool R_UseTemporalAA() { if( !r_useTemporalAA.GetBool() ) diff --git a/neo/shaders/builtin/post/retro_c64.ps.hlsl b/neo/shaders/builtin/post/retro_c64.ps.hlsl index 52a616d1..be36cfe4 100644 --- a/neo/shaders/builtin/post/retro_c64.ps.hlsl +++ b/neo/shaders/builtin/post/retro_c64.ps.hlsl @@ -145,15 +145,15 @@ float3 BinarySearch( float3 target, float3 pal[NUM_COLORS] ) void main( PS_IN fragment, out PS_OUT result ) { float2 uv = ( fragment.texcoord0 ); - float2 uvPixellated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; + float2 uvPixelated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; float3 quantizationPeriod = _float3( 1.0 / NUM_COLORS ); // get pixellated base color - float3 color = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; + float3 color = t_BaseColor.Sample( samp0, uvPixelated * rpWindowCoord.xy ).rgb; // add Bayer 8x8 dithering - float2 uvDither = uvPixellated; + float2 uvDither = uvPixelated; //if( rpJitterTexScale.x > 1.0 ) { uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / rpJitterTexScale.x ); diff --git a/neo/shaders/builtin/post/retro_genesis.ps.hlsl b/neo/shaders/builtin/post/retro_genesis.ps.hlsl index 3b5f8817..60b1a4ce 100644 --- a/neo/shaders/builtin/post/retro_genesis.ps.hlsl +++ b/neo/shaders/builtin/post/retro_genesis.ps.hlsl @@ -104,7 +104,7 @@ float3 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) void main( PS_IN fragment, out PS_OUT result ) { float2 uv = ( fragment.texcoord0 ); - float2 uvPixellated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; + float2 uvPixelated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; // the Sega Mega Drive has a 9 bit HW palette making a total of 512 available colors // that is 3 bit per RGB channel @@ -116,13 +116,13 @@ void main( PS_IN fragment, out PS_OUT result ) float3 quantizationPeriod = _float3( 1.0 / ( quantizationSteps - 1 ) ); // get pixellated base color - float3 color = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; + float3 color = t_BaseColor.Sample( samp0, uvPixelated * rpWindowCoord.xy ).rgb; //color = _float3( uv.x ); //color = Quantize( color, quantizationPeriod ); // add Bayer 8x8 dithering - float2 uvDither = uvPixellated; + float2 uvDither = uvPixelated; //if( rpJitterTexScale.x > 1.0 ) { uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / rpJitterTexScale.x ); diff --git a/neo/shaders/builtin/post/retro_ps1.ps.hlsl b/neo/shaders/builtin/post/retro_ps1.ps.hlsl index 897c4880..3a6ad3a3 100644 --- a/neo/shaders/builtin/post/retro_ps1.ps.hlsl +++ b/neo/shaders/builtin/post/retro_ps1.ps.hlsl @@ -64,7 +64,7 @@ float3 Quantize( float3 color, float3 period ) void main( PS_IN fragment, out PS_OUT result ) { float2 uv = ( fragment.texcoord0 ); - float2 uvPixellated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; + float2 uvPixelated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; // most Sony Playstation 1 titles used 5 bit per RGB channel // 2^5 = 32 @@ -74,7 +74,7 @@ void main( PS_IN fragment, out PS_OUT result ) float3 quantizationPeriod = _float3( 1.0 / ( quantizationSteps - 1 ) ); // get pixellated base color - float3 color = t_BaseColor.Sample( samp0, uvPixellated * rpWindowCoord.xy ).rgb; + float3 color = t_BaseColor.Sample( samp0, uvPixelated * rpWindowCoord.xy ).rgb; // add Bayer 8x8 dithering float2 uvDither = fragment.position.xy / RESOLUTION_DIVISOR; From 4879fb78ea4bcca447fb767ea8f2f4d928b0c735 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Wed, 3 Jan 2024 16:13:51 +0100 Subject: [PATCH 15/37] Changed ENGINE_BRANCH string --- neo/framework/Licensee.h | 2 +- neo/renderer/RenderSystem_init.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/framework/Licensee.h b/neo/framework/Licensee.h index 5fad2a69..dda877cd 100644 --- a/neo/framework/Licensee.h +++ b/neo/framework/Licensee.h @@ -44,7 +44,7 @@ If you have questions concerning this license or the applicable additional terms // RB end // jmarshall -#define ENGINE_BRANCH "Imgui docking" +#define ENGINE_BRANCH "Retro 80s/90s" // jmarshall end #ifdef STANDALONE diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 8dfd2bfe..9b0528ab 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -302,7 +302,7 @@ idCVar r_useCRTPostFX( "r_useCRTPostFX", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVA idCVar r_crtCurvature( "r_crtCurvature", "2", CVAR_RENDERER | CVAR_FLOAT, "rounded borders" ); idCVar r_crtVignette( "r_crtVignette", "0.8", CVAR_RENDERER | CVAR_FLOAT, "fading into the borders" ); -idCVar r_renderMode( "r_renderMode", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "0 = Doom, 1 = Commodore 64 240p, 2 = Commodore 64 480i, 3 = Sega Genesis 240p, 4 = Sega Genesis 480i, 5 = Sony PSX 240p", 0, 5 ); +idCVar r_renderMode( "r_renderMode", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "0 = Doom, 1 = Commodore 64, 2 = Commodore 64 Highres, 3 = Sega Genesis, 4 = Sega Genesis Highres, 5 = Sony PSX", 0, 5 ); // RB end const char* fileExten[4] = { "tga", "png", "jpg", "exr" }; From 4bc81a1cd7a68c67ffb8135ff8e402fbf276ef21 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Wed, 10 Jan 2024 00:04:26 +0100 Subject: [PATCH 16/37] Added Amstrad CPC 6128 mode --- base/devtools.cfg | 2 +- .../menus/MenuScreen_Shell_SystemOptions.cpp | 4 +- neo/renderer/Image_intrinsic.cpp | 45 ++- neo/renderer/RenderBackend.cpp | 6 + neo/renderer/RenderCommon.h | 2 + neo/renderer/RenderProgs.cpp | 1 + neo/renderer/RenderProgs.h | 6 + neo/renderer/RenderSystem_init.cpp | 2 +- neo/shaders/builtin/post/retro_c64.ps.hlsl | 16 + neo/shaders/builtin/post/retro_cpc.ps.hlsl | 367 ++++++++++++++++++ neo/shaders/builtin/post/retro_cpc.vs.hlsl | 55 +++ .../builtin/post/retro_genesis.ps.hlsl | 20 +- neo/shaders/global_inc.hlsl | 37 ++ neo/shaders/shaders.cfg | 2 + 14 files changed, 550 insertions(+), 15 deletions(-) create mode 100644 neo/shaders/builtin/post/retro_cpc.ps.hlsl create mode 100644 neo/shaders/builtin/post/retro_cpc.vs.hlsl diff --git a/base/devtools.cfg b/base/devtools.cfg index 1388508f..20dc3373 100644 --- a/base/devtools.cfg +++ b/base/devtools.cfg @@ -9,7 +9,7 @@ bind "F3" "toggle r_forceAmbient 0.5 1.0 0" bind "F4" "toggle r_skipInteractions" bind "F5" "savegame quick" bind "F6" "toggle r_showLightGrid 1 3 4 0" -bind "F7" "toggle r_renderMode 0 1 2 3 4 5" +bind "F7" "toggle r_renderMode 0 1 2 3 4 5 6 7" bind "F8" "toggle r_useCRTPostFX 0 1 2" bind "F9" "loadgame quick" //bind "F10" "toggle com_fixedTic" diff --git a/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp b/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp index ad9d113b..e1bc1c94 100755 --- a/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp +++ b/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp @@ -791,12 +791,14 @@ idSWFScriptVar idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings } case SYSTEM_FIELD_RENDERMODE: { - static const int numValues = 6; + static const int numValues = 8; static const char* values[numValues] = { "Doom 3", "Commodore 64", "Commodore 64 Highres", + "Amstrad CPC 6128", + "Amstrad CPC 6128 Highres", "Sega Genesis", "Sega Genesis Highres", "Sony PSX", diff --git a/neo/renderer/Image_intrinsic.cpp b/neo/renderer/Image_intrinsic.cpp index 1642d444..ad570fc2 100644 --- a/neo/renderer/Image_intrinsic.cpp +++ b/neo/renderer/Image_intrinsic.cpp @@ -3,7 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. -Copyright (C) 2013-2023 Robert Beckebans +Copyright (C) 2013-2024 Robert Beckebans Copyright (C) 2022 Stephen Pridham This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -1239,9 +1239,6 @@ CONSOLE_COMMAND( makeImageHeader, "load an image and turn it into a .h file", NU CONSOLE_COMMAND( makePaletteHeader, "load a .pal palette, build an image from it and turn it into a .h file", NULL ) { - byte* buffer; - int width = 0, height = 0; - if( args.Argc() < 2 ) { common->Printf( "USAGE: makePaletteHeader filename [exportname]\n" ); @@ -1249,16 +1246,49 @@ CONSOLE_COMMAND( makePaletteHeader, "load a .pal palette, build an image from it } idStr filename = args.Argv( 1 ); + filename.DefaultFileExtension( ".pal" ); - R_LoadImage( filename, &buffer, &width, &height, NULL, true, NULL ); - if( !buffer ) + ID_TIME_T timeStamp; + char* palBuffer; + int palBufferLen = fileSystem->ReadFile( filename, ( void** )&palBuffer, &timeStamp ); + if( palBufferLen <= 0 || palBuffer == nullptr ) { - common->Printf( "loading %s failed.\n", filename.c_str() ); return; } + // parse JASC-PAL file + idLexer src; + idToken token, token2; + + src.LoadMemory( palBuffer, palBufferLen, filename, 0 ); + + src.ExpectTokenString( "JASC" ); + src.ExpectTokenString( "-" ); + src.ExpectTokenString( "PAL" ); + int palVersion = src.ParseInt(); + + int numColors = src.ParseInt(); + + //idListFreeFile( palBuffer ); + filename.StripFileExtension(); + // TODO build image and convert to header + //byte* buffer; + //int width = 0, height = 0; + + /* idStr exportname; if( args.Argc() == 3 ) @@ -1322,4 +1352,5 @@ CONSOLE_COMMAND( makePaletteHeader, "load a .pal palette, build an image from it headerFile->Printf( "\n};\n#endif\n" ); Mem_Free( buffer ); + */ } \ No newline at end of file diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 6eea515a..da237443 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -6122,6 +6122,12 @@ void idRenderBackend::PostProcess( const void* data ) renderProgManager.BindShader_PostProcess_RetroC64(); } + else if( r_renderMode.GetInteger() == RENDERMODE_CPC || r_renderMode.GetInteger() == RENDERMODE_CPC_HIGHRES ) + { + jitterTexScale[0] = r_renderMode.GetInteger() == RENDERMODE_CPC_HIGHRES ? 2.0 : 1.0; + + renderProgManager.BindShader_PostProcess_RetroCPC(); + } else if( r_renderMode.GetInteger() == RENDERMODE_GENESIS || r_renderMode.GetInteger() == RENDERMODE_GENESIS_HIGHRES ) { jitterTexScale[0] = r_renderMode.GetInteger() == RENDERMODE_GENESIS_HIGHRES ? 2.0 : 1.0; diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index 6cdf801c..12dc65f8 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -1301,6 +1301,8 @@ enum RenderMode RENDERMODE_DOOM, RENDERMODE_C64, RENDERMODE_C64_HIGHRES, + RENDERMODE_CPC, + RENDERMODE_CPC_HIGHRES, RENDERMODE_GENESIS, RENDERMODE_GENESIS_HIGHRES, RENDERMODE_PSX, diff --git a/neo/renderer/RenderProgs.cpp b/neo/renderer/RenderProgs.cpp index ceb9ed20..7a7cc931 100644 --- a/neo/renderer/RenderProgs.cpp +++ b/neo/renderer/RenderProgs.cpp @@ -522,6 +522,7 @@ void idRenderProgManager::Init( nvrhi::IDevice* device ) { BUILTIN_WOBBLESKY, "builtin/legacy/wobblesky", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, { BUILTIN_POSTPROCESS, "builtin/post/postprocess", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_POSTPROCESS_RETRO_C64, "builtin/post/retro_c64", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, + { BUILTIN_POSTPROCESS_RETRO_CPC, "builtin/post/retro_cpc", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_POSTPROCESS_RETRO_GENESIS, "builtin/post/retro_genesis", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_POSTPROCESS_RETRO_PSX, "builtin/post/retro_ps1", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_CRT_MATTIAS, "builtin/post/crt_mattias", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, diff --git a/neo/renderer/RenderProgs.h b/neo/renderer/RenderProgs.h index 72497da2..25f44899 100644 --- a/neo/renderer/RenderProgs.h +++ b/neo/renderer/RenderProgs.h @@ -367,6 +367,7 @@ enum BUILTIN_POSTPROCESS, // RB begin BUILTIN_POSTPROCESS_RETRO_C64, // Commodore 64 + BUILTIN_POSTPROCESS_RETRO_CPC, // Amstrad 6128 BUILTIN_POSTPROCESS_RETRO_GENESIS, // Sega Genesis / Megadrive BUILTIN_POSTPROCESS_RETRO_PSX, // Sony Playstation 1 BUILTIN_CRT_MATTIAS, @@ -827,6 +828,11 @@ public: BindShader_Builtin( BUILTIN_POSTPROCESS_RETRO_C64 ); } + void BindShader_PostProcess_RetroCPC() + { + BindShader_Builtin( BUILTIN_POSTPROCESS_RETRO_CPC ); + } + void BindShader_PostProcess_RetroGenesis() { BindShader_Builtin( BUILTIN_POSTPROCESS_RETRO_GENESIS ); diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 9b0528ab..a8dcb2f8 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -302,7 +302,7 @@ idCVar r_useCRTPostFX( "r_useCRTPostFX", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVA idCVar r_crtCurvature( "r_crtCurvature", "2", CVAR_RENDERER | CVAR_FLOAT, "rounded borders" ); idCVar r_crtVignette( "r_crtVignette", "0.8", CVAR_RENDERER | CVAR_FLOAT, "fading into the borders" ); -idCVar r_renderMode( "r_renderMode", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "0 = Doom, 1 = Commodore 64, 2 = Commodore 64 Highres, 3 = Sega Genesis, 4 = Sega Genesis Highres, 5 = Sony PSX", 0, 5 ); +idCVar r_renderMode( "r_renderMode", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "0 = Doom, 1 = Commodore 64, 2 = Commodore 64 Highres, 3 = Amstrad CPC 6128, 4 = Amstrad CPC 6128 Highres, 5 = Sega Genesis, 6 = Sega Genesis Highres, 7 = Sony PSX", 0, 7 ); // RB end const char* fileExten[4] = { "tga", "png", "jpg", "exr" }; diff --git a/neo/shaders/builtin/post/retro_c64.ps.hlsl b/neo/shaders/builtin/post/retro_c64.ps.hlsl index be36cfe4..65e1e775 100644 --- a/neo/shaders/builtin/post/retro_c64.ps.hlsl +++ b/neo/shaders/builtin/post/retro_c64.ps.hlsl @@ -152,6 +152,22 @@ void main( PS_IN fragment, out PS_OUT result ) // get pixellated base color float3 color = t_BaseColor.Sample( samp0, uvPixelated * rpWindowCoord.xy ).rgb; +#if 0 + if( uv.y < 0.125 ) + { + color = HSVToRGB( float3( uv.x, 1.0, uv.y * 8.0 ) ); + color = floor( color * NUM_COLORS ) * ( 1.0 / ( NUM_COLORS - 1.0 ) ); + + //result.color = float4( color, 1.0 ); + //return; + } + else if( uv.y < 0.1875 ) + { + color = _float3( uv.x ); + color = floor( color * NUM_COLORS ) * ( 1.0 / ( NUM_COLORS - 1.0 ) ); + } +#endif + // add Bayer 8x8 dithering float2 uvDither = uvPixelated; //if( rpJitterTexScale.x > 1.0 ) diff --git a/neo/shaders/builtin/post/retro_cpc.ps.hlsl b/neo/shaders/builtin/post/retro_cpc.ps.hlsl new file mode 100644 index 00000000..850ed405 --- /dev/null +++ b/neo/shaders/builtin/post/retro_cpc.ps.hlsl @@ -0,0 +1,367 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2024 Robert Beckebans + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code 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 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "global_inc.hlsl" + + +// *INDENT-OFF* +Texture2D t_BaseColor : register( t0 VK_DESCRIPTOR_SET( 0 ) ); +Texture2D t_BlueNoise : register( t1 VK_DESCRIPTOR_SET( 0 ) ); + +SamplerState samp0 : register(s0 VK_DESCRIPTOR_SET( 1 ) ); +SamplerState samp1 : register(s1 VK_DESCRIPTOR_SET( 1 ) ); // blue noise 256 + +struct PS_IN +{ + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; + +struct PS_OUT +{ + float4 color : SV_Target0; +}; +// *INDENT-ON* + + +#define RESOLUTION_DIVISOR 4.0 +#define NUM_COLORS 32 // original 27 + + +float LinearTweak1( float c ) +{ + return ( c <= 0.04045 ) ? c / 12.92 : pow( ( c + 0.055 ) / 1.055, 1.4 ); +} + +float3 LinearTweak3( float3 c ) +{ + return float3( Linear1( c.r ), Linear1( c.g ), Linear1( c.b ) ); +} + +// find nearest palette color using Euclidean distance +float3 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) +{ + int idx = 0; + float nd = distance( c, pal[0] ); + + for( int i = 1; i < NUM_COLORS; i++ ) + { + //float d = distance( c, pal[i] ); + float d = distance( c, ( pal[i] ) ); + + if( d < nd ) + { + nd = d; + idx = i; + } + } + + return pal[idx]; +} + +#define RGB(r, g, b) float3(float(r)/255.0, float(g)/255.0, float(b)/255.0) + + + +void main( PS_IN fragment, out PS_OUT result ) +{ + float2 uv = ( fragment.texcoord0 ); + float2 uvPixelated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; + + float3 quantizationPeriod = _float3( 1.0 / NUM_COLORS ); + + // get pixellated base color + float3 color = t_BaseColor.Sample( samp0, uvPixelated * rpWindowCoord.xy ).rgb; + +#if 0 + if( uv.y < 0.125 ) + { + color = HSVToRGB( float3( uv.x, 1.0, uv.y * 8.0 ) ); + color = floor( color * NUM_COLORS ) * ( 1.0 / ( NUM_COLORS - 1.0 ) ); + + //result.color = float4( color, 1.0 ); + //return; + } + else if( uv.y < 0.1875 ) + { + color = _float3( uv.x ); + color = floor( color * NUM_COLORS ) * ( 1.0 / ( NUM_COLORS - 1.0 ) ); + } +#endif + + // add Bayer 8x8 dithering + float2 uvDither = uvPixelated; + //if( rpJitterTexScale.x > 1.0 ) + { + uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / rpJitterTexScale.x ); + } + float dither = ( DitherArray8x8( uvDither ) - 0.5 ) * 1.0; + + color.rgb += float3( dither, dither, dither ) * quantizationPeriod; + + +#if 1 + // Amstrad CPC colors https://www.cpcwiki.eu/index.php/CPC_Palette + const float3 palette[NUM_COLORS] = + { + RGB( 0, 0, 0 ), // black + RGB( 0, 0, 128 ), // blue + RGB( 0, 0, 255 ), // bright blue + RGB( 128, 0, 0 ), // red + RGB( 128, 0, 128 ), // magenta + RGB( 128, 0, 255 ), // mauve + RGB( 255, 0, 0 ), // bright red + RGB( 255, 0, 128 ), // purple + RGB( 255, 0, 255 ), // bright magenta + RGB( 0, 128, 0 ), // green + RGB( 0, 128, 128 ), // cyan + RGB( 0, 128, 255 ), // sky blue + RGB( 128, 128, 0 ), // yellow + RGB( 128, 128, 128 ), // white + RGB( 128, 128, 255 ), // pastel blue + RGB( 255, 128, 0 ), // orange + RGB( 255, 128, 128 ), // pink + RGB( 255, 128, 255 ), // pastel magenta + RGB( 0, 255, 0 ), // bright green + RGB( 0, 255, 128 ), // sea green + RGB( 0, 255, 255 ), // bright cyan + RGB( 128, 255, 0 ), // lime + RGB( 128, 255, 128 ), // pastel green + RGB( 128, 255, 255 ), // pastel cyan + RGB( 255, 255, 0 ), // bright yellow + RGB( 255, 255, 128 ), // pastel yellow + RGB( 255, 255, 255 ), // bright white + + //RGB( 79, 69, 0 ), // brown + //RGB( 120, 120, 120 ), // dark grey + //RGB( 164, 215, 142 ), // grey + +#if 0 + RGB( 68, 68, 68 ), // dark grey + RGB( 80, 80, 80 ), + RGB( 108, 108, 108 ), // grey + RGB( 120, 120, 120 ), + RGB( 149, 149, 149 ), // light grey +#else + //RGB( 4, 4, 4 ), // black + RGB( 16, 16, 16 ), // black + RGB( 0, 28, 28 ), // dark cyan + RGB( 128, 0, 255 ) * 0.9, // mauve + RGB( 111, 79, 37 ) * 1.2, // orange + //RGB( 149, 149, 149 ), // light grey + //RGB( 154, 210, 132 ), // light green + RGB( 112, 164, 178 ) * 1.3, // cyan +#endif + }; + +#elif 0 + // Tweaked LOSPEC CPC BOY PALETTE which is less saturated by Arne Niklas Jansson + // https://lospec.com/palette-list/cpc-boy + + const float3 palette[NUM_COLORS] = // 32 + { + RGB( 0, 0, 0 ), + RGB( 27, 27, 101 ), + RGB( 53, 53, 201 ), + RGB( 102, 30, 37 ), + RGB( 85, 51, 97 ), + RGB( 127, 53, 201 ), + RGB( 188, 53, 53 ), + RGB( 192, 70, 110 ), + RGB( 223, 109, 155 ), + RGB( 27, 101, 27 ), + RGB( 27, 110, 131 ), + RGB( 30, 121, 229 ), + RGB( 121, 95, 27 ), + RGB( 128, 128, 128 ), + RGB( 145, 148, 223 ), + RGB( 201, 127, 53 ), + RGB( 227, 155, 141 ), + RGB( 248, 120, 248 ), + RGB( 53, 175, 53 ), + RGB( 53, 183, 143 ), + RGB( 53, 193, 215 ), + RGB( 127, 201, 53 ), + RGB( 173, 200, 170 ), + RGB( 141, 225, 199 ), + RGB( 225, 198, 67 ), + RGB( 228, 221, 154 ), + RGB( 255, 255, 255 ), + RGB( 238, 234, 224 ), + RGB( 172, 181, 107 ), + RGB( 118, 132, 72 ), + RGB( 63, 80, 63 ), + RGB( 36, 49, 55 ), + }; + +#elif 1 + + // NES 1 very good + // https://lospec.com/palette-list/nintendo-entertainment-system + + const float3 palette[NUM_COLORS] = // 55 + { + RGB( 0, 0, 0 ), + //RGB( 0, 0, 0 ), + //RGB( 0, 0, 0 ), + //RGB( 0, 0, 0 ), + //RGB( 0, 0, 0 ), + //RGB( 0, 0, 0 ), + //RGB( 0, 0, 0 ), + //RGB( 0, 0, 0 ), + //RGB( 0, 0, 0 ), + //RGB( 0, 0, 0 ), + + RGB( 252, 252, 252 ), + RGB( 248, 248, 248 ), + RGB( 188, 188, 188 ), + RGB( 124, 124, 124 ), + RGB( 164, 228, 252 ), + RGB( 60, 188, 252 ), + RGB( 0, 120, 248 ), + RGB( 0, 0, 252 ), + RGB( 184, 184, 248 ), + RGB( 104, 136, 252 ), + RGB( 0, 88, 248 ), + RGB( 0, 0, 188 ), + RGB( 216, 184, 248 ), + RGB( 152, 120, 248 ), + RGB( 104, 68, 252 ), + RGB( 68, 40, 188 ), + RGB( 248, 184, 248 ), + RGB( 248, 120, 248 ), + RGB( 216, 0, 204 ), + RGB( 148, 0, 132 ), + RGB( 248, 164, 192 ), + RGB( 248, 88, 152 ), + RGB( 228, 0, 88 ), + RGB( 168, 0, 32 ), + RGB( 240, 208, 176 ), + RGB( 248, 120, 88 ), + RGB( 248, 56, 0 ), + RGB( 168, 16, 0 ), + RGB( 252, 224, 168 ), + RGB( 252, 160, 68 ), + RGB( 228, 92, 16 ), + RGB( 136, 20, 0 ), + RGB( 248, 216, 120 ), + RGB( 248, 184, 0 ), + RGB( 172, 124, 0 ), + RGB( 80, 48, 0 ), + RGB( 216, 248, 120 ), + RGB( 184, 248, 24 ), + RGB( 0, 184, 0 ), + RGB( 0, 120, 0 ), + RGB( 184, 248, 184 ), + RGB( 88, 216, 84 ), + RGB( 0, 168, 0 ), + RGB( 0, 104, 0 ), + RGB( 184, 248, 216 ), + RGB( 88, 248, 152 ), + RGB( 0, 168, 68 ), + RGB( 0, 88, 0 ), + RGB( 0, 252, 252 ), + RGB( 0, 232, 216 ), + RGB( 0, 136, 136 ), + RGB( 0, 64, 88 ), + RGB( 248, 216, 248 ), + RGB( 120, 120, 120 ), + }; + +#elif 0 + + const float3 palette[NUM_COLORS] = // 32 + { + RGB( 0, 0, 0 ), + RGB( 192, 64, 80 ), + RGB( 240, 240, 240 ), + RGB( 192, 192, 176 ), + RGB( 128, 144, 144 ), + RGB( 96, 96, 112 ), + RGB( 96, 64, 64 ), + RGB( 64, 48, 32 ), + RGB( 112, 80, 48 ), + RGB( 176, 112, 64 ), + RGB( 224, 160, 80 ), + RGB( 224, 192, 128 ), + RGB( 240, 224, 96 ), + RGB( 224, 128, 48 ), + RGB( 208, 80, 32 ), + RGB( 144, 48, 32 ), + RGB( 96, 48, 112 ), + RGB( 176, 96, 160 ), + RGB( 224, 128, 192 ), + RGB( 192, 160, 208 ), + RGB( 112, 112, 192 ), + RGB( 48, 64, 144 ), + RGB( 32, 32, 64 ), + RGB( 32, 96, 208 ), + RGB( 64, 160, 224 ), + RGB( 128, 208, 224 ), + RGB( 160, 240, 144 ), + RGB( 48, 160, 96 ), + RGB( 48, 64, 48 ), + RGB( 48, 112, 32 ), + RGB( 112, 160, 48 ), + RGB( 160, 208, 80 ), + }; + +#elif 0 + + // Gameboy + const float3 palette[NUM_COLORS] = // 4 + { + RGB( 27, 42, 9 ), + RGB( 14, 69, 11 ), + RGB( 73, 107, 34 ), + RGB( 154, 158, 63 ), + }; + +#else + + // https://lospec.com/palette-list/existential-demo + const float3 palette[NUM_COLORS] = // 8 + { + RGB( 248, 243, 253 ), + RGB( 250, 198, 180 ), + RGB( 154, 218, 231 ), + RGB( 151, 203, 29 ), + RGB( 93, 162, 202 ), + RGB( 218, 41, 142 ), + RGB( 11, 134, 51 ), + RGB( 46, 43, 18 ), + }; + +#endif + + // find closest color match from CPC color palette + color = LinearSearch( color.rgb, palette ); + + result.color = float4( color, 1.0 ); +} diff --git a/neo/shaders/builtin/post/retro_cpc.vs.hlsl b/neo/shaders/builtin/post/retro_cpc.vs.hlsl new file mode 100644 index 00000000..75519fe8 --- /dev/null +++ b/neo/shaders/builtin/post/retro_cpc.vs.hlsl @@ -0,0 +1,55 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code 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 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "global_inc.hlsl" + + +// *INDENT-OFF* +struct VS_IN +{ + float4 position : POSITION; + float2 texcoord : TEXCOORD0; + float4 normal : NORMAL; + float4 tangent : TANGENT; + float4 color : COLOR0; + float4 color2 : COLOR1; +}; + +struct VS_OUT { + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; +// *INDENT-ON* + +void main( VS_IN vertex, out VS_OUT result ) +{ + result.position = vertex.position; + result.position.y = -result.position.y; + + result.texcoord0 = vertex.texcoord; +} \ No newline at end of file diff --git a/neo/shaders/builtin/post/retro_genesis.ps.hlsl b/neo/shaders/builtin/post/retro_genesis.ps.hlsl index 60b1a4ce..4a0e1abb 100644 --- a/neo/shaders/builtin/post/retro_genesis.ps.hlsl +++ b/neo/shaders/builtin/post/retro_genesis.ps.hlsl @@ -118,8 +118,21 @@ void main( PS_IN fragment, out PS_OUT result ) // get pixellated base color float3 color = t_BaseColor.Sample( samp0, uvPixelated * rpWindowCoord.xy ).rgb; - //color = _float3( uv.x ); - //color = Quantize( color, quantizationPeriod ); +#if 0 + if( uv.y < 0.125 ) + { + color = HSVToRGB( float3( uv.x, 1.0, uv.y * 8.0 ) ); + color = Quantize( color, quantizationPeriod ); + + //result.color = float4( color, 1.0 ); + //return; + } + else if( uv.y < 0.1875 ) + { + color = _float3( uv.x ); + color = Quantize( color, quantizationPeriod ); + } +#endif // add Bayer 8x8 dithering float2 uvDither = uvPixelated; @@ -134,8 +147,5 @@ void main( PS_IN fragment, out PS_OUT result ) // find closest color match from Sega Mega Drive color palette color = Quantize( color, quantizationPeriod ); - //color = LinearSearch( color.rgb, palette ); - //color = float4( BinarySearch( color.rgb, palette ), 1.0 ); - result.color = float4( color, 1.0 ); } diff --git a/neo/shaders/global_inc.hlsl b/neo/shaders/global_inc.hlsl index 70a09fec..7a769262 100644 --- a/neo/shaders/global_inc.hlsl +++ b/neo/shaders/global_inc.hlsl @@ -244,6 +244,43 @@ float4 LinearRGBToSRGB( float4 c ) #endif } +float3 HSVToRGB( float3 HSV ) +{ + float3 RGB = HSV.z; + + float var_h = HSV.x * 6; + float var_i = floor( var_h ); // Or ... var_i = floor( var_h ) + float var_1 = HSV.z * ( 1.0 - HSV.y ); + float var_2 = HSV.z * ( 1.0 - HSV.y * ( var_h - var_i ) ); + float var_3 = HSV.z * ( 1.0 - HSV.y * ( 1 - ( var_h - var_i ) ) ); + if( var_i == 0 ) + { + RGB = float3( HSV.z, var_3, var_1 ); + } + else if( var_i == 1 ) + { + RGB = float3( var_2, HSV.z, var_1 ); + } + else if( var_i == 2 ) + { + RGB = float3( var_1, HSV.z, var_3 ); + } + else if( var_i == 3 ) + { + RGB = float3( var_1, var_2, HSV.z ); + } + else if( var_i == 4 ) + { + RGB = float3( var_3, var_1, HSV.z ); + } + else + { + RGB = float3( HSV.z, var_1, var_2 ); + } + + return ( RGB ); +} + /** Efficient GPU implementation of the octahedral unit vector encoding from Cigolle, Donow, Evangelakos, Mara, McGuire, Meyer, diff --git a/neo/shaders/shaders.cfg b/neo/shaders/shaders.cfg index 7b52b129..48ca9524 100644 --- a/neo/shaders/shaders.cfg +++ b/neo/shaders/shaders.cfg @@ -54,6 +54,8 @@ builtin/post/postprocess.vs.hlsl -T vs_5_0 builtin/post/postprocess.ps.hlsl -T ps_5_0 builtin/post/retro_c64.vs.hlsl -T vs_5_0 builtin/post/retro_c64.ps.hlsl -T ps_5_0 +builtin/post/retro_cpc.vs.hlsl -T vs_5_0 +builtin/post/retro_cpc.ps.hlsl -T ps_5_0 builtin/post/retro_genesis.vs.hlsl -T vs_5_0 builtin/post/retro_genesis.ps.hlsl -T ps_5_0 builtin/post/retro_ps1.vs.hlsl -T vs_5_0 From bbbb14159f335fb0e1ae1c681af41577e1625240 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Thu, 11 Jan 2024 22:06:55 +0100 Subject: [PATCH 17/37] Tweaked dithering with standard deviation --- .../menus/MenuScreen_Shell_SystemOptions.cpp | 8 +- neo/renderer/RenderBackend.cpp | 3 +- neo/renderer/RenderCommon.h | 2 + neo/renderer/RenderSystem_init.cpp | 2 + neo/shaders/builtin/post/retro_c64.ps.hlsl | 138 +++++++++----- neo/shaders/builtin/post/retro_cpc.ps.hlsl | 172 ++++++++++-------- .../builtin/post/retro_genesis.ps.hlsl | 75 ++++---- neo/shaders/builtin/post/retro_ps1.ps.hlsl | 52 +++--- 8 files changed, 270 insertions(+), 182 deletions(-) diff --git a/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp b/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp index e1bc1c94..7321e312 100755 --- a/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp +++ b/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp @@ -611,8 +611,8 @@ void idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings::AdjustFi // RB begin case SYSTEM_FIELD_RENDERMODE: { - static const int numValues = 6; - static const int values[numValues] = { 0, 1, 2, 3, 4, 5 }; + static const int numValues = 8; + static const int values[numValues] = { 0, 1, 2, 3, 4, 5, 6, 7 }; r_renderMode.SetInteger( AdjustOption( r_renderMode.GetInteger(), values, numValues, adjustAmount ) ); break; } @@ -796,9 +796,9 @@ idSWFScriptVar idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings { "Doom 3", "Commodore 64", - "Commodore 64 Highres", + "Commodore 64 Hi", "Amstrad CPC 6128", - "Amstrad CPC 6128 Highres", + "Amstrad CPC 6128 Hi", "Sega Genesis", "Sega Genesis Highres", "Sony PSX", diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index da237443..4e9bfae2 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -6143,7 +6143,8 @@ void idRenderBackend::PostProcess( const void* data ) renderProgManager.BindShader_PostProcess(); } - SetFragmentParm( RENDERPARM_JITTERTEXSCALE, jitterTexScale ); // rpWindowCoord + jitterTexScale[1] = r_retroDitherScale.GetFloat(); + SetFragmentParm( RENDERPARM_JITTERTEXSCALE, jitterTexScale ); // rpJitterTexScale float jitterTexOffset[4]; jitterTexOffset[0] = 1.0f / globalImages->blueNoiseImage256->GetUploadWidth(); diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index 12dc65f8..0775d0a3 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -1308,6 +1308,8 @@ enum RenderMode RENDERMODE_PSX, }; +extern idCVar r_retroDitherScale; + extern idCVar r_renderMode; extern idCVar image_pixelLook; // RB end diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index a8dcb2f8..a09b214c 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -302,6 +302,8 @@ idCVar r_useCRTPostFX( "r_useCRTPostFX", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVA idCVar r_crtCurvature( "r_crtCurvature", "2", CVAR_RENDERER | CVAR_FLOAT, "rounded borders" ); idCVar r_crtVignette( "r_crtVignette", "0.8", CVAR_RENDERER | CVAR_FLOAT, "fading into the borders" ); +idCVar r_retroDitherScale( "r_retroDitherScale", "0.3", CVAR_RENDERER | CVAR_FLOAT, "" ); + idCVar r_renderMode( "r_renderMode", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "0 = Doom, 1 = Commodore 64, 2 = Commodore 64 Highres, 3 = Amstrad CPC 6128, 4 = Amstrad CPC 6128 Highres, 5 = Sega Genesis, 6 = Sega Genesis Highres, 7 = Sony PSX", 0, 7 ); // RB end diff --git a/neo/shaders/builtin/post/retro_c64.ps.hlsl b/neo/shaders/builtin/post/retro_c64.ps.hlsl index 65e1e775..ed52c40f 100644 --- a/neo/shaders/builtin/post/retro_c64.ps.hlsl +++ b/neo/shaders/builtin/post/retro_c64.ps.hlsl @@ -3,7 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. -Copyright (C) 2023 Robert Beckebans +Copyright (C) 2024 Robert Beckebans This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -54,26 +54,59 @@ struct PS_OUT #define NUM_COLORS 16 +float3 Average( float3 pal[NUM_COLORS] ) +{ + float3 sum = _float3( 0 ); + + for( int i = 0; i < NUM_COLORS; i++ ) + { + sum += pal[i]; + } + + return sum / float( NUM_COLORS ); +} + +float3 Deviation( float3 pal[NUM_COLORS] ) +{ + float3 sum = _float3( 0 ); + float3 avg = Average( pal ); + + for( int i = 0; i < NUM_COLORS; i++ ) + { + sum += abs( pal[i] - avg ); + } + + return sum / float( NUM_COLORS ); +} + +// squared distance to avoid the sqrt of distance function +float ColorCompare( float3 a, float3 b ) +{ + float3 diff = b - a; + return dot( diff, diff ); +} + // find nearest palette color using Euclidean distance float3 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) { - int idx = 0; - float nd = distance( c, pal[0] ); + int index = 0; + float minDist = ColorCompare( c, pal[0] ); for( int i = 1; i < NUM_COLORS; i++ ) { - float d = distance( c, pal[i] ); + float dist = ColorCompare( c, pal[i] ); - if( d < nd ) + if( dist < minDist ) { - nd = d; - idx = i; + minDist = dist; + index = i; } } - return pal[idx]; + return pal[index]; } +/* float3 GetClosest( float3 val1, float3 val2, float3 target ) { if( distance( target, val1 ) >= distance( val2, target ) ) @@ -138,47 +171,13 @@ float3 BinarySearch( float3 target, float3 pal[NUM_COLORS] ) // only single element left after search return pal[mid]; } +*/ #define RGB(r, g, b) float3(float(r)/255.0, float(g)/255.0, float(b)/255.0) void main( PS_IN fragment, out PS_OUT result ) { - float2 uv = ( fragment.texcoord0 ); - float2 uvPixelated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; - - float3 quantizationPeriod = _float3( 1.0 / NUM_COLORS ); - - // get pixellated base color - float3 color = t_BaseColor.Sample( samp0, uvPixelated * rpWindowCoord.xy ).rgb; - -#if 0 - if( uv.y < 0.125 ) - { - color = HSVToRGB( float3( uv.x, 1.0, uv.y * 8.0 ) ); - color = floor( color * NUM_COLORS ) * ( 1.0 / ( NUM_COLORS - 1.0 ) ); - - //result.color = float4( color, 1.0 ); - //return; - } - else if( uv.y < 0.1875 ) - { - color = _float3( uv.x ); - color = floor( color * NUM_COLORS ) * ( 1.0 / ( NUM_COLORS - 1.0 ) ); - } -#endif - - // add Bayer 8x8 dithering - float2 uvDither = uvPixelated; - //if( rpJitterTexScale.x > 1.0 ) - { - uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / rpJitterTexScale.x ); - } - float dither = DitherArray8x8( uvDither ) - 0.5; - - color.rgb += float3( dither, dither, dither ) * quantizationPeriod; - - // C64 colors http://unusedino.de/ec64/technical/misc/vic656x/colors/ #if 0 const float3 palette[NUM_COLORS] = @@ -223,6 +222,59 @@ void main( PS_IN fragment, out PS_OUT result ) }; #endif + float2 uv = ( fragment.texcoord0 ); + float2 uvPixelated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; + + float3 quantizationPeriod = _float3( 1.0 / NUM_COLORS ); + + // get pixellated base color + float3 color = t_BaseColor.Sample( samp0, uvPixelated * rpWindowCoord.xy ).rgb; + + float2 uvDither = uvPixelated; + //if( rpJitterTexScale.x > 1.0 ) + { + uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / rpJitterTexScale.x ); + } + float dither = DitherArray8x8( uvDither ) - 0.5; + +#if 0 + if( uv.y < 0.0625 ) + { + color = HSVToRGB( float3( uv.x, 1.0, uv.y * 16.0 ) ); + + result.color = float4( color, 1.0 ); + return; + } + else if( uv.y < 0.125 ) + { + // quantized + color = HSVToRGB( float3( uv.x, 1.0, ( uv.y - 0.0625 ) * 16.0 ) ); + color = LinearSearch( color, palette ); + + result.color = float4( color, 1.0 ); + return; + } + else if( uv.y < 0.1875 ) + { + // dithered quantized + color = HSVToRGB( float3( uv.x, 1.0, ( uv.y - 0.125 ) * 16.0 ) ); + + color.rgb += float3( dither, dither, dither ) * quantizationPeriod; + color = LinearSearch( color, palette ); + + result.color = float4( color, 1.0 ); + return; + } + else if( uv.y < 0.25 ) + { + color = _float3( uv.x ); + color = floor( color * NUM_COLORS ) * ( 1.0 / ( NUM_COLORS - 1.0 ) ); + } +#endif + + //color.rgb += float3( dither, dither, dither ) * quantizationPeriod; + color.rgb += float3( dither, dither, dither ) * Deviation( palette ) * rpJitterTexScale.y; + // find closest color match from C64 color palette color = LinearSearch( color.rgb, palette ); //color = float4( BinarySearch( color.rgb, palette ), 1.0 ); diff --git a/neo/shaders/builtin/post/retro_cpc.ps.hlsl b/neo/shaders/builtin/post/retro_cpc.ps.hlsl index 850ed405..30940f40 100644 --- a/neo/shaders/builtin/post/retro_cpc.ps.hlsl +++ b/neo/shaders/builtin/post/retro_cpc.ps.hlsl @@ -53,7 +53,7 @@ struct PS_OUT #define RESOLUTION_DIVISOR 4.0 #define NUM_COLORS 32 // original 27 - +/* float LinearTweak1( float c ) { return ( c <= 0.04045 ) ? c / 12.92 : pow( ( c + 0.055 ) / 1.055, 1.4 ); @@ -63,26 +63,58 @@ float3 LinearTweak3( float3 c ) { return float3( Linear1( c.r ), Linear1( c.g ), Linear1( c.b ) ); } +*/ + +float3 Average( float3 pal[NUM_COLORS] ) +{ + float3 sum = _float3( 0 ); + + for( int i = 0; i < NUM_COLORS; i++ ) + { + sum += pal[i]; + } + + return sum / float( NUM_COLORS ); +} + +float3 Deviation( float3 pal[NUM_COLORS] ) +{ + float3 sum = _float3( 0 ); + float3 avg = Average( pal ); + + for( int i = 0; i < NUM_COLORS; i++ ) + { + sum += abs( pal[i] - avg ); + } + + return sum / float( NUM_COLORS ); +} + +// squared distance to avoid the sqrt of distance function +float ColorCompare( float3 a, float3 b ) +{ + float3 diff = b - a; + return dot( diff, diff ); +} // find nearest palette color using Euclidean distance float3 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) { - int idx = 0; - float nd = distance( c, pal[0] ); + int index = 0; + float minDist = ColorCompare( c, pal[0] ); for( int i = 1; i < NUM_COLORS; i++ ) { - //float d = distance( c, pal[i] ); - float d = distance( c, ( pal[i] ) ); + float dist = ColorCompare( c, pal[i] ); - if( d < nd ) + if( dist < minDist ) { - nd = d; - idx = i; + minDist = dist; + index = i; } } - return pal[idx]; + return pal[index]; } #define RGB(r, g, b) float3(float(r)/255.0, float(g)/255.0, float(b)/255.0) @@ -91,42 +123,7 @@ float3 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) void main( PS_IN fragment, out PS_OUT result ) { - float2 uv = ( fragment.texcoord0 ); - float2 uvPixelated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; - - float3 quantizationPeriod = _float3( 1.0 / NUM_COLORS ); - - // get pixellated base color - float3 color = t_BaseColor.Sample( samp0, uvPixelated * rpWindowCoord.xy ).rgb; - #if 0 - if( uv.y < 0.125 ) - { - color = HSVToRGB( float3( uv.x, 1.0, uv.y * 8.0 ) ); - color = floor( color * NUM_COLORS ) * ( 1.0 / ( NUM_COLORS - 1.0 ) ); - - //result.color = float4( color, 1.0 ); - //return; - } - else if( uv.y < 0.1875 ) - { - color = _float3( uv.x ); - color = floor( color * NUM_COLORS ) * ( 1.0 / ( NUM_COLORS - 1.0 ) ); - } -#endif - - // add Bayer 8x8 dithering - float2 uvDither = uvPixelated; - //if( rpJitterTexScale.x > 1.0 ) - { - uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / rpJitterTexScale.x ); - } - float dither = ( DitherArray8x8( uvDither ) - 0.5 ) * 1.0; - - color.rgb += float3( dither, dither, dither ) * quantizationPeriod; - - -#if 1 // Amstrad CPC colors https://www.cpcwiki.eu/index.php/CPC_Palette const float3 palette[NUM_COLORS] = { @@ -158,29 +155,14 @@ void main( PS_IN fragment, out PS_OUT result ) RGB( 255, 255, 128 ), // pastel yellow RGB( 255, 255, 255 ), // bright white - //RGB( 79, 69, 0 ), // brown - //RGB( 120, 120, 120 ), // dark grey - //RGB( 164, 215, 142 ), // grey - -#if 0 - RGB( 68, 68, 68 ), // dark grey - RGB( 80, 80, 80 ), - RGB( 108, 108, 108 ), // grey - RGB( 120, 120, 120 ), - RGB( 149, 149, 149 ), // light grey -#else - //RGB( 4, 4, 4 ), // black +#if 1 RGB( 16, 16, 16 ), // black RGB( 0, 28, 28 ), // dark cyan - RGB( 128, 0, 255 ) * 0.9, // mauve - RGB( 111, 79, 37 ) * 1.2, // orange - //RGB( 149, 149, 149 ), // light grey - //RGB( 154, 210, 132 ), // light green RGB( 112, 164, 178 ) * 1.3, // cyan #endif }; -#elif 0 +#elif 1 // Tweaked LOSPEC CPC BOY PALETTE which is less saturated by Arne Niklas Jansson // https://lospec.com/palette-list/cpc-boy @@ -220,24 +202,14 @@ void main( PS_IN fragment, out PS_OUT result ) RGB( 36, 49, 55 ), }; -#elif 1 +#elif 0 - // NES 1 very good + // NES 1 // https://lospec.com/palette-list/nintendo-entertainment-system const float3 palette[NUM_COLORS] = // 55 { RGB( 0, 0, 0 ), - //RGB( 0, 0, 0 ), - //RGB( 0, 0, 0 ), - //RGB( 0, 0, 0 ), - //RGB( 0, 0, 0 ), - //RGB( 0, 0, 0 ), - //RGB( 0, 0, 0 ), - //RGB( 0, 0, 0 ), - //RGB( 0, 0, 0 ), - //RGB( 0, 0, 0 ), - RGB( 252, 252, 252 ), RGB( 248, 248, 248 ), RGB( 188, 188, 188 ), @@ -360,6 +332,60 @@ void main( PS_IN fragment, out PS_OUT result ) #endif + float2 uv = ( fragment.texcoord0 ); + float2 uvPixelated = floor( fragment.position.xy / RESOLUTION_DIVISOR ) * RESOLUTION_DIVISOR; + + float3 quantizationPeriod = _float3( 1.0 / NUM_COLORS ); + float3 quantDeviation = Deviation( palette ); + + // get pixellated base color + float3 color = t_BaseColor.Sample( samp0, uvPixelated * rpWindowCoord.xy ).rgb; + + float2 uvDither = uvPixelated; + //if( rpJitterTexScale.x > 1.0 ) + { + uvDither = fragment.position.xy / ( RESOLUTION_DIVISOR / rpJitterTexScale.x ); + } + float dither = DitherArray8x8( uvDither ) - 0.5; + +#if 0 + if( uv.y < 0.0625 ) + { + color = HSVToRGB( float3( uv.x, 1.0, uv.y * 16.0 ) ); + + result.color = float4( color, 1.0 ); + return; + } + else if( uv.y < 0.125 ) + { + // quantized + color = HSVToRGB( float3( uv.x, 1.0, ( uv.y - 0.0625 ) * 16.0 ) ); + color = LinearSearch( color, palette ); + + result.color = float4( color, 1.0 ); + return; + } + else if( uv.y < 0.1875 ) + { + // dithered quantized + color = HSVToRGB( float3( uv.x, 1.0, ( uv.y - 0.125 ) * 16.0 ) ); + + color.rgb += float3( dither, dither, dither ) * quantDeviation * rpJitterTexScale.y; + color = LinearSearch( color, palette ); + + result.color = float4( color, 1.0 ); + return; + } + else if( uv.y < 0.25 ) + { + color = _float3( uv.x ); + color = floor( color * NUM_COLORS ) * ( 1.0 / ( NUM_COLORS - 1.0 ) ); + } +#endif + + //color.rgb += float3( dither, dither, dither ) * quantizationPeriod; + color.rgb += float3( dither, dither, dither ) * quantDeviation * rpJitterTexScale.y; + // find closest color match from CPC color palette color = LinearSearch( color.rgb, palette ); diff --git a/neo/shaders/builtin/post/retro_genesis.ps.hlsl b/neo/shaders/builtin/post/retro_genesis.ps.hlsl index 4a0e1abb..350ba7a3 100644 --- a/neo/shaders/builtin/post/retro_genesis.ps.hlsl +++ b/neo/shaders/builtin/post/retro_genesis.ps.hlsl @@ -51,7 +51,6 @@ struct PS_OUT #define RESOLUTION_DIVISOR 4.0 -#define NUM_COLORS 64 #define Dithering_QuantizationSteps 8.0 // 8.0 = 2 ^ 3 quantization bits float3 Quantize( float3 color, float3 period ) @@ -79,26 +78,6 @@ float3 BlueNoise3( float2 n, float x ) return noise; } -// find nearest palette color using Euclidean distance -float3 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) -{ - int idx = 0; - float nd = distance( c, pal[0] ); - - for( int i = 1; i < NUM_COLORS; i++ ) - { - float d = distance( c, pal[i] ); - - if( d < nd ) - { - nd = d; - idx = i; - } - } - - return pal[idx]; -} - #define RGB(r, g, b) float3(float(r)/255.0, float(g)/255.0, float(b)/255.0) void main( PS_IN fragment, out PS_OUT result ) @@ -118,23 +97,6 @@ void main( PS_IN fragment, out PS_OUT result ) // get pixellated base color float3 color = t_BaseColor.Sample( samp0, uvPixelated * rpWindowCoord.xy ).rgb; -#if 0 - if( uv.y < 0.125 ) - { - color = HSVToRGB( float3( uv.x, 1.0, uv.y * 8.0 ) ); - color = Quantize( color, quantizationPeriod ); - - //result.color = float4( color, 1.0 ); - //return; - } - else if( uv.y < 0.1875 ) - { - color = _float3( uv.x ); - color = Quantize( color, quantizationPeriod ); - } -#endif - - // add Bayer 8x8 dithering float2 uvDither = uvPixelated; //if( rpJitterTexScale.x > 1.0 ) { @@ -142,7 +104,42 @@ void main( PS_IN fragment, out PS_OUT result ) } float dither = DitherArray8x8( uvDither ) - 0.5; - color.rgb += float3( dither, dither, dither ) * quantizationPeriod; +#if 0 + if( uv.y < 0.0625 ) + { + color = HSVToRGB( float3( uv.x, 1.0, uv.y * 16.0 ) ); + + result.color = float4( color, 1.0 ); + return; + } + else if( uv.y < 0.125 ) + { + // quantized + color = HSVToRGB( float3( uv.x, 1.0, ( uv.y - 0.0625 ) * 16.0 ) ); + color = Quantize( color, quantizationPeriod ); + + result.color = float4( color, 1.0 ); + return; + } + else if( uv.y < 0.1875 ) + { + // dithered quantized + color = HSVToRGB( float3( uv.x, 1.0, ( uv.y - 0.125 ) * 16.0 ) ); + + color.rgb += float3( dither, dither, dither ) * quantizationPeriod; + color = Quantize( color, quantizationPeriod ); + + result.color = float4( color, 1.0 ); + return; + } + else if( uv.y < 0.25 ) + { + color = _float3( uv.x ); + color = Quantize( color, quantizationPeriod ); + } +#endif + + color.rgb += float3( dither, dither, dither ) * quantizationPeriod;// * rpJitterTexScale.y; // find closest color match from Sega Mega Drive color palette color = Quantize( color, quantizationPeriod ); diff --git a/neo/shaders/builtin/post/retro_ps1.ps.hlsl b/neo/shaders/builtin/post/retro_ps1.ps.hlsl index 3a6ad3a3..63089270 100644 --- a/neo/shaders/builtin/post/retro_ps1.ps.hlsl +++ b/neo/shaders/builtin/post/retro_ps1.ps.hlsl @@ -50,8 +50,8 @@ struct PS_OUT // *INDENT-ON* -#define RESOLUTION_DIVISOR 4.0 -#define Dithering_QuantizationSteps 32.0 // 8.0 = 2 ^ 3 quantization bits +#define RESOLUTION_DIVISOR 4.0 +#define Dithering_QuantizationSteps 32.0 // 8.0 = 2 ^ 3 quantization bits float3 Quantize( float3 color, float3 period ) { @@ -82,36 +82,44 @@ void main( PS_IN fragment, out PS_OUT result ) float dither = DitherArray8x8( uvDither ) - 0.5; #if 0 - if( uv.y < 0.05 ) + if( uv.y < 0.0625 ) { - color = _float3( uv.x ); + color = HSVToRGB( float3( uv.x, 1.0, uv.y * 16.0 ) ); + + result.color = float4( color, 1.0 ); + return; } - else if( uv.y < 0.1 ) + else if( uv.y < 0.125 ) { - // quantized signal - color = _float3( uv.x ); - color = Quantize( color, quantizationPeriod ); - } - else if( uv.y < 0.15 ) - { - // quantized signal dithered - color = _float3( uv.x ); + // quantized + color = HSVToRGB( float3( uv.x, 1.0, ( uv.y - 0.0625 ) * 16.0 ) ); color = Quantize( color, quantizationPeriod ); - color.rgb += float3( dither, dither, dither ) * quantizationPeriod; + result.color = float4( color, 1.0 ); + return; } - else if( uv.y < 0.2 ) + else if( uv.y < 0.1875 ) { - color.rgb = float3( dither, dither, dither ) * quantizationPeriod; + // dithered quantized + color = HSVToRGB( float3( uv.x, 1.0, ( uv.y - 0.125 ) * 16.0 ) ); + + color.rgb += float3( dither, dither, dither ) * quantizationPeriod; + color = Quantize( color, quantizationPeriod ); + + result.color = float4( color, 1.0 ); + return; + } + else if( uv.y < 0.25 ) + { + color = _float3( uv.x ); + color = Quantize( color, quantizationPeriod ); } - else #endif - { - color.rgb += float3( dither, dither, dither ) * quantizationPeriod; - // PSX color quantization - color = Quantize( color, quantizationPeriod ); - } + color.rgb += float3( dither, dither, dither ) * quantizationPeriod; + + // PSX color quantization with 15-bit + color = Quantize( color, quantizationPeriod ); From 8db4752fc0541b20520504b0f2f4f95dd4fa5709 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Sat, 20 Jan 2024 13:57:41 +0100 Subject: [PATCH 18/37] Updated .plan in RELEASE-NOTES --- RELEASE-NOTES.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index bbbd8054..2900a3c9 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -19,6 +19,70 @@ TBD - RBDOOM-3-BFG 1.6.0 _______________________________ +## .plan - January 20, 2024 + +Cudos to Stephen Saunders for this build and to reeFridge for finding the issue. +This fixes a number of multiplayer issues. Create Private Match with clients now works reliably. The missing functionality of the online game browser and leaderboards hasn't been fixed. + +Changelog: + +* Fixes regression Multiplayer: Accessing memory after it has been freed #846 caused by an earlier, but broken memory leak fix on my part. The fix is almost identical to PR Free idLobby memory inside destructor #847 + +* Fixes a critical issue with mis-reading network snapshot data on the client side, which caused no end of problems with mis-rendering, slowdowns, unstable connections, etc. The client was ignoring the entity network-synced flag which tells it whether it should read entity class-specific data. It was trying to read it all the time and sometimes coming up with null data which caused tons of problems with rendering and physics calculations (e.g. operations on NaN numbers). Simple fix was to respect the entity's network-synced flag on the client side but it makes all the difference. + +* Fixes an incorrect assert on multiplayer VoiceChat shutdown + +* Allows r_useScissor and r_useParallelAdd cvars to be changed in multiplayer mode for use in bake* operations on multiplayer maps + +* Fixed a couple of uninitialized variables that showed up in valgrind when in multiplayer mode + +* Added Amstrad CPC 6128 Retro rendering mode + + +## .plan - January 03, 2024 + +This is a preview build of the new Retro 8-bit/16-bit/PSX rendering modes. + +The new rendering modes can be set in the menu options but it's controlled mainly r_renderMode. +The values are 0 = Doom, 1 = Commodore 64, 2 = Commodore 64 Highres, 3 = Sega Genesis, 4 = Sega Genesis Highres, 5 = Sony PSX + +The Commodore 64 mode regulates all colors down to the original 16 color palette. +The Sega mode mimics 9 bit color HW which means 3 bit per color channel resulting in a total of 512 colors. +The PSX mode only turns off linear filtering for the textures and applies a screen space dithering effect. + +All retro rendering modes try to mimic the 320x240 resolution but it is extended to 16:9 so it is 480 x 270. +Highres modes only apply a higher resolution dithering on the pixelated output. + +The PSX mode has no additional artifacts yet like wiggling vertices or textures. + +There are also 2 new CRT filters that are drawn on top of everything else (even the console) for more arcade vibes. + +Changelog: + +* Fixed scissor clipping issues of regular surfaces like light flares #651 + +* Duplicating lights with Ctrl+D works now + +* Merged script interpreter improvements from Dhewm3, especially that fixes https://github.com/dhewm/dhewm3/issues/303 + +* Doubled MAX_GLOBALS for the Runners 2.6 mod + +* Crash fix between level switching and loading of new textures for D3HDP and other mods + +* Fixed many small memory leaks (thanks to Steve Saunders) + +* Reduced console spam and got rid of the depth-stencil is read-only warnings + +* Added image_pixelLook to disable texture filtering on most textures regardless of the render mode + +* Changed devtools.cfg so you can easily switch between the new render modes with F7 and F8 + + +Changelog TrenchBroomBFG: + +* Added Show patches option to View Options + + ## .plan - October 27, 2023 This is a preview build of the new WIP ingame Light Editor with some important bugfixes in the convertMapToValve220 command. From 7bd543f6990c8debf79a2764fdaefd53d99b0407 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Wed, 4 Oct 2023 12:24:49 -0400 Subject: [PATCH 19/37] Remove need for barrier command list on Vulkan, simplifies code and enables macOS previous command statistics (cherry picked from commit 9956923ee656f1294446cadd692ca61edfe9ba6a) --- neo/renderer/NVRHI/RenderBackend_NVRHI.cpp | 4 ++-- neo/renderer/RenderBackend.cpp | 4 ++-- neo/sys/DeviceManager_VK.cpp | 13 +++++++------ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp b/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp index baf70f90..7718b08e 100644 --- a/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp +++ b/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp @@ -1597,11 +1597,11 @@ void idRenderBackend::GL_EndFrame() commandList->close(); - deviceManager->GetDevice()->executeCommandList( commandList ); - // required for Vulkan: transition our swap image to present deviceManager->EndFrame(); + deviceManager->GetDevice()->executeCommandList( commandList ); + // update jitter for perspective matrix taaPass->AdvanceFrame(); } diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 60951145..94bccaef 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -5382,12 +5382,12 @@ void idRenderBackend::ExecuteBackEndCommands( const emptyCommand_t* cmds ) DrawFlickerBox(); - GL_EndFrame(); - // stop rendering on this thread uint64 backEndFinishTime = Sys_Microseconds(); pc.cpuTotalMicroSec = backEndFinishTime - backEndStartTime; + GL_EndFrame(); + if( r_debugRenderToTexture.GetInteger() == 1 ) { common->Printf( "3d: %i, 2d: %i, SetBuf: %i, CpyRenders: %i, CpyFrameBuf: %i\n", c_draw3d, c_draw2d, c_setBuffers, c_copyRenders, pc.c_copyFrameBuffer ); diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index 98135277..77f420a5 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -272,7 +272,7 @@ private: nvrhi::vulkan::DeviceHandle m_NvrhiDevice; nvrhi::DeviceHandle m_ValidationLayer; - nvrhi::CommandListHandle m_BarrierCommandList; + //nvrhi::CommandListHandle m_BarrierCommandList; // SRS - no longer needed std::queue m_PresentSemaphoreQueue; vk::Semaphore m_PresentSemaphore; @@ -1220,7 +1220,7 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() CHECK( createSwapChain() ); - m_BarrierCommandList = m_NvrhiDevice->createCommandList(); + //m_BarrierCommandList = m_NvrhiDevice->createCommandList(); // SRS - no longer needed // SRS - Give each swapchain image its own semaphore in case of overlap (e.g. MoltenVK async queue submit) for( int i = 0; i < m_SwapChainImages.size(); i++ ) @@ -1257,7 +1257,7 @@ void DeviceManager_VK::DestroyDeviceAndSwapChain() } m_PresentSemaphore = vk::Semaphore(); - m_BarrierCommandList = nullptr; + //m_BarrierCommandList = nullptr; // SRS - no longer needed destroySwapChain(); @@ -1320,9 +1320,10 @@ void DeviceManager_VK::EndFrame() { m_NvrhiDevice->queueSignalSemaphore( nvrhi::CommandQueue::Graphics, m_PresentSemaphore, 0 ); - m_BarrierCommandList->open(); // umm... - m_BarrierCommandList->close(); - m_NvrhiDevice->executeCommandList( m_BarrierCommandList ); + // SRS - Don't need barrier commandlist if EndFrame() is called before executeCommandList() in idRenderBackend::GL_EndFrame() + //m_BarrierCommandList->open(); // umm... + //m_BarrierCommandList->close(); + //m_NvrhiDevice->executeCommandList( m_BarrierCommandList ); } void DeviceManager_VK::Present() From 6e8f74154f21d9048afa3b60d4bf59e402d7a030 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Wed, 4 Oct 2023 12:33:32 -0400 Subject: [PATCH 20/37] Add CPU / GPU usage % to HUD overlay and display MoltenVK's Metal encoding time when available (cherry picked from commit 54759765ff384bd5a3109d8c801e50952c97c4aa) --- neo/cmake-xcode-debug.sh | 3 ++- neo/framework/Common_local.h | 11 +++++--- neo/framework/Console.cpp | 11 +++++++- neo/sys/DeviceManager_VK.cpp | 52 +++++++++++++++++++++++++----------- 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/neo/cmake-xcode-debug.sh b/neo/cmake-xcode-debug.sh index df7738a5..a3b8b907 100755 --- a/neo/cmake-xcode-debug.sh +++ b/neo/cmake-xcode-debug.sh @@ -8,4 +8,5 @@ cd xcode-debug # note 3: env variable MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE=1 enables MoltenVK's image view swizzle which may be required on older macOS versions or hardware (see vulkaninfo) # note 4: env variable MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS=0 disables synchronous queue submits which is optimal for the synchronization method used by the game # note 5: env variable MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2 enables MoltenVK's use of Metal argument buffers only if VK_EXT_descriptor_indexing is enabled -cmake -G Xcode -DCMAKE_BUILD_TYPE=Debug -DCMAKE_XCODE_GENERATE_SCHEME=ON -DCMAKE_XCODE_SCHEME_ENVIRONMENT="MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE=1;MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS=0;MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2" -DCMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION=OFF -DCMAKE_SUPPRESS_REGENERATION=ON -DOPENAL_LIBRARY=/usr/local/opt/openal-soft/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=/usr/local/opt/openal-soft/include ../neo -DCMAKE_POLICY_DEFAULT_CMP0142=NEW -Wno-dev +# note 6: env variable MVK_CONFIG_TIMESTAMP_PERIOD_LOWPASS_ALPHA=1.0 disables MoltenVK's timestampPeriod lowpass filter for non-Apple GPUs +cmake -G Xcode -DCMAKE_BUILD_TYPE=Debug -DCMAKE_XCODE_GENERATE_SCHEME=ON -DCMAKE_XCODE_SCHEME_ENVIRONMENT="MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE=1;MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS=0;MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2;MVK_CONFIG_TIMESTAMP_PERIOD_LOWPASS_ALPHA=1.0" -DCMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION=OFF -DCMAKE_SUPPRESS_REGENERATION=ON -DOPENAL_LIBRARY=/usr/local/opt/openal-soft/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=/usr/local/opt/openal-soft/include ../neo -DCMAKE_POLICY_DEFAULT_CMP0142=NEW -Wno-dev diff --git a/neo/framework/Common_local.h b/neo/framework/Common_local.h index 58ff9d25..267b5184 100644 --- a/neo/framework/Common_local.h +++ b/neo/framework/Common_local.h @@ -380,14 +380,15 @@ public: // RB end // SRS start - uint64 GetRendererStartFrameSyncMicroseconds() const + void SetRendererMvkEncodeMicroseconds( uint64 mvkEncodeMicroSeconds ) { - return mainFrameTiming.finishSyncTime - mainFrameTiming.startSyncTime; + mvkEncodeMicroSec = mvkEncodeMicroSeconds; + return; } - uint64 GetRendererEndFrameSyncMicroseconds() const + uint64 GetRendererMvkEncodeMicroseconds() const { - return mainFrameTiming.finishSyncTime_EndFrame - mainFrameTiming.startRenderTime; + return mvkEncodeMicroSec; } // SRS end @@ -603,6 +604,8 @@ private: // RB: r_speeds counters backEndCounters_t stats_backend; performanceCounters_t stats_frontend; + // SRS - MoltenVK's Vulkan to Metal command buffer encoding time, set default to 0 for non-macOS platforms (Windows and Linux) + uint64 mvkEncodeMicroSec = 0; // Used during loading screens int lastPacifierSessionTime; diff --git a/neo/framework/Console.cpp b/neo/framework/Console.cpp index 35e2dd43..5846a13d 100644 --- a/neo/framework/Console.cpp +++ b/neo/framework/Console.cpp @@ -290,6 +290,7 @@ float idConsoleLocal::DrawFPS( float y ) const uint64 rendererBackEndTime = commonLocal.GetRendererBackEndMicroseconds(); const uint64 rendererShadowsTime = commonLocal.GetRendererShadowsMicroseconds(); + const uint64 rendererMvkEncodeTime = commonLocal.GetRendererMvkEncodeMicroseconds(); const uint64 rendererGPUTime = commonLocal.GetRendererGPUMicroseconds(); const uint64 rendererGPUEarlyZTime = commonLocal.GetRendererGpuEarlyZMicroseconds(); const uint64 rendererGPU_SSAOTime = commonLocal.GetRendererGpuSSAOMicroseconds(); @@ -322,7 +323,7 @@ float idConsoleLocal::DrawFPS( float y ) { // start smaller int32 statsWindowWidth = 320; - int32 statsWindowHeight = 315; + int32 statsWindowHeight = 330; if( com_showFPS.GetInteger() > 2 ) { @@ -494,12 +495,20 @@ float idConsoleLocal::DrawFPS( float y ) ImGui::TextColored( gameThreadRenderTime > maxTime ? colorRed : colorWhite, "RF: %5llu us SSR: %5llu us", gameThreadRenderTime, rendererGPU_SSRTime ); ImGui::TextColored( rendererBackEndTime > maxTime ? colorRed : colorWhite, "RB: %5llu us Ambient Pass: %5llu us", rendererBackEndTime, rendererGPUAmbientPassTime ); ImGui::TextColored( rendererGPUShadowAtlasTime > maxTime ? colorRed : colorWhite, "Shadows: %5llu us Shadow Atlas: %5llu us", rendererShadowsTime, rendererGPUShadowAtlasTime ); +#if defined(__APPLE__) && defined( USE_MoltenVK ) + // SRS - For more recent versions of MoltenVK with enhanced performance statistics (v1.2.6 and later), display the Vulkan to Metal encoding thread time on macOS + ImGui::TextColored( rendererMvkEncodeTime > maxTime || rendererGPUInteractionsTime > maxTime ? colorRed : colorWhite, "Encode: %5lld us Interactions: %5llu us", rendererMvkEncodeTime, rendererGPUInteractionsTime ); + ImGui::TextColored( rendererGPUShaderPassesTime > maxTime ? colorRed : colorWhite, "Sync: %5lld us Shader Pass: %5llu us", frameSyncTime, rendererGPUShaderPassesTime ); +#else ImGui::TextColored( rendererGPUInteractionsTime > maxTime ? colorRed : colorWhite, "Sync: %5lld us Interactions: %5llu us", frameSyncTime, rendererGPUInteractionsTime ); ImGui::TextColored( rendererGPUShaderPassesTime > maxTime ? colorRed : colorWhite, " Shader Pass: %5llu us", rendererGPUShaderPassesTime ); +#endif ImGui::TextColored( rendererGPU_TAATime > maxTime ? colorRed : colorWhite, " TAA: %5llu us", rendererGPU_TAATime ); ImGui::TextColored( rendererGPUPostProcessingTime > maxTime ? colorRed : colorWhite, " PostFX: %5llu us", rendererGPUPostProcessingTime ); ImGui::TextColored( frameBusyTime > maxTime || rendererGPUTime > maxTime ? colorRed : colorWhite, "Total: %5lld us Total: %5lld us", frameBusyTime, rendererGPUTime ); ImGui::TextColored( colorWhite, "Idle: %5lld us Idle: %5lld us", frameIdleTime, rendererGPUIdleTime ); + // SRS - Show CPU and GPU overall usage statistics + ImGui::TextColored( colorWhite, "Usage: %3.0f %% Usage: %3.0f %%", float( frameBusyTime - frameSyncTime ) / float( frameBusyTime + frameIdleTime ) * 100.0, float( rendererGPUTime ) / float( rendererGPUTime + rendererGPUIdleTime ) * 100.0 ); ImGui::End(); } diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index 77f420a5..58656c83 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -38,7 +38,7 @@ // SRS - optionally needed for MoltenVK runtime config visibility #if defined(__APPLE__) && defined( USE_MoltenVK ) #include - + #include "framework/Common_local.h" idCVar r_mvkSynchronousQueueSubmits( "r_mvkSynchronousQueueSubmits", "0", CVAR_BOOL | CVAR_INIT, "Use MoltenVK's synchronous queue submit option." ); #endif #include @@ -210,8 +210,8 @@ private: }, // layers { -#if defined(__APPLE__) - // SRS - synchronization2 not supported natively on MoltenVK, use layer implementation instead +#if defined(__APPLE__) && !defined( USE_MoltenVK ) + // SRS - Enable synchronization2 layer when using Vulkan loader and MoltenVK version unknown "VK_LAYER_KHRONOS_synchronization2" #endif }, @@ -1157,30 +1157,42 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() deviceFeatures2.setPNext( &portabilityFeatures ); m_VulkanPhysicalDevice.getFeatures2( &deviceFeatures2 ); - MVKConfiguration pConfig; - size_t pConfigSize = sizeof( pConfig ); + MVKConfiguration mvkConfig; + size_t mvkConfigSize = sizeof( mvkConfig ); - vkGetMoltenVKConfigurationMVK( m_VulkanInstance, &pConfig, &pConfigSize ); + vkGetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); // SRS - Set MoltenVK's synchronous queue submit option for vkQueueSubmit() & vkQueuePresentKHR() - pConfig.synchronousQueueSubmits = r_mvkSynchronousQueueSubmits.GetBool() ? VK_TRUE : VK_FALSE; - vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &pConfig, &pConfigSize ); + if( mvkConfig.synchronousQueueSubmits == VK_TRUE && !r_mvkSynchronousQueueSubmits.GetBool() ) + { + idLib::Printf( "Disabled MoltenVK's synchronous queue submits...\n" ); + mvkConfig.synchronousQueueSubmits = VK_FALSE; + vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); + } // SRS - If we don't have native image view swizzle, enable MoltenVK's image view swizzle feature if( portabilityFeatures.imageViewFormatSwizzle == VK_FALSE ) { - idLib::Printf( "Enabling MoltenVK's image view swizzle...\n" ); - pConfig.fullImageViewSwizzle = VK_TRUE; - vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &pConfig, &pConfigSize ); + idLib::Printf( "Enabled MoltenVK's image view swizzle...\n" ); + mvkConfig.fullImageViewSwizzle = VK_TRUE; + vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); } // SRS - Turn MoltenVK's Metal argument buffer feature on for descriptor indexing only - if( pConfig.useMetalArgumentBuffers == MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_NEVER ) + if( mvkConfig.useMetalArgumentBuffers == MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_NEVER ) { - idLib::Printf( "Enabling MoltenVK's Metal argument buffers for descriptor indexing...\n" ); - pConfig.useMetalArgumentBuffers = MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_DESCRIPTOR_INDEXING; - vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &pConfig, &pConfigSize ); + idLib::Printf( "Enabled MoltenVK's Metal argument buffers for descriptor indexing...\n" ); + mvkConfig.useMetalArgumentBuffers = MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_DESCRIPTOR_INDEXING; + vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); } + +#if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) + // SRS - Disable MoltenVK's timestampPeriod filter for HUD / Optick profiler timing calibration + mvkConfig.timestampPeriodLowPassAlpha = 1.0; + // SRS - Enable MoltenVK's performance tracking for display of Metal encoding timer on macOS + mvkConfig.performanceTracking = VK_TRUE; + vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); +#endif #endif CHECK( createDevice() ); @@ -1305,6 +1317,16 @@ void DeviceManager_VK::DestroyDeviceAndSwapChain() void DeviceManager_VK::BeginFrame() { +#if defined(__APPLE__) && defined( USE_MoltenVK ) +#if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) + // SRS - fetch MoltenVK's Vulkan to Metal encoding time for the previous frame + MVKPerformanceStatistics mvkPerfStats; + size_t mvkPerfStatsSize = sizeof( mvkPerfStats ); + vkGetPerformanceStatisticsMVK( m_VulkanDevice, &mvkPerfStats, &mvkPerfStatsSize ); + commonLocal.SetRendererMvkEncodeMicroseconds( uint64( Max( 0.0, mvkPerfStats.queue.submitCommandBuffers.latest - mvkPerfStats.queue.retrieveCAMetalDrawable.latest ) * 1000.0 ) ); +#endif +#endif + const vk::Result res = m_VulkanDevice.acquireNextImageKHR( m_SwapChain, std::numeric_limits::max(), // timeout m_PresentSemaphore, From 9c62802810d0a46bd8603a55ba0b88176a1331c0 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Wed, 11 Oct 2023 22:48:44 -0400 Subject: [PATCH 21/37] CMakeLists: Add wildcards to remove tmp files from ZERO_CHECK regeneration (Xcode) (cherry picked from commit 0f9f4f6022dae813092be3a16247e3312a00c46c) --- neo/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt index a99f27d2..f2e1ef70 100644 --- a/neo/CMakeLists.txt +++ b/neo/CMakeLists.txt @@ -1776,7 +1776,8 @@ else() # delete precompiled header file after executable is compiled: IDE build case (e.g. Xcode) else() add_custom_command(TARGET RBDoom3BFG POST_BUILD - COMMAND ${remove_command} "idlib/precompiled.h.gch" + # SRS - added wildcards to remove tmp files from cmake ZERO_CHECK regeneration + COMMAND ${remove_command} "idlib/precompiled.h*.gch*" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "remove idlib/precompiled.h.gch" ) From b42e64b180dcaf757790702009c1ede50031f3c7 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Thu, 12 Oct 2023 22:35:31 -0400 Subject: [PATCH 22/37] macOS: Disable GPU skinning on Apple Silicon to eliminate rendering artifacts (cherry picked from commit 86dc341567573b3bb91761e1309ed6be540050c9) --- neo/sys/posix/platform_osx.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/neo/sys/posix/platform_osx.cpp b/neo/sys/posix/platform_osx.cpp index 0ff48f59..18a427bd 100644 --- a/neo/sys/posix/platform_osx.cpp +++ b/neo/sys/posix/platform_osx.cpp @@ -452,6 +452,8 @@ main */ int main( int argc, const char** argv ) { + extern idCVar r_useGPUSkinning; + // DG: needed for Sys_ReLaunch() cmdargc = argc; cmdargv = argv; @@ -481,6 +483,21 @@ int main( int argc, const char** argv ) common->Init( 0, NULL, NULL ); } + // SRS - Determine the machine name, e.g. "x86_64" or "arm64" + // Might be cleaner in posix Sys_Init(), but only needed on + // macOS and all the required sys includes are located here. + size_t size; + sysctlbyname( "hw.machine", NULL, &size, NULL, 0 ); + char* machineName = ( char* )Mem_Alloc( size, TAG_SYSTEM ); + sysctlbyname( "hw.machine", machineName, &size, NULL, 0 ); + + // FIXME: On Apple Silicon disable GPU skinning to eliminate rendering artifacts + if( strcmp( machineName, "arm64" ) == 0 ) + { + r_useGPUSkinning.SetInteger( 0 ); + } + Mem_Free( machineName ); + Posix_LateInit(); while( 1 ) From e37ede5f7ff87de76eb33cc4b729df77a7eee6da Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Sun, 15 Oct 2023 16:39:05 -0400 Subject: [PATCH 23/37] macOS: Update cmake*.sh build scripts for openal-soft path portability - thanks asemarafa (cherry picked from commit 6707d4286dd281036c3401d3c3d4381186577b69) --- README.md | 2 +- neo/cmake-macos-release.sh | 13 ++++++++++++- neo/cmake-macos-retail.sh | 13 ++++++++++++- neo/cmake-xcode-debug.sh | 13 ++++++++++++- neo/cmake-xcode-release.sh | 13 ++++++++++++- neo/cmake-xcode-universal.sh | 9 +++++++++ 6 files changed, 58 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f093db43..70ce4414 100644 --- a/README.md +++ b/README.md @@ -510,7 +510,7 @@ Recommended in this case is `cmake-vs2022-win64-no-ffmpeg.bat` Xcode release and universal builds now automatically package the executable into a macOS app bundle, defining an Info.plist file and copying the base directory and custom icon into the application bundle's Contents/Resources folder. This is controlled by adding -DMACOSX_BUNDLE=ON to the CMake options. - Depending on which package manager you install (Homebrew or MacPorts) you may need to change the openal-soft library and include paths specified in the cmake shell scripts. For single architecture builds (debug, release, retail) the default openal-soft paths are set for Homebrew on x86, while for universal builds the default paths are set for MacPorts on x86 or Apple Silicon. If you want to build using the single architecture shell scripts (debug, release, retail) on Apple Silicon, you will need to change the openal-soft paths from `/usr/local/...` to either `/opt/homebrew/...` (Homebrew) or `/opt/local/...` (MacPorts). + For single architecture builds (debug, release, retail) the default openal-soft paths are set for Homebrew, while for universal builds the default paths are set for MacPorts. The single architecture build scripts are now portable and automatically detect Homebrew's openal-soft path prefix for x86 and Apple Silicon. The universal build script remains portable since MacPorts uses the same openal-soft installation path on x86 and Apple Silicon. 4. Compile RBDOOM-3-BFG targets: diff --git a/neo/cmake-macos-release.sh b/neo/cmake-macos-release.sh index e9064c69..8d041a92 100755 --- a/neo/cmake-macos-release.sh +++ b/neo/cmake-macos-release.sh @@ -2,5 +2,16 @@ cd .. rm -rf build mkdir build cd build + +# asemarafa/SRS - Determine the Homebrew path prefix for openal-soft +if [ -z "$OPENAL_PREFIX" ]; then + OPENAL_PREFIX=$(brew --prefix openal-soft 2>/dev/null) + if [ -z "$OPENAL_PREFIX" ]; then + echo "Error: openal-soft is not installed via Homebrew." + echo "Either install it using 'brew install openal-soft' or define the path prefix via OPENAL_PREFIX." + exit 1 + fi +fi + # change or remove -DCMAKE_OSX_DEPLOYMENT_TARGET= to match supported runtime targets -cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS_RELEASE="-DNDEBUG" -DCMAKE_OSX_DEPLOYMENT_TARGET=12.1 -DFFMPEG=OFF -DBINKDEC=ON -DUSE_MoltenVK=ON -DOPENAL_LIBRARY=/usr/local/opt/openal-soft/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=/usr/local/opt/openal-soft/include ../neo -Wno-dev +cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS_RELEASE="-DNDEBUG" -DCMAKE_OSX_DEPLOYMENT_TARGET=12.1 -DFFMPEG=OFF -DBINKDEC=ON -DUSE_MoltenVK=ON -DOPENAL_LIBRARY=$OPENAL_PREFIX/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=$OPENAL_PREFIX/include ../neo -Wno-dev diff --git a/neo/cmake-macos-retail.sh b/neo/cmake-macos-retail.sh index 47844789..e02ed5d0 100755 --- a/neo/cmake-macos-retail.sh +++ b/neo/cmake-macos-retail.sh @@ -2,5 +2,16 @@ cd .. rm -rf build mkdir build cd build + +# asemarafa/SRS - Determine the Homebrew path prefix for openal-soft +if [ -z "$OPENAL_PREFIX" ]; then + OPENAL_PREFIX=$(brew --prefix openal-soft 2>/dev/null) + if [ -z "$OPENAL_PREFIX" ]; then + echo "Error: openal-soft is not installed via Homebrew." + echo "Either install it using 'brew install openal-soft' or define the path prefix via OPENAL_PREFIX." + exit 1 + fi +fi + # change or remove -DCMAKE_OSX_DEPLOYMENT_TARGET= to match supported runtime targets -cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS_RELEASE="-DNDEBUG -DID_RETAIL" -DCMAKE_OSX_DEPLOYMENT_TARGET=12.1 -DFFMPEG=OFF -DBINKDEC=ON -DUSE_MoltenVK=ON -DOPENAL_LIBRARY=/usr/local/opt/openal-soft/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=/usr/local/opt/openal-soft/include ../neo -Wno-dev +cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS_RELEASE="-DNDEBUG -DID_RETAIL" -DCMAKE_OSX_DEPLOYMENT_TARGET=12.1 -DFFMPEG=OFF -DBINKDEC=ON -DUSE_MoltenVK=ON -DOPENAL_LIBRARY=$OPENAL_PREFIX/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=$OPENAL_PREFIX/include ../neo -Wno-dev diff --git a/neo/cmake-xcode-debug.sh b/neo/cmake-xcode-debug.sh index a3b8b907..6087a670 100755 --- a/neo/cmake-xcode-debug.sh +++ b/neo/cmake-xcode-debug.sh @@ -2,6 +2,17 @@ cd .. rm -rf xcode-debug mkdir xcode-debug cd xcode-debug + +# asemarafa/SRS - Determine the Homebrew path prefix for openal-soft +if [ -z "$OPENAL_PREFIX" ]; then + OPENAL_PREFIX=$(brew --prefix openal-soft 2>/dev/null) + if [ -z "$OPENAL_PREFIX" ]; then + echo "Error: openal-soft is not installed via Homebrew." + echo "Either install it using 'brew install openal-soft' or define the path prefix via OPENAL_PREFIX." + exit 1 + fi +fi + # note 1: remove or set -DCMAKE_SUPPRESS_REGENERATION=OFF to reenable ZERO_CHECK target which checks for CMakeLists.txt changes and re-runs CMake before builds # however, if ZERO_CHECK is reenabled **must** add VULKAN_SDK location to Xcode Custom Paths (under Prefs/Locations) otherwise build failures may occur # note 2: policy CMAKE_POLICY_DEFAULT_CMP0142=NEW suppresses non-existant per-config suffixes on Xcode library search paths, works for cmake version 3.25 and later @@ -9,4 +20,4 @@ cd xcode-debug # note 4: env variable MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS=0 disables synchronous queue submits which is optimal for the synchronization method used by the game # note 5: env variable MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2 enables MoltenVK's use of Metal argument buffers only if VK_EXT_descriptor_indexing is enabled # note 6: env variable MVK_CONFIG_TIMESTAMP_PERIOD_LOWPASS_ALPHA=1.0 disables MoltenVK's timestampPeriod lowpass filter for non-Apple GPUs -cmake -G Xcode -DCMAKE_BUILD_TYPE=Debug -DCMAKE_XCODE_GENERATE_SCHEME=ON -DCMAKE_XCODE_SCHEME_ENVIRONMENT="MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE=1;MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS=0;MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2;MVK_CONFIG_TIMESTAMP_PERIOD_LOWPASS_ALPHA=1.0" -DCMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION=OFF -DCMAKE_SUPPRESS_REGENERATION=ON -DOPENAL_LIBRARY=/usr/local/opt/openal-soft/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=/usr/local/opt/openal-soft/include ../neo -DCMAKE_POLICY_DEFAULT_CMP0142=NEW -Wno-dev +cmake -G Xcode -DCMAKE_BUILD_TYPE=Debug -DCMAKE_XCODE_GENERATE_SCHEME=ON -DCMAKE_XCODE_SCHEME_ENVIRONMENT="MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE=1;MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS=0;MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2;MVK_CONFIG_TIMESTAMP_PERIOD_LOWPASS_ALPHA=1.0" -DCMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION=OFF -DCMAKE_SUPPRESS_REGENERATION=ON -DOPENAL_LIBRARY=$OPENAL_PREFIX/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=$OPENAL_PREFIX/include ../neo -DCMAKE_POLICY_DEFAULT_CMP0142=NEW -Wno-dev diff --git a/neo/cmake-xcode-release.sh b/neo/cmake-xcode-release.sh index 3d9991cb..538c1ed4 100755 --- a/neo/cmake-xcode-release.sh +++ b/neo/cmake-xcode-release.sh @@ -2,7 +2,18 @@ cd .. rm -rf xcode-release mkdir xcode-release cd xcode-release + +# asemarafa/SRS - Determine the Homebrew path prefix for openal-soft +if [ -z "$OPENAL_PREFIX" ]; then + OPENAL_PREFIX=$(brew --prefix openal-soft 2>/dev/null) + if [ -z "$OPENAL_PREFIX" ]; then + echo "Error: openal-soft is not installed via Homebrew." + echo "Either install it using 'brew install openal-soft' or define the path prefix via OPENAL_PREFIX." + exit 1 + fi +fi + # note 1: remove or set -DCMAKE_SUPPRESS_REGENERATION=OFF to reenable ZERO_CHECK target which checks for CMakeLists.txt changes and re-runs CMake before builds # however, if ZERO_CHECK is reenabled **must** add VULKAN_SDK location to Xcode Custom Paths (under Prefs/Locations) otherwise build failures may occur # note 2: policy CMAKE_POLICY_DEFAULT_CMP0142=NEW suppresses non-existant per-config suffixes on Xcode library search paths, works for cmake version 3.25 and later -cmake -G Xcode -DCMAKE_BUILD_TYPE=Release -DCMAKE_CONFIGURATION_TYPES="Release;MinSizeRel;RelWithDebInfo" -DMACOSX_BUNDLE=ON -DFFMPEG=OFF -DBINKDEC=ON -DUSE_MoltenVK=ON -DCMAKE_XCODE_GENERATE_SCHEME=ON -DCMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION=OFF -DCMAKE_SUPPRESS_REGENERATION=ON -DOPENAL_LIBRARY=/usr/local/opt/openal-soft/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=/usr/local/opt/openal-soft/include ../neo -DCMAKE_POLICY_DEFAULT_CMP0142=NEW -Wno-dev +cmake -G Xcode -DCMAKE_BUILD_TYPE=Release -DCMAKE_CONFIGURATION_TYPES="Release;MinSizeRel;RelWithDebInfo" -DMACOSX_BUNDLE=ON -DFFMPEG=OFF -DBINKDEC=ON -DUSE_MoltenVK=ON -DCMAKE_XCODE_GENERATE_SCHEME=ON -DCMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION=OFF -DCMAKE_SUPPRESS_REGENERATION=ON -DOPENAL_LIBRARY=$OPENAL_PREFIX/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=$OPENAL_PREFIX/include ../neo -DCMAKE_POLICY_DEFAULT_CMP0142=NEW -Wno-dev diff --git a/neo/cmake-xcode-universal.sh b/neo/cmake-xcode-universal.sh index d28fedf3..667c44fe 100755 --- a/neo/cmake-xcode-universal.sh +++ b/neo/cmake-xcode-universal.sh @@ -2,6 +2,15 @@ cd .. rm -rf xcode-universal mkdir xcode-universal cd xcode-universal + +# SRS - Determine if openal-soft universal variant is installed via MacPorts +OPENAL_VARIANTS=$(port info --variants openal-soft 2>/dev/null) +if [[ $OPENAL_VARIANTS != *universal* ]]; then + echo "Error: openal-soft universal variant is not installed via MacPorts." + echo "Please install it using 'sudo port install openal-soft +universal'" + exit 1 +fi + # note 1: remove or set -DCMAKE_SUPPRESS_REGENERATION=OFF to reenable ZERO_CHECK target which checks for CMakeLists.txt changes and re-runs CMake before builds # however, if ZERO_CHECK is reenabled **must** add VULKAN_SDK location to Xcode Custom Paths (under Prefs/Locations) otherwise build failures may occur # note 2: policy CMAKE_POLICY_DEFAULT_CMP0142=NEW suppresses non-existant per-config suffixes on Xcode library search paths, works for cmake version 3.25 and later From f434a650db4f1086b5f71fd7e79b9979cdd8af93 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Tue, 17 Oct 2023 14:07:53 -0400 Subject: [PATCH 24/37] Work around missing Vulkan shaderStorageImageReadWithoutFormat on Intel GPUs (cherry picked from commit 83b97d0e1c13efad0bad86c80dcecff0f0b4628c) --- neo/sys/DeviceManager_VK.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index 58656c83..ba62b7ba 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -225,6 +225,7 @@ private: #if USE_OPTICK VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, #endif + VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME, VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME }, }; @@ -849,9 +850,12 @@ bool DeviceManager_VK::createDevice() APPEND_EXTENSION( sync2Supported, sync2Features ) #undef APPEND_EXTENSION + vk::PhysicalDeviceFeatures actualDeviceFeatures; + m_VulkanPhysicalDevice.getFeatures( &actualDeviceFeatures ); + auto deviceFeatures = vk::PhysicalDeviceFeatures() .setShaderImageGatherExtended( true ) - .setShaderStorageImageReadWithoutFormat( true ) + .setShaderStorageImageReadWithoutFormat( actualDeviceFeatures.shaderStorageImageReadWithoutFormat ) .setSamplerAnisotropy( true ) .setTessellationShader( true ) .setTextureCompressionBC( true ) From bc658e4203fd865b844ed664c03b1c161b370c82 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Thu, 19 Oct 2023 12:21:37 -0400 Subject: [PATCH 25/37] Vulkan: Detect and enable fragment shading rate features at individual per-feature granularity (cherry picked from commit 638ae85c6c420160e022af171304717adb14f1f2) --- neo/sys/DeviceManager_VK.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index ba62b7ba..330ce360 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -225,7 +225,9 @@ private: #if USE_OPTICK VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, #endif +#if defined( VK_KHR_format_feature_flags2 ) VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME, +#endif VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME }, }; @@ -821,10 +823,17 @@ bool DeviceManager_VK::createDevice() auto meshletFeatures = vk::PhysicalDeviceMeshShaderFeaturesNV() .setTaskShader( true ) .setMeshShader( true ); + + // SRS - get/set shading rate features which are detected individually by nvrhi (not just at extension level) + vk::PhysicalDeviceFeatures2 actualDeviceFeatures2; + vk::PhysicalDeviceFragmentShadingRateFeaturesKHR fragmentShadingRateFeatures; + actualDeviceFeatures2.pNext = &fragmentShadingRateFeatures; + m_VulkanPhysicalDevice.getFeatures2( &actualDeviceFeatures2 ); + auto vrsFeatures = vk::PhysicalDeviceFragmentShadingRateFeaturesKHR() - .setPipelineFragmentShadingRate( true ) - .setPrimitiveFragmentShadingRate( true ) - .setAttachmentFragmentShadingRate( true ); + .setPipelineFragmentShadingRate( fragmentShadingRateFeatures.pipelineFragmentShadingRate ) + .setPrimitiveFragmentShadingRate( fragmentShadingRateFeatures.primitiveFragmentShadingRate ) + .setAttachmentFragmentShadingRate( fragmentShadingRateFeatures.attachmentFragmentShadingRate ); auto sync2Features = vk::PhysicalDeviceSynchronization2FeaturesKHR() .setSynchronization2( true ); @@ -850,12 +859,9 @@ bool DeviceManager_VK::createDevice() APPEND_EXTENSION( sync2Supported, sync2Features ) #undef APPEND_EXTENSION - vk::PhysicalDeviceFeatures actualDeviceFeatures; - m_VulkanPhysicalDevice.getFeatures( &actualDeviceFeatures ); - auto deviceFeatures = vk::PhysicalDeviceFeatures() .setShaderImageGatherExtended( true ) - .setShaderStorageImageReadWithoutFormat( actualDeviceFeatures.shaderStorageImageReadWithoutFormat ) + .setShaderStorageImageReadWithoutFormat( actualDeviceFeatures2.features.shaderStorageImageReadWithoutFormat ) .setSamplerAnisotropy( true ) .setTessellationShader( true ) .setTextureCompressionBC( true ) From 48381ec0b94b22befc8b50b64933a519c9950626 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Fri, 27 Oct 2023 13:26:13 -0400 Subject: [PATCH 26/37] Add comments, remove redundant call to Vulkan getProperties, enable Khronos sync2 layer based on macOS SDK version (cherry picked from commit 18769ecce66ce75b8bb8609c71194146464786e7) --- neo/renderer/NVRHI/RenderBackend_NVRHI.cpp | 1 + neo/renderer/RenderBackend.cpp | 1 + neo/sys/DeviceManager_VK.cpp | 20 ++++++++++++++------ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp b/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp index 7718b08e..47eed086 100644 --- a/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp +++ b/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp @@ -1600,6 +1600,7 @@ void idRenderBackend::GL_EndFrame() // required for Vulkan: transition our swap image to present deviceManager->EndFrame(); + // SRS - execute after EndFrame() to avoid need for barrier command list on Vulkan deviceManager->GetDevice()->executeCommandList( commandList ); // update jitter for perspective matrix diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 94bccaef..3b16a260 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -5386,6 +5386,7 @@ void idRenderBackend::ExecuteBackEndCommands( const emptyCommand_t* cmds ) uint64 backEndFinishTime = Sys_Microseconds(); pc.cpuTotalMicroSec = backEndFinishTime - backEndStartTime; + // SRS - capture backend timing before GL_EndFrame() since it can block when r_mvkSynchronousQueueSubmits is enabled on macOS/MoltenVK GL_EndFrame(); if( r_debugRenderToTexture.GetInteger() == 1 ) diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index 330ce360..3a623274 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -209,12 +209,7 @@ private: VK_EXT_DEBUG_UTILS_EXTENSION_NAME }, // layers - { -#if defined(__APPLE__) && !defined( USE_MoltenVK ) - // SRS - Enable synchronization2 layer when using Vulkan loader and MoltenVK version unknown - "VK_LAYER_KHRONOS_synchronization2" -#endif - }, + { }, // device { VK_EXT_DEBUG_MARKER_EXTENSION_NAME, @@ -436,6 +431,13 @@ bool DeviceManager_VK::createInstance() { enabledExtensions.layers.insert( name ); } +#if defined(__APPLE__) + // SRS - Vulkan SDK < 1.3.268.1 does not have native VK_KHR_synchronization2 support on macOS, add Khronos layer to emulate + else if( name == "VK_LAYER_KHRONOS_synchronization2" && VK_HEADER_VERSION_COMPLETE < VK_MAKE_API_VERSION( 0, 1, 3, 268 ) ) + { + enabledExtensions.layers.insert( name ); + } +#endif requiredLayers.erase( name ); } @@ -938,6 +940,12 @@ bool DeviceManager_VK::createDevice() auto prop = m_VulkanPhysicalDevice.getProperties(); m_RendererString = std::string( prop.deviceName.data() ); + // SRS - Determine maxPushConstantSize for Vulkan device + if( r_useVulkanPushConstants.GetBool() ) + { + m_DeviceParams.maxPushConstantSize = Min( prop.limits.maxPushConstantsSize, nvrhi::c_MaxPushConstantSize ); + } + #if defined( USE_AMD_ALLOCATOR ) // SRS - initialize the vma allocator VmaVulkanFunctions vulkanFunctions = {}; From ec2719b099cced4f7f9d1d768f6699516fb46e4e Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Thu, 30 Nov 2023 12:26:38 -0500 Subject: [PATCH 27/37] Statistics HUD: smooth CPU/GPU usage, add GPU Memory for mode 3; CMakeLists: make VMA header visible in IDE (cherry picked from commit 8a0c493f1c4ef45312005c7e5b02cdde706bcc2b) --- neo/CMakeLists.txt | 3 +++ neo/framework/Common_local.h | 20 +++++++++++++++++--- neo/framework/Console.cpp | 20 ++++++++++++++++---- neo/sys/DeviceManager_DX12.cpp | 11 +++++++++-- neo/sys/DeviceManager_VK.cpp | 23 +++++++++++++++++++++-- 5 files changed, 66 insertions(+), 11 deletions(-) diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt index f2e1ef70..96cea187 100644 --- a/neo/CMakeLists.txt +++ b/neo/CMakeLists.txt @@ -429,6 +429,8 @@ if(USE_VULKAN) if(USE_VMA) add_definitions(-DUSE_AMD_ALLOCATOR) include_directories("libs/vma/include") + file(GLOB VMA_INCLUDES libs/vma/include/*.h) + source_group("libs\\vma" FILES ${VMA_INCLUDES}) endif() endif() @@ -1304,6 +1306,7 @@ set(RBDOOM3_INCLUDES #${FREETYPE_SOURCES} ${SOUND_INCLUDES} ${OGGVORBIS_INCLUDES} + ${VMA_INCLUDES} ${OPTICK_INCLUDES} ${UI_INCLUDES} ${SWF_INCLUDES} diff --git a/neo/framework/Common_local.h b/neo/framework/Common_local.h index 267b5184..911c197b 100644 --- a/neo/framework/Common_local.h +++ b/neo/framework/Common_local.h @@ -382,13 +382,24 @@ public: // SRS start void SetRendererMvkEncodeMicroseconds( uint64 mvkEncodeMicroSeconds ) { - mvkEncodeMicroSec = mvkEncodeMicroSeconds; + metal_encode = mvkEncodeMicroSeconds; return; } uint64 GetRendererMvkEncodeMicroseconds() const { - return mvkEncodeMicroSec; + return metal_encode; + } + + void SetRendererGpuMemoryMB( int gpuMemoryMB ) + { + gpu_memory = gpuMemoryMB; + return; + } + + int GetRendererGpuMemoryMB() const + { + return gpu_memory; } // SRS end @@ -604,8 +615,11 @@ private: // RB: r_speeds counters backEndCounters_t stats_backend; performanceCounters_t stats_frontend; + // SRS - MoltenVK's Vulkan to Metal command buffer encoding time, set default to 0 for non-macOS platforms (Windows and Linux) - uint64 mvkEncodeMicroSec = 0; + uint64 metal_encode = 0; + // SRS - Cross-platform GPU Memory usage counter, set default to 0 in case platform or graphics API does not support queries + int gpu_memory = 0; // Used during loading screens int lastPacifierSessionTime; diff --git a/neo/framework/Console.cpp b/neo/framework/Console.cpp index 5846a13d..f5811566 100644 --- a/neo/framework/Console.cpp +++ b/neo/framework/Console.cpp @@ -225,8 +225,10 @@ float idConsoleLocal::DrawFPS( float y ) extern idCVar r_swapInterval; static float previousTimes[FPS_FRAMES]; + static float previousCpuUsage[FPS_FRAMES] = {}; + static float previousGpuUsage[FPS_FRAMES] = {}; static float previousTimesNormalized[FPS_FRAMES_HISTORY]; - static int index; + static int index = 0; static int previous; static int valuesOffset = 0; @@ -239,6 +241,8 @@ float idConsoleLocal::DrawFPS( float y ) previous = t; int fps = 0; + float cpuUsage = 0.0; + float gpuUsage = 0.0; const float milliSecondsPerFrame = 1000.0f / com_engineHz_latched; @@ -253,6 +257,8 @@ float idConsoleLocal::DrawFPS( float y ) for( int i = 0 ; i < FPS_FRAMES ; i++ ) { total += previousTimes[i]; + cpuUsage += previousCpuUsage[i]; + gpuUsage += previousGpuUsage[i]; } if( !total ) { @@ -260,6 +266,8 @@ float idConsoleLocal::DrawFPS( float y ) } fps = 1000000 * FPS_FRAMES / total; fps = ( fps + 500 ) / 1000; + cpuUsage /= FPS_FRAMES; + gpuUsage /= FPS_FRAMES; const char* s = va( "%ifps", fps ); int w = strlen( s ) * BIGCHAR_WIDTH; @@ -316,6 +324,10 @@ float idConsoleLocal::DrawFPS( float y ) // SRS - GPU idle time is simply the difference between measured frame-over-frame time and GPU busy time (directly from GPU timers) const int64 rendererGPUIdleTime = frameBusyTime + frameIdleTime - rendererGPUTime; + // SRS - Save current CPU and GPU usage factors in ring buffer to calculate smoothed averages for future frames + previousCpuUsage[(index - 1) % FPS_FRAMES] = float( frameBusyTime - frameSyncTime ) / float( frameBusyTime + frameIdleTime ) * 100.0; + previousGpuUsage[(index - 1) % FPS_FRAMES] = float( rendererGPUTime ) / float( rendererGPUTime + rendererGPUIdleTime ) * 100.0; + #if 1 // RB: use ImGui to show more detailed stats about the scene loads @@ -428,7 +440,7 @@ float idConsoleLocal::DrawFPS( float y ) ImGui::TextColored( colorCyan, "API: %s, AA[%i, %i]: %s, %s", API, width, height, aaMode, resolutionText.c_str() ); - ImGui::TextColored( colorGold, "Device: %s", deviceManager->GetRendererString() ); + ImGui::TextColored( colorGold, "Device: %s, Memory: %i MB", deviceManager->GetRendererString(), commonLocal.GetRendererGpuMemoryMB() ); ImGui::TextColored( colorLtGrey, "GENERAL: views:%i draws:%i tris:%i", commonLocal.stats_frontend.c_numViews, @@ -478,7 +490,7 @@ float idConsoleLocal::DrawFPS( float y ) if( com_showFPS.GetInteger() > 2 ) { - const char* overlay = va( "Average FPS %i", fps ); + const char* overlay = va( "Average FPS %-4i", fps ); ImGui::PlotLines( "Relative\nFrametime ms", previousTimesNormalized, FPS_FRAMES_HISTORY, valuesOffset, overlay, -10.0f, 10.0f, ImVec2( 0, 50 ) ); } @@ -508,7 +520,7 @@ float idConsoleLocal::DrawFPS( float y ) ImGui::TextColored( frameBusyTime > maxTime || rendererGPUTime > maxTime ? colorRed : colorWhite, "Total: %5lld us Total: %5lld us", frameBusyTime, rendererGPUTime ); ImGui::TextColored( colorWhite, "Idle: %5lld us Idle: %5lld us", frameIdleTime, rendererGPUIdleTime ); // SRS - Show CPU and GPU overall usage statistics - ImGui::TextColored( colorWhite, "Usage: %3.0f %% Usage: %3.0f %%", float( frameBusyTime - frameSyncTime ) / float( frameBusyTime + frameIdleTime ) * 100.0, float( rendererGPUTime ) / float( rendererGPUTime + rendererGPUIdleTime ) * 100.0 ); + ImGui::TextColored( colorWhite, "Usage: %3.0f %% Usage: %3.0f %%", cpuUsage, gpuUsage ); ImGui::End(); } diff --git a/neo/sys/DeviceManager_DX12.cpp b/neo/sys/DeviceManager_DX12.cpp index 29d9142a..7ef47746 100644 --- a/neo/sys/DeviceManager_DX12.cpp +++ b/neo/sys/DeviceManager_DX12.cpp @@ -27,6 +27,7 @@ #include "renderer/RenderCommon.h" #include "renderer/RenderSystem.h" +#include "framework/Common_local.h" #include #include @@ -57,7 +58,7 @@ class DeviceManager_DX12 : public DeviceManager RefCountPtr m_SwapChain; DXGI_SWAP_CHAIN_DESC1 m_SwapChainDesc{}; DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_FullScreenDesc{}; - RefCountPtr m_DxgiAdapter; + RefCountPtr m_DxgiAdapter; bool m_TearingSupported = false; std::vector> m_SwapChainBuffers; @@ -388,7 +389,7 @@ bool DeviceManager_DX12::CreateDeviceAndSwapChain() } } - m_DxgiAdapter = targetAdapter; + targetAdapter->QueryInterface( IID_PPV_ARGS( &m_DxgiAdapter ) ); D3D12_COMMAND_QUEUE_DESC queueDesc; ZeroMemory( &queueDesc, sizeof( queueDesc ) ); @@ -566,6 +567,12 @@ void DeviceManager_DX12::ResizeSwapChain() void DeviceManager_DX12::BeginFrame() { OPTICK_CATEGORY( "DX12_BeginFrame", Optick::Category::Wait ); + + // SRS - get DXGI GPU memory usage for display in statistics overlay HUD + DXGI_QUERY_VIDEO_MEMORY_INFO memoryInfoLocal = {}, memoryInfoNonLocal = {}; + m_DxgiAdapter->QueryVideoMemoryInfo( 0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &memoryInfoLocal ); + m_DxgiAdapter->QueryVideoMemoryInfo( 0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &memoryInfoNonLocal ); + commonLocal.SetRendererGpuMemoryMB( int( ( memoryInfoLocal.CurrentUsage + memoryInfoNonLocal.CurrentUsage ) / 1024 / 1024 ) ); } nvrhi::ITexture* DeviceManager_DX12::GetCurrentBackBuffer() diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index 3a623274..4dc09861 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -32,13 +32,13 @@ #include #include "renderer/RenderCommon.h" +#include "framework/Common_local.h" #include #include // SRS - optionally needed for MoltenVK runtime config visibility #if defined(__APPLE__) && defined( USE_MoltenVK ) #include - #include "framework/Common_local.h" idCVar r_mvkSynchronousQueueSubmits( "r_mvkSynchronousQueueSubmits", "0", CVAR_BOOL | CVAR_INIT, "Use MoltenVK's synchronous queue submit option." ); #endif #include @@ -222,6 +222,9 @@ private: #endif #if defined( VK_KHR_format_feature_flags2 ) VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME, +#endif +#if defined( VK_EXT_memory_budget ) + VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, #endif VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME }, @@ -1335,14 +1338,30 @@ void DeviceManager_VK::DestroyDeviceAndSwapChain() void DeviceManager_VK::BeginFrame() { + OPTICK_CATEGORY( "Vulkan_BeginFrame", Optick::Category::Wait ); + #if defined(__APPLE__) && defined( USE_MoltenVK ) #if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) - // SRS - fetch MoltenVK's Vulkan to Metal encoding time for the previous frame + // SRS - get MoltenVK's Metal encoding time and GPU memory usage for display in statistics overlay HUD MVKPerformanceStatistics mvkPerfStats; size_t mvkPerfStatsSize = sizeof( mvkPerfStats ); vkGetPerformanceStatisticsMVK( m_VulkanDevice, &mvkPerfStats, &mvkPerfStatsSize ); commonLocal.SetRendererMvkEncodeMicroseconds( uint64( Max( 0.0, mvkPerfStats.queue.submitCommandBuffers.latest - mvkPerfStats.queue.retrieveCAMetalDrawable.latest ) * 1000.0 ) ); + commonLocal.SetRendererGpuMemoryMB( int( mvkPerfStats.device.gpuMemoryAllocated.latest / 1024.0 ) ); #endif +#elif defined( VK_EXT_memory_budget ) + // SRS - get Vulkan GPU memory usage for display in statistics overlay HUD + vk::PhysicalDeviceMemoryProperties2 memoryProperties2; + vk::PhysicalDeviceMemoryBudgetPropertiesEXT memoryBudget; + memoryProperties2.pNext = &memoryBudget; + m_VulkanPhysicalDevice.getMemoryProperties2( &memoryProperties2 ); + + VkDeviceSize gpuMemoryAllocated = 0; + for( uint32_t i = 0; i < memoryProperties2.memoryProperties.memoryHeapCount; i++ ) + { + gpuMemoryAllocated += memoryBudget.heapUsage[i]; + } + commonLocal.SetRendererGpuMemoryMB( int( gpuMemoryAllocated / 1024 / 1024 ) ); #endif const vk::Result res = m_VulkanDevice.acquireNextImageKHR( m_SwapChain, From e80b6a3e21af4fd2fd83cb6f3845644cb499f4dd Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Thu, 28 Dec 2023 14:28:20 -0500 Subject: [PATCH 28/37] macOS: Support VK_EXT_layer_settings for MoltenVK >= 1.2.7 / Vulkan SDK >= 1.3.272.0 (cherry picked from commit f3c65eef3e75ea2846cffd97aff1fd2f9bf92f7f) --- neo/sys/DeviceManager_VK.cpp | 224 ++++++++++++++++++++++++----------- 1 file changed, 154 insertions(+), 70 deletions(-) diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index 4dc09861..afeb4aae 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -38,7 +38,12 @@ #include // SRS - optionally needed for MoltenVK runtime config visibility #if defined(__APPLE__) && defined( USE_MoltenVK ) - #include +#if 0 + #include + #include // SRS - will eventually move to these mvk include files for MoltenVK >= 1.2.7 / SDK >= 1.3.272.0 +#else + #include // SRS - now deprecated, but provides backwards compatibility for MoltenVK < 1.2.7 / SDK < 1.3.272.0 +#endif idCVar r_mvkSynchronousQueueSubmits( "r_mvkSynchronousQueueSubmits", "0", CVAR_BOOL | CVAR_INIT, "Use MoltenVK's synchronous queue submit option." ); #endif #include @@ -201,9 +206,15 @@ private: { // instance { -#if defined(__APPLE__) && defined( VK_KHR_portability_enumeration ) +#if defined(__APPLE__) +#if defined( VK_KHR_portability_enumeration ) // SRS - This is optional since it only became manadatory with Vulkan SDK 1.3.216.0 or later VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, +#endif +#if defined( VK_EXT_layer_settings ) + // SRS - This is optional since implemented only for MoltenVK 1.2.7 / SDK 1.3.272.0 or later + VK_EXT_LAYER_SETTINGS_EXTENSION_NAME, +#endif #endif VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME, VK_EXT_DEBUG_UTILS_EXTENSION_NAME @@ -223,10 +234,8 @@ private: #if defined( VK_KHR_format_feature_flags2 ) VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME, #endif -#if defined( VK_EXT_memory_budget ) - VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, -#endif - VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME + VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME, + VK_EXT_MEMORY_BUDGET_EXTENSION_NAME }, }; @@ -287,6 +296,9 @@ private: // SRS - flag indicating support for presentation timing via VK_GOOGLE_display_timing extension bool displayTimingEnabled = false; + // SRS - slot for Vulkan device API version at runtime (initialize to Vulkan build version) + uint32_t m_DeviceApiVersion = VK_HEADER_VERSION_COMPLETE; + private: static VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback( VkDebugReportFlagsEXT flags, @@ -427,6 +439,7 @@ bool DeviceManager_VK::createInstance() std::unordered_set requiredLayers = enabledExtensions.layers; + auto instanceVersion = vk::enumerateInstanceVersion(); for( const auto& layer : vk::enumerateInstanceLayerProperties() ) { const std::string name = layer.layerName; @@ -434,9 +447,9 @@ bool DeviceManager_VK::createInstance() { enabledExtensions.layers.insert( name ); } -#if defined(__APPLE__) +#if defined(__APPLE__) && !defined( USE_MoltenVK ) // SRS - Vulkan SDK < 1.3.268.1 does not have native VK_KHR_synchronization2 support on macOS, add Khronos layer to emulate - else if( name == "VK_LAYER_KHRONOS_synchronization2" && VK_HEADER_VERSION_COMPLETE < VK_MAKE_API_VERSION( 0, 1, 3, 268 ) ) + else if( name == "VK_LAYER_KHRONOS_synchronization2" && instanceVersion < VK_MAKE_API_VERSION( 0, 1, 3, 268 ) ) { enabledExtensions.layers.insert( name ); } @@ -478,11 +491,63 @@ bool DeviceManager_VK::createInstance() .setPpEnabledExtensionNames( instanceExtVec.data() ) .setPApplicationInfo( &applicationInfo ); -#if defined(__APPLE__) && defined( VK_KHR_portability_enumeration ) +#if defined(__APPLE__) +#if defined( VK_KHR_portability_enumeration ) if( enabledExtensions.instance.find( VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME ) != enabledExtensions.instance.end() ) { info.setFlags( vk::InstanceCreateFlagBits( VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR ) ); } +#endif +#if defined( USE_MoltenVK ) && defined( VK_EXT_layer_settings ) + // SRS - when USE_MoltenVK defined, set MoltenVK runtime configuration parameters on macOS via VK_EXT_layer_settings + std::vector layerSettings; + vk::LayerSettingsCreateInfoEXT layerSettingsCreateInfo; + + const vk::Bool32 valueTrue = vk::True, valueFalse = vk::False; + const int32_t useMetalArgumentBuffers = int32_t( MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_DESCRIPTOR_INDEXING ); + const Float32 timestampPeriodLowPassAlpha = 1.0; + + if( enabledExtensions.instance.find( VK_EXT_LAYER_SETTINGS_EXTENSION_NAME ) != enabledExtensions.instance.end() ) + { + // SRS - use MoltenVK layer for configuration via VK_EXT_layer_settings extension + vk::LayerSettingEXT layerSetting = { kMVKMoltenVKDriverLayerName, "", vk::LayerSettingTypeEXT( 0 ), 1, nullptr }; + + // SRS - Set MoltenVK's synchronous queue submit option for vkQueueSubmit() & vkQueuePresentKHR() + layerSetting.pSettingName = "MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS"; + layerSetting.type = vk::LayerSettingTypeEXT::eBool32; + layerSetting.pValues = r_mvkSynchronousQueueSubmits.GetBool() ? &valueTrue : &valueFalse; + layerSettings.push_back( layerSetting ); + + // SRS - Enable MoltenVK's image view swizzle feature in case we don't have native image view swizzle + layerSetting.pSettingName = "MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE"; + layerSetting.type = vk::LayerSettingTypeEXT::eBool32; + layerSetting.pValues = &valueTrue; + layerSettings.push_back( layerSetting ); + + // SRS - Turn MoltenVK's Metal argument buffer feature on for descriptor indexing only + layerSetting.pSettingName = "MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS"; + layerSetting.type = vk::LayerSettingTypeEXT::eInt32; + layerSetting.pValues = &useMetalArgumentBuffers; + layerSettings.push_back( layerSetting ); + + // SRS - Disable MoltenVK's timestampPeriod filter for HUD / Optick profiler timing calibration + layerSetting.pSettingName = "MVK_CONFIG_TIMESTAMP_PERIOD_LOWPASS_ALPHA"; + layerSetting.type = vk::LayerSettingTypeEXT::eFloat32; + layerSetting.pValues = ×tampPeriodLowPassAlpha; + layerSettings.push_back( layerSetting ); + + // SRS - Enable MoltenVK's performance tracking for display of Metal encoding timer on macOS + layerSetting.pSettingName = "MVK_CONFIG_PERFORMANCE_TRACKING"; + layerSetting.type = vk::LayerSettingTypeEXT::eBool32; + layerSetting.pValues = &valueTrue; + layerSettings.push_back( layerSetting ); + + layerSettingsCreateInfo.settingCount = uint32_t( layerSettings.size() ); + layerSettingsCreateInfo.pSettings = layerSettings.data(); + + info.setPNext( &layerSettingsCreateInfo ); + } +#endif #endif const vk::Result res = vk::createInstance( &info, nullptr, &m_VulkanInstance ); @@ -939,9 +1004,10 @@ bool DeviceManager_VK::createDevice() enablePModeImmediate = find( surfacePModes.begin(), surfacePModes.end(), vk::PresentModeKHR::eImmediate ) != surfacePModes.end(); enablePModeFifoRelaxed = find( surfacePModes.begin(), surfacePModes.end(), vk::PresentModeKHR::eFifoRelaxed ) != surfacePModes.end(); - // stash the renderer string + // stash the device renderer string and api version auto prop = m_VulkanPhysicalDevice.getProperties(); m_RendererString = std::string( prop.deviceName.data() ); + m_DeviceApiVersion = prop.apiVersion; // SRS - Determine maxPushConstantSize for Vulkan device if( r_useVulkanPushConstants.GetBool() ) @@ -1123,7 +1189,6 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() { enabledExtensions.instance.insert( VK_EXT_DEBUG_REPORT_EXTENSION_NAME ); #if defined(__APPLE__) && defined( USE_MoltenVK ) - enabledExtensions.layers.insert( "MoltenVK" ); } // SRS - when USE_MoltenVK defined, load libMoltenVK vs. the default libvulkan @@ -1171,49 +1236,57 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() CHECK( pickPhysicalDevice() ); CHECK( findQueueFamilies( m_VulkanPhysicalDevice, m_WindowSurface ) ); - // SRS - when USE_MoltenVK defined, set MoltenVK runtime configuration parameters on macOS + // SRS - when USE_MoltenVK defined, set MoltenVK runtime configuration parameters on macOS (deprecated version) #if defined(__APPLE__) && defined( USE_MoltenVK ) - vk::PhysicalDeviceFeatures2 deviceFeatures2; - vk::PhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures; - deviceFeatures2.setPNext( &portabilityFeatures ); - m_VulkanPhysicalDevice.getFeatures2( &deviceFeatures2 ); - - MVKConfiguration mvkConfig; - size_t mvkConfigSize = sizeof( mvkConfig ); - - vkGetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); - - // SRS - Set MoltenVK's synchronous queue submit option for vkQueueSubmit() & vkQueuePresentKHR() - if( mvkConfig.synchronousQueueSubmits == VK_TRUE && !r_mvkSynchronousQueueSubmits.GetBool() ) - { - idLib::Printf( "Disabled MoltenVK's synchronous queue submits...\n" ); - mvkConfig.synchronousQueueSubmits = VK_FALSE; - vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); - } - - // SRS - If we don't have native image view swizzle, enable MoltenVK's image view swizzle feature - if( portabilityFeatures.imageViewFormatSwizzle == VK_FALSE ) - { - idLib::Printf( "Enabled MoltenVK's image view swizzle...\n" ); - mvkConfig.fullImageViewSwizzle = VK_TRUE; - vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); - } - - // SRS - Turn MoltenVK's Metal argument buffer feature on for descriptor indexing only - if( mvkConfig.useMetalArgumentBuffers == MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_NEVER ) - { - idLib::Printf( "Enabled MoltenVK's Metal argument buffers for descriptor indexing...\n" ); - mvkConfig.useMetalArgumentBuffers = MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_DESCRIPTOR_INDEXING; - vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); - } - -#if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) - // SRS - Disable MoltenVK's timestampPeriod filter for HUD / Optick profiler timing calibration - mvkConfig.timestampPeriodLowPassAlpha = 1.0; - // SRS - Enable MoltenVK's performance tracking for display of Metal encoding timer on macOS - mvkConfig.performanceTracking = VK_TRUE; - vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); + // SRS - deprecated by VK_EXT_layer_settings, but retained for older versions of MoltenVK < 1.2.7 / SDK < 1.3.272.0 +#if defined( VK_EXT_layer_settings ) + // SRS - for backwards compatibility at runtime: execute only if we can't find the VK_EXT_layer_settings extension + if( enabledExtensions.instance.find( VK_EXT_LAYER_SETTINGS_EXTENSION_NAME ) == enabledExtensions.instance.end() ) #endif + { + vk::PhysicalDeviceFeatures2 deviceFeatures2; + vk::PhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures; + deviceFeatures2.setPNext( &portabilityFeatures ); + m_VulkanPhysicalDevice.getFeatures2( &deviceFeatures2 ); + + MVKConfiguration mvkConfig; + size_t mvkConfigSize = sizeof( mvkConfig ); + + vkGetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); + + // SRS - Set MoltenVK's synchronous queue submit option for vkQueueSubmit() & vkQueuePresentKHR() + if( mvkConfig.synchronousQueueSubmits == VK_TRUE && !r_mvkSynchronousQueueSubmits.GetBool() ) + { + idLib::Printf( "Disabled MoltenVK's synchronous queue submits...\n" ); + mvkConfig.synchronousQueueSubmits = VK_FALSE; + } + + // SRS - If we don't have native image view swizzle, enable MoltenVK's image view swizzle feature + if( portabilityFeatures.imageViewFormatSwizzle == VK_FALSE ) + { + idLib::Printf( "Enabled MoltenVK's image view swizzle...\n" ); + mvkConfig.fullImageViewSwizzle = VK_TRUE; + } + + // SRS - Turn MoltenVK's Metal argument buffer feature on for descriptor indexing only + if( mvkConfig.useMetalArgumentBuffers == MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_NEVER ) + { + idLib::Printf( "Enabled MoltenVK's Metal argument buffers for descriptor indexing...\n" ); + mvkConfig.useMetalArgumentBuffers = MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_DESCRIPTOR_INDEXING; + } + + #if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) + if( mvkConfig.apiVersionToAdvertise >= VK_MAKE_API_VERSION( 0, 1, 2, 268 ) ) + { + // SRS - Disable MoltenVK's timestampPeriod filter for HUD / Optick profiler timing calibration + mvkConfig.timestampPeriodLowPassAlpha = 1.0; + // SRS - Enable MoltenVK's performance tracking for display of Metal encoding timer on macOS + mvkConfig.performanceTracking = VK_TRUE; + } + #endif + + vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); + } #endif CHECK( createDevice() ); @@ -1342,27 +1415,38 @@ void DeviceManager_VK::BeginFrame() #if defined(__APPLE__) && defined( USE_MoltenVK ) #if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) - // SRS - get MoltenVK's Metal encoding time and GPU memory usage for display in statistics overlay HUD - MVKPerformanceStatistics mvkPerfStats; - size_t mvkPerfStatsSize = sizeof( mvkPerfStats ); - vkGetPerformanceStatisticsMVK( m_VulkanDevice, &mvkPerfStats, &mvkPerfStatsSize ); - commonLocal.SetRendererMvkEncodeMicroseconds( uint64( Max( 0.0, mvkPerfStats.queue.submitCommandBuffers.latest - mvkPerfStats.queue.retrieveCAMetalDrawable.latest ) * 1000.0 ) ); - commonLocal.SetRendererGpuMemoryMB( int( mvkPerfStats.device.gpuMemoryAllocated.latest / 1024.0 ) ); -#endif -#elif defined( VK_EXT_memory_budget ) - // SRS - get Vulkan GPU memory usage for display in statistics overlay HUD - vk::PhysicalDeviceMemoryProperties2 memoryProperties2; - vk::PhysicalDeviceMemoryBudgetPropertiesEXT memoryBudget; - memoryProperties2.pNext = &memoryBudget; - m_VulkanPhysicalDevice.getMemoryProperties2( &memoryProperties2 ); - - VkDeviceSize gpuMemoryAllocated = 0; - for( uint32_t i = 0; i < memoryProperties2.memoryProperties.memoryHeapCount; i++ ) + if( m_DeviceApiVersion >= VK_MAKE_API_VERSION( 0, 1, 2, 268 ) ) { - gpuMemoryAllocated += memoryBudget.heapUsage[i]; + // SRS - get MoltenVK's Metal encoding time and GPU memory usage for display in statistics overlay HUD + MVKPerformanceStatistics mvkPerfStats; + size_t mvkPerfStatsSize = sizeof( mvkPerfStats ); + vkGetPerformanceStatisticsMVK( m_VulkanDevice, &mvkPerfStats, &mvkPerfStatsSize ); + commonLocal.SetRendererMvkEncodeMicroseconds( uint64( Max( 0.0, mvkPerfStats.queue.submitCommandBuffers.latest - mvkPerfStats.queue.retrieveCAMetalDrawable.latest ) * 1000.0 ) ); + commonLocal.SetRendererGpuMemoryMB( int( mvkPerfStats.device.gpuMemoryAllocated.latest / 1024.0 ) ); } - commonLocal.SetRendererGpuMemoryMB( int( gpuMemoryAllocated / 1024 / 1024 ) ); + else #endif +#endif + { + // SRS - get Vulkan GPU memory usage for display in statistics overlay HUD + vk::PhysicalDeviceMemoryProperties2 memoryProperties2; + vk::PhysicalDeviceMemoryBudgetPropertiesEXT memoryBudget; + memoryProperties2.pNext = &memoryBudget; + m_VulkanPhysicalDevice.getMemoryProperties2( &memoryProperties2 ); + + VkDeviceSize gpuMemoryAllocated = 0; + for( uint32_t i = 0; i < memoryProperties2.memoryProperties.memoryHeapCount; i++ ) + { + gpuMemoryAllocated += memoryBudget.heapUsage[i]; + +#if defined(__APPLE__) + // SRS - macOS Vulkan API <= 1.2.268 has heap reporting defect, use heapUsage[0] only + if( m_DeviceApiVersion <= VK_MAKE_API_VERSION( 0, 1, 2, 268 ) ) + break; +#endif + } + commonLocal.SetRendererGpuMemoryMB( int( gpuMemoryAllocated / 1024 / 1024 ) ); + } const vk::Result res = m_VulkanDevice.acquireNextImageKHR( m_SwapChain, std::numeric_limits::max(), // timeout From e2956643a7cb81ddd0c1d0b596e99ebadafac599 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Tue, 2 Jan 2024 12:51:19 -0500 Subject: [PATCH 29/37] Don't reset or lock r_useGPUSkinning cvar in multiplayer mode (not a cheat) (cherry picked from commit 3b6598b88d71bc443c431a04e0f075410679ea36) --- neo/renderer/Model_md5.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/renderer/Model_md5.cpp b/neo/renderer/Model_md5.cpp index d3ece1e4..2d750415 100644 --- a/neo/renderer/Model_md5.cpp +++ b/neo/renderer/Model_md5.cpp @@ -44,7 +44,7 @@ static const char* MD5_SnapshotName = "_MD5_Snapshot_"; static const byte MD5B_VERSION = 106; static const unsigned int MD5B_MAGIC = ( '5' << 24 ) | ( 'D' << 16 ) | ( 'M' << 8 ) | MD5B_VERSION; -idCVar r_useGPUSkinning( "r_useGPUSkinning", "1", CVAR_INTEGER, "animate normals and tangents instead of deriving" ); +idCVar r_useGPUSkinning( "r_useGPUSkinning", "1", CVAR_INTEGER | CVAR_NOCHEAT, "animate normals and tangents instead of deriving" ); /*********************************************************************** From 62e9c561d5e79d54cbf7fa28e9c7c6df5603e9b7 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Tue, 16 Jan 2024 23:35:09 -0500 Subject: [PATCH 30/37] Optick: Add support for dynamic vs. static Vulkan functions (cherry picked from commit 821c8b46896932ff20595871b2088b0334367ed7) --- neo/libs/optick/optick.config.h | 6 +++ neo/libs/optick/optick.h | 9 ++-- neo/libs/optick/optick_gpu.vulkan.cpp | 66 ++++++++++++++++++++++++--- 3 files changed, 71 insertions(+), 10 deletions(-) diff --git a/neo/libs/optick/optick.config.h b/neo/libs/optick/optick.config.h index c024a9ca..190d9c8f 100644 --- a/neo/libs/optick/optick.config.h +++ b/neo/libs/optick/optick.config.h @@ -70,5 +70,11 @@ #else #define OPTICK_ENABLE_GPU_VULKAN (OPTICK_ENABLE_GPU /*&& 0*/) #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Vulkan Functions +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if !defined(OPTICK_STATIC_VULKAN_FUNCTIONS) +#define OPTICK_STATIC_VULKAN_FUNCTIONS (0 /*1*/) +#endif #endif diff --git a/neo/libs/optick/optick.h b/neo/libs/optick/optick.h index f90257c5..26ad0749 100644 --- a/neo/libs/optick/optick.h +++ b/neo/libs/optick/optick.h @@ -96,6 +96,7 @@ // Vulkan Forward Declarations #define OPTICK_DEFINE_HANDLE(object) typedef struct object##_T *object; +OPTICK_DEFINE_HANDLE(VkInstance); OPTICK_DEFINE_HANDLE(VkDevice); OPTICK_DEFINE_HANDLE(VkPhysicalDevice); OPTICK_DEFINE_HANDLE(VkQueue); @@ -125,6 +126,7 @@ struct VkCommandBufferBeginInfo; #endif #endif +typedef void* (VKAPI_PTR *PFN_vkGetInstanceProcAddr_)(VkInstance instance, const char* pName); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties_)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties); typedef int32_t (VKAPI_PTR *PFN_vkCreateQueryPool_)(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool); typedef int32_t (VKAPI_PTR *PFN_vkCreateCommandPool_)(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool); @@ -165,6 +167,7 @@ namespace Optick { struct OPTICK_API VulkanFunctions { + PFN_vkGetInstanceProcAddr_ vkGetInstanceProcAddr; PFN_vkGetPhysicalDeviceProperties_ vkGetPhysicalDeviceProperties; PFN_vkCreateQueryPool_ vkCreateQueryPool; PFN_vkCreateCommandPool_ vkCreateCommandPool; @@ -778,7 +781,7 @@ struct OPTICK_API GPUContext }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// OPTICK_API void InitGpuD3D12(ID3D12Device* device, ID3D12CommandQueue** cmdQueues, uint32_t numQueues); -OPTICK_API void InitGpuVulkan(VkDevice* vkDevices, VkPhysicalDevice* vkPhysicalDevices, VkQueue* vkQueues, uint32_t* cmdQueuesFamily, uint32_t numQueues, const VulkanFunctions* functions); +OPTICK_API void InitGpuVulkan(VkInstance vkInstance, VkDevice* vkDevices, VkPhysicalDevice* vkPhysicalDevices, VkQueue* vkQueues, uint32_t* cmdQueuesFamily, uint32_t numQueues, const VulkanFunctions* functions); OPTICK_API void GpuFlip(void* swapChain, uint32_t frameID = 0); OPTICK_API GPUContext SetGpuContext(GPUContext context); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1046,7 +1049,7 @@ struct OptickApp // GPU events #define OPTICK_GPU_INIT_D3D12(DEVICE, CMD_QUEUES, NUM_CMD_QUEUS) ::Optick::InitGpuD3D12(DEVICE, CMD_QUEUES, NUM_CMD_QUEUS); -#define OPTICK_GPU_INIT_VULKAN(DEVICES, PHYSICAL_DEVICES, CMD_QUEUES, CMD_QUEUES_FAMILY, NUM_CMD_QUEUS, FUNCTIONS) ::Optick::InitGpuVulkan(DEVICES, PHYSICAL_DEVICES, CMD_QUEUES, CMD_QUEUES_FAMILY, NUM_CMD_QUEUS, FUNCTIONS); +#define OPTICK_GPU_INIT_VULKAN(INSTANCE, DEVICES, PHYSICAL_DEVICES, CMD_QUEUES, CMD_QUEUES_FAMILY, NUM_CMD_QUEUS, FUNCTIONS) ::Optick::InitGpuVulkan(INSTANCE, DEVICES, PHYSICAL_DEVICES, CMD_QUEUES, CMD_QUEUES_FAMILY, NUM_CMD_QUEUS, FUNCTIONS); // Setup GPU context: // Params: @@ -1121,7 +1124,7 @@ struct OptickApp #define OPTICK_SET_MEMORY_ALLOCATOR(ALLOCATE_FUNCTION, DEALLOCATE_FUNCTION, INIT_THREAD_CALLBACK) #define OPTICK_SHUTDOWN() #define OPTICK_GPU_INIT_D3D12(DEVICE, CMD_QUEUES, NUM_CMD_QUEUS) -#define OPTICK_GPU_INIT_VULKAN(DEVICES, PHYSICAL_DEVICES, CMD_QUEUES, CMD_QUEUES_FAMILY, NUM_CMD_QUEUS, FUNCTIONS) +#define OPTICK_GPU_INIT_VULKAN(INSTANCE, DEVICES, PHYSICAL_DEVICES, CMD_QUEUES, CMD_QUEUES_FAMILY, NUM_CMD_QUEUS, FUNCTIONS) #define OPTICK_GPU_CONTEXT(...) #define OPTICK_GPU_EVENT(NAME) #define OPTICK_GPU_FLIP(...) diff --git a/neo/libs/optick/optick_gpu.vulkan.cpp b/neo/libs/optick/optick_gpu.vulkan.cpp index db138155..0ee637fd 100644 --- a/neo/libs/optick/optick_gpu.vulkan.cpp +++ b/neo/libs/optick/optick_gpu.vulkan.cpp @@ -71,7 +71,7 @@ namespace Optick GPUProfilerVulkan(); ~GPUProfilerVulkan(); - void InitDevice(VkDevice* devices, VkPhysicalDevice* physicalDevices, VkQueue* cmdQueues, uint32_t* cmdQueuesFamily, uint32_t nodeCount, const VulkanFunctions* functions); + void InitDevice(VkInstance instance, VkDevice* devices, VkPhysicalDevice* physicalDevices, VkQueue* cmdQueues, uint32_t* cmdQueuesFamily, uint32_t nodeCount, const VulkanFunctions* functions); void QueryTimestamp(VkCommandBuffer commandBuffer, int64_t* outCpuTimestamp); void Flip(VkSwapchainKHR swapChain); @@ -94,10 +94,10 @@ namespace Optick } }; - void InitGpuVulkan(VkDevice* vkDevices, VkPhysicalDevice* vkPhysicalDevices, VkQueue* vkQueues, uint32_t* cmdQueuesFamily, uint32_t numQueues, const VulkanFunctions* functions) + void InitGpuVulkan(VkInstance vkInstance, VkDevice* vkDevices, VkPhysicalDevice* vkPhysicalDevices, VkQueue* vkQueues, uint32_t* cmdQueuesFamily, uint32_t numQueues, const VulkanFunctions* functions) { GPUProfilerVulkan* gpuProfiler = Memory::New(); - gpuProfiler->InitDevice(vkDevices, vkPhysicalDevices, vkQueues, cmdQueuesFamily, numQueues, functions); + gpuProfiler->InitDevice(vkInstance, vkDevices, vkPhysicalDevices, vkQueues, cmdQueuesFamily, numQueues, functions); Core::Get().InitGPUProfiler(gpuProfiler); } @@ -107,15 +107,17 @@ namespace Optick prevPresentID = 0; } - void GPUProfilerVulkan::InitDevice(VkDevice* devices, VkPhysicalDevice* physicalDevices, VkQueue* cmdQueues, uint32_t* cmdQueuesFamily, uint32_t nodeCount, const VulkanFunctions* functions) + void GPUProfilerVulkan::InitDevice(VkInstance instance, VkDevice* devices, VkPhysicalDevice* physicalDevices, VkQueue* cmdQueues, uint32_t* cmdQueuesFamily, uint32_t nodeCount, const VulkanFunctions* functions) { if (functions != nullptr) { vulkanFunctions = *functions; } - else + else { +#if OPTICK_STATIC_VULKAN_FUNCTIONS vulkanFunctions = { + nullptr, // don't define vkGetInstanceProcAddr if vulkan functions are static vkGetPhysicalDeviceProperties, (PFN_vkCreateQueryPool_)vkCreateQueryPool, (PFN_vkCreateCommandPool_)vkCreateCommandPool, @@ -142,6 +144,23 @@ namespace Optick vkFreeCommandBuffers, nullptr, // dynamically define vkGetPastPresentationTimingGOOGLE if VK_GOOGLE_display_timing extension available }; +#else + OPTICK_FAILED("Either set OPTICK_STATIC_VULKAN_FUNCTIONS = 1 or VulkanFunctions must be defined! Can't initialize GPU Profiler!"); +#endif + } + + PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr_ = nullptr; + if (vulkanFunctions.vkGetInstanceProcAddr) + { + if (instance) + { + vkGetDeviceProcAddr_ = (PFN_vkGetDeviceProcAddr)(*vulkanFunctions.vkGetInstanceProcAddr)(instance, "vkGetDeviceProcAddr"); + vulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties_)(*vulkanFunctions.vkGetInstanceProcAddr)(instance, "vkGetPhysicalDeviceProperties"); + } + else + { + OPTICK_FAILED("VkInstance must be defined if VulkanFunctions::vkGetInstanceProcAddr is defined! Can't initialize GPU Profiler!"); + } } VkQueryPoolCreateInfo queryPoolCreateInfo; @@ -168,6 +187,40 @@ namespace Optick VkResult r; for (uint32_t i = 0; i < nodeCount; ++i) { + if (vkGetDeviceProcAddr_) + { + vulkanFunctions.vkCreateQueryPool = (PFN_vkCreateQueryPool_)vkGetDeviceProcAddr_(devices[i], "vkCreateQueryPool"); + vulkanFunctions.vkCreateCommandPool = (PFN_vkCreateCommandPool_)vkGetDeviceProcAddr_(devices[i], "vkCreateCommandPool"); + vulkanFunctions.vkCreateEvent = (PFN_vkCreateEvent_)vkGetDeviceProcAddr_(devices[i], "vkCreateEvent"); + vulkanFunctions.vkAllocateCommandBuffers = (PFN_vkAllocateCommandBuffers_)vkGetDeviceProcAddr_(devices[i], "vkAllocateCommandBuffers"); + vulkanFunctions.vkCreateFence = (PFN_vkCreateFence_)vkGetDeviceProcAddr_(devices[i], "vkCreateFence"); + vulkanFunctions.vkCmdResetQueryPool = (PFN_vkCmdResetQueryPool_)vkGetDeviceProcAddr_(devices[i], "vkCmdResetQueryPool"); + vulkanFunctions.vkResetQueryPool = (PFN_vkResetQueryPool_)vkGetDeviceProcAddr_(devices[i], "vkResetQueryPool"); + vulkanFunctions.vkCmdWaitEvents = (PFN_vkCmdWaitEvents_)vkGetDeviceProcAddr_(devices[i], "vkCmdWaitEvents"); + vulkanFunctions.vkResetEvent = (PFN_vkResetEvent_)vkGetDeviceProcAddr_(devices[i], "vkResetEvent"); + vulkanFunctions.vkSetEvent = (PFN_vkSetEvent_)vkGetDeviceProcAddr_(devices[i], "vkSetEvent"); + vulkanFunctions.vkQueueSubmit = (PFN_vkQueueSubmit_)vkGetDeviceProcAddr_(devices[i], "vkQueueSubmit"); + vulkanFunctions.vkWaitForFences = (PFN_vkWaitForFences_)vkGetDeviceProcAddr_(devices[i], "vkWaitForFences"); + vulkanFunctions.vkResetCommandBuffer = (PFN_vkResetCommandBuffer_)vkGetDeviceProcAddr_(devices[i], "vkResetCommandBuffer"); + vulkanFunctions.vkCmdWriteTimestamp = (PFN_vkCmdWriteTimestamp_)vkGetDeviceProcAddr_(devices[i], "vkCmdWriteTimestamp"); + vulkanFunctions.vkGetQueryPoolResults = (PFN_vkGetQueryPoolResults_)vkGetDeviceProcAddr_(devices[i], "vkGetQueryPoolResults"); + vulkanFunctions.vkBeginCommandBuffer = (PFN_vkBeginCommandBuffer_)vkGetDeviceProcAddr_(devices[i], "vkBeginCommandBuffer"); + vulkanFunctions.vkEndCommandBuffer = (PFN_vkEndCommandBuffer_)vkGetDeviceProcAddr_(devices[i], "vkEndCommandBuffer"); + vulkanFunctions.vkResetFences = (PFN_vkResetFences_)vkGetDeviceProcAddr_(devices[i], "vkResetFences"); + vulkanFunctions.vkDestroyCommandPool = (PFN_vkDestroyCommandPool_)vkGetDeviceProcAddr_(devices[i], "vkDestroyCommandPool"); + vulkanFunctions.vkDestroyQueryPool = (PFN_vkDestroyQueryPool_)vkGetDeviceProcAddr_(devices[i], "vkDestroyQueryPool"); + vulkanFunctions.vkDestroyEvent = (PFN_vkDestroyEvent_)vkGetDeviceProcAddr_(devices[i], "vkDestroyEvent"); + vulkanFunctions.vkDestroyFence = (PFN_vkDestroyFence_)vkGetDeviceProcAddr_(devices[i], "vkDestroyFence"); + vulkanFunctions.vkFreeCommandBuffers = (PFN_vkFreeCommandBuffers_)vkGetDeviceProcAddr_(devices[i], "vkFreeCommandBuffers"); + vulkanFunctions.vkGetPastPresentationTimingGOOGLE = (PFN_vkGetPastPresentationTimingGOOGLE_)vkGetDeviceProcAddr_(devices[i], "vkGetPastPresentationTimingGOOGLE"); + } +#if OPTICK_STATIC_VULKAN_FUNCTIONS + else if (!vulkanFunctions.vkGetPastPresentationTimingGOOGLE) + { + vulkanFunctions.vkGetPastPresentationTimingGOOGLE = (PFN_vkGetPastPresentationTimingGOOGLE_)vkGetDeviceProcAddr(devices[i], "vkGetPastPresentationTimingGOOGLE"); + } +#endif + VkPhysicalDeviceProperties properties = { 0 }; (*vulkanFunctions.vkGetPhysicalDeviceProperties)(physicalDevices[i], &properties); GPUProfiler::InitNode(properties.deviceName, i); @@ -175,7 +228,6 @@ namespace Optick NodePayload* nodePayload = Memory::New(); nodePayloads[i] = nodePayload; nodePayload->vulkanFunctions = &vulkanFunctions; - nodePayload->vulkanFunctions->vkGetPastPresentationTimingGOOGLE = (PFN_vkGetPastPresentationTimingGOOGLE_)vkGetDeviceProcAddr(devices[i], "vkGetPastPresentationTimingGOOGLE"); nodePayload->device = devices[i]; nodePayload->physicalDevice = physicalDevices[i]; nodePayload->queue = cmdQueues[i]; @@ -498,7 +550,7 @@ namespace Optick #include "optick_common.h" namespace Optick { - void InitGpuVulkan(VkDevice* /*vkDevices*/, VkPhysicalDevice* /*vkPhysicalDevices*/, VkQueue* /*vkQueues*/, uint32_t* /*cmdQueuesFamily*/, uint32_t /*numQueues*/, const VulkanFunctions* /*functions*/) + void InitGpuVulkan(VkInstance /*vkInstance*/, VkDevice* /*vkDevices*/, VkPhysicalDevice* /*vkPhysicalDevices*/, VkQueue* /*vkQueues*/, uint32_t* /*cmdQueuesFamily*/, uint32_t /*numQueues*/, const VulkanFunctions* /*functions*/) { OPTICK_FAILED("OPTICK_ENABLE_GPU_VULKAN is disabled! Can't initialize GPU Profiler!"); } From 4e30cdce0626d3b972e9dd939c198bad160173db Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Tue, 16 Jan 2024 23:39:24 -0500 Subject: [PATCH 31/37] Optick: Add support for reporting runtime errors with text descriptions (cherry picked from commit a743dfb54554a9524e47471b138e4a757e92cb6c) --- neo/libs/optick/optick_common.h | 6 ++++-- neo/libs/optick/optick_server.cpp | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/neo/libs/optick/optick_common.h b/neo/libs/optick/optick_common.h index 2c52c7be..5efa6358 100644 --- a/neo/libs/optick/optick_common.h +++ b/neo/libs/optick/optick_common.h @@ -33,6 +33,7 @@ #include #include #include +#include #if defined(OPTICK_MSVC) @@ -143,11 +144,12 @@ static const ProcessID INVALID_PROCESS_ID = (ProcessID)-1; #ifdef _DEBUG #define OPTICK_ASSERT(arg, description) if (!(arg)) { OPTICK_DEBUG_BREAK; } #define OPTICK_FAILED(description) { OPTICK_DEBUG_BREAK; } + #define OPTICK_VERIFY(arg, description, operation) if (!(arg)) { OPTICK_DEBUG_BREAK; operation; } #else #define OPTICK_ASSERT(arg, description) - #define OPTICK_FAILED(description) + #define OPTICK_FAILED(description) { throw std::runtime_error(description); } + #define OPTICK_VERIFY(arg, description, operation) if (!(arg)) { OPTICK_FAILED(description); operation; } #endif -#define OPTICK_VERIFY(arg, description, operation) if (!(arg)) { OPTICK_DEBUG_BREAK; operation; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/neo/libs/optick/optick_server.cpp b/neo/libs/optick/optick_server.cpp index cf93d5aa..0e22478d 100644 --- a/neo/libs/optick/optick_server.cpp +++ b/neo/libs/optick/optick_server.cpp @@ -289,7 +289,7 @@ Server::Server(short port) : socket(Memory::New()), saveCb(nullptr) { if (!socket->Bind(port, 4)) { - OPTICK_FAILED("Failed to bind a socket! Most probably the port is blocked by anti-virus! Change the port and verify that your game has enough permissions to communicate over the TCP\IP."); + OPTICK_FAILED("Failed to bind a socket! Most probably the port is blocked by anti-virus! Change the port and verify that your game has enough permissions to communicate over the TCP/IP."); } else { @@ -499,4 +499,4 @@ Server & Server::Get() } -#endif //USE_OPTICK \ No newline at end of file +#endif //USE_OPTICK From 68d0ab062fcb7214fcc132ac787fff5902e7019c Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Tue, 16 Jan 2024 23:56:32 -0500 Subject: [PATCH 32/37] Vulkan: Use dynamic pointers for all Vulkan functions used by VMA, Optick, and MoltenVK (cherry picked from commit a247df05e5b604cff288ef9c90dd42fd6829f0c1) --- neo/sys/DeviceManager_VK.cpp | 39 +++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index afeb4aae..6cd24e3c 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -40,13 +40,14 @@ #if defined(__APPLE__) && defined( USE_MoltenVK ) #if 0 #include - #include // SRS - will eventually move to these mvk include files for MoltenVK >= 1.2.7 / SDK >= 1.3.272.0 + #include // SRS - will eventually move to these mvk include files for MoltenVK >= 1.2.7 / SDK >= 1.3.275.0 #else - #include // SRS - now deprecated, but provides backwards compatibility for MoltenVK < 1.2.7 / SDK < 1.3.272.0 + #include // SRS - now deprecated, but provides backwards compatibility for MoltenVK < 1.2.7 / SDK < 1.3.275.0 #endif idCVar r_mvkSynchronousQueueSubmits( "r_mvkSynchronousQueueSubmits", "0", CVAR_BOOL | CVAR_INIT, "Use MoltenVK's synchronous queue submit option." ); #endif #include +#include #if defined( USE_AMD_ALLOCATOR ) #define VMA_IMPLEMENTATION @@ -212,7 +213,7 @@ private: VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, #endif #if defined( VK_EXT_layer_settings ) - // SRS - This is optional since implemented only for MoltenVK 1.2.7 / SDK 1.3.272.0 or later + // SRS - This is optional since implemented only for MoltenVK 1.2.7 / SDK 1.3.275.0 or later VK_EXT_LAYER_SETTINGS_EXTENSION_NAME, #endif #endif @@ -299,6 +300,16 @@ private: // SRS - slot for Vulkan device API version at runtime (initialize to Vulkan build version) uint32_t m_DeviceApiVersion = VK_HEADER_VERSION_COMPLETE; + // SRS - function pointer for initing Vulkan DynamicLoader, VMA, Optick, and MoltenVK functions + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = nullptr; + +#if defined(__APPLE__) && defined( USE_MoltenVK ) +#if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) + // SRS - function pointer for retrieving MoltenVK advanced performance statistics + PFN_vkGetPerformanceStatisticsMVK vkGetPerformanceStatisticsMVK = nullptr; +#endif +#endif + private: static VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback( VkDebugReportFlagsEXT flags, @@ -1018,8 +1029,8 @@ bool DeviceManager_VK::createDevice() #if defined( USE_AMD_ALLOCATOR ) // SRS - initialize the vma allocator VmaVulkanFunctions vulkanFunctions = {}; - vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; - vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; + vulkanFunctions.vkGetInstanceProcAddr = vkGetInstanceProcAddr; + vulkanFunctions.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr( m_VulkanInstance, "vkGetDeviceProcAddr" ); VmaAllocatorCreateInfo allocatorCreateInfo = {}; allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2; @@ -1200,8 +1211,7 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() // SRS - make static so ~DynamicLoader() does not prematurely unload vulkan dynamic lib static const vk::DynamicLoader dl; #endif - const PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = // NOLINT(misc-misplaced-const) - dl.getProcAddress( "vkGetInstanceProcAddr" ); + vkGetInstanceProcAddr = dl.getProcAddress( "vkGetInstanceProcAddr" ); VULKAN_HPP_DEFAULT_DISPATCHER.init( vkGetInstanceProcAddr ); #define CHECK(a) if (!(a)) { return false; } @@ -1238,7 +1248,11 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() // SRS - when USE_MoltenVK defined, set MoltenVK runtime configuration parameters on macOS (deprecated version) #if defined(__APPLE__) && defined( USE_MoltenVK ) - // SRS - deprecated by VK_EXT_layer_settings, but retained for older versions of MoltenVK < 1.2.7 / SDK < 1.3.272.0 + // SRS - deprecated by VK_EXT_layer_settings, but retained for older versions of MoltenVK < 1.2.7 / SDK < 1.3.275.0 + const PFN_vkGetMoltenVKConfigurationMVK vkGetMoltenVKConfigurationMVK = // NOLINT(misc-misplaced-const) + (PFN_vkGetMoltenVKConfigurationMVK)vkGetInstanceProcAddr( m_VulkanInstance, "vkGetMoltenVKConfigurationMVK" ); + const PFN_vkSetMoltenVKConfigurationMVK vkSetMoltenVKConfigurationMVK = // NOLINT(misc-misplaced-const) + (PFN_vkSetMoltenVKConfigurationMVK)vkGetInstanceProcAddr( m_VulkanInstance, "vkSetMoltenVKConfigurationMVK" ); #if defined( VK_EXT_layer_settings ) // SRS - for backwards compatibility at runtime: execute only if we can't find the VK_EXT_layer_settings extension if( enabledExtensions.instance.find( VK_EXT_LAYER_SETTINGS_EXTENSION_NAME ) == enabledExtensions.instance.end() ) @@ -1287,6 +1301,11 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); } + +#if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) + // SRS - Get function pointer for retrieving MoltenVK advanced performance statistics in DeviceManager_VK::BeginFrame() + vkGetPerformanceStatisticsMVK = (PFN_vkGetPerformanceStatisticsMVK)vkGetInstanceProcAddr( m_VulkanInstance, "vkGetPerformanceStatisticsMVK" ); +#endif #endif CHECK( createDevice() ); @@ -1340,7 +1359,9 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() #undef CHECK - OPTICK_GPU_INIT_VULKAN( ( VkDevice* )&m_VulkanDevice, ( VkPhysicalDevice* )&m_VulkanPhysicalDevice, ( VkQueue* )&m_GraphicsQueue, ( uint32_t* )&m_GraphicsQueueFamily, 1, nullptr ); + const Optick::VulkanFunctions optickVulkanFunctions = { (PFN_vkGetInstanceProcAddr_)vkGetInstanceProcAddr }; + + OPTICK_GPU_INIT_VULKAN( ( VkInstance )m_VulkanInstance, ( VkDevice* )&m_VulkanDevice, ( VkPhysicalDevice* )&m_VulkanPhysicalDevice, ( VkQueue* )&m_GraphicsQueue, ( uint32_t* )&m_GraphicsQueueFamily, 1, &optickVulkanFunctions ); return true; } From 6bea48476538d79d0c3aec925dc6464cdd7e8a09 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Wed, 17 Jan 2024 21:35:08 -0500 Subject: [PATCH 33/37] Vulkan: Add USE_OPTICK guard for Optick::VulkanFunctions (cherry picked from commit f23198e2d497e2e211dff51fe375f333a50b10cf) --- neo/sys/DeviceManager_VK.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index 6cd24e3c..dc646641 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -1359,7 +1359,9 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() #undef CHECK +#if USE_OPTICK const Optick::VulkanFunctions optickVulkanFunctions = { (PFN_vkGetInstanceProcAddr_)vkGetInstanceProcAddr }; +#endif OPTICK_GPU_INIT_VULKAN( ( VkInstance )m_VulkanInstance, ( VkDevice* )&m_VulkanDevice, ( VkPhysicalDevice* )&m_VulkanPhysicalDevice, ( VkQueue* )&m_GraphicsQueue, ( uint32_t* )&m_GraphicsQueueFamily, 1, &optickVulkanFunctions ); From fea41b50ba5862417a286e2f0d3d50652a378a22 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Thu, 25 Jan 2024 15:09:54 -0500 Subject: [PATCH 34/37] HUD Statistics Overlay: Improve frame sync time and CPU usage % calculations (all platforms) --- neo/framework/Common_demos.cpp | 1 + neo/framework/Console.cpp | 17 +++++++++++++---- neo/framework/common_frame.cpp | 6 +++--- neo/sys/DeviceManager_VK.cpp | 6 ------ 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/neo/framework/Common_demos.cpp b/neo/framework/Common_demos.cpp index 2e7ba775..0b6ade63 100644 --- a/neo/framework/Common_demos.cpp +++ b/neo/framework/Common_demos.cpp @@ -315,6 +315,7 @@ void idCommonLocal::TimeRenderDemo( const char* demoName, bool twice, bool quit while( readDemo ) { BusyWait(); // SRS - BusyWait() calls UpdateScreen() which draws and renders out-of-sequence but still supports frame timing + commonLocal.frameTiming.finishSyncTime_EndFrame = Sys_Microseconds(); commonLocal.mainFrameTiming = commonLocal.frameTiming; // ** End of current logical frame ** diff --git a/neo/framework/Console.cpp b/neo/framework/Console.cpp index f5811566..d874c842 100644 --- a/neo/framework/Console.cpp +++ b/neo/framework/Console.cpp @@ -298,7 +298,6 @@ float idConsoleLocal::DrawFPS( float y ) const uint64 rendererBackEndTime = commonLocal.GetRendererBackEndMicroseconds(); const uint64 rendererShadowsTime = commonLocal.GetRendererShadowsMicroseconds(); - const uint64 rendererMvkEncodeTime = commonLocal.GetRendererMvkEncodeMicroseconds(); const uint64 rendererGPUTime = commonLocal.GetRendererGPUMicroseconds(); const uint64 rendererGPUEarlyZTime = commonLocal.GetRendererGpuEarlyZMicroseconds(); const uint64 rendererGPU_SSAOTime = commonLocal.GetRendererGpuSSAOMicroseconds(); @@ -318,14 +317,24 @@ float idConsoleLocal::DrawFPS( float y ) const int64 frameIdleTime = int64( commonLocal.mainFrameTiming.startGameTime ) - int64( commonLocal.mainFrameTiming.finishSyncTime ); const int64 frameBusyTime = int64( commonLocal.frameTiming.finishSyncTime ) - int64( commonLocal.mainFrameTiming.startGameTime ); - // SRS - Frame sync time represents swap buffer synchronization + game thread wait + other time spent outside of rendering - const int64 frameSyncTime = int64( commonLocal.frameTiming.finishSyncTime ) - int64( commonLocal.mainFrameTiming.startRenderTime ) - int64( rendererBackEndTime ); + // SRS - Frame sync time represents swap buffer synchronization + other frame time spent outside of game thread and renderer backend + const int64 gameThreadWaitTime = int64( commonLocal.mainFrameTiming.finishSyncTime_EndFrame ) - int64( commonLocal.mainFrameTiming.finishRenderTime ); + const int64 frameSyncTime = int64( commonLocal.frameTiming.finishSyncTime ) - int64( commonLocal.mainFrameTiming.startRenderTime + rendererBackEndTime ) - gameThreadWaitTime; // SRS - GPU idle time is simply the difference between measured frame-over-frame time and GPU busy time (directly from GPU timers) const int64 rendererGPUIdleTime = frameBusyTime + frameIdleTime - rendererGPUTime; + // SRS - Estimate CPU busy time measured from start of game thread until completion of game thread and renderer backend (including excess MoltenVK encoding time if applicable) +#if defined(__APPLE__) && defined( USE_MoltenVK ) + const int64 rendererMvkEncodeTime = commonLocal.GetRendererMvkEncodeMicroseconds(); + const int64 rendererQueueSubmitTime = int64( commonLocal.mainFrameTiming.finishRenderTime - commonLocal.mainFrameTiming.startRenderTime ) - int64( rendererBackEndTime ); + const int64 rendererCPUBusyTime = int64( commonLocal.mainFrameTiming.finishSyncTime_EndFrame - commonLocal.mainFrameTiming.startGameTime ) + Min( Max( int64( 0 ), rendererMvkEncodeTime - rendererQueueSubmitTime - gameThreadWaitTime ), frameSyncTime - rendererQueueSubmitTime ); +#else + const int64 rendererCPUBusyTime = int64( commonLocal.mainFrameTiming.finishSyncTime_EndFrame - commonLocal.mainFrameTiming.startGameTime ); +#endif + // SRS - Save current CPU and GPU usage factors in ring buffer to calculate smoothed averages for future frames - previousCpuUsage[(index - 1) % FPS_FRAMES] = float( frameBusyTime - frameSyncTime ) / float( frameBusyTime + frameIdleTime ) * 100.0; + previousCpuUsage[(index - 1) % FPS_FRAMES] = float( rendererCPUBusyTime ) / float( frameBusyTime + frameIdleTime ) * 100.0; previousGpuUsage[(index - 1) % FPS_FRAMES] = float( rendererGPUTime ) / float( rendererGPUTime + rendererGPUIdleTime ) * 100.0; #if 1 diff --git a/neo/framework/common_frame.cpp b/neo/framework/common_frame.cpp index 3062c5ba..c83e84d9 100644 --- a/neo/framework/common_frame.cpp +++ b/neo/framework/common_frame.cpp @@ -880,13 +880,13 @@ void idCommonLocal::Frame() } frameTiming.finishRenderTime = Sys_Microseconds(); - // SRS - Use finishSyncTime_EndFrame to record timing just before gameThread.WaitForThread() for com_smp = 1 - frameTiming.finishSyncTime_EndFrame = Sys_Microseconds(); - // make sure the game / draw thread has completed // This may block if the game is taking longer than the render back end gameThread.WaitForThread(); + // SRS - Use finishSyncTime_EndFrame to record timing just after gameThread.WaitForThread() + frameTiming.finishSyncTime_EndFrame = Sys_Microseconds(); + // Send local usermds to the server. // This happens after the game frame has run so that prediction data is up to date. SendUsercmds( Game()->GetLocalClientNum() ); diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index dc646641..d60bffa3 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -1020,12 +1020,6 @@ bool DeviceManager_VK::createDevice() m_RendererString = std::string( prop.deviceName.data() ); m_DeviceApiVersion = prop.apiVersion; - // SRS - Determine maxPushConstantSize for Vulkan device - if( r_useVulkanPushConstants.GetBool() ) - { - m_DeviceParams.maxPushConstantSize = Min( prop.limits.maxPushConstantsSize, nvrhi::c_MaxPushConstantSize ); - } - #if defined( USE_AMD_ALLOCATOR ) // SRS - initialize the vma allocator VmaVulkanFunctions vulkanFunctions = {}; From 41a9f5fa59029bc6a21140c136a2d3249f38b6f7 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Mon, 29 Jan 2024 20:58:28 -0500 Subject: [PATCH 35/37] macOS: Use VK_EXT_layer_settings config for all build types, add r_mvkUseMetalArgumentBuffers cvar --- neo/cmake-xcode-debug.sh | 8 +++---- neo/sys/DeviceManager_VK.cpp | 46 +++++++++++++++++++++++------------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/neo/cmake-xcode-debug.sh b/neo/cmake-xcode-debug.sh index 6087a670..7b89593a 100755 --- a/neo/cmake-xcode-debug.sh +++ b/neo/cmake-xcode-debug.sh @@ -16,8 +16,8 @@ fi # note 1: remove or set -DCMAKE_SUPPRESS_REGENERATION=OFF to reenable ZERO_CHECK target which checks for CMakeLists.txt changes and re-runs CMake before builds # however, if ZERO_CHECK is reenabled **must** add VULKAN_SDK location to Xcode Custom Paths (under Prefs/Locations) otherwise build failures may occur # note 2: policy CMAKE_POLICY_DEFAULT_CMP0142=NEW suppresses non-existant per-config suffixes on Xcode library search paths, works for cmake version 3.25 and later -# note 3: env variable MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE=1 enables MoltenVK's image view swizzle which may be required on older macOS versions or hardware (see vulkaninfo) -# note 4: env variable MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS=0 disables synchronous queue submits which is optimal for the synchronization method used by the game -# note 5: env variable MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2 enables MoltenVK's use of Metal argument buffers only if VK_EXT_descriptor_indexing is enabled -# note 6: env variable MVK_CONFIG_TIMESTAMP_PERIOD_LOWPASS_ALPHA=1.0 disables MoltenVK's timestampPeriod lowpass filter for non-Apple GPUs +# note 3: env variable MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE=1 enables MoltenVK's image view swizzle which may be required on older macOS versions or hardware (see vulkaninfo) - only used for VulkanSDK < 1.3.275 +# note 4: env variable MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS=0 disables synchronous queue submits which is optimal for the synchronization method used by the game - only used for VulkanSDK < 1.3.275 +# note 5: env variable MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2 enables MoltenVK's use of Metal argument buffers only if VK_EXT_descriptor_indexing is enabled - only used for VulkanSDK < 1.3.275 +# note 6: env variable MVK_CONFIG_TIMESTAMP_PERIOD_LOWPASS_ALPHA=1.0 disables MoltenVK's timestampPeriod lowpass filter for non-Apple GPUs - only used for VulkanSDK < 1.3.275 cmake -G Xcode -DCMAKE_BUILD_TYPE=Debug -DCMAKE_XCODE_GENERATE_SCHEME=ON -DCMAKE_XCODE_SCHEME_ENVIRONMENT="MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE=1;MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS=0;MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2;MVK_CONFIG_TIMESTAMP_PERIOD_LOWPASS_ALPHA=1.0" -DCMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION=OFF -DCMAKE_SUPPRESS_REGENERATION=ON -DOPENAL_LIBRARY=$OPENAL_PREFIX/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=$OPENAL_PREFIX/include ../neo -DCMAKE_POLICY_DEFAULT_CMP0142=NEW -Wno-dev diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index d60bffa3..33f54e12 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -37,14 +37,19 @@ #include // SRS - optionally needed for MoltenVK runtime config visibility -#if defined(__APPLE__) && defined( USE_MoltenVK ) +#if defined(__APPLE__) +#if defined( USE_MoltenVK ) #if 0 #include #include // SRS - will eventually move to these mvk include files for MoltenVK >= 1.2.7 / SDK >= 1.3.275.0 #else #include // SRS - now deprecated, but provides backwards compatibility for MoltenVK < 1.2.7 / SDK < 1.3.275.0 #endif +#endif +#if defined( VK_EXT_layer_settings ) || defined( USE_MoltenVK ) idCVar r_mvkSynchronousQueueSubmits( "r_mvkSynchronousQueueSubmits", "0", CVAR_BOOL | CVAR_INIT, "Use MoltenVK's synchronous queue submit option." ); + idCVar r_mvkUseMetalArgumentBuffers( "r_mvkUseMetalArgumentBuffers", "2", CVAR_INTEGER | CVAR_INIT, "Use MoltenVK's Metal argument buffers option (0=Off, 1=Always On, 2=On when VK_EXT_descriptor_indexing enabled)", 0, 2 ); +#endif #endif #include #include @@ -509,19 +514,19 @@ bool DeviceManager_VK::createInstance() info.setFlags( vk::InstanceCreateFlagBits( VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR ) ); } #endif -#if defined( USE_MoltenVK ) && defined( VK_EXT_layer_settings ) - // SRS - when USE_MoltenVK defined, set MoltenVK runtime configuration parameters on macOS via VK_EXT_layer_settings +#if defined( VK_EXT_layer_settings ) + // SRS - set MoltenVK runtime configuration parameters on macOS via standardized VK_EXT_layer_settings extension std::vector layerSettings; vk::LayerSettingsCreateInfoEXT layerSettingsCreateInfo; const vk::Bool32 valueTrue = vk::True, valueFalse = vk::False; - const int32_t useMetalArgumentBuffers = int32_t( MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_DESCRIPTOR_INDEXING ); - const Float32 timestampPeriodLowPassAlpha = 1.0; + const int32_t useMetalArgumentBuffers = r_mvkUseMetalArgumentBuffers.GetInteger(); + const float timestampPeriodLowPassAlpha = 1.0; if( enabledExtensions.instance.find( VK_EXT_LAYER_SETTINGS_EXTENSION_NAME ) != enabledExtensions.instance.end() ) { // SRS - use MoltenVK layer for configuration via VK_EXT_layer_settings extension - vk::LayerSettingEXT layerSetting = { kMVKMoltenVKDriverLayerName, "", vk::LayerSettingTypeEXT( 0 ), 1, nullptr }; + vk::LayerSettingEXT layerSetting = { "MoltenVK", "", vk::LayerSettingTypeEXT( 0 ), 1, nullptr }; // SRS - Set MoltenVK's synchronous queue submit option for vkQueueSubmit() & vkQueuePresentKHR() layerSetting.pSettingName = "MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS"; @@ -547,11 +552,16 @@ bool DeviceManager_VK::createInstance() layerSetting.pValues = ×tampPeriodLowPassAlpha; layerSettings.push_back( layerSetting ); + // SRS - Only enable MoltenVK performance tracking if using API and available based on version +#if defined( USE_MoltenVK ) +#if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) // SRS - Enable MoltenVK's performance tracking for display of Metal encoding timer on macOS layerSetting.pSettingName = "MVK_CONFIG_PERFORMANCE_TRACKING"; layerSetting.type = vk::LayerSettingTypeEXT::eBool32; layerSetting.pValues = &valueTrue; layerSettings.push_back( layerSetting ); +#endif +#endif layerSettingsCreateInfo.settingCount = uint32_t( layerSettings.size() ); layerSettingsCreateInfo.pSettings = layerSettings.data(); @@ -1242,16 +1252,17 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() // SRS - when USE_MoltenVK defined, set MoltenVK runtime configuration parameters on macOS (deprecated version) #if defined(__APPLE__) && defined( USE_MoltenVK ) - // SRS - deprecated by VK_EXT_layer_settings, but retained for older versions of MoltenVK < 1.2.7 / SDK < 1.3.275.0 - const PFN_vkGetMoltenVKConfigurationMVK vkGetMoltenVKConfigurationMVK = // NOLINT(misc-misplaced-const) - (PFN_vkGetMoltenVKConfigurationMVK)vkGetInstanceProcAddr( m_VulkanInstance, "vkGetMoltenVKConfigurationMVK" ); - const PFN_vkSetMoltenVKConfigurationMVK vkSetMoltenVKConfigurationMVK = // NOLINT(misc-misplaced-const) - (PFN_vkSetMoltenVKConfigurationMVK)vkGetInstanceProcAddr( m_VulkanInstance, "vkSetMoltenVKConfigurationMVK" ); #if defined( VK_EXT_layer_settings ) // SRS - for backwards compatibility at runtime: execute only if we can't find the VK_EXT_layer_settings extension if( enabledExtensions.instance.find( VK_EXT_LAYER_SETTINGS_EXTENSION_NAME ) == enabledExtensions.instance.end() ) #endif { + // SRS - vkSetMoltenVKConfigurationMVK() now deprecated, but retained for MoltenVK < 1.2.7 / SDK < 1.3.275.0 + const PFN_vkGetMoltenVKConfigurationMVK vkGetMoltenVKConfigurationMVK = // NOLINT(misc-misplaced-const) + (PFN_vkGetMoltenVKConfigurationMVK)vkGetInstanceProcAddr( m_VulkanInstance, "vkGetMoltenVKConfigurationMVK" ); + const PFN_vkSetMoltenVKConfigurationMVK vkSetMoltenVKConfigurationMVK = // NOLINT(misc-misplaced-const) + (PFN_vkSetMoltenVKConfigurationMVK)vkGetInstanceProcAddr( m_VulkanInstance, "vkSetMoltenVKConfigurationMVK" ); + vk::PhysicalDeviceFeatures2 deviceFeatures2; vk::PhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures; deviceFeatures2.setPNext( &portabilityFeatures ); @@ -1276,11 +1287,14 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() mvkConfig.fullImageViewSwizzle = VK_TRUE; } - // SRS - Turn MoltenVK's Metal argument buffer feature on for descriptor indexing only - if( mvkConfig.useMetalArgumentBuffers == MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_NEVER ) + // SRS - Set MoltenVK's Metal argument buffer option for descriptor resource scaling + // - Also needed for Vulkan SDK 1.3.268.1 to work around SPIRV-Cross issue for Metal conversion. + // - See https://github.com/KhronosGroup/MoltenVK/issues/2016 and https://github.com/goki/vgpu/issues/9 + // - Issue solved in Vulkan SDK >= 1.3.275.0, but config uses VK_EXT_layer_settings instead of this code. + if( mvkConfig.useMetalArgumentBuffers == MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_NEVER && r_mvkUseMetalArgumentBuffers.GetInteger() ) { - idLib::Printf( "Enabled MoltenVK's Metal argument buffers for descriptor indexing...\n" ); - mvkConfig.useMetalArgumentBuffers = MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_DESCRIPTOR_INDEXING; + idLib::Printf( "Enabled MoltenVK's Metal argument buffers...\n" ); + mvkConfig.useMetalArgumentBuffers = MVKUseMetalArgumentBuffers( r_mvkUseMetalArgumentBuffers.GetInteger() ); } #if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) @@ -1432,7 +1446,7 @@ void DeviceManager_VK::BeginFrame() #if defined(__APPLE__) && defined( USE_MoltenVK ) #if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) - if( m_DeviceApiVersion >= VK_MAKE_API_VERSION( 0, 1, 2, 268 ) ) + if( vkGetPerformanceStatisticsMVK && m_DeviceApiVersion >= VK_MAKE_API_VERSION( 0, 1, 2, 268 ) ) { // SRS - get MoltenVK's Metal encoding time and GPU memory usage for display in statistics overlay HUD MVKPerformanceStatistics mvkPerfStats; From 1ebd07521506cb6be72a15413e846ef26ad5dc69 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Sun, 4 Feb 2024 15:36:55 +0100 Subject: [PATCH 36/37] Fixed compile problems with -DSTANDALONE flag --- neo/cmake-vs2022-win64-standalone.bat | 6 ++++++ neo/framework/Licensee.h | 2 +- neo/renderer/RenderSystem_init.cpp | 2 ++ neo/shaders/CMakeLists.txt | 24 +++++++++++++++++------- 4 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 neo/cmake-vs2022-win64-standalone.bat diff --git a/neo/cmake-vs2022-win64-standalone.bat b/neo/cmake-vs2022-win64-standalone.bat new file mode 100644 index 00000000..aae708f3 --- /dev/null +++ b/neo/cmake-vs2022-win64-standalone.bat @@ -0,0 +1,6 @@ +cd .. +del /s /q build +mkdir build +cd build +cmake -G "Visual Studio 17" -A x64 -DFFMPEG=OFF -DBINKDEC=ON -DSTANDALONE=ON ../neo +pause \ No newline at end of file diff --git a/neo/framework/Licensee.h b/neo/framework/Licensee.h index dda877cd..704f292b 100644 --- a/neo/framework/Licensee.h +++ b/neo/framework/Licensee.h @@ -44,7 +44,7 @@ If you have questions concerning this license or the applicable additional terms // RB end // jmarshall -#define ENGINE_BRANCH "Retro 80s/90s" +#define ENGINE_BRANCH "master" // jmarshall end #ifdef STANDALONE diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 7fdfe6a9..fcba7e95 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -2211,10 +2211,12 @@ void idRenderSystemLocal::Shutdown() // SRS - wait for fence to hit before freeing any resources the GPU may be using, otherwise get Vulkan validation layer errors on shutdown // SRS - skip this step if we are in a Doom Classic game +#if defined(USE_DOOMCLASSIC) if( common->GetCurrentGame() == DOOM3_BFG ) { backend.GL_BlockingSwapBuffers(); } +#endif // free the vertex cache, which should have nothing allocated now vertexCache.Shutdown(); diff --git a/neo/shaders/CMakeLists.txt b/neo/shaders/CMakeLists.txt index 2617a5e5..55c7e2d5 100644 --- a/neo/shaders/CMakeLists.txt +++ b/neo/shaders/CMakeLists.txt @@ -31,10 +31,20 @@ source_group("global" FILES ${globalshaders}) source_group("user" FILES ${usershaders}) -compile_shaders_all_platforms( - TARGET Shaders - CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/shaders.cfg - OUTPUT_BASE ${CMAKE_CURRENT_SOURCE_DIR}/../../base/renderprogs2 # This path scheme is a bit odd. - SHADER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} - SOURCES ${shaders} -) \ No newline at end of file +if(STANDALONE) + compile_shaders_all_platforms( + TARGET Shaders + CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/shaders.cfg + OUTPUT_BASE ${CMAKE_CURRENT_SOURCE_DIR}/../../content/renderprogs2 + SHADER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} + SOURCES ${shaders} + ) +else() + compile_shaders_all_platforms( + TARGET Shaders + CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/shaders.cfg + OUTPUT_BASE ${CMAKE_CURRENT_SOURCE_DIR}/../../base/renderprogs2 # This path scheme is a bit odd. + SHADER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} + SOURCES ${shaders} + ) +endif() \ No newline at end of file From 2a0c742b1aae0867d995330e113cc246580f1fb7 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Sun, 4 Feb 2024 16:40:18 +0100 Subject: [PATCH 37/37] Removed CPU/GPU usage from com_showFPS > 1 --- neo/framework/Console.cpp | 8 +++---- neo/sys/DeviceManager_VK.cpp | 40 ++++++++++++++++++---------------- neo/sys/posix/platform_osx.cpp | 28 ++++++++++++------------ 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/neo/framework/Console.cpp b/neo/framework/Console.cpp index d874c842..573e8db2 100644 --- a/neo/framework/Console.cpp +++ b/neo/framework/Console.cpp @@ -334,8 +334,8 @@ float idConsoleLocal::DrawFPS( float y ) #endif // SRS - Save current CPU and GPU usage factors in ring buffer to calculate smoothed averages for future frames - previousCpuUsage[(index - 1) % FPS_FRAMES] = float( rendererCPUBusyTime ) / float( frameBusyTime + frameIdleTime ) * 100.0; - previousGpuUsage[(index - 1) % FPS_FRAMES] = float( rendererGPUTime ) / float( rendererGPUTime + rendererGPUIdleTime ) * 100.0; + previousCpuUsage[( index - 1 ) % FPS_FRAMES] = float( rendererCPUBusyTime ) / float( frameBusyTime + frameIdleTime ) * 100.0; + previousGpuUsage[( index - 1 ) % FPS_FRAMES] = float( rendererGPUTime ) / float( rendererGPUTime + rendererGPUIdleTime ) * 100.0; #if 1 @@ -344,7 +344,7 @@ float idConsoleLocal::DrawFPS( float y ) { // start smaller int32 statsWindowWidth = 320; - int32 statsWindowHeight = 330; + int32 statsWindowHeight = 315; if( com_showFPS.GetInteger() > 2 ) { @@ -529,7 +529,7 @@ float idConsoleLocal::DrawFPS( float y ) ImGui::TextColored( frameBusyTime > maxTime || rendererGPUTime > maxTime ? colorRed : colorWhite, "Total: %5lld us Total: %5lld us", frameBusyTime, rendererGPUTime ); ImGui::TextColored( colorWhite, "Idle: %5lld us Idle: %5lld us", frameIdleTime, rendererGPUIdleTime ); // SRS - Show CPU and GPU overall usage statistics - ImGui::TextColored( colorWhite, "Usage: %3.0f %% Usage: %3.0f %%", cpuUsage, gpuUsage ); + //ImGui::TextColored( colorWhite, "Usage: %3.0f %% Usage: %3.0f %%", cpuUsage, gpuUsage ); ImGui::End(); } diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index 33f54e12..b17cf5b4 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -38,18 +38,18 @@ #include // SRS - optionally needed for MoltenVK runtime config visibility #if defined(__APPLE__) -#if defined( USE_MoltenVK ) -#if 0 - #include - #include // SRS - will eventually move to these mvk include files for MoltenVK >= 1.2.7 / SDK >= 1.3.275.0 -#else - #include // SRS - now deprecated, but provides backwards compatibility for MoltenVK < 1.2.7 / SDK < 1.3.275.0 -#endif -#endif -#if defined( VK_EXT_layer_settings ) || defined( USE_MoltenVK ) - idCVar r_mvkSynchronousQueueSubmits( "r_mvkSynchronousQueueSubmits", "0", CVAR_BOOL | CVAR_INIT, "Use MoltenVK's synchronous queue submit option." ); - idCVar r_mvkUseMetalArgumentBuffers( "r_mvkUseMetalArgumentBuffers", "2", CVAR_INTEGER | CVAR_INIT, "Use MoltenVK's Metal argument buffers option (0=Off, 1=Always On, 2=On when VK_EXT_descriptor_indexing enabled)", 0, 2 ); -#endif + #if defined( USE_MoltenVK ) + #if 0 + #include + #include // SRS - will eventually move to these mvk include files for MoltenVK >= 1.2.7 / SDK >= 1.3.275.0 + #else + #include // SRS - now deprecated, but provides backwards compatibility for MoltenVK < 1.2.7 / SDK < 1.3.275.0 + #endif + #endif + #if defined( VK_EXT_layer_settings ) || defined( USE_MoltenVK ) + idCVar r_mvkSynchronousQueueSubmits( "r_mvkSynchronousQueueSubmits", "0", CVAR_BOOL | CVAR_INIT, "Use MoltenVK's synchronous queue submit option." ); + idCVar r_mvkUseMetalArgumentBuffers( "r_mvkUseMetalArgumentBuffers", "2", CVAR_INTEGER | CVAR_INIT, "Use MoltenVK's Metal argument buffers option (0=Off, 1=Always On, 2=On when VK_EXT_descriptor_indexing enabled)", 0, 2 ); + #endif #endif #include #include @@ -1034,7 +1034,7 @@ bool DeviceManager_VK::createDevice() // SRS - initialize the vma allocator VmaVulkanFunctions vulkanFunctions = {}; vulkanFunctions.vkGetInstanceProcAddr = vkGetInstanceProcAddr; - vulkanFunctions.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr( m_VulkanInstance, "vkGetDeviceProcAddr" ); + vulkanFunctions.vkGetDeviceProcAddr = ( PFN_vkGetDeviceProcAddr )vkGetInstanceProcAddr( m_VulkanInstance, "vkGetDeviceProcAddr" ); VmaAllocatorCreateInfo allocatorCreateInfo = {}; allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2; @@ -1259,9 +1259,9 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() { // SRS - vkSetMoltenVKConfigurationMVK() now deprecated, but retained for MoltenVK < 1.2.7 / SDK < 1.3.275.0 const PFN_vkGetMoltenVKConfigurationMVK vkGetMoltenVKConfigurationMVK = // NOLINT(misc-misplaced-const) - (PFN_vkGetMoltenVKConfigurationMVK)vkGetInstanceProcAddr( m_VulkanInstance, "vkGetMoltenVKConfigurationMVK" ); + ( PFN_vkGetMoltenVKConfigurationMVK )vkGetInstanceProcAddr( m_VulkanInstance, "vkGetMoltenVKConfigurationMVK" ); const PFN_vkSetMoltenVKConfigurationMVK vkSetMoltenVKConfigurationMVK = // NOLINT(misc-misplaced-const) - (PFN_vkSetMoltenVKConfigurationMVK)vkGetInstanceProcAddr( m_VulkanInstance, "vkSetMoltenVKConfigurationMVK" ); + ( PFN_vkSetMoltenVKConfigurationMVK )vkGetInstanceProcAddr( m_VulkanInstance, "vkSetMoltenVKConfigurationMVK" ); vk::PhysicalDeviceFeatures2 deviceFeatures2; vk::PhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures; @@ -1297,7 +1297,7 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() mvkConfig.useMetalArgumentBuffers = MVKUseMetalArgumentBuffers( r_mvkUseMetalArgumentBuffers.GetInteger() ); } - #if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) +#if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) if( mvkConfig.apiVersionToAdvertise >= VK_MAKE_API_VERSION( 0, 1, 2, 268 ) ) { // SRS - Disable MoltenVK's timestampPeriod filter for HUD / Optick profiler timing calibration @@ -1305,14 +1305,14 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() // SRS - Enable MoltenVK's performance tracking for display of Metal encoding timer on macOS mvkConfig.performanceTracking = VK_TRUE; } - #endif +#endif vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &mvkConfig, &mvkConfigSize ); } #if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 ) // SRS - Get function pointer for retrieving MoltenVK advanced performance statistics in DeviceManager_VK::BeginFrame() - vkGetPerformanceStatisticsMVK = (PFN_vkGetPerformanceStatisticsMVK)vkGetInstanceProcAddr( m_VulkanInstance, "vkGetPerformanceStatisticsMVK" ); + vkGetPerformanceStatisticsMVK = ( PFN_vkGetPerformanceStatisticsMVK )vkGetInstanceProcAddr( m_VulkanInstance, "vkGetPerformanceStatisticsMVK" ); #endif #endif @@ -1368,7 +1368,7 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain() #undef CHECK #if USE_OPTICK - const Optick::VulkanFunctions optickVulkanFunctions = { (PFN_vkGetInstanceProcAddr_)vkGetInstanceProcAddr }; + const Optick::VulkanFunctions optickVulkanFunctions = { ( PFN_vkGetInstanceProcAddr_ )vkGetInstanceProcAddr }; #endif OPTICK_GPU_INIT_VULKAN( ( VkInstance )m_VulkanInstance, ( VkDevice* )&m_VulkanDevice, ( VkPhysicalDevice* )&m_VulkanPhysicalDevice, ( VkQueue* )&m_GraphicsQueue, ( uint32_t* )&m_GraphicsQueueFamily, 1, &optickVulkanFunctions ); @@ -1473,7 +1473,9 @@ void DeviceManager_VK::BeginFrame() #if defined(__APPLE__) // SRS - macOS Vulkan API <= 1.2.268 has heap reporting defect, use heapUsage[0] only if( m_DeviceApiVersion <= VK_MAKE_API_VERSION( 0, 1, 2, 268 ) ) + { break; + } #endif } commonLocal.SetRendererGpuMemoryMB( int( gpuMemoryAllocated / 1024 / 1024 ) ); diff --git a/neo/sys/posix/platform_osx.cpp b/neo/sys/posix/platform_osx.cpp index 18a427bd..d8ddfa3c 100644 --- a/neo/sys/posix/platform_osx.cpp +++ b/neo/sys/posix/platform_osx.cpp @@ -452,7 +452,7 @@ main */ int main( int argc, const char** argv ) { - extern idCVar r_useGPUSkinning; + extern idCVar r_useGPUSkinning; // DG: needed for Sys_ReLaunch() cmdargc = argc; @@ -483,20 +483,20 @@ int main( int argc, const char** argv ) common->Init( 0, NULL, NULL ); } - // SRS - Determine the machine name, e.g. "x86_64" or "arm64" - // Might be cleaner in posix Sys_Init(), but only needed on - // macOS and all the required sys includes are located here. - size_t size; - sysctlbyname( "hw.machine", NULL, &size, NULL, 0 ); - char* machineName = ( char* )Mem_Alloc( size, TAG_SYSTEM ); - sysctlbyname( "hw.machine", machineName, &size, NULL, 0 ); + // SRS - Determine the machine name, e.g. "x86_64" or "arm64" + // Might be cleaner in posix Sys_Init(), but only needed on + // macOS and all the required sys includes are located here. + size_t size; + sysctlbyname( "hw.machine", NULL, &size, NULL, 0 ); + char* machineName = ( char* )Mem_Alloc( size, TAG_SYSTEM ); + sysctlbyname( "hw.machine", machineName, &size, NULL, 0 ); - // FIXME: On Apple Silicon disable GPU skinning to eliminate rendering artifacts - if( strcmp( machineName, "arm64" ) == 0 ) - { - r_useGPUSkinning.SetInteger( 0 ); - } - Mem_Free( machineName ); + // FIXME: On Apple Silicon disable GPU skinning to eliminate rendering artifacts + if( strcmp( machineName, "arm64" ) == 0 ) + { + r_useGPUSkinning.SetInteger( 0 ); + } + Mem_Free( machineName ); Posix_LateInit();