From efc831c02c2f77ed58f86e6560797b3c9d8044ee Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Mon, 13 May 2024 15:01:50 +0200 Subject: [PATCH 1/6] tr_shader: fix a bug about noPicMip being set when IF_NOPICMIP is not set --- src/engine/renderer/tr_shader.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index 1af486e9b3..a8d7ab6477 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -6327,13 +6327,17 @@ shader_t *R_FindShader( const char *name, shaderType_t type, // if not defined in the in-memory shader descriptions, // look for a single supported image file bits = IF_NONE; - if( flags & RSF_NOMIP ) + + if ( flags & RSF_NOMIP ) + { bits |= IF_NOPICMIP; - else shader.noPicMip = true; + } - if( flags & RSF_NOLIGHTSCALE ) + if ( flags & RSF_NOLIGHTSCALE ) + { bits |= IF_NOLIGHTSCALE; + } Log::Debug( "loading '%s' image as shader", fileName ); From cb10cd5344420ed9db2986437e55922deab4c12f Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Mon, 13 May 2024 14:39:57 +0200 Subject: [PATCH 2/6] tr_image: introduce IF_FITSCREEN to rescale image to the smaller size larger than screen --- src/engine/renderer/InternalImage.cpp | 33 +++++++++++++++++++++++---- src/engine/renderer/tr_local.h | 1 + 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/engine/renderer/InternalImage.cpp b/src/engine/renderer/InternalImage.cpp index 39ba422e98..971e83415a 100644 --- a/src/engine/renderer/InternalImage.cpp +++ b/src/engine/renderer/InternalImage.cpp @@ -105,6 +105,35 @@ int R_GetImageCustomScalingStep( const image_t *image, const imageParams_t &imag return 0; } + // Consider the larger edge as the "image dimension" + int scaledDimension = std::max( image->width, image->height ); + + int scalingStep = 0; + + // Scale down the image size until it's not smaller than screen. + if ( image->bits & IF_FITSCREEN ) + { + int largerSide = std::max( glConfig.vidWidth, glConfig.vidHeight ); + + if ( scaledDimension > largerSide ) + { + while ( scaledDimension > largerSide ) + { + scaledDimension >>= 1; + scalingStep++; + } + + // We need the larger image size before it becomes smaller than screen. + if ( scaledDimension != largerSide ) + { + scaledDimension <<= 1; + scalingStep--; + } + } + + return scalingStep; + } + int materialMinDimension = r_ignoreMaterialMinDimension->integer ? 0 : imageParams.minDimension; int materialMaxDimension = r_ignoreMaterialMaxDimension->integer ? 0 : imageParams.maxDimension; @@ -130,10 +159,6 @@ int R_GetImageCustomScalingStep( const image_t *image, const imageParams_t &imag maxDimension = std::min( maxDimension, r_imageMaxDimension->integer ); } - // Consider the larger edge as the "image dimension" - int scaledDimension = std::max( image->width, image->height ); - int scalingStep = 0; - // 1st priority: scaledDimension >= minDimension // 2nd priority: scaledDimension <= maxDimension // 3rd priority: scalingStep >= r_picMip->integer diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 9c772415a9..e047d83f63 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -537,6 +537,7 @@ enum class dynamicLightRenderer_t { LEGACY, TILED }; { IF_NONE, IF_NOPICMIP = BIT( 0 ), + IF_FITSCREEN = BIT( 1 ), IF_NORMALMAP = BIT( 2 ), IF_RGBA16F = BIT( 3 ), IF_RGBA32F = BIT( 4 ), From 9e78b7cefea91a82d9a3cb06910f8f66f26998f4 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Mon, 13 May 2024 15:00:04 +0200 Subject: [PATCH 3/6] tr_shader: implement fitScreen shader keyword --- src/engine/renderer/tr_local.h | 2 ++ src/engine/renderer/tr_shader.cpp | 31 +++++++++++++++++++++++++++++++ src/engine/renderer/tr_types.h | 11 ++++++----- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index e047d83f63..77a98d1c75 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -1148,6 +1148,7 @@ enum class dynamicLightRenderer_t { LEGACY, TILED }; int deformIndex; bool overrideNoPicMip; // for images that must always be full resolution + bool fitScreen; // For images that should be scaled to fit the screen size. bool overrideFilterType; // for console fonts, 2D elements, etc. filterType_t filterType; bool overrideWrapType; @@ -1283,6 +1284,7 @@ enum class dynamicLightRenderer_t { LEGACY, TILED }; float polygonOffsetValue; bool noPicMip; // for images that must always be full resolution + bool fitScreen; // For images that should be scaled to fit the screen size. int imageMinDimension; // for images that must not be loaded with smaller size int imageMaxDimension; // for images that must not be loaded with larger size filterType_t filterType; // for console fonts, 2D elements, etc. diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index a8d7ab6477..6856256bb3 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -1479,6 +1479,11 @@ static bool LoadMap( shaderStage_t *stage, const char *buffer, stageType_t type, imageParams.bits |= IF_NOPICMIP; } + if ( stage->fitScreen || shader.fitScreen ) + { + imageParams.bits |= IF_FITSCREEN; + } + switch ( type ) { case stageType_t::ST_NORMALMAP: @@ -2173,6 +2178,11 @@ static bool ParseStage( shaderStage_t *stage, const char **text ) imageBits |= IF_NOPICMIP; } + if ( stage->fitScreen || shader.fitScreen ) + { + imageBits |= IF_FITSCREEN; + } + if ( stage->overrideFilterType ) { filterType = stage->filterType; @@ -2294,6 +2304,11 @@ static bool ParseStage( shaderStage_t *stage, const char **text ) imageBits |= IF_NOPICMIP; } + if ( stage->fitScreen || shader.fitScreen ) + { + imageBits |= IF_FITSCREEN; + } + if ( stage->overrideFilterType ) { filterType = stage->filterType; @@ -2386,6 +2401,10 @@ static bool ParseStage( shaderStage_t *stage, const char **text ) { stage->overrideNoPicMip = true; } + else if ( !Q_stricmp( token, "fitScreen" ) ) + { + stage->fitScreen = true; + } // clamp, edgeClamp etc. else if ( ParseClampType( token, &stage->wrapType ) ) { @@ -4178,6 +4197,12 @@ static bool ParseShader( const char *_text ) shader.noPicMip = true; continue; } + // fit screen adjustment + else if ( !Q_stricmp( token, "fitScreen" ) ) + { + shader.fitScreen = true; + continue; + } // imageMinDimension enforcement else if ( !Q_stricmp( token, "imageMinDimension" ) ) { @@ -6334,6 +6359,12 @@ shader_t *R_FindShader( const char *name, shaderType_t type, shader.noPicMip = true; } + if ( flags & RSF_FITSCREEN ) + { + bits |= IF_FITSCREEN; + shader.fitScreen = true; + } + if ( flags & RSF_NOLIGHTSCALE ) { bits |= IF_NOLIGHTSCALE; diff --git a/src/engine/renderer/tr_types.h b/src/engine/renderer/tr_types.h index 610db62ff7..b18f6566a3 100644 --- a/src/engine/renderer/tr_types.h +++ b/src/engine/renderer/tr_types.h @@ -94,11 +94,12 @@ using bool8_t = uint8_t; using glIndex_t = unsigned int; enum RegisterShaderFlags_t { - RSF_DEFAULT = 0x00, - RSF_NOMIP = 0x01, - RSF_LIGHT_ATTENUATION = 0x02, - RSF_NOLIGHTSCALE = 0x04, - RSF_SPRITE = 0x08 + RSF_DEFAULT = BIT( 0 ), + RSF_NOMIP = BIT( 1 ), + RSF_FITSCREEN = BIT( 2 ), + RSF_LIGHT_ATTENUATION = BIT( 3 ), + RSF_NOLIGHTSCALE = BIT( 4 ), + RSF_SPRITE = BIT( 5 ), }; struct polyVert_t From deac739ae81cc36e14f9b137ee3b80b1848e506a Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Mon, 13 May 2024 15:15:27 +0200 Subject: [PATCH 4/6] tr_shader: HACK the HACK The current implementation of this code is an ugly HACK, make this HACK work with RSF_FITSCREEN. This HACK will disappear once RSF_2D is implemented. --- src/engine/renderer/tr_shader.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index 6856256bb3..8ebed98268 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -6373,21 +6373,24 @@ shader_t *R_FindShader( const char *name, shaderType_t type, Log::Debug( "loading '%s' image as shader", fileName ); // choosing filter based on the NOMIP flag seems strange, - // maybe it should be changed to type == SHADER_2D - if( !(bits & RSF_NOMIP) ) { - LoadExtraMaps( &stages[ 0 ], fileName ); - + // we better introduce RSF_2D. + if( ( bits & RSF_NOMIP ) || ( bits & RSF_FITSCREEN ) ) + { imageParams_t imageParams = {}; imageParams.bits = bits; - imageParams.filterType = filterType_t::FT_DEFAULT; - imageParams.wrapType = wrapTypeEnum_t::WT_REPEAT; + imageParams.filterType = filterType_t::FT_LINEAR; + imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP; image = R_FindImageFile( fileName, imageParams ); - } else { + } + else + { + LoadExtraMaps( &stages[ 0 ], fileName ); + imageParams_t imageParams = {}; imageParams.bits = bits; - imageParams.filterType = filterType_t::FT_LINEAR; - imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP; + imageParams.filterType = filterType_t::FT_DEFAULT; + imageParams.wrapType = wrapTypeEnum_t::WT_REPEAT; image = R_FindImageFile( fileName, imageParams ); } From df5ea71e0018b4168af34dddce10bad529c8c770 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Mon, 13 May 2024 15:39:09 +0200 Subject: [PATCH 5/6] renderer: add RSF_2D and make RegisterShaderFlags_t cummulative --- src/engine/client/cg_api.h | 2 +- src/engine/client/cl_cgame.cpp | 2 +- src/engine/null/null_renderer.cpp | 2 +- src/engine/renderer/tr_local.h | 5 ++--- src/engine/renderer/tr_public.h | 3 +-- src/engine/renderer/tr_shader.cpp | 9 +++------ src/engine/renderer/tr_types.h | 11 ++++++----- src/shared/client/cg_api.cpp | 2 +- 8 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/engine/client/cg_api.h b/src/engine/client/cg_api.h index c271511c37..f6bc84c296 100644 --- a/src/engine/client/cg_api.h +++ b/src/engine/client/cg_api.h @@ -121,7 +121,7 @@ void trap_S_StartBackgroundTrack( const char *intro, const char *loop void trap_R_LoadWorldMap( const char *mapname ); qhandle_t trap_R_RegisterModel( const char *name ); qhandle_t trap_R_RegisterSkin( const char *name ); -qhandle_t trap_R_RegisterShader( const char *name, RegisterShaderFlags_t flags ); +qhandle_t trap_R_RegisterShader( const char *name, int flags ); void trap_R_ClearScene(); void trap_R_AddRefEntityToScene( const refEntity_t *re ); diff --git a/src/engine/client/cl_cgame.cpp b/src/engine/client/cl_cgame.cpp index 3a8843e571..23f9325064 100644 --- a/src/engine/client/cl_cgame.cpp +++ b/src/engine/client/cl_cgame.cpp @@ -1259,7 +1259,7 @@ void CGameVM::QVMSyscall(int syscallNum, Util::Reader& reader, IPC::Channel& cha case CG_R_REGISTERSHADER: IPC::HandleMsg(channel, std::move(reader), [this] (const std::string& name, int flags, int& handle) { - handle = re.RegisterShader(name.c_str(), (RegisterShaderFlags_t) flags); + handle = re.RegisterShader(name.c_str(), flags); }); break; diff --git a/src/engine/null/null_renderer.cpp b/src/engine/null/null_renderer.cpp index 9c1876d2f3..ca96c51d3d 100644 --- a/src/engine/null/null_renderer.cpp +++ b/src/engine/null/null_renderer.cpp @@ -46,7 +46,7 @@ qhandle_t RE_RegisterSkin( const char *name ) { return FS_FOpenFileRead( name, nullptr ); } -qhandle_t RE_RegisterShader( const char *, RegisterShaderFlags_t ) +qhandle_t RE_RegisterShader( const char *, int ) { return 1; } diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 77a98d1c75..5847ca5f1c 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -3266,12 +3266,11 @@ inline bool checkGLErrors() ==================================================================== */ - qhandle_t RE_RegisterShader( const char *name, RegisterShaderFlags_t flags ); + qhandle_t RE_RegisterShader( const char *name, int flags ); qhandle_t RE_RegisterShaderFromImage( const char *name, image_t *image ); bool RE_LoadDynamicShader( const char *shadername, const char *shadertext ); - shader_t *R_FindShader( const char *name, shaderType_t type, - RegisterShaderFlags_t flags ); + shader_t *R_FindShader( const char *name, shaderType_t type, int flags ); shader_t *R_GetShaderByHandle( qhandle_t hShader ); shader_t *R_FindShaderByName( const char *name ); const char *RE_GetShaderNameFromHandle( qhandle_t shader ); diff --git a/src/engine/renderer/tr_public.h b/src/engine/renderer/tr_public.h index a079d37e5d..578f1f29c5 100644 --- a/src/engine/renderer/tr_public.h +++ b/src/engine/renderer/tr_public.h @@ -130,8 +130,7 @@ struct refexport_t qhandle_t ( *RegisterModel )( const char *name ); //qhandle_t (*RegisterModelAllLODs) (const char *name); qhandle_t ( *RegisterSkin )( const char *name ); - qhandle_t ( *RegisterShader )( const char *name, - RegisterShaderFlags_t flags ); + qhandle_t ( *RegisterShader )( const char *name, int flags ); fontInfo_t* ( *RegisterFont )( const char *fontName, int pointSize ); void ( *UnregisterFont )( fontInfo_t *font ); void ( *Glyph )( fontInfo_t *font, const char *str, glyphInfo_t *glyph ); diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index 8ebed98268..11a1c12409 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -6243,8 +6243,7 @@ the vertex rgba modulate values, as appropriate for misc_model pre-lit surfaces. =============== */ -shader_t *R_FindShader( const char *name, shaderType_t type, - RegisterShaderFlags_t flags ) +shader_t *R_FindShader( const char *name, shaderType_t type, int flags ) { char strippedName[ MAX_QPATH ]; char fileName[ MAX_QPATH ]; @@ -6372,9 +6371,7 @@ shader_t *R_FindShader( const char *name, shaderType_t type, Log::Debug( "loading '%s' image as shader", fileName ); - // choosing filter based on the NOMIP flag seems strange, - // we better introduce RSF_2D. - if( ( bits & RSF_NOMIP ) || ( bits & RSF_FITSCREEN ) ) + if( bits & RSF_2D ) { imageParams_t imageParams = {}; imageParams.bits = bits; @@ -6530,7 +6527,7 @@ This should really only be used for explicit shaders, because there is no way to ask for different implicit lighting modes (vertex, lightmap, etc) ==================== */ -qhandle_t RE_RegisterShader( const char *name, RegisterShaderFlags_t flags ) +qhandle_t RE_RegisterShader( const char *name, int flags ) { shader_t *sh; diff --git a/src/engine/renderer/tr_types.h b/src/engine/renderer/tr_types.h index b18f6566a3..31e06871c5 100644 --- a/src/engine/renderer/tr_types.h +++ b/src/engine/renderer/tr_types.h @@ -95,11 +95,12 @@ using glIndex_t = unsigned int; enum RegisterShaderFlags_t { RSF_DEFAULT = BIT( 0 ), - RSF_NOMIP = BIT( 1 ), - RSF_FITSCREEN = BIT( 2 ), - RSF_LIGHT_ATTENUATION = BIT( 3 ), - RSF_NOLIGHTSCALE = BIT( 4 ), - RSF_SPRITE = BIT( 5 ), + RSF_2D = BIT( 1 ), + RSF_NOMIP = BIT( 2 ), + RSF_FITSCREEN = BIT( 3 ), + RSF_LIGHT_ATTENUATION = BIT( 4 ), + RSF_NOLIGHTSCALE = BIT( 5 ), + RSF_SPRITE = BIT( 6 ), }; struct polyVert_t diff --git a/src/shared/client/cg_api.cpp b/src/shared/client/cg_api.cpp index 5c8b016864..db21a8ecef 100644 --- a/src/shared/client/cg_api.cpp +++ b/src/shared/client/cg_api.cpp @@ -317,7 +317,7 @@ qhandle_t trap_R_RegisterSkin( const char *name ) return handle; } -qhandle_t trap_R_RegisterShader( const char *name, RegisterShaderFlags_t flags ) +qhandle_t trap_R_RegisterShader( const char *name, int flags ) { int handle; VM::SendMsg(name, flags, handle); From b1a8f014def3501520d0ba8484125a18fb9d928e Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Wed, 29 May 2024 19:36:01 +0200 Subject: [PATCH 6/6] renderer: add r_imageFitScreen cvar --- src/engine/renderer/InternalImage.cpp | 11 ++++++++--- src/engine/renderer/tr_image.cpp | 8 ++++++++ src/engine/renderer/tr_init.cpp | 2 ++ src/engine/renderer/tr_local.h | 1 + 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/engine/renderer/InternalImage.cpp b/src/engine/renderer/InternalImage.cpp index 971e83415a..d0c870b059 100644 --- a/src/engine/renderer/InternalImage.cpp +++ b/src/engine/renderer/InternalImage.cpp @@ -110,7 +110,7 @@ int R_GetImageCustomScalingStep( const image_t *image, const imageParams_t &imag int scalingStep = 0; - // Scale down the image size until it's not smaller than screen. + // Scale down the image size according to the screen size. if ( image->bits & IF_FITSCREEN ) { int largerSide = std::max( glConfig.vidWidth, glConfig.vidHeight ); @@ -123,8 +123,13 @@ int R_GetImageCustomScalingStep( const image_t *image, const imageParams_t &imag scalingStep++; } - // We need the larger image size before it becomes smaller than screen. - if ( scaledDimension != largerSide ) + /* With r_imageFitScreen == 1, we need the larger image size before + it becomes smaller than screen. + + With r_imageFitScreen == 2 the image is never larger than screen, as + we allow the larger size that is not larger than screen, it can be the + larger size smaller than screen. */ + if ( scaledDimension != largerSide && r_imageFitScreen.Get() != 2 ) { scaledDimension <<= 1; scalingStep--; diff --git a/src/engine/renderer/tr_image.cpp b/src/engine/renderer/tr_image.cpp index 077b42e100..91330ff001 100644 --- a/src/engine/renderer/tr_image.cpp +++ b/src/engine/renderer/tr_image.cpp @@ -763,6 +763,14 @@ void R_UploadImage( const byte **dataArray, int numLayers, int numMips, image_t int scaledWidth = image->width; int scaledHeight = image->height; + + // If r_imageFitScreen is disabled, use nopicmip instead. + if ( ( image->bits & IF_FITSCREEN ) && !r_imageFitScreen.Get() ) + { + image->bits &= ~IF_FITSCREEN; + image->bits |= IF_NOPICMIP; + } + int customScalingStep = R_GetImageCustomScalingStep( image, imageParams ); R_DownscaleImageDimensions( customScalingStep, &scaledWidth, &scaledHeight, &dataArray, numLayers, &numMips ); diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index de118a313a..47c3fc3ad9 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -150,6 +150,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA cvar_t *r_ignoreMaterialMinDimension; cvar_t *r_ignoreMaterialMaxDimension; cvar_t *r_replaceMaterialMinDimensionIfPresentWithMaxDimension; + Cvar::Range> r_imageFitScreen("r_imageFitScreen", "downscale “fitscreen” images to fit the screen size: 0: disable, 1: downscale as much as possible without being smaller than screen size (default), 2: downscale to never be larger then screen size", Cvar::NONE, 1, 0, 2); cvar_t *r_finish; cvar_t *r_clear; cvar_t *r_textureMode; @@ -1095,6 +1096,7 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p r_ignoreMaterialMaxDimension = Cvar_Get( "r_ignoreMaterialMaxDimension", "0", CVAR_LATCH | CVAR_ARCHIVE ); r_replaceMaterialMinDimensionIfPresentWithMaxDimension = Cvar_Get( "r_replaceMaterialMinDimensionIfPresentWithMaxDimension", "0", CVAR_LATCH | CVAR_ARCHIVE ); + Cvar::Latch(r_imageFitScreen); r_colorbits = Cvar_Get( "r_colorbits", "0", CVAR_LATCH ); r_mode = Cvar_Get( "r_mode", "-2", CVAR_LATCH | CVAR_ARCHIVE ); r_customwidth = Cvar_Get( "r_customwidth", "1600", CVAR_LATCH | CVAR_ARCHIVE ); diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 5847ca5f1c..e56d815d09 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -2909,6 +2909,7 @@ enum class dynamicLightRenderer_t { LEGACY, TILED }; extern cvar_t *r_ignoreMaterialMinDimension; extern cvar_t *r_ignoreMaterialMaxDimension; extern cvar_t *r_replaceMaterialMinDimensionIfPresentWithMaxDimension; + extern Cvar::Range> r_imageFitScreen; extern cvar_t *r_finish; extern cvar_t *r_drawBuffer; extern cvar_t *r_textureMode;