From 0156db0dc1ca225a9e035721e53d53f33cfcb881 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Wed, 6 Mar 2024 22:35:02 -0600 Subject: [PATCH 1/3] Add optional CRT screen effect --- src/cvars.cpp | 1 + src/hwr2/blit_rect.cpp | 174 +++++++++++++++++++++++++++++++---- src/hwr2/blit_rect.hpp | 13 +++ src/hwr2/hardware_state.hpp | 1 + src/i_video_common.cpp | 17 +++- src/menus/options-video-1.c | 3 + src/rhi/gl2/gl2_rhi.cpp | 17 +++- src/rhi/rhi.cpp | 12 +++ src/rhi/rhi.hpp | 12 ++- src/screen.h | 1 + src/sdl/rhi_gl2_platform.cpp | 2 + 11 files changed, 228 insertions(+), 25 deletions(-) diff --git a/src/cvars.cpp b/src/cvars.cpp index 495305827..594b04563 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -430,6 +430,7 @@ consvar_t cv_scr_depth = Player("scr_depth", "16 bits").values({{8, "8 bits"}, { //added : 03-02-98: default screen mode, as loaded/saved in config consvar_t cv_scr_width = Player("scr_width", "640").values(CV_Unsigned); consvar_t cv_scr_height = Player("scr_height", "400").values(CV_Unsigned); +consvar_t cv_scr_effect = Player("scr_effect", "Nearest").values({{0, "Nearest"}, {1, "SalCRT"}}).save(); consvar_t cv_scr_scale = Player("scr_scale", "1.0").floating_point(); consvar_t cv_scr_x = Player("scr_x", "0.0").floating_point(); diff --git a/src/hwr2/blit_rect.cpp b/src/hwr2/blit_rect.cpp index 77c620875..772e8a90e 100644 --- a/src/hwr2/blit_rect.cpp +++ b/src/hwr2/blit_rect.cpp @@ -51,7 +51,22 @@ static const PipelineDesc kUnshadedPipelineDescription = { FaceWinding::kCounterClockwise, {0.f, 0.f, 0.f, 1.f}}; -BlitRectPass::BlitRectPass() = default; +/// @brief Pipeline used for CRT special blit +static const PipelineDesc kCrtPipelineDescription = { + PipelineProgram::kCrt, + {{{sizeof(BlitVertex)}}, {{VertexAttributeName::kPosition, 0, 0}, {VertexAttributeName::kTexCoord0, 0, 12}}}, + {{{UniformName::kProjection}, {{UniformName::kModelView, UniformName::kTexCoord0Transform, UniformName::kSampler0Size}}}}, + {{// RGB/A texture + SamplerName::kSampler0, SamplerName::kSampler1}}, + std::nullopt, + {std::nullopt, {true, true, true, true}}, + PrimitiveType::kTriangles, + CullMode::kNone, + FaceWinding::kCounterClockwise, + {0.f, 0.f, 0.f, 1.f}}; + +BlitRectPass::BlitRectPass() : BlitRectPass(BlitRectPass::BlitMode::kNearest) {} +BlitRectPass::BlitRectPass(BlitRectPass::BlitMode blit_mode) : blit_mode_(blit_mode) {} BlitRectPass::~BlitRectPass() = default; void BlitRectPass::draw(Rhi& rhi, Handle ctx) @@ -65,7 +80,18 @@ void BlitRectPass::prepass(Rhi& rhi) { if (!pipeline_) { - pipeline_ = rhi.create_pipeline(kUnshadedPipelineDescription); + switch (blit_mode_) + { + case BlitRectPass::BlitMode::kNearest: + pipeline_ = rhi.create_pipeline(kUnshadedPipelineDescription); + break; + case BlitRectPass::BlitMode::kCrt: + pipeline_ = rhi.create_pipeline(kCrtPipelineDescription); + break; + default: + std::terminate(); + } + } if (!quad_vbo_) @@ -79,6 +105,20 @@ void BlitRectPass::prepass(Rhi& rhi) quad_ibo_ = rhi.create_buffer({sizeof(kIndices), BufferType::kIndexBuffer, BufferUsage::kImmutable}); quad_ibo_needs_upload_ = true; } + + if (blit_mode_ == BlitRectPass::BlitMode::kCrt && !dot_pattern_) + { + dot_pattern_ = rhi.create_texture({ + rhi::TextureFormat::kRGBA, + 12, + 4, + rhi::TextureWrapMode::kRepeat, + rhi::TextureWrapMode::kRepeat, + rhi::TextureFilterMode::kLinear, + rhi::TextureFilterMode::kLinear + }); + dot_pattern_needs_upload_ = true; + } } void BlitRectPass::transfer(Rhi& rhi, Handle ctx) @@ -95,6 +135,67 @@ void BlitRectPass::transfer(Rhi& rhi, Handle ctx) quad_ibo_needs_upload_ = false; } + if (dot_pattern_needs_upload_ && dot_pattern_) + { + // Listen. I'm a *very* particular kind of lazy. + // If I'm being honest, I just don't want to have to embed a .png in the pk3s and deal with that. + static const uint8_t kDotPattern[] = { + 255, 0, 0, 255, + 0, 0, 0, 255, + 0, 255, 0, 255, + 0, 0, 0, 255, + 0, 0, 255, 255, + 0, 0, 0, 255, + 255, 0, 0, 255, + 0, 0, 0, 255, + 0, 255, 0, 255, + 0, 0, 0, 255, + 0, 0, 255, 255, + 0, 0, 0, 255, + + 255, 0, 0, 255, + 0, 0, 0, 255, + 0, 255, 0, 255, + 0, 0, 0, 255, + 0, 0, 255, 255, + 0, 0, 0, 255, + 0, 0, 0, 255, + 0, 0, 0, 255, + 0, 0, 0, 255, + 0, 0, 0, 255, + 0, 0, 0, 255, + 0, 0, 0, 255, + + 255, 0, 0, 255, + 0, 0, 0, 255, + 0, 255, 0, 255, + 0, 0, 0, 255, + 0, 0, 255, 255, + 0, 0, 0, 255, + 255, 0, 0, 255, + 0, 0, 0, 255, + 0, 255, 0, 255, + 0, 0, 0, 255, + 0, 0, 255, 255, + 0, 0, 0, 255, + + 0, 0, 0, 255, + 0, 0, 0, 255, + 0, 0, 0, 255, + 0, 0, 0, 255, + 0, 0, 0, 255, + 0, 0, 0, 255, + 255, 0, 0, 255, + 0, 0, 0, 255, + 0, 255, 0, 255, + 0, 0, 0, 255, + 0, 0, 255, 255, + 0, 0, 0, 255, + }; + rhi.update_texture(ctx, dot_pattern_, {0, 0, 12, 4}, PixelFormat::kRGBA8, tcb::as_bytes(tcb::span(kDotPattern))); + dot_pattern_needs_upload_ = false; + } + float aspect = 1.0; float output_aspect = 1.0; if (output_correct_aspect_) @@ -104,6 +205,8 @@ void BlitRectPass::transfer(Rhi& rhi, Handle ctx) } bool taller = aspect > output_aspect; + rhi::TextureDetails texture_details = rhi.get_texture_details(texture_); + std::array g1_uniforms = {{ // Projection glm::scale( @@ -112,26 +215,57 @@ void BlitRectPass::transfer(Rhi& rhi, Handle ctx) ) }}; - std::array g2_uniforms = { - // ModelView - glm::scale( - glm::identity(), - glm::vec3(taller ? 2.f : 2.f * aspect, taller ? 2.f * (1.f / aspect) : 2.f, 1.f) - ), - // Texcoord0 Transform - glm::mat3( - glm::vec3(1.f, 0.f, 0.f), - glm::vec3(0.f, output_flip_ ? -1.f : 1.f, 0.f), - glm::vec3(0.f, output_flip_ ? 1.f : 0.f, 1.f) - ) - }; - uniform_sets_[0] = rhi.create_uniform_set(ctx, {g1_uniforms}); - uniform_sets_[1] = rhi.create_uniform_set(ctx, {g2_uniforms}); - std::array vbs = {{{0, quad_vbo_}}}; - std::array tbs = {{{rhi::SamplerName::kSampler0, texture_}}}; - binding_set_ = rhi.create_binding_set(ctx, pipeline_, {vbs, tbs}); + switch (blit_mode_) + { + case BlitRectPass::BlitMode::kCrt: + { + std::array g2_uniforms = { + // ModelView + glm::scale( + glm::identity(), + glm::vec3(taller ? 2.f : 2.f * aspect, taller ? 2.f * (1.f / aspect) : 2.f, 1.f) + ), + // Texcoord0 Transform + glm::mat3( + glm::vec3(1.f, 0.f, 0.f), + glm::vec3(0.f, output_flip_ ? -1.f : 1.f, 0.f), + glm::vec3(0.f, output_flip_ ? 1.f : 0.f, 1.f) + ), + // Sampler 0 Size + glm::vec2(texture_details.width, texture_details.height) + }; + uniform_sets_[1] = rhi.create_uniform_set(ctx, {g2_uniforms}); + + std::array vbs = {{{0, quad_vbo_}}}; + std::array tbs = {{{rhi::SamplerName::kSampler0, texture_}, {rhi::SamplerName::kSampler1, dot_pattern_}}}; + binding_set_ = rhi.create_binding_set(ctx, pipeline_, {vbs, tbs}); + break; + } + default: + { + std::array g2_uniforms = { + // ModelView + glm::scale( + glm::identity(), + glm::vec3(taller ? 2.f : 2.f * aspect, taller ? 2.f * (1.f / aspect) : 2.f, 1.f) + ), + // Texcoord0 Transform + glm::mat3( + glm::vec3(1.f, 0.f, 0.f), + glm::vec3(0.f, output_flip_ ? -1.f : 1.f, 0.f), + glm::vec3(0.f, output_flip_ ? 1.f : 0.f, 1.f) + ) + }; + uniform_sets_[1] = rhi.create_uniform_set(ctx, {g2_uniforms}); + + std::array vbs = {{{0, quad_vbo_}}}; + std::array tbs = {{{rhi::SamplerName::kSampler0, texture_}}}; + binding_set_ = rhi.create_binding_set(ctx, pipeline_, {vbs, tbs}); + break; + } + } } void BlitRectPass::graphics(Rhi& rhi, Handle ctx) diff --git a/src/hwr2/blit_rect.hpp b/src/hwr2/blit_rect.hpp index f2acd9887..3aff33261 100644 --- a/src/hwr2/blit_rect.hpp +++ b/src/hwr2/blit_rect.hpp @@ -21,6 +21,14 @@ namespace srb2::hwr2 /// @brief A render pass which blits a rect using a source texture or textures. class BlitRectPass { +public: + enum class BlitMode + { + kNearest, + kCrt, + }; + +private: rhi::Handle pipeline_; rhi::Handle texture_; uint32_t texture_width_ = 0; @@ -33,15 +41,20 @@ class BlitRectPass rhi::Handle quad_ibo_; std::array, 2> uniform_sets_; rhi::Handle binding_set_; + BlitMode blit_mode_; + rhi::Handle dot_pattern_; bool quad_vbo_needs_upload_ = false; bool quad_ibo_needs_upload_ = false; + bool dot_pattern_needs_upload_ = false; void prepass(rhi::Rhi& rhi); void transfer(rhi::Rhi& rhi, rhi::Handle ctx); void graphics(rhi::Rhi& rhi, rhi::Handle ctx); public: + + explicit BlitRectPass(BlitMode blit_mode); BlitRectPass(); ~BlitRectPass(); diff --git a/src/hwr2/hardware_state.hpp b/src/hwr2/hardware_state.hpp index 86849f60c..d32f54d4f 100644 --- a/src/hwr2/hardware_state.hpp +++ b/src/hwr2/hardware_state.hpp @@ -38,6 +38,7 @@ struct HardwareState std::unique_ptr blit_postimg_screens; std::unique_ptr wipe; std::unique_ptr blit_rect; + std::unique_ptr crt_blit_rect; std::unique_ptr screen_capture; std::unique_ptr backbuffer; WipeFrames wipe_frames; diff --git a/src/i_video_common.cpp b/src/i_video_common.cpp index 84241491a..5f02430eb 100644 --- a/src/i_video_common.cpp +++ b/src/i_video_common.cpp @@ -82,7 +82,8 @@ static void reset_hardware_state(Rhi* rhi) g_hw_state.software_screen_renderer = std::make_unique(); g_hw_state.blit_postimg_screens = std::make_unique(g_hw_state.palette_manager.get()); g_hw_state.wipe = std::make_unique(); - g_hw_state.blit_rect = std::make_unique(); + g_hw_state.blit_rect = std::make_unique(BlitRectPass::BlitMode::kNearest); + g_hw_state.crt_blit_rect = std::make_unique(BlitRectPass::BlitMode::kCrt); g_hw_state.screen_capture = std::make_unique(); g_hw_state.backbuffer = std::make_unique(); g_hw_state.wipe_frames = {}; @@ -294,13 +295,25 @@ void I_FinishUpdate(void) float y = (vid.realheight - h) * (0.5f + (FixedToFloat(cv_scr_y.value) * 0.5f)); g_hw_state.blit_rect->set_output(x, y, w, h, true, true); + g_hw_state.crt_blit_rect->set_output(x, y, w, h, true, true); } else { g_hw_state.blit_rect->set_output(0, 0, vid.realwidth, vid.realheight, true, true); + g_hw_state.crt_blit_rect->set_output(0, 0, vid.realwidth, vid.realheight, true, true); } g_hw_state.blit_rect->set_texture(g_hw_state.backbuffer->color(), static_cast(vid.width), static_cast(vid.height)); - g_hw_state.blit_rect->draw(*rhi, ctx); + g_hw_state.crt_blit_rect->set_texture(g_hw_state.backbuffer->color(), static_cast(vid.width), static_cast(vid.height)); + + switch (cv_scr_effect.value) + { + case 1: + g_hw_state.crt_blit_rect->draw(*rhi, ctx); + break; + default: + g_hw_state.blit_rect->draw(*rhi, ctx); + break; + } rhi->end_render_pass(ctx); rhi->end_graphics(ctx); diff --git a/src/menus/options-video-1.c b/src/menus/options-video-1.c index a1e1dcecc..b4d2bb0f9 100644 --- a/src/menus/options-video-1.c +++ b/src/menus/options-video-1.c @@ -27,6 +27,9 @@ menuitem_t OPTIONS_Video[] = {IT_STRING | IT_CVAR, "FPS Cap", "Handles the frame rate of the game (35 to match game logic)", NULL, {.cvar = &cv_fpscap}, 0, 0}, + {IT_STRING | IT_CVAR, "Screen Effect", "Uses a special effect when displaying the game.", + NULL, {.cvar = &cv_scr_effect}, 0, 0}, + {IT_NOTHING|IT_SPACE, NULL, NULL, NULL, {NULL}, 0, 0}, diff --git a/src/rhi/gl2/gl2_rhi.cpp b/src/rhi/gl2/gl2_rhi.cpp index 27d882ecc..6568c422f 100644 --- a/src/rhi/gl2/gl2_rhi.cpp +++ b/src/rhi/gl2/gl2_rhi.cpp @@ -127,6 +127,19 @@ constexpr GLenum map_texture_wrap(rhi::TextureWrapMode wrap) return GL_REPEAT; case rhi::TextureWrapMode::kMirroredRepeat: return GL_MIRRORED_REPEAT; + default: + return GL_REPEAT; + } +} + +constexpr GLenum map_texture_filter(rhi::TextureFilterMode filter) +{ + switch (filter) + { + case rhi::TextureFilterMode::kNearest: + return GL_NEAREST; + case rhi::TextureFilterMode::kLinear: + return GL_LINEAR; default: return GL_NEAREST; } @@ -595,9 +608,9 @@ rhi::Handle Gl2Rhi::create_texture(const rhi::TextureDesc& desc) gl_->BindTexture(GL_TEXTURE_2D, name); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, map_texture_filter(desc.min)); GL_ASSERT; - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, map_texture_filter(desc.mag)); GL_ASSERT; gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, map_texture_wrap(desc.u_wrap)); GL_ASSERT; diff --git a/src/rhi/rhi.cpp b/src/rhi/rhi.cpp index 76668af83..7e9cb725a 100644 --- a/src/rhi/rhi.cpp +++ b/src/rhi/rhi.cpp @@ -67,6 +67,16 @@ const ProgramRequirements srb2::rhi::kProgramRequirementsPostimg = { ProgramSamplerRequirements {{{SamplerName::kSampler0, true}, {SamplerName::kSampler1, false}}} }; +const ProgramRequirements srb2::rhi::kProgramRequirementsCrt = { + ProgramVertexInputRequirements { + {ProgramVertexInput {VertexAttributeName::kPosition, VertexAttributeFormat::kFloat3, true}, + ProgramVertexInput {VertexAttributeName::kTexCoord0, VertexAttributeFormat::kFloat2, false}, + ProgramVertexInput {VertexAttributeName::kColor, VertexAttributeFormat::kFloat4, false}}}, + ProgramUniformRequirements { + {{{{UniformName::kProjection, true}}}, + {{{UniformName::kModelView, true}, {UniformName::kTexCoord0Transform, true}, {UniformName::kSampler0Size, true}}}}}, + ProgramSamplerRequirements {{{SamplerName::kSampler0, true}, {SamplerName::kSampler1, true}}}}; + const ProgramRequirements& rhi::program_requirements_for_program(PipelineProgram program) noexcept { switch (program) @@ -79,6 +89,8 @@ const ProgramRequirements& rhi::program_requirements_for_program(PipelineProgram return kProgramRequirementsPostprocessWipe; case PipelineProgram::kPostimg: return kProgramRequirementsPostimg; + case PipelineProgram::kCrt: + return kProgramRequirementsCrt; default: std::terminate(); } diff --git a/src/rhi/rhi.hpp b/src/rhi/rhi.hpp index c094cf990..27142e3ed 100644 --- a/src/rhi/rhi.hpp +++ b/src/rhi/rhi.hpp @@ -172,7 +172,8 @@ enum class PipelineProgram kUnshaded, kUnshadedPaletted, kPostprocessWipe, - kPostimg + kPostimg, + kCrt }; enum class BufferType @@ -285,6 +286,7 @@ extern const ProgramRequirements kProgramRequirementsUnshaded; extern const ProgramRequirements kProgramRequirementsUnshadedPaletted; extern const ProgramRequirements kProgramRequirementsPostprocessWipe; extern const ProgramRequirements kProgramRequirementsPostimg; +extern const ProgramRequirements kProgramRequirementsCrt; const ProgramRequirements& program_requirements_for_program(PipelineProgram program) noexcept; @@ -480,6 +482,12 @@ enum class TextureWrapMode kClamp }; +enum class TextureFilterMode +{ + kNearest, + kLinear +}; + struct TextureDesc { TextureFormat format; @@ -487,6 +495,8 @@ struct TextureDesc uint32_t height; TextureWrapMode u_wrap; TextureWrapMode v_wrap; + TextureFilterMode mag; + TextureFilterMode min; }; struct BufferDesc diff --git a/src/screen.h b/src/screen.h index cbfe01724..ee8f4a204 100644 --- a/src/screen.h +++ b/src/screen.h @@ -135,6 +135,7 @@ extern INT32 scr_bpp; extern UINT8 *scr_borderpatch; // patch used to fill the view borders extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_renderhitbox, cv_fullscreen; +extern consvar_t cv_scr_effect; extern consvar_t cv_vhseffect, cv_shittyscreen; extern consvar_t cv_parallelsoftware; diff --git a/src/sdl/rhi_gl2_platform.cpp b/src/sdl/rhi_gl2_platform.cpp index bc6e0d2c1..03833dd7f 100644 --- a/src/sdl/rhi_gl2_platform.cpp +++ b/src/sdl/rhi_gl2_platform.cpp @@ -44,6 +44,8 @@ static constexpr const char* pipeline_lump_slug(rhi::PipelineProgram program) return "postprocesswipe"; case rhi::PipelineProgram::kPostimg: return "postimg"; + case rhi::PipelineProgram::kCrt: + return "crt"; default: return ""; } From 123477dac978188dc2621cafbe3a5bf799565262 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 7 Mar 2024 22:03:36 -0600 Subject: [PATCH 2/3] Add Sharp Bilinear mode, make default --- src/cvars.cpp | 2 +- src/hwr2/blit_rect.cpp | 41 ++++++++++++++++++++++++++++++++++++ src/hwr2/blit_rect.hpp | 1 + src/hwr2/hardware_state.hpp | 1 + src/i_video_common.cpp | 10 +++++++++ src/rhi/gl2/gl2_rhi.cpp | 28 ++++++++++++++++++++++++ src/rhi/gl2/gl2_rhi.hpp | 8 +++++++ src/rhi/rhi.cpp | 12 +++++++++++ src/rhi/rhi.hpp | 12 ++++++++++- src/sdl/rhi_gl2_platform.cpp | 2 ++ 10 files changed, 115 insertions(+), 2 deletions(-) diff --git a/src/cvars.cpp b/src/cvars.cpp index 594b04563..11a2b9378 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -430,7 +430,7 @@ consvar_t cv_scr_depth = Player("scr_depth", "16 bits").values({{8, "8 bits"}, { //added : 03-02-98: default screen mode, as loaded/saved in config consvar_t cv_scr_width = Player("scr_width", "640").values(CV_Unsigned); consvar_t cv_scr_height = Player("scr_height", "400").values(CV_Unsigned); -consvar_t cv_scr_effect = Player("scr_effect", "Nearest").values({{0, "Nearest"}, {1, "SalCRT"}}).save(); +consvar_t cv_scr_effect = Player("scr_effect", "Sharp Bilinear").values({{0, "Nearest"}, {1, "Sharp Bilinear"}, {2, "SalCRT"}}).save(); consvar_t cv_scr_scale = Player("scr_scale", "1.0").floating_point(); consvar_t cv_scr_x = Player("scr_x", "0.0").floating_point(); diff --git a/src/hwr2/blit_rect.cpp b/src/hwr2/blit_rect.cpp index 772e8a90e..41290604c 100644 --- a/src/hwr2/blit_rect.cpp +++ b/src/hwr2/blit_rect.cpp @@ -51,6 +51,20 @@ static const PipelineDesc kUnshadedPipelineDescription = { FaceWinding::kCounterClockwise, {0.f, 0.f, 0.f, 1.f}}; +/// @brief Pipeline used for sharp bilinear special blit. +static const PipelineDesc kSharpBilinearPipelineDescription = { + PipelineProgram::kSharpBilinear, + {{{sizeof(BlitVertex)}}, {{VertexAttributeName::kPosition, 0, 0}, {VertexAttributeName::kTexCoord0, 0, 12}}}, + {{{UniformName::kProjection}, {{UniformName::kModelView, UniformName::kTexCoord0Transform, UniformName::kSampler0Size}}}}, + {{// RGB/A texture + SamplerName::kSampler0}}, + std::nullopt, + {std::nullopt, {true, true, true, true}}, + PrimitiveType::kTriangles, + CullMode::kNone, + FaceWinding::kCounterClockwise, + {0.f, 0.f, 0.f, 1.f}}; + /// @brief Pipeline used for CRT special blit static const PipelineDesc kCrtPipelineDescription = { PipelineProgram::kCrt, @@ -85,6 +99,9 @@ void BlitRectPass::prepass(Rhi& rhi) case BlitRectPass::BlitMode::kNearest: pipeline_ = rhi.create_pipeline(kUnshadedPipelineDescription); break; + case BlitRectPass::BlitMode::kSharpBilinear: + pipeline_ = rhi.create_pipeline(kSharpBilinearPipelineDescription); + break; case BlitRectPass::BlitMode::kCrt: pipeline_ = rhi.create_pipeline(kCrtPipelineDescription); break; @@ -243,6 +260,30 @@ void BlitRectPass::transfer(Rhi& rhi, Handle ctx) binding_set_ = rhi.create_binding_set(ctx, pipeline_, {vbs, tbs}); break; } + case BlitRectPass::BlitMode::kSharpBilinear: + { + std::array g2_uniforms = { + // ModelView + glm::scale( + glm::identity(), + glm::vec3(taller ? 2.f : 2.f * aspect, taller ? 2.f * (1.f / aspect) : 2.f, 1.f) + ), + // Texcoord0 Transform + glm::mat3( + glm::vec3(1.f, 0.f, 0.f), + glm::vec3(0.f, output_flip_ ? -1.f : 1.f, 0.f), + glm::vec3(0.f, output_flip_ ? 1.f : 0.f, 1.f) + ), + // Sampler0 size + glm::vec2(texture_details.width, texture_details.height) + }; + uniform_sets_[1] = rhi.create_uniform_set(ctx, {g2_uniforms}); + + std::array vbs = {{{0, quad_vbo_}}}; + std::array tbs = {{{rhi::SamplerName::kSampler0, texture_}}}; + binding_set_ = rhi.create_binding_set(ctx, pipeline_, {vbs, tbs}); + break; + } default: { std::array g2_uniforms = { diff --git a/src/hwr2/blit_rect.hpp b/src/hwr2/blit_rect.hpp index 3aff33261..ba256e6ee 100644 --- a/src/hwr2/blit_rect.hpp +++ b/src/hwr2/blit_rect.hpp @@ -25,6 +25,7 @@ public: enum class BlitMode { kNearest, + kSharpBilinear, kCrt, }; diff --git a/src/hwr2/hardware_state.hpp b/src/hwr2/hardware_state.hpp index d32f54d4f..fa8871f0e 100644 --- a/src/hwr2/hardware_state.hpp +++ b/src/hwr2/hardware_state.hpp @@ -38,6 +38,7 @@ struct HardwareState std::unique_ptr blit_postimg_screens; std::unique_ptr wipe; std::unique_ptr blit_rect; + std::unique_ptr sharp_bilinear_blit_rect; std::unique_ptr crt_blit_rect; std::unique_ptr screen_capture; std::unique_ptr backbuffer; diff --git a/src/i_video_common.cpp b/src/i_video_common.cpp index 5f02430eb..5ed0cb7aa 100644 --- a/src/i_video_common.cpp +++ b/src/i_video_common.cpp @@ -83,6 +83,7 @@ static void reset_hardware_state(Rhi* rhi) g_hw_state.blit_postimg_screens = std::make_unique(g_hw_state.palette_manager.get()); g_hw_state.wipe = std::make_unique(); g_hw_state.blit_rect = std::make_unique(BlitRectPass::BlitMode::kNearest); + g_hw_state.sharp_bilinear_blit_rect = std::make_unique(BlitRectPass::BlitMode::kSharpBilinear); g_hw_state.crt_blit_rect = std::make_unique(BlitRectPass::BlitMode::kCrt); g_hw_state.screen_capture = std::make_unique(); g_hw_state.backbuffer = std::make_unique(); @@ -295,22 +296,31 @@ void I_FinishUpdate(void) float y = (vid.realheight - h) * (0.5f + (FixedToFloat(cv_scr_y.value) * 0.5f)); g_hw_state.blit_rect->set_output(x, y, w, h, true, true); + g_hw_state.sharp_bilinear_blit_rect->set_output(x, y, w, h, true, true); g_hw_state.crt_blit_rect->set_output(x, y, w, h, true, true); } else { g_hw_state.blit_rect->set_output(0, 0, vid.realwidth, vid.realheight, true, true); + g_hw_state.sharp_bilinear_blit_rect->set_output(0, 0, vid.realwidth, vid.realheight, true, true); g_hw_state.crt_blit_rect->set_output(0, 0, vid.realwidth, vid.realheight, true, true); } g_hw_state.blit_rect->set_texture(g_hw_state.backbuffer->color(), static_cast(vid.width), static_cast(vid.height)); + g_hw_state.sharp_bilinear_blit_rect->set_texture(g_hw_state.backbuffer->color(), static_cast(vid.width), static_cast(vid.height)); g_hw_state.crt_blit_rect->set_texture(g_hw_state.backbuffer->color(), static_cast(vid.width), static_cast(vid.height)); switch (cv_scr_effect.value) { case 1: + rhi->update_texture_settings(ctx, g_hw_state.backbuffer->color(), TextureWrapMode::kClamp, TextureWrapMode::kClamp, TextureFilterMode::kLinear, TextureFilterMode::kLinear); + g_hw_state.sharp_bilinear_blit_rect->draw(*rhi, ctx); + break; + case 2: + rhi->update_texture_settings(ctx, g_hw_state.backbuffer->color(), TextureWrapMode::kClamp, TextureWrapMode::kClamp, TextureFilterMode::kNearest, TextureFilterMode::kNearest); g_hw_state.crt_blit_rect->draw(*rhi, ctx); break; default: + rhi->update_texture_settings(ctx, g_hw_state.backbuffer->color(), TextureWrapMode::kClamp, TextureWrapMode::kClamp, TextureFilterMode::kNearest, TextureFilterMode::kNearest); g_hw_state.blit_rect->draw(*rhi, ctx); break; } diff --git a/src/rhi/gl2/gl2_rhi.cpp b/src/rhi/gl2/gl2_rhi.cpp index 6568c422f..b73da22a9 100644 --- a/src/rhi/gl2/gl2_rhi.cpp +++ b/src/rhi/gl2/gl2_rhi.cpp @@ -685,6 +685,34 @@ void Gl2Rhi::update_texture( GL_ASSERT; } +void Gl2Rhi::update_texture_settings( + Handle ctx, + Handle texture, + TextureWrapMode u_wrap, + TextureWrapMode v_wrap, + TextureFilterMode min, + TextureFilterMode mag +) +{ + SRB2_ASSERT(graphics_context_active_ == true); + + SRB2_ASSERT(texture_slab_.is_valid(texture) == true); + auto& t = texture_slab_[texture]; + + gl_->ActiveTexture(GL_TEXTURE0); + GL_ASSERT; + gl_->BindTexture(GL_TEXTURE_2D, t.texture); + GL_ASSERT; + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, map_texture_wrap(u_wrap)); + GL_ASSERT; + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, map_texture_wrap(v_wrap)); + GL_ASSERT; + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, map_texture_filter(min)); + GL_ASSERT; + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, map_texture_filter(mag)); + GL_ASSERT; +} + rhi::Handle Gl2Rhi::create_buffer(const rhi::BufferDesc& desc) { GLenum target = map_buffer_type(desc.type); diff --git a/src/rhi/gl2/gl2_rhi.hpp b/src/rhi/gl2/gl2_rhi.hpp index f530d49d9..5dc54cc7a 100644 --- a/src/rhi/gl2/gl2_rhi.hpp +++ b/src/rhi/gl2/gl2_rhi.hpp @@ -198,6 +198,14 @@ public: srb2::rhi::PixelFormat data_format, tcb::span data ) override; + virtual void update_texture_settings( + Handle ctx, + Handle texture, + TextureWrapMode u_wrap, + TextureWrapMode v_wrap, + TextureFilterMode min, + TextureFilterMode mag + ) override; virtual Handle create_uniform_set(Handle ctx, const CreateUniformSetInfo& info) override; virtual Handle diff --git a/src/rhi/rhi.cpp b/src/rhi/rhi.cpp index 7e9cb725a..6813122bb 100644 --- a/src/rhi/rhi.cpp +++ b/src/rhi/rhi.cpp @@ -67,6 +67,16 @@ const ProgramRequirements srb2::rhi::kProgramRequirementsPostimg = { ProgramSamplerRequirements {{{SamplerName::kSampler0, true}, {SamplerName::kSampler1, false}}} }; +const ProgramRequirements srb2::rhi::kProgramRequirementsSharpBilinear = { + ProgramVertexInputRequirements { + {ProgramVertexInput {VertexAttributeName::kPosition, VertexAttributeFormat::kFloat3, true}, + ProgramVertexInput {VertexAttributeName::kTexCoord0, VertexAttributeFormat::kFloat2, false}, + ProgramVertexInput {VertexAttributeName::kColor, VertexAttributeFormat::kFloat4, false}}}, + ProgramUniformRequirements { + {{{{UniformName::kProjection, true}}}, + {{{UniformName::kModelView, true}, {UniformName::kTexCoord0Transform, true}, {UniformName::kSampler0Size, true}}}}}, + ProgramSamplerRequirements {{{SamplerName::kSampler0, true}}}}; + const ProgramRequirements srb2::rhi::kProgramRequirementsCrt = { ProgramVertexInputRequirements { {ProgramVertexInput {VertexAttributeName::kPosition, VertexAttributeFormat::kFloat3, true}, @@ -89,6 +99,8 @@ const ProgramRequirements& rhi::program_requirements_for_program(PipelineProgram return kProgramRequirementsPostprocessWipe; case PipelineProgram::kPostimg: return kProgramRequirementsPostimg; + case PipelineProgram::kSharpBilinear: + return kProgramRequirementsSharpBilinear; case PipelineProgram::kCrt: return kProgramRequirementsCrt; default: diff --git a/src/rhi/rhi.hpp b/src/rhi/rhi.hpp index 27142e3ed..2b42a6cca 100644 --- a/src/rhi/rhi.hpp +++ b/src/rhi/rhi.hpp @@ -173,6 +173,7 @@ enum class PipelineProgram kUnshadedPaletted, kPostprocessWipe, kPostimg, + kSharpBilinear, kCrt }; @@ -286,6 +287,7 @@ extern const ProgramRequirements kProgramRequirementsUnshaded; extern const ProgramRequirements kProgramRequirementsUnshadedPaletted; extern const ProgramRequirements kProgramRequirementsPostprocessWipe; extern const ProgramRequirements kProgramRequirementsPostimg; +extern const ProgramRequirements kProgramRequirementsSharpBilinear; extern const ProgramRequirements kProgramRequirementsCrt; const ProgramRequirements& program_requirements_for_program(PipelineProgram program) noexcept; @@ -495,8 +497,8 @@ struct TextureDesc uint32_t height; TextureWrapMode u_wrap; TextureWrapMode v_wrap; - TextureFilterMode mag; TextureFilterMode min; + TextureFilterMode mag; }; struct BufferDesc @@ -628,6 +630,14 @@ struct Rhi srb2::rhi::PixelFormat data_format, tcb::span data ) = 0; + virtual void update_texture_settings( + Handle ctx, + Handle texture, + TextureWrapMode u_wrap, + TextureWrapMode v_wrap, + TextureFilterMode min, + TextureFilterMode mag + ) = 0; virtual Handle create_uniform_set(Handle ctx, const CreateUniformSetInfo& info) = 0; virtual Handle create_binding_set(Handle ctx, Handle pipeline, const CreateBindingSetInfo& info) = 0; diff --git a/src/sdl/rhi_gl2_platform.cpp b/src/sdl/rhi_gl2_platform.cpp index 03833dd7f..47e6203ff 100644 --- a/src/sdl/rhi_gl2_platform.cpp +++ b/src/sdl/rhi_gl2_platform.cpp @@ -44,6 +44,8 @@ static constexpr const char* pipeline_lump_slug(rhi::PipelineProgram program) return "postprocesswipe"; case rhi::PipelineProgram::kPostimg: return "postimg"; + case rhi::PipelineProgram::kSharpBilinear: + return "sharpbilinear"; case rhi::PipelineProgram::kCrt: return "crt"; default: From 2e0fb999b380f741e770234f43d545283984c7d1 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 7 Mar 2024 23:25:47 -0600 Subject: [PATCH 3/3] Use bilinear filtering for CRT shader --- src/i_video_common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i_video_common.cpp b/src/i_video_common.cpp index 5ed0cb7aa..f357fb178 100644 --- a/src/i_video_common.cpp +++ b/src/i_video_common.cpp @@ -316,7 +316,7 @@ void I_FinishUpdate(void) g_hw_state.sharp_bilinear_blit_rect->draw(*rhi, ctx); break; case 2: - rhi->update_texture_settings(ctx, g_hw_state.backbuffer->color(), TextureWrapMode::kClamp, TextureWrapMode::kClamp, TextureFilterMode::kNearest, TextureFilterMode::kNearest); + rhi->update_texture_settings(ctx, g_hw_state.backbuffer->color(), TextureWrapMode::kClamp, TextureWrapMode::kClamp, TextureFilterMode::kLinear, TextureFilterMode::kLinear); g_hw_state.crt_blit_rect->draw(*rhi, ctx); break; default: