From 7dd1b3816832930c79af3b9747673c48e8cedfed Mon Sep 17 00:00:00 2001 From: VReaperV Date: Fri, 27 Dec 2024 18:38:49 +0300 Subject: [PATCH 1/8] Implement MSAA Add `r_msaa`. When set to > 0, an MSAA FBO will be created with that many samples. This FBO will be used for rendering, other than when it requires sampling from current render/depth image. When such rendering is required the MSAA FBO will be blit into the main FBO and vice versa, to resolve the MSAA texture. --- src/engine/renderer/Material.cpp | 4 + src/engine/renderer/tr_backend.cpp | 75 +++++++- src/engine/renderer/tr_fbo.cpp | 19 +- src/engine/renderer/tr_image.cpp | 300 ++++++++++++++++------------- src/engine/renderer/tr_init.cpp | 2 + src/engine/renderer/tr_local.h | 16 +- src/engine/renderer/tr_shade.cpp | 10 + src/engine/sys/sdl_glimp.cpp | 2 + 8 files changed, 287 insertions(+), 141 deletions(-) diff --git a/src/engine/renderer/Material.cpp b/src/engine/renderer/Material.cpp index be88e1cde2..19b7834a53 100644 --- a/src/engine/renderer/Material.cpp +++ b/src/engine/renderer/Material.cpp @@ -1062,6 +1062,8 @@ void BindShaderHeatHaze( Material* material ) { gl_heatHazeShaderMaterial->SetUniform_DeformEnable( true ); // draw to background image + TransitionMSAAToMain(); + R_BindFBO( tr.mainFBO[1 - backEnd.currentMainFBO] ); } @@ -2237,6 +2239,8 @@ void MaterialSystem::RenderMaterial( Material& material, const uint32_t viewID ) R_BindFBO( tr.mainFBO[backEnd.currentMainFBO] ); RenderIndirect( material, viewID ); + + TransitionMainToMSAA(); } if ( r_showTris->integer diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index ee8427aa94..9b9e2ea36c 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -199,6 +199,53 @@ GLuint64 GL_BindToTMU( int unit, image_t *image ) return 0; } +static void BlitFBOToMSAA( FBO_t* fbo ) { + glState.currentFBO = nullptr; + + R_BindFBO( GL_READ_FRAMEBUFFER, fbo ); + R_BindFBO( GL_DRAW_FRAMEBUFFER, tr.msaaFBO ); + glBlitFramebuffer( 0, 0, fbo->width, fbo->height, 0, 0, tr.msaaFBO->width, tr.msaaFBO->height, + GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST ); + + glState.currentFBO = nullptr; + R_BindFBO( GL_DRAW_FRAMEBUFFER, fbo ); + glState.currentFBO = fbo; +} + +static void BlitMSAAToFBO( FBO_t* fbo ) { + glState.currentFBO = nullptr; + + R_BindFBO( GL_READ_FRAMEBUFFER, tr.msaaFBO ); + R_BindFBO( GL_DRAW_FRAMEBUFFER, fbo ); + glBlitFramebuffer( 0, 0, tr.msaaFBO->width, tr.msaaFBO->height, 0, 0, fbo->width, fbo->height, + GL_COLOR_BUFFER_BIT /* | GL_DEPTH_BUFFER_BIT */, GL_NEAREST ); + + glState.currentFBO = nullptr; + R_BindFBO( GL_READ_FRAMEBUFFER, fbo ); + glState.currentFBO = fbo; +} + +void TransitionMainToMSAA() { + if ( glConfig.MSAA ) { + BlitFBOToMSAA( tr.mainFBO[backEnd.currentMainFBO] ); + R_BindFBO( tr.msaaFBO ); + } +} + +void TransitionMSAAToMain() { + if ( glConfig.MSAA ) { + BlitMSAAToFBO( tr.mainFBO[backEnd.currentMainFBO] ); + } +} + +void BindMSAAOrMainFBO() { + if ( glConfig.MSAA ) { + R_BindFBO( tr.msaaFBO ); + } else { + R_BindFBO( tr.mainFBO[backEnd.currentMainFBO] ); + } +} + void GL_BlendFunc( GLenum sfactor, GLenum dfactor ) { if ( glState.blendSrc != ( signed ) sfactor || glState.blendDst != ( signed ) dfactor ) @@ -797,7 +844,13 @@ void GL_TexImage2D( GLenum target, GLint level, GLint internalFormat, GLsizei wi GLint finalFormat = GL_ToSRGB( internalFormat, isSRGB ); glTexImage2D( target, level, finalFormat, width, height, border, format, type, data ); +} + +void GL_TexImage2DMultisample( GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, bool fixedSampleLocations, bool isSRGB ) +{ + GLint finalFormat = GL_ToSRGB( internalFormat, isSRGB ); + glTexImage2DMultisample( target, samples, finalFormat, width, height, fixedSampleLocations ); } void GL_TexImage3D( GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *data, bool isSRGB ) @@ -1365,7 +1418,8 @@ void RB_RenderPostDepthLightTile() Tess_Clear(); // back to main image - R_BindFBO( tr.mainFBO[ backEnd.currentMainFBO ] ); + BindMSAAOrMainFBO(); + GL_Viewport( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY, backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight ); GL_Scissor( backEnd.viewParms.scissorX, backEnd.viewParms.scissorY, @@ -1446,6 +1500,8 @@ void RB_RenderBloom() GL_BindToTMU( 0, tr.currentRenderImage[backEnd.currentMainFBO] ) ); + TransitionMSAAToMain(); + R_BindFBO( tr.contrastRenderFBO ); GL_ClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); glClear( GL_COLOR_BUFFER_BIT ); @@ -1510,6 +1566,8 @@ void RB_RenderBloom() GL_PopMatrix(); } + TransitionMainToMSAA(); + GL_CheckErrors(); } @@ -1530,6 +1588,8 @@ void RB_RenderMotionBlur() gl_motionblurShader->BindProgram(); + TransitionMSAAToMain(); + // Swap main FBOs gl_motionblurShader->SetUniform_ColorMapBindless( GL_BindToTMU( 0, tr.currentRenderImage[backEnd.currentMainFBO] ) @@ -1545,6 +1605,8 @@ void RB_RenderMotionBlur() Tess_InstantScreenSpaceQuad(); + TransitionMainToMSAA(); + GL_CheckErrors(); } @@ -2666,7 +2728,7 @@ static void RB_RenderView( bool depthPass ) backEnd.pc.c_surfaces += backEnd.viewParms.numDrawSurfs; // disable offscreen rendering - R_BindFBO( tr.mainFBO[ backEnd.currentMainFBO ] ); + BindMSAAOrMainFBO(); // we will need to change the projection matrix before drawing // 2D images again @@ -2794,6 +2856,8 @@ static void RB_RenderPostProcess() materialSystem.EndFrame(); } + TransitionMSAAToMain(); + RB_FXAA(); // render chromatic aberration @@ -3463,7 +3527,7 @@ const RenderCommand *ClearBufferCommand::ExecuteSelf( ) const } // disable offscreen rendering - R_BindFBO( tr.mainFBO[ backEnd.currentMainFBO ] ); + R_BindFBO( tr.mainFBO[backEnd.currentMainFBO] ); // we will need to change the projection matrix before drawing // 2D images again @@ -3484,6 +3548,11 @@ const RenderCommand *ClearBufferCommand::ExecuteSelf( ) const glClear( clearBits ); + if ( glConfig.MSAA ) { + R_BindFBO( tr.msaaFBO ); + glClear( clearBits ); + } + return this + 1; } diff --git a/src/engine/renderer/tr_fbo.cpp b/src/engine/renderer/tr_fbo.cpp index 6083935446..7087c47e39 100644 --- a/src/engine/renderer/tr_fbo.cpp +++ b/src/engine/renderer/tr_fbo.cpp @@ -152,7 +152,8 @@ R_AttachFBOTexture2D */ void R_AttachFBOTexture2D( int target, int texId, int index ) { - if ( target != GL_TEXTURE_2D && ( target < GL_TEXTURE_CUBE_MAP_POSITIVE_X || target > GL_TEXTURE_CUBE_MAP_NEGATIVE_Z ) ) + if ( target != GL_TEXTURE_2D && target != GL_TEXTURE_2D_MULTISAMPLE + && ( target < GL_TEXTURE_CUBE_MAP_POSITIVE_X || target > GL_TEXTURE_CUBE_MAP_NEGATIVE_Z ) ) { Log::Warn("R_AttachFBOTexture2D: invalid target %i", target ); return; @@ -194,6 +195,11 @@ void R_AttachFBOTexturePackedDepthStencil( int texId ) GL_fboShim.glFramebufferTexture2D( GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texId, 0 ); } +void R_AttachFBOTexturePackedDepthStencilMSAA( int texId ) { + GL_fboShim.glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, texId, 0 ); + GL_fboShim.glFramebufferTexture2D( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, texId, 0 ); +} + /* ============ R_BindFBO @@ -271,6 +277,17 @@ void R_InitFBOs() glConfig.usingReadonlyDepth = R_CheckFBO( tr.readonlyDepthFBO ); } + if ( glConfig.MSAA ) { + tr.msaaFBO = R_CreateFBO( "msaa", width, height ); + R_BindFBO( tr.msaaFBO ); + GL_CheckErrors(); + R_AttachFBOTexture2D( GL_TEXTURE_2D_MULTISAMPLE, tr.currentRenderImageMSAA->texnum, 0 ); + GL_CheckErrors(); + R_AttachFBOTexturePackedDepthStencilMSAA( tr.currentDepthImageMSAA->texnum ); + GL_CheckErrors(); + R_CheckFBO( tr.msaaFBO ); + } + if ( glConfig.realtimeLighting ) { /* It's only required to create frame buffers only used by the diff --git a/src/engine/renderer/tr_image.cpp b/src/engine/renderer/tr_image.cpp index b430bedcd4..d245d8fd08 100644 --- a/src/engine/renderer/tr_image.cpp +++ b/src/engine/renderer/tr_image.cpp @@ -841,7 +841,8 @@ level 1 has only numLayers/2 layers. There are still numLayers pointers in the dataArray for every mip level, the unneeded elements at the end aren't used. =============== */ -void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int numMips, image_t *image, const imageParams_t &imageParams ) +void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int numMips, image_t *image, const imageParams_t &imageParams, + const uint32_t samples, const bool fixedSampleLocations ) { const byte *data; byte *scaledBuffer = nullptr; @@ -911,6 +912,14 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int break; } + if ( samples ) { + if ( target == GL_TEXTURE_2D ) { + target = GL_TEXTURE_2D_MULTISAMPLE; + } else { + Log::Warn( "Multisampling is currently only supported for 2D textures (image: %s)", name ); + } + } + if ( image->bits & ( IF_DEPTH16 | IF_DEPTH24 | IF_DEPTH32 ) ) { format = GL_DEPTH_COMPONENT; @@ -1150,12 +1159,21 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int default: if ( image->bits & IF_PACKED_DEPTH24_STENCIL8 ) { - GL_TexImage2D( target, 0, internalFormat, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_INT_24_8, nullptr, isSRGB ); + if ( samples ) { + GL_TexImage2DMultisample( target, samples, internalFormat, scaledWidth, scaledHeight, fixedSampleLocations, isSRGB ); + } else { + GL_TexImage2D( target, 0, internalFormat, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_INT_24_8, nullptr, isSRGB ); + } } else { - GL_TexImage2D( target, 0, internalFormat, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_BYTE, scaledBuffer, isSRGB ); + if ( samples ) { + GL_TexImage2DMultisample( target, samples, internalFormat, scaledWidth, scaledHeight, fixedSampleLocations, isSRGB ); + } else { + GL_TexImage2D( target, 0, internalFormat, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_BYTE, scaledBuffer, isSRGB ); + } } + GL_CheckErrors(); break; } @@ -1217,9 +1235,9 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int GL_CheckErrors(); // set filter type - switch ( image->filterType ) - { - case filterType_t::FT_DEFAULT: + if ( !samples ) { + switch ( image->filterType ) { + case filterType_t::FT_DEFAULT: // set texture anisotropy if ( glConfig.textureAnisotropyAvailable ) @@ -1227,139 +1245,137 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int glTexParameterf( image->type, GL_TEXTURE_MAX_ANISOTROPY_EXT, glConfig.textureAnisotropy ); } - glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, gl_filter_min ); - glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, gl_filter_max ); - break; + glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, gl_filter_min ); + glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, gl_filter_max ); + break; - case filterType_t::FT_LINEAR: - glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - break; + case filterType_t::FT_LINEAR: + glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + break; - case filterType_t::FT_NEAREST: - glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - break; + case filterType_t::FT_NEAREST: + glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + break; - default: - Log::Warn("unknown filter type for image '%s'", image->name ); - glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - break; + default: + Log::Warn( "unknown filter type for image '%s'", image->name ); + glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + break; + } } GL_CheckErrors(); // set wrap type - if ( image->wrapType.s == image->wrapType.t ) - { - switch ( image->wrapType.s ) - { - case wrapTypeEnum_t::WT_REPEAT: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); - break; - - case wrapTypeEnum_t::WT_CLAMP: - case wrapTypeEnum_t::WT_EDGE_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - break; - case wrapTypeEnum_t::WT_ONE_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, oneClampBorder ); - break; - case wrapTypeEnum_t::WT_ZERO_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, zeroClampBorder ); - break; - - case wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder ); - break; - - default: - Log::Warn("unknown wrap type for image '%s'", image->name ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); - break; - } - } else { - // warn about mismatched clamp types if both require a border colour to be set - if ( ( image->wrapType.s == wrapTypeEnum_t::WT_ZERO_CLAMP || image->wrapType.s == wrapTypeEnum_t::WT_ONE_CLAMP || image->wrapType.s == wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP ) && - ( image->wrapType.t == wrapTypeEnum_t::WT_ZERO_CLAMP || image->wrapType.t == wrapTypeEnum_t::WT_ONE_CLAMP || image->wrapType.t == wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP ) ) - { - Log::Warn("mismatched wrap types for image '%s'", image->name ); - } - - switch ( image->wrapType.s ) - { - case wrapTypeEnum_t::WT_REPEAT: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); - break; - - case wrapTypeEnum_t::WT_CLAMP: - case wrapTypeEnum_t::WT_EDGE_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - break; - - case wrapTypeEnum_t::WT_ONE_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, oneClampBorder ); - break; - - case wrapTypeEnum_t::WT_ZERO_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, zeroClampBorder ); - break; - - case wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder ); - break; - - default: - Log::Warn("unknown wrap type for image '%s' axis S", image->name ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); - break; - } - - switch ( image->wrapType.t ) - { - case wrapTypeEnum_t::WT_REPEAT: - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); - break; - - case wrapTypeEnum_t::WT_CLAMP: - case wrapTypeEnum_t::WT_EDGE_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - break; - - case wrapTypeEnum_t::WT_ONE_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, oneClampBorder ); - break; - - case wrapTypeEnum_t::WT_ZERO_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, zeroClampBorder ); - break; - - case wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder ); - break; - - default: - Log::Warn("unknown wrap type for image '%s' axis T", image->name ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); - break; - } - } + if ( !samples ) { + if ( image->wrapType.s == image->wrapType.t ) { + switch ( image->wrapType.s ) { + case wrapTypeEnum_t::WT_REPEAT: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); + break; + + case wrapTypeEnum_t::WT_CLAMP: + case wrapTypeEnum_t::WT_EDGE_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + break; + case wrapTypeEnum_t::WT_ONE_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, oneClampBorder ); + break; + case wrapTypeEnum_t::WT_ZERO_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, zeroClampBorder ); + break; + + case wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder ); + break; + + default: + Log::Warn( "unknown wrap type for image '%s'", image->name ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); + break; + } + } else { + // warn about mismatched clamp types if both require a border colour to be set + if ( ( image->wrapType.s == wrapTypeEnum_t::WT_ZERO_CLAMP || image->wrapType.s == wrapTypeEnum_t::WT_ONE_CLAMP || image->wrapType.s == wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP ) && + ( image->wrapType.t == wrapTypeEnum_t::WT_ZERO_CLAMP || image->wrapType.t == wrapTypeEnum_t::WT_ONE_CLAMP || image->wrapType.t == wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP ) ) { + Log::Warn( "mismatched wrap types for image '%s'", image->name ); + } + + switch ( image->wrapType.s ) { + case wrapTypeEnum_t::WT_REPEAT: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); + break; + + case wrapTypeEnum_t::WT_CLAMP: + case wrapTypeEnum_t::WT_EDGE_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + break; + + case wrapTypeEnum_t::WT_ONE_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, oneClampBorder ); + break; + + case wrapTypeEnum_t::WT_ZERO_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, zeroClampBorder ); + break; + + case wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder ); + break; + + default: + Log::Warn( "unknown wrap type for image '%s' axis S", image->name ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); + break; + } + + switch ( image->wrapType.t ) { + case wrapTypeEnum_t::WT_REPEAT: + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); + break; + + case wrapTypeEnum_t::WT_CLAMP: + case wrapTypeEnum_t::WT_EDGE_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + break; + + case wrapTypeEnum_t::WT_ONE_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, oneClampBorder ); + break; + + case wrapTypeEnum_t::WT_ZERO_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, zeroClampBorder ); + break; + + case wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder ); + break; + + default: + Log::Warn( "unknown wrap type for image '%s' axis T", image->name ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); + break; + } + } + } GL_CheckErrors(); @@ -1448,7 +1464,8 @@ static void R_ExportTexture( image_t *image ) R_CreateImage ================ */ -image_t *R_CreateImage( const char *name, const byte **pic, int width, int height, int numMips, const imageParams_t &imageParams ) +image_t *R_CreateImage( const char *name, const byte **pic, int width, int height, int numMips, const imageParams_t &imageParams, + const uint32_t samples, const bool fixedSampleLocations ) { const char* colorspaces[] = { "linear", "sRGB" }; const char* colorspace = colorspaces[ bool( imageParams.bits & IF_SRGB ) ]; @@ -1464,8 +1481,13 @@ image_t *R_CreateImage( const char *name, const byte **pic, int width, int heigh return nullptr; } - image->type = GL_TEXTURE_2D; - image->texture->target = GL_TEXTURE_2D; + if ( samples ) { + image->type = GL_TEXTURE_2D_MULTISAMPLE; + image->texture->target = GL_TEXTURE_2D_MULTISAMPLE; + } else { + image->type = GL_TEXTURE_2D; + image->texture->target = GL_TEXTURE_2D; + } image->width = width; image->height = height; @@ -1474,7 +1496,7 @@ image_t *R_CreateImage( const char *name, const byte **pic, int width, int heigh image->filterType = imageParams.filterType; image->wrapType = imageParams.wrapType; - R_UploadImage( name, pic, 1, numMips, image, imageParams ); + R_UploadImage( name, pic, 1, numMips, image, imageParams, samples, fixedSampleLocations ); if( r_exportTextures->integer ) { R_ExportTexture( image ); @@ -2466,6 +2488,10 @@ static void R_CreateCurrentRenderImage() tr.currentRenderImage[0] = R_CreateImage( "*currentRender0", nullptr, width, height, 1, imageParams ); tr.currentRenderImage[1] = R_CreateImage( "*currentRender1", nullptr, width, height, 1, imageParams ); + if ( glConfig.MSAA ) { + tr.currentRenderImageMSAA = R_CreateImage( "_currentRenderMSAA", nullptr, width, height, 1, imageParams, glConfig.MSAA, false ); + } + imageParams = {}; imageParams.bits = IF_NOPICMIP | IF_PACKED_DEPTH24_STENCIL8; imageParams.filterType = filterType_t::FT_NEAREST; @@ -2479,6 +2505,10 @@ static void R_CreateCurrentRenderImage() tr.depthSamplerImage = R_CreateImage( "*readonlyDepth", nullptr, width, height, 1, imageParams ); } + if( glConfig.MSAA ) { + tr.currentDepthImageMSAA = R_CreateImage( "_currentDepthMSAA", nullptr, width, height, 1, imageParams, glConfig.MSAA, false ); + } + if ( glConfig.usingMaterialSystem ) { materialSystem.GenerateDepthImages( width, height, imageParams ); } diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index fa336629f3..bbe2fb2db1 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -287,6 +287,7 @@ Cvar::Cvar r_rendererAPI( "r_rendererAPI", "Renderer API: 0: OpenGL, 1: Vul Cvar::Cvar r_bloomBlur( "r_bloomBlur", "Bloom strength", Cvar::NONE, 1.0 ); Cvar::Cvar r_bloomPasses( "r_bloomPasses", "Amount of bloom passes in each direction", Cvar::NONE, 2 ); cvar_t *r_FXAA; + Cvar::Range> r_msaa( "r_msaa", "Amount of MSAA samples. 0 to disable", Cvar::NONE, 0, 0, 64 ); Cvar::Range> r_ssao( "r_ssao", "Screen space ambient occlusion: " "-1: show, 0: disabled, 1: enabled", @@ -1194,6 +1195,7 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p Cvar::Latch( r_bloom ); r_FXAA = Cvar_Get( "r_FXAA", "0", CVAR_LATCH | CVAR_ARCHIVE ); Cvar::Latch( r_ssao ); + Cvar::Latch( r_msaa ); // temporary variables that can change at any time r_showImages = Cvar_Get( "r_showImages", "0", CVAR_TEMP ); diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index a6cfc3d47e..e26a36ea6a 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -2466,6 +2466,8 @@ enum image_t *currentRenderImage[ 2 ]; image_t *currentDepthImage; image_t *depthSamplerImage; + image_t *currentRenderImageMSAA; + image_t *currentDepthImageMSAA; image_t *depthtile1RenderImage; image_t *depthtile2RenderImage; image_t *lighttileRenderImage; @@ -2478,6 +2480,7 @@ enum // framebuffer objects FBO_t *mainFBO[ 2 ]; FBO_t *readonlyDepthFBO; + FBO_t *msaaFBO; FBO_t *depthtile1FBO; FBO_t *depthtile2FBO; FBO_t *lighttileFBO; @@ -2783,6 +2786,7 @@ enum extern Cvar::Cvar r_bloomPasses; extern cvar_t *r_FXAA; extern Cvar::Range> r_ssao; + extern Cvar::Range> r_msaa; extern cvar_t *r_evsmPostProcess; @@ -2912,6 +2916,11 @@ inline bool checkGLErrors() void GL_BindProgram( ShaderProgramDescriptor* program ); GLuint64 GL_BindToTMU( int unit, image_t *image ); void GL_BindNullProgram(); + + void TransitionMainToMSAA(); + void TransitionMSAAToMain(); + void BindMSAAOrMainFBO(); + void GL_SetDefaultState(); void GL_SelectTexture( int unit ); void GL_TextureMode( const char *string ); @@ -2938,6 +2947,7 @@ inline bool checkGLErrors() void GL_VertexAttribsState( uint32_t stateBits, const bool settingUpVAO = false ); void GL_Cull( cullType_t cullType ); void GL_TexImage2D( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *data, bool isSRGB ); +void GL_TexImage2DMultisample( GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, bool fixedSampleLocations, bool isSRGB ); void GL_TexImage3D( GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *data, bool isSRGB ); void GL_CompressedTexImage2D( GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data, bool isSRGB ); void GL_CompressedTexSubImage3D( GLenum target, GLint level, GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, GLsizei depth, GLenum internalFormat, GLsizei size, const void *data, bool isSRGB ); @@ -2990,7 +3000,8 @@ void GL_CompressedTexSubImage3D( GLenum target, GLint level, GLint xOffset, GLin image_t *R_FindImageFile( const char *name, imageParams_t &imageParams ); image_t *R_FindCubeImage( const char *name, imageParams_t &imageParams ); - image_t *R_CreateImage( const char *name, const byte **pic, int width, int height, int numMips, const imageParams_t &imageParams ); + image_t *R_CreateImage( const char *name, const byte **pic, int width, int height, int numMips, const imageParams_t &imageParams, + const uint32_t samples = 0, const bool fixedSampleLocations = true ); image_t *R_CreateCubeImage( const char *name, const byte *pic[ 6 ], int width, int height, const imageParams_t &imageParams ); image_t *R_Create3DImage( const char *name, const byte *pic, int width, int height, int depth, const imageParams_t &imageParams ); @@ -2999,7 +3010,8 @@ void GL_CompressedTexSubImage3D( GLenum target, GLint level, GLint xOffset, GLin qhandle_t RE_GenerateTexture( const byte *pic, int width, int height ); image_t *R_AllocImage( const char *name, bool linkIntoHashTable ); - void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int numMips, image_t *image, const imageParams_t &imageParams ); + void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int numMips, image_t *image, const imageParams_t &imageParams, + const uint32_t samples = 0, const bool fixedSampleLocations = true ); void RE_GetTextureSize( int textureID, int *width, int *height ); diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index 5364e921f7..255b4ae42f 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -180,6 +180,13 @@ static void EnableAvailableFeatures() } } + glConfig.MSAA = r_msaa.Get(); + const int maxSamples = std::min( glConfig.maxColorTextureSamples, glConfig.maxDepthTextureSamples ); + if ( glConfig.MSAA > maxSamples ) { + Log::Warn( "MSAA samples %i > %i, setting to %i", r_msaa.Get(), maxSamples, maxSamples ); + glConfig.MSAA = maxSamples; + } + glConfig.usingMaterialSystem = r_materialSystem.Get() && glConfig.materialSystemAvailable; glConfig.usingBindlessTextures = glConfig.usingMaterialSystem || ( r_preferBindlessTextures.Get() && glConfig.bindlessTexturesAvailable ); @@ -1458,6 +1465,7 @@ void Render_heatHaze( shaderStage_t *pStage ) } // draw to background image + TransitionMSAAToMain(); R_BindFBO( tr.mainFBO[ 1 - backEnd.currentMainFBO ] ); // bind u_NormalMap @@ -1493,6 +1501,8 @@ void Render_heatHaze( shaderStage_t *pStage ) gl_heatHazeShader->SetUniform_DeformMagnitude( 0.0f ); Tess_DrawElements(); + TransitionMainToMSAA(); + GL_CheckErrors(); } diff --git a/src/engine/sys/sdl_glimp.cpp b/src/engine/sys/sdl_glimp.cpp index f025f97322..b131507138 100644 --- a/src/engine/sys/sdl_glimp.cpp +++ b/src/engine/sys/sdl_glimp.cpp @@ -2075,6 +2075,8 @@ static void GLimp_InitExtensions() glGetIntegerv( GL_MAX_TEXTURE_SIZE, &glConfig.maxTextureSize ); glGetIntegerv( GL_MAX_3D_TEXTURE_SIZE, &glConfig.max3DTextureSize ); glGetIntegerv( GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, &glConfig.maxCubeMapTextureSize ); + glGetIntegerv( GL_MAX_COLOR_TEXTURE_SAMPLES, &glConfig.maxColorTextureSamples ); + glGetIntegerv( GL_MAX_DEPTH_TEXTURE_SAMPLES, &glConfig.maxDepthTextureSamples ); // Stubbed or broken drivers may report garbage. From b2f51b6c32847da608546b82244dd5a8d5a8cf37 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Tue, 2 Sep 2025 15:04:21 +0300 Subject: [PATCH 2/8] R_UploadImage(): early return for MSAA images --- src/engine/renderer/tr_image.cpp | 256 ++++++++++++++++--------------- 1 file changed, 129 insertions(+), 127 deletions(-) diff --git a/src/engine/renderer/tr_image.cpp b/src/engine/renderer/tr_image.cpp index d245d8fd08..3d87ecf326 100644 --- a/src/engine/renderer/tr_image.cpp +++ b/src/engine/renderer/tr_image.cpp @@ -1234,146 +1234,161 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int GL_CheckErrors(); + switch ( image->internalFormat ) { + case GL_RGBA: + case GL_RGBA8: + case GL_RGBA16: + case GL_RGBA16F: + case GL_RGBA32F: + case GL_RGBA32UI: + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + image->bits |= IF_ALPHA; + } + + if ( samples ) { + ASSERT_EQ( scaledBuffer, nullptr ); + + GL_Unbind( image ); + return; + } + // set filter type - if ( !samples ) { - switch ( image->filterType ) { - case filterType_t::FT_DEFAULT: + switch ( image->filterType ) { + case filterType_t::FT_DEFAULT: - // set texture anisotropy - if ( glConfig.textureAnisotropyAvailable ) - { - glTexParameterf( image->type, GL_TEXTURE_MAX_ANISOTROPY_EXT, glConfig.textureAnisotropy ); - } + // set texture anisotropy + if ( glConfig.textureAnisotropyAvailable ) + { + glTexParameterf( image->type, GL_TEXTURE_MAX_ANISOTROPY_EXT, glConfig.textureAnisotropy ); + } - glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, gl_filter_min ); - glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, gl_filter_max ); - break; + glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, gl_filter_min ); + glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, gl_filter_max ); + break; - case filterType_t::FT_LINEAR: - glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - break; + case filterType_t::FT_LINEAR: + glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + break; - case filterType_t::FT_NEAREST: - glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - break; + case filterType_t::FT_NEAREST: + glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + break; - default: - Log::Warn( "unknown filter type for image '%s'", image->name ); - glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - break; - } + default: + Log::Warn( "unknown filter type for image '%s'", image->name ); + glTexParameterf( image->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameterf( image->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + break; } GL_CheckErrors(); // set wrap type - if ( !samples ) { - if ( image->wrapType.s == image->wrapType.t ) { - switch ( image->wrapType.s ) { - case wrapTypeEnum_t::WT_REPEAT: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); - break; + if ( image->wrapType.s == image->wrapType.t ) { + switch ( image->wrapType.s ) { + case wrapTypeEnum_t::WT_REPEAT: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); + break; - case wrapTypeEnum_t::WT_CLAMP: - case wrapTypeEnum_t::WT_EDGE_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - break; - case wrapTypeEnum_t::WT_ONE_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, oneClampBorder ); - break; - case wrapTypeEnum_t::WT_ZERO_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, zeroClampBorder ); - break; + case wrapTypeEnum_t::WT_CLAMP: + case wrapTypeEnum_t::WT_EDGE_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + break; + case wrapTypeEnum_t::WT_ONE_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, oneClampBorder ); + break; + case wrapTypeEnum_t::WT_ZERO_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, zeroClampBorder ); + break; - case wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder ); - break; + case wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder ); + break; - default: - Log::Warn( "unknown wrap type for image '%s'", image->name ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); - break; - } - } else { - // warn about mismatched clamp types if both require a border colour to be set - if ( ( image->wrapType.s == wrapTypeEnum_t::WT_ZERO_CLAMP || image->wrapType.s == wrapTypeEnum_t::WT_ONE_CLAMP || image->wrapType.s == wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP ) && - ( image->wrapType.t == wrapTypeEnum_t::WT_ZERO_CLAMP || image->wrapType.t == wrapTypeEnum_t::WT_ONE_CLAMP || image->wrapType.t == wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP ) ) { - Log::Warn( "mismatched wrap types for image '%s'", image->name ); - } + default: + Log::Warn( "unknown wrap type for image '%s'", image->name ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); + break; + } + } else { + // warn about mismatched clamp types if both require a border colour to be set + if ( ( image->wrapType.s == wrapTypeEnum_t::WT_ZERO_CLAMP || image->wrapType.s == wrapTypeEnum_t::WT_ONE_CLAMP || image->wrapType.s == wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP ) && + ( image->wrapType.t == wrapTypeEnum_t::WT_ZERO_CLAMP || image->wrapType.t == wrapTypeEnum_t::WT_ONE_CLAMP || image->wrapType.t == wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP ) ) { + Log::Warn( "mismatched wrap types for image '%s'", image->name ); + } - switch ( image->wrapType.s ) { - case wrapTypeEnum_t::WT_REPEAT: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); - break; + switch ( image->wrapType.s ) { + case wrapTypeEnum_t::WT_REPEAT: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); + break; - case wrapTypeEnum_t::WT_CLAMP: - case wrapTypeEnum_t::WT_EDGE_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - break; + case wrapTypeEnum_t::WT_CLAMP: + case wrapTypeEnum_t::WT_EDGE_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + break; - case wrapTypeEnum_t::WT_ONE_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, oneClampBorder ); - break; + case wrapTypeEnum_t::WT_ONE_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, oneClampBorder ); + break; - case wrapTypeEnum_t::WT_ZERO_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, zeroClampBorder ); - break; + case wrapTypeEnum_t::WT_ZERO_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, zeroClampBorder ); + break; - case wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder ); - break; + case wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder ); + break; - default: - Log::Warn( "unknown wrap type for image '%s' axis S", image->name ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); - break; - } + default: + Log::Warn( "unknown wrap type for image '%s' axis S", image->name ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_S, GL_REPEAT ); + break; + } - switch ( image->wrapType.t ) { - case wrapTypeEnum_t::WT_REPEAT: - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); - break; + switch ( image->wrapType.t ) { + case wrapTypeEnum_t::WT_REPEAT: + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); + break; - case wrapTypeEnum_t::WT_CLAMP: - case wrapTypeEnum_t::WT_EDGE_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - break; + case wrapTypeEnum_t::WT_CLAMP: + case wrapTypeEnum_t::WT_EDGE_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + break; - case wrapTypeEnum_t::WT_ONE_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, oneClampBorder ); - break; + case wrapTypeEnum_t::WT_ONE_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, oneClampBorder ); + break; - case wrapTypeEnum_t::WT_ZERO_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, zeroClampBorder ); - break; + case wrapTypeEnum_t::WT_ZERO_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, zeroClampBorder ); + break; - case wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP: - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); - glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder ); - break; + case wrapTypeEnum_t::WT_ALPHA_ZERO_CLAMP: + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + glTexParameterfv( image->type, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder ); + break; - default: - Log::Warn( "unknown wrap type for image '%s' axis T", image->name ); - glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); - break; - } + default: + Log::Warn( "unknown wrap type for image '%s' axis T", image->name ); + glTexParameterf( image->type, GL_TEXTURE_WRAP_T, GL_REPEAT ); + break; } } @@ -1384,19 +1399,6 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int ri.Hunk_FreeTempMemory( scaledBuffer ); } - switch ( image->internalFormat ) - { - case GL_RGBA: - case GL_RGBA8: - case GL_RGBA16: - case GL_RGBA16F: - case GL_RGBA32F: - case GL_RGBA32UI: - case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - image->bits |= IF_ALPHA; - } - GL_Unbind( image ); } From bb3dee88461b8f483f2ce93800bc4eb7b1efaf3a Mon Sep 17 00:00:00 2001 From: VReaperV Date: Fri, 5 Sep 2025 15:01:42 +0300 Subject: [PATCH 3/8] MSAA clean-up and fixes --- src/engine/renderer/Material.cpp | 4 ++-- src/engine/renderer/tr_backend.cpp | 36 ++++++++++++++---------------- src/engine/renderer/tr_fbo.cpp | 3 --- src/engine/renderer/tr_local.h | 4 ++-- src/engine/renderer/tr_shade.cpp | 16 ++++++++++--- 5 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/engine/renderer/Material.cpp b/src/engine/renderer/Material.cpp index 19b7834a53..a091109746 100644 --- a/src/engine/renderer/Material.cpp +++ b/src/engine/renderer/Material.cpp @@ -1062,7 +1062,7 @@ void BindShaderHeatHaze( Material* material ) { gl_heatHazeShaderMaterial->SetUniform_DeformEnable( true ); // draw to background image - TransitionMSAAToMain(); + TransitionMSAAToMain( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); R_BindFBO( tr.mainFBO[1 - backEnd.currentMainFBO] ); } @@ -2240,7 +2240,7 @@ void MaterialSystem::RenderMaterial( Material& material, const uint32_t viewID ) RenderIndirect( material, viewID ); - TransitionMainToMSAA(); + TransitionMainToMSAA( GL_COLOR_BUFFER_BIT ); } if ( r_showTris->integer diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 9b9e2ea36c..15bff364b1 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -199,42 +199,36 @@ GLuint64 GL_BindToTMU( int unit, image_t *image ) return 0; } -static void BlitFBOToMSAA( FBO_t* fbo ) { - glState.currentFBO = nullptr; - +static void BlitFBOToMSAA( FBO_t* fbo, const GLbitfield mask ) { R_BindFBO( GL_READ_FRAMEBUFFER, fbo ); R_BindFBO( GL_DRAW_FRAMEBUFFER, tr.msaaFBO ); glBlitFramebuffer( 0, 0, fbo->width, fbo->height, 0, 0, tr.msaaFBO->width, tr.msaaFBO->height, - GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST ); + mask, GL_NEAREST ); - glState.currentFBO = nullptr; R_BindFBO( GL_DRAW_FRAMEBUFFER, fbo ); glState.currentFBO = fbo; } -static void BlitMSAAToFBO( FBO_t* fbo ) { - glState.currentFBO = nullptr; - +static void BlitMSAAToFBO( FBO_t* fbo, const GLbitfield mask ) { R_BindFBO( GL_READ_FRAMEBUFFER, tr.msaaFBO ); R_BindFBO( GL_DRAW_FRAMEBUFFER, fbo ); glBlitFramebuffer( 0, 0, tr.msaaFBO->width, tr.msaaFBO->height, 0, 0, fbo->width, fbo->height, - GL_COLOR_BUFFER_BIT /* | GL_DEPTH_BUFFER_BIT */, GL_NEAREST ); + mask, GL_NEAREST ); - glState.currentFBO = nullptr; R_BindFBO( GL_READ_FRAMEBUFFER, fbo ); glState.currentFBO = fbo; } -void TransitionMainToMSAA() { +void TransitionMainToMSAA( const GLbitfield mask ) { if ( glConfig.MSAA ) { - BlitFBOToMSAA( tr.mainFBO[backEnd.currentMainFBO] ); + BlitFBOToMSAA( tr.mainFBO[backEnd.currentMainFBO], mask ); R_BindFBO( tr.msaaFBO ); } } -void TransitionMSAAToMain() { +void TransitionMSAAToMain( const GLbitfield mask ) { if ( glConfig.MSAA ) { - BlitMSAAToFBO( tr.mainFBO[backEnd.currentMainFBO] ); + BlitMSAAToFBO( tr.mainFBO[backEnd.currentMainFBO], mask ); } } @@ -1309,6 +1303,8 @@ static void RenderDepthTiles() { RB_PrepareForSamplingDepthMap(); } + + TransitionMSAAToMain( GL_DEPTH_BUFFER_BIT ); // 1st step R_BindFBO( tr.depthtile1FBO ); @@ -1500,7 +1496,7 @@ void RB_RenderBloom() GL_BindToTMU( 0, tr.currentRenderImage[backEnd.currentMainFBO] ) ); - TransitionMSAAToMain(); + TransitionMSAAToMain( GL_COLOR_BUFFER_BIT ); R_BindFBO( tr.contrastRenderFBO ); GL_ClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); @@ -1566,7 +1562,7 @@ void RB_RenderBloom() GL_PopMatrix(); } - TransitionMainToMSAA(); + TransitionMainToMSAA( GL_COLOR_BUFFER_BIT ); GL_CheckErrors(); } @@ -1588,7 +1584,7 @@ void RB_RenderMotionBlur() gl_motionblurShader->BindProgram(); - TransitionMSAAToMain(); + TransitionMSAAToMain( GL_COLOR_BUFFER_BIT ); // Swap main FBOs gl_motionblurShader->SetUniform_ColorMapBindless( @@ -1605,7 +1601,7 @@ void RB_RenderMotionBlur() Tess_InstantScreenSpaceQuad(); - TransitionMainToMSAA(); + TransitionMainToMSAA( GL_COLOR_BUFFER_BIT ); GL_CheckErrors(); } @@ -1627,6 +1623,8 @@ void RB_RenderSSAO() RB_PrepareForSamplingDepthMap(); + TransitionMSAAToMain( GL_DEPTH_BUFFER_BIT ); + GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO ); GL_Cull( cullType_t::CT_TWO_SIDED ); @@ -2856,7 +2854,7 @@ static void RB_RenderPostProcess() materialSystem.EndFrame(); } - TransitionMSAAToMain(); + TransitionMSAAToMain( GL_COLOR_BUFFER_BIT ); RB_FXAA(); diff --git a/src/engine/renderer/tr_fbo.cpp b/src/engine/renderer/tr_fbo.cpp index 7087c47e39..06c3a8b89a 100644 --- a/src/engine/renderer/tr_fbo.cpp +++ b/src/engine/renderer/tr_fbo.cpp @@ -280,11 +280,8 @@ void R_InitFBOs() if ( glConfig.MSAA ) { tr.msaaFBO = R_CreateFBO( "msaa", width, height ); R_BindFBO( tr.msaaFBO ); - GL_CheckErrors(); R_AttachFBOTexture2D( GL_TEXTURE_2D_MULTISAMPLE, tr.currentRenderImageMSAA->texnum, 0 ); - GL_CheckErrors(); R_AttachFBOTexturePackedDepthStencilMSAA( tr.currentDepthImageMSAA->texnum ); - GL_CheckErrors(); R_CheckFBO( tr.msaaFBO ); } diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index e26a36ea6a..bc54905438 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -2917,8 +2917,8 @@ inline bool checkGLErrors() GLuint64 GL_BindToTMU( int unit, image_t *image ); void GL_BindNullProgram(); - void TransitionMainToMSAA(); - void TransitionMSAAToMain(); + void TransitionMainToMSAA( const GLbitfield mask ); + void TransitionMSAAToMain( const GLbitfield mask ); void BindMSAAOrMainFBO(); void GL_SetDefaultState(); diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index 255b4ae42f..60b8a723dc 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -891,11 +891,21 @@ void Render_generic3D( shaderStage_t *pStage ) bool hasDepthFade = pStage->hasDepthFade; bool needDepthMap = pStage->hasDepthFade; - if ( needDepthMap ) + const bool needMSAATransion = needDepthMap && backEnd.dirtyDepthBuffer; + + if ( needDepthMap && backEnd.dirtyDepthBuffer && glConfig.textureBarrierAvailable ) { RB_PrepareForSamplingDepthMap(); } + if ( needMSAATransion ) { + TransitionMSAAToMain( GL_DEPTH_BUFFER_BIT ); + + if ( glConfig.MSAA ) { + R_BindFBO( GL_DRAW_FRAMEBUFFER, tr.msaaFBO ); + } + } + // choose right shader program ---------------------------------- ProcessShaderGeneric3D( pStage ); gl_genericShader->BindProgram(); @@ -1465,7 +1475,7 @@ void Render_heatHaze( shaderStage_t *pStage ) } // draw to background image - TransitionMSAAToMain(); + TransitionMSAAToMain( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); R_BindFBO( tr.mainFBO[ 1 - backEnd.currentMainFBO ] ); // bind u_NormalMap @@ -1501,7 +1511,7 @@ void Render_heatHaze( shaderStage_t *pStage ) gl_heatHazeShader->SetUniform_DeformMagnitude( 0.0f ); Tess_DrawElements(); - TransitionMainToMSAA(); + TransitionMainToMSAA( GL_COLOR_BUFFER_BIT ); GL_CheckErrors(); } From d201588f7f051104736afa2336b0eef0e9c6c3b7 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Fri, 26 Dec 2025 10:06:25 +0300 Subject: [PATCH 4/8] Fix fog and transparent surfaces with MSAA --- src/engine/renderer/GLUtils.h | 3 +++ src/engine/renderer/tr_backend.cpp | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/engine/renderer/GLUtils.h b/src/engine/renderer/GLUtils.h index e0561e49b2..9ea2aff90d 100644 --- a/src/engine/renderer/GLUtils.h +++ b/src/engine/renderer/GLUtils.h @@ -78,6 +78,8 @@ struct GLConfig int max3DTextureSize; int maxCubeMapTextureSize; int maxTextureUnits; + int maxColorTextureSamples; + int maxDepthTextureSamples; char shadingLanguageVersionString[MAX_STRING_CHARS]; int shadingLanguageVersion; @@ -154,6 +156,7 @@ struct GLConfig bool reflectionMappingAvailable; bool reflectionMapping; bool bloom; + int MSAA; // 0 == disabled, otherwise used as sample count bool ssao; bool motionBlur; }; diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 15bff364b1..3d6cf31917 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -1623,7 +1623,7 @@ void RB_RenderSSAO() RB_PrepareForSamplingDepthMap(); - TransitionMSAAToMain( GL_DEPTH_BUFFER_BIT ); + TransitionMSAAToMain( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO ); GL_Cull( cullType_t::CT_TWO_SIDED ); @@ -1656,6 +1656,8 @@ void RB_RenderSSAO() Tess_InstantScreenSpaceQuad(); + TransitionMainToMSAA( GL_COLOR_BUFFER_BIT ); + GL_CheckErrors(); } From 7b0743405d81f6982bd9ebe853c819e03067901f Mon Sep 17 00:00:00 2001 From: VReaperV Date: Sun, 4 Jan 2026 03:09:38 +0300 Subject: [PATCH 5/8] MSAA: clean-up FBO bindings --- src/engine/renderer/tr_backend.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 3d6cf31917..8177605ef3 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -202,27 +202,26 @@ GLuint64 GL_BindToTMU( int unit, image_t *image ) static void BlitFBOToMSAA( FBO_t* fbo, const GLbitfield mask ) { R_BindFBO( GL_READ_FRAMEBUFFER, fbo ); R_BindFBO( GL_DRAW_FRAMEBUFFER, tr.msaaFBO ); + glBlitFramebuffer( 0, 0, fbo->width, fbo->height, 0, 0, tr.msaaFBO->width, tr.msaaFBO->height, mask, GL_NEAREST ); - R_BindFBO( GL_DRAW_FRAMEBUFFER, fbo ); - glState.currentFBO = fbo; + R_BindFBO( GL_READ_FRAMEBUFFER, tr.msaaFBO ); } static void BlitMSAAToFBO( FBO_t* fbo, const GLbitfield mask ) { R_BindFBO( GL_READ_FRAMEBUFFER, tr.msaaFBO ); R_BindFBO( GL_DRAW_FRAMEBUFFER, fbo ); + glBlitFramebuffer( 0, 0, tr.msaaFBO->width, tr.msaaFBO->height, 0, 0, fbo->width, fbo->height, mask, GL_NEAREST ); R_BindFBO( GL_READ_FRAMEBUFFER, fbo ); - glState.currentFBO = fbo; } void TransitionMainToMSAA( const GLbitfield mask ) { if ( glConfig.MSAA ) { BlitFBOToMSAA( tr.mainFBO[backEnd.currentMainFBO], mask ); - R_BindFBO( tr.msaaFBO ); } } From 2cdcc8947c79d9e64be2f17b6febbdabe70f91fb Mon Sep 17 00:00:00 2001 From: VReaperV Date: Sun, 4 Jan 2026 03:12:56 +0300 Subject: [PATCH 6/8] Clean-up Render_generic3D() MSAA resolve --- src/engine/renderer/tr_shade.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index 60b8a723dc..062dc9977f 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -891,17 +891,12 @@ void Render_generic3D( shaderStage_t *pStage ) bool hasDepthFade = pStage->hasDepthFade; bool needDepthMap = pStage->hasDepthFade; - const bool needMSAATransion = needDepthMap && backEnd.dirtyDepthBuffer; - - if ( needDepthMap && backEnd.dirtyDepthBuffer && glConfig.textureBarrierAvailable ) + if ( needDepthMap ) { RB_PrepareForSamplingDepthMap(); - } - - if ( needMSAATransion ) { - TransitionMSAAToMain( GL_DEPTH_BUFFER_BIT ); - if ( glConfig.MSAA ) { + if ( glConfig.MSAA && backEnd.dirtyDepthBuffer ) { + TransitionMSAAToMain( GL_DEPTH_BUFFER_BIT ); R_BindFBO( GL_DRAW_FRAMEBUFFER, tr.msaaFBO ); } } From 353b2ce768bb2b799124baa79e731d2acb0b4436 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Sun, 4 Jan 2026 23:00:16 +0300 Subject: [PATCH 7/8] Add a GL version check for MSAA --- src/engine/renderer/tr_shade.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index 062dc9977f..161914f3cf 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -180,11 +180,16 @@ static void EnableAvailableFeatures() } } - glConfig.MSAA = r_msaa.Get(); - const int maxSamples = std::min( glConfig.maxColorTextureSamples, glConfig.maxDepthTextureSamples ); - if ( glConfig.MSAA > maxSamples ) { - Log::Warn( "MSAA samples %i > %i, setting to %i", r_msaa.Get(), maxSamples, maxSamples ); - glConfig.MSAA = maxSamples; + if ( std::make_pair( glConfig.glMajor, glConfig.glMinor ) >= std::make_pair( 3, 2 ) ) { + glConfig.MSAA = r_msaa.Get(); + const int maxSamples = std::min( glConfig.maxColorTextureSamples, glConfig.maxDepthTextureSamples ); + + if ( glConfig.MSAA > maxSamples ) { + Log::Warn( "MSAA samples %i > %i, setting to %i", r_msaa.Get(), maxSamples, maxSamples ); + glConfig.MSAA = maxSamples; + } + } else if ( r_msaa.Get() ) { + Log::Warn( "MSAA unavailable because GL version is lower than required (%i.%i < %i.%i)", glConfig.glMajor, glConfig.glMinor, 3, 2 ); } glConfig.usingMaterialSystem = r_materialSystem.Get() && glConfig.materialSystemAvailable; From 26720f5b10b9540fcdceae1f274e21fdc0fe50e4 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Fri, 9 Jan 2026 04:15:57 +0300 Subject: [PATCH 8/8] Fix MSAA with depth-fade and global fog --- src/engine/renderer/tr_backend.cpp | 9 +++++---- src/engine/renderer/tr_shade.cpp | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 8177605ef3..0430f55462 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -1622,8 +1622,6 @@ void RB_RenderSSAO() RB_PrepareForSamplingDepthMap(); - TransitionMSAAToMain( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO ); GL_Cull( cullType_t::CT_TWO_SIDED ); @@ -1655,8 +1653,6 @@ void RB_RenderSSAO() Tess_InstantScreenSpaceQuad(); - TransitionMainToMSAA( GL_COLOR_BUFFER_BIT ); - GL_CheckErrors(); } @@ -2800,11 +2796,16 @@ static void RB_RenderView( bool depthPass ) RB_RenderDrawSurfaces( shaderSort_t::SS_ENVIRONMENT_FOG, shaderSort_t::SS_OPAQUE, DRAWSURFACES_ALL ); } + // Here so we don't do an extra MSAA resolve if both SSAO and global fog are present + TransitionMSAAToMain( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + RB_RenderSSAO(); // render global fog post process effect RB_RenderGlobalFog(); + TransitionMainToMSAA( GL_COLOR_BUFFER_BIT ); + // draw everything that is translucent if ( glConfig.usingMaterialSystem ) { materialSystem.RenderMaterials( shaderSort_t::SS_ENVIRONMENT_NOFOG, shaderSort_t::SS_POST_PROCESS, backEnd.viewParms.viewID ); diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index 161914f3cf..713934f9d1 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -900,10 +900,7 @@ void Render_generic3D( shaderStage_t *pStage ) { RB_PrepareForSamplingDepthMap(); - if ( glConfig.MSAA && backEnd.dirtyDepthBuffer ) { - TransitionMSAAToMain( GL_DEPTH_BUFFER_BIT ); - R_BindFBO( GL_DRAW_FRAMEBUFFER, tr.msaaFBO ); - } + TransitionMSAAToMain( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); } // choose right shader program ---------------------------------- @@ -992,6 +989,10 @@ void Render_generic3D( shaderStage_t *pStage ) Tess_DrawElements(); + if ( needDepthMap ) { + TransitionMainToMSAA( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + } + GL_CheckErrors(); }