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: