From 20002f83c445b9eba79f75c0a9d2a2fcf71e316d Mon Sep 17 00:00:00 2001 From: Eidolon Date: Fri, 19 May 2023 18:29:36 -0500 Subject: [PATCH 1/5] rhi: Make some stencil state dynamic The reference, compare mask and write mask for each face direction in the stencil test is now dynamic pipeline state and are implicitly set to default values when a pipeline is bound. This is implementable using Vulkan dynamic pipeline state bits and so there is no reason not to provide it. In the OpenGL implementation of RHI, this requires tracking some stencil state internally in the graphics context because the stencil state functions require multiple inputs that do not cleanly map to the Vulkan equivalents. --- src/rhi/gl3_core/gl3_core_rhi.cpp | 99 +++++++++++++++++++++++++++++-- src/rhi/gl3_core/gl3_core_rhi.hpp | 10 ++++ src/rhi/rhi.hpp | 6 +- 3 files changed, 106 insertions(+), 9 deletions(-) diff --git a/src/rhi/gl3_core/gl3_core_rhi.cpp b/src/rhi/gl3_core/gl3_core_rhi.cpp index 18985ac43..eab28f852 100644 --- a/src/rhi/gl3_core/gl3_core_rhi.cpp +++ b/src/rhi/gl3_core/gl3_core_rhi.cpp @@ -1414,26 +1414,32 @@ void GlCoreRhi::bind_pipeline(Handle ctx, Handle pipe if (desc.depth_stencil_state->stencil_test) { gl_->Enable(GL_STENCIL_TEST); + stencil_front_reference_ = 0; + stencil_back_reference_ = 0; + stencil_front_compare_mask_ = 0xFF; + stencil_back_compare_mask_ = 0xFF; + stencil_front_write_mask_ = 0xFF; + stencil_back_write_mask_ = 0xFF; GL_ASSERT; gl_->StencilFuncSeparate( GL_FRONT, map_compare_func(desc.depth_stencil_state->front.stencil_compare), - desc.depth_stencil_state->front.reference, - desc.depth_stencil_state->front.compare_mask + stencil_front_reference_, + stencil_front_compare_mask_ ); GL_ASSERT; gl_->StencilFuncSeparate( GL_BACK, map_compare_func(desc.depth_stencil_state->back.stencil_compare), - desc.depth_stencil_state->back.reference, - desc.depth_stencil_state->back.compare_mask + stencil_back_reference_, + stencil_back_compare_mask_ ); GL_ASSERT; - gl_->StencilMaskSeparate(GL_FRONT, desc.depth_stencil_state->front.write_mask); + gl_->StencilMaskSeparate(GL_FRONT, stencil_front_write_mask_); GL_ASSERT; - gl_->StencilMaskSeparate(GL_BACK, desc.depth_stencil_state->back.write_mask); + gl_->StencilMaskSeparate(GL_BACK, stencil_back_write_mask_); GL_ASSERT; } else @@ -1732,6 +1738,87 @@ void GlCoreRhi::read_pixels(Handle ctx, const Rect& rect, Pixel gl_->ReadPixels(rect.x, rect.y, rect.w, rect.h, layout, type, out.data()); } +void GlCoreRhi::set_stencil_reference(Handle ctx, CullMode face, uint8_t reference) +{ + SRB2_ASSERT(face != CullMode::kNone); + SRB2_ASSERT(graphics_context_active_ == true && graphics_context_generation_ == ctx.generation()); + SRB2_ASSERT(current_render_pass_.has_value()); + SRB2_ASSERT(current_pipeline_.has_value()); + + auto& pl = pipeline_slab_[*current_pipeline_]; + + if (face == CullMode::kFront) + { + stencil_front_reference_ = reference; + gl_->StencilFuncSeparate( + GL_FRONT, + map_compare_func(pl.desc.depth_stencil_state->front.stencil_compare), + stencil_front_reference_, + stencil_front_compare_mask_ + ); + } + else if (face == CullMode::kBack) + { + stencil_back_reference_ = reference; + gl_->StencilFuncSeparate( + GL_BACK, + map_compare_func(pl.desc.depth_stencil_state->back.stencil_compare), + stencil_back_reference_, + stencil_back_compare_mask_ + ); + } +} + +void GlCoreRhi::set_stencil_compare_mask(Handle ctx, CullMode face, uint8_t compare_mask) +{ + SRB2_ASSERT(face != CullMode::kNone); + SRB2_ASSERT(graphics_context_active_ == true && graphics_context_generation_ == ctx.generation()); + SRB2_ASSERT(current_render_pass_.has_value()); + SRB2_ASSERT(current_pipeline_.has_value()); + + auto& pl = pipeline_slab_[*current_pipeline_]; + + if (face == CullMode::kFront) + { + stencil_front_compare_mask_ = compare_mask; + gl_->StencilFuncSeparate( + GL_FRONT, + map_compare_func(pl.desc.depth_stencil_state->front.stencil_compare), + stencil_front_reference_, + stencil_front_compare_mask_ + ); + } + else if (face == CullMode::kBack) + { + stencil_back_compare_mask_ = compare_mask; + gl_->StencilFuncSeparate( + GL_BACK, + map_compare_func(pl.desc.depth_stencil_state->back.stencil_compare), + stencil_back_reference_, + stencil_back_compare_mask_ + ); + } +} + +void GlCoreRhi::set_stencil_write_mask(Handle ctx, CullMode face, uint8_t write_mask) +{ + SRB2_ASSERT(face != CullMode::kNone); + SRB2_ASSERT(graphics_context_active_ == true && graphics_context_generation_ == ctx.generation()); + SRB2_ASSERT(current_render_pass_.has_value()); + SRB2_ASSERT(current_pipeline_.has_value()); + + if (face == CullMode::kFront) + { + stencil_front_write_mask_ = write_mask; + gl_->StencilMaskSeparate(GL_FRONT, stencil_front_write_mask_); + } + else if (face == CullMode::kBack) + { + stencil_back_write_mask_ = write_mask; + gl_->StencilMaskSeparate(GL_BACK, stencil_back_write_mask_); + } +} + TextureDetails GlCoreRhi::get_texture_details(Handle texture) { SRB2_ASSERT(texture_slab_.is_valid(texture)); diff --git a/src/rhi/gl3_core/gl3_core_rhi.hpp b/src/rhi/gl3_core/gl3_core_rhi.hpp index 8814ac36f..31e85ad1d 100644 --- a/src/rhi/gl3_core/gl3_core_rhi.hpp +++ b/src/rhi/gl3_core/gl3_core_rhi.hpp @@ -164,6 +164,13 @@ class GlCoreRhi final : public Rhi uint32_t index_buffer_offset_ = 0; uint32_t transfer_context_generation_ = 0; + uint8_t stencil_front_reference_ = 0; + uint8_t stencil_front_compare_mask_ = 0xFF; + uint8_t stencil_front_write_mask_ = 0xFF; + uint8_t stencil_back_reference_ = 0; + uint8_t stencil_back_compare_mask_ = 0xFF; + uint8_t stencil_back_write_mask_ = 0xFF; + std::vector> disposal_; public: @@ -225,6 +232,9 @@ public: virtual void draw_indexed(Handle ctx, uint32_t index_count, uint32_t first_index) override; virtual void read_pixels(Handle ctx, const Rect& rect, PixelFormat format, tcb::span out) override; + virtual void set_stencil_reference(Handle ctx, CullMode face, uint8_t reference) override; + virtual void set_stencil_compare_mask(Handle ctx, CullMode face, uint8_t mask) override; + virtual void set_stencil_write_mask(Handle ctx, CullMode face, uint8_t mask) override; virtual void present() override; diff --git a/src/rhi/rhi.hpp b/src/rhi/rhi.hpp index 0187cf9fc..20f0bb7ae 100644 --- a/src/rhi/rhi.hpp +++ b/src/rhi/rhi.hpp @@ -424,9 +424,6 @@ struct PipelineStencilOpStateDesc StencilOp pass; StencilOp depth_fail; CompareFunc stencil_compare; - uint32_t compare_mask; - uint32_t write_mask; - uint32_t reference; }; struct PipelineDepthStencilStateDesc @@ -648,6 +645,9 @@ struct Rhi virtual void draw_indexed(Handle ctx, uint32_t index_count, uint32_t first_index) = 0; virtual void read_pixels(Handle ctx, const Rect& rect, PixelFormat format, tcb::span out) = 0; + virtual void set_stencil_reference(Handle ctx, CullMode face, uint8_t reference) = 0; + virtual void set_stencil_compare_mask(Handle ctx, CullMode face, uint8_t mask) = 0; + virtual void set_stencil_write_mask(Handle ctx, CullMode face, uint8_t mask) = 0; virtual void present() = 0; From 8d390c58d43f82e3ff12cf9fd8cff88bb76864c2 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Fri, 19 May 2023 18:38:16 -0500 Subject: [PATCH 2/5] hwr2: Extract BlendMode enum from twodee This enum is usable across multiple parts of hwr2 so it would be easier to just keep it separate. --- src/hwr2/CMakeLists.txt | 1 + src/hwr2/blendmode.hpp | 28 ++++++++++++++++++++++++++++ src/hwr2/pass_twodee.cpp | 37 +++++++++++++++++++------------------ src/hwr2/pass_twodee.hpp | 2 +- src/hwr2/twodee.cpp | 2 +- src/hwr2/twodee.hpp | 21 ++++++--------------- src/v_video.cpp | 31 ++++++++++++++++--------------- 7 files changed, 72 insertions(+), 50 deletions(-) create mode 100644 src/hwr2/blendmode.hpp diff --git a/src/hwr2/CMakeLists.txt b/src/hwr2/CMakeLists.txt index 8e525cad9..d961c57e6 100644 --- a/src/hwr2/CMakeLists.txt +++ b/src/hwr2/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources(SRB2SDL2 PRIVATE + blendmode.hpp pass_blit_postimg_screens.cpp pass_blit_postimg_screens.hpp pass_blit_rect.cpp diff --git a/src/hwr2/blendmode.hpp b/src/hwr2/blendmode.hpp new file mode 100644 index 000000000..b90e0b02e --- /dev/null +++ b/src/hwr2/blendmode.hpp @@ -0,0 +1,28 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by Ronald "Eidolon" Kinard +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef __SRB2_HWR2_BLENDMODE_HPP__ +#define __SRB2_HWR2_BLENDMODE_HPP__ + +namespace srb2::hwr2 +{ + +enum class BlendMode +{ + kAlphaTransparent, + kModulate, + kAdditive, + kSubtractive, + kReverseSubtractive, + kInvertDest +}; + +} // namespace srb2::hwr2 + +#endif // __SRB2_HWR2_BLENDMODE_HPP__ diff --git a/src/hwr2/pass_twodee.cpp b/src/hwr2/pass_twodee.cpp index 326caab0c..acac41bd0 100644 --- a/src/hwr2/pass_twodee.cpp +++ b/src/hwr2/pass_twodee.cpp @@ -14,6 +14,7 @@ #include #include +#include "blendmode.hpp" #include "../r_patch.h" #include "../v_video.h" #include "../z_zone.h" @@ -61,7 +62,7 @@ static PipelineDesc make_pipeline_desc(TwodeePipelineKey key) BlendDesc blend_desc; switch (key.blend) { - case Draw2dBlend::kAlphaTransparent: + case BlendMode::kAlphaTransparent: blend_desc.source_factor_color = BlendFactor::kSourceAlpha; blend_desc.dest_factor_color = BlendFactor::kOneMinusSourceAlpha; blend_desc.color_function = BlendFunction::kAdd; @@ -69,7 +70,7 @@ static PipelineDesc make_pipeline_desc(TwodeePipelineKey key) blend_desc.dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha; blend_desc.alpha_function = BlendFunction::kAdd; break; - case Draw2dBlend::kModulate: + case BlendMode::kModulate: blend_desc.source_factor_color = BlendFactor::kDest; blend_desc.dest_factor_color = BlendFactor::kZero; blend_desc.color_function = BlendFunction::kAdd; @@ -77,7 +78,7 @@ static PipelineDesc make_pipeline_desc(TwodeePipelineKey key) blend_desc.dest_factor_alpha = BlendFactor::kZero; blend_desc.alpha_function = BlendFunction::kAdd; break; - case Draw2dBlend::kAdditive: + case BlendMode::kAdditive: blend_desc.source_factor_color = BlendFactor::kSourceAlpha; blend_desc.dest_factor_color = BlendFactor::kOne; blend_desc.color_function = BlendFunction::kAdd; @@ -85,7 +86,7 @@ static PipelineDesc make_pipeline_desc(TwodeePipelineKey key) blend_desc.dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha; blend_desc.alpha_function = BlendFunction::kAdd; break; - case Draw2dBlend::kSubtractive: + case BlendMode::kSubtractive: blend_desc.source_factor_color = BlendFactor::kSourceAlpha; blend_desc.dest_factor_color = BlendFactor::kOne; blend_desc.color_function = BlendFunction::kSubtract; @@ -93,7 +94,7 @@ static PipelineDesc make_pipeline_desc(TwodeePipelineKey key) blend_desc.dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha; blend_desc.alpha_function = BlendFunction::kAdd; break; - case Draw2dBlend::kReverseSubtractive: + case BlendMode::kReverseSubtractive: blend_desc.source_factor_color = BlendFactor::kSourceAlpha; blend_desc.dest_factor_color = BlendFactor::kOne; blend_desc.color_function = BlendFunction::kReverseSubtract; @@ -101,7 +102,7 @@ static PipelineDesc make_pipeline_desc(TwodeePipelineKey key) blend_desc.dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha; blend_desc.alpha_function = BlendFunction::kAdd; break; - case Draw2dBlend::kInvertDest: + case BlendMode::kInvertDest: blend_desc.source_factor_color = BlendFactor::kOne; blend_desc.dest_factor_color = BlendFactor::kOne; blend_desc.color_function = BlendFunction::kSubtract; @@ -248,18 +249,18 @@ void TwodeePass::prepass(Rhi& rhi) if (data_->pipelines.size() == 0) { - TwodeePipelineKey alpha_transparent_tris = {Draw2dBlend::kAlphaTransparent, false}; - TwodeePipelineKey modulate_tris = {Draw2dBlend::kModulate, false}; - TwodeePipelineKey additive_tris = {Draw2dBlend::kAdditive, false}; - TwodeePipelineKey subtractive_tris = {Draw2dBlend::kSubtractive, false}; - TwodeePipelineKey revsubtractive_tris = {Draw2dBlend::kReverseSubtractive, false}; - TwodeePipelineKey invertdest_tris = {Draw2dBlend::kInvertDest, false}; - TwodeePipelineKey alpha_transparent_lines = {Draw2dBlend::kAlphaTransparent, true}; - TwodeePipelineKey modulate_lines = {Draw2dBlend::kModulate, true}; - TwodeePipelineKey additive_lines = {Draw2dBlend::kAdditive, true}; - TwodeePipelineKey subtractive_lines = {Draw2dBlend::kSubtractive, true}; - TwodeePipelineKey revsubtractive_lines = {Draw2dBlend::kReverseSubtractive, true}; - TwodeePipelineKey invertdest_lines = {Draw2dBlend::kInvertDest, true}; + TwodeePipelineKey alpha_transparent_tris = {BlendMode::kAlphaTransparent, false}; + TwodeePipelineKey modulate_tris = {BlendMode::kModulate, false}; + TwodeePipelineKey additive_tris = {BlendMode::kAdditive, false}; + TwodeePipelineKey subtractive_tris = {BlendMode::kSubtractive, false}; + TwodeePipelineKey revsubtractive_tris = {BlendMode::kReverseSubtractive, false}; + TwodeePipelineKey invertdest_tris = {BlendMode::kInvertDest, false}; + TwodeePipelineKey alpha_transparent_lines = {BlendMode::kAlphaTransparent, true}; + TwodeePipelineKey modulate_lines = {BlendMode::kModulate, true}; + TwodeePipelineKey additive_lines = {BlendMode::kAdditive, true}; + TwodeePipelineKey subtractive_lines = {BlendMode::kSubtractive, true}; + TwodeePipelineKey revsubtractive_lines = {BlendMode::kReverseSubtractive, true}; + TwodeePipelineKey invertdest_lines = {BlendMode::kInvertDest, true}; data_->pipelines.insert({alpha_transparent_tris, rhi.create_pipeline(make_pipeline_desc(alpha_transparent_tris))}); data_->pipelines.insert({modulate_tris, rhi.create_pipeline(make_pipeline_desc(modulate_tris))}); data_->pipelines.insert({additive_tris, rhi.create_pipeline(make_pipeline_desc(additive_tris))}); diff --git a/src/hwr2/pass_twodee.hpp b/src/hwr2/pass_twodee.hpp index 15a0924a4..8c72fc92e 100644 --- a/src/hwr2/pass_twodee.hpp +++ b/src/hwr2/pass_twodee.hpp @@ -34,7 +34,7 @@ struct TwodeePassData; /// @brief Hash map key for caching pipelines struct TwodeePipelineKey { - Draw2dBlend blend; + BlendMode blend; bool lines; bool operator==(const TwodeePipelineKey& r) const noexcept { return !(blend != r.blend || lines != r.lines); } diff --git a/src/hwr2/twodee.cpp b/src/hwr2/twodee.cpp index 8a15234fe..a54f13218 100644 --- a/src/hwr2/twodee.cpp +++ b/src/hwr2/twodee.cpp @@ -89,7 +89,7 @@ void Draw2dVerticesBuilder::done() list.cmds.push_back(tris_); } -Draw2dBlend srb2::hwr2::get_blend_mode(const Draw2dCmd& cmd) noexcept +BlendMode srb2::hwr2::get_blend_mode(const Draw2dCmd& cmd) noexcept { auto visitor = srb2::Overload { [&](const Draw2dPatchQuad& cmd) { return cmd.blend; }, diff --git a/src/hwr2/twodee.hpp b/src/hwr2/twodee.hpp index af0f60bc5..319034e6c 100644 --- a/src/hwr2/twodee.hpp +++ b/src/hwr2/twodee.hpp @@ -19,6 +19,7 @@ #include +#include "blendmode.hpp" #include "../cxxutil.hpp" #include "../doomtype.h" @@ -38,16 +39,6 @@ struct TwodeeVertex float a; }; -enum class Draw2dBlend -{ - kAlphaTransparent, - kModulate, - kAdditive, - kSubtractive, - kReverseSubtractive, - kInvertDest -}; - struct Draw2dPatchQuad { std::size_t begin_index = 0; @@ -56,7 +47,7 @@ struct Draw2dPatchQuad // A null patch ptr means no patch is drawn const patch_t* patch = nullptr; const uint8_t* colormap = nullptr; - Draw2dBlend blend; + BlendMode blend; float r = 0.f; float g = 0.f; float b = 0.f; @@ -81,14 +72,14 @@ struct Draw2dVertices std::size_t begin_index = 0; std::size_t begin_element = 0; std::size_t elements = 0; - Draw2dBlend blend = Draw2dBlend::kAlphaTransparent; + BlendMode blend = BlendMode::kAlphaTransparent; lumpnum_t flat_lump = UINT32_MAX; // LUMPERROR but not loading w_wad.h from this header bool lines = false; }; using Draw2dCmd = std::variant; -Draw2dBlend get_blend_mode(const Draw2dCmd& cmd) noexcept; +BlendMode get_blend_mode(const Draw2dCmd& cmd) noexcept; bool is_draw_lines(const Draw2dCmd& cmd) noexcept; std::size_t elements(const Draw2dCmd& cmd) noexcept; @@ -194,7 +185,7 @@ public: return *this; } - Draw2dQuadBuilder& blend(Draw2dBlend blend) + Draw2dQuadBuilder& blend(BlendMode blend) { quad_.blend = blend; return *this; @@ -245,7 +236,7 @@ public: return *this; } - Draw2dVerticesBuilder& blend(Draw2dBlend blend) + Draw2dVerticesBuilder& blend(BlendMode blend) { tris_.blend = blend; return *this; diff --git a/src/v_video.cpp b/src/v_video.cpp index e60a5462d..d25778061 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -31,6 +31,7 @@ #include "m_misc.h" #include "m_random.h" #include "doomstat.h" +#include "hwr2/blendmode.hpp" #ifdef HWRENDER #include "hardware/hw_glob.h" @@ -914,27 +915,27 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca { falpha = (10 - alphalevel) / 10.f; } - hwr2::Draw2dBlend blend = hwr2::Draw2dBlend::kAlphaTransparent; + hwr2::BlendMode blend = hwr2::BlendMode::kAlphaTransparent; switch (blendmode) { case AST_MODULATE: - blend = hwr2::Draw2dBlend::kModulate; + blend = hwr2::BlendMode::kModulate; break; case AST_ADD: - blend = hwr2::Draw2dBlend::kAdditive; + blend = hwr2::BlendMode::kAdditive; break; // Note: SRB2 has these blend modes flipped compared to GL and Vulkan. // SRB2's Subtract is Dst - Src. OpenGL is Src - Dst. And vice versa for reverse. // Twodee will use the GL definitions. case AST_SUBTRACT: - blend = hwr2::Draw2dBlend::kReverseSubtractive; + blend = hwr2::BlendMode::kReverseSubtractive; break; case AST_REVERSESUBTRACT: - blend = hwr2::Draw2dBlend::kSubtractive; + blend = hwr2::BlendMode::kSubtractive; break; default: - blend = hwr2::Draw2dBlend::kAlphaTransparent; + blend = hwr2::BlendMode::kAlphaTransparent; break; } @@ -1187,7 +1188,7 @@ void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) float a = 0.5f; // alphalevel is unused in GL?? g_2d.begin_quad() .rect(x, y, w, h) - .blend(hwr2::Draw2dBlend::kAlphaTransparent) + .blend(hwr2::BlendMode::kAlphaTransparent) .color(r, g, b, a) .done(); } @@ -1332,7 +1333,7 @@ void V_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c, UINT16 color, U float g; float b; float a; - hwr2::Draw2dBlend blendmode; + hwr2::BlendMode blendmode; if (color & 0xFF00) { @@ -1345,7 +1346,7 @@ void V_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c, UINT16 color, U b = std::clamp((fstrength - (2.f / 3.f)) * 3.f, 0.f, 1.f); a = 1; - blendmode = hwr2::Draw2dBlend::kReverseSubtractive; + blendmode = hwr2::BlendMode::kReverseSubtractive; } else { @@ -1356,7 +1357,7 @@ void V_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c, UINT16 color, U g = bc.green / 255.f; b = bc.blue / 255.f; a = softwaretranstohwr[std::clamp(static_cast(strength), 0, 10)] / 255.f; - blendmode = hwr2::Draw2dBlend::kAlphaTransparent; + blendmode = hwr2::BlendMode::kAlphaTransparent; } g_2d.begin_quad() @@ -1529,7 +1530,7 @@ void V_DrawFadeScreen(UINT16 color, UINT8 strength) float g; float b; float a; - hwr2::Draw2dBlend blendmode; + hwr2::BlendMode blendmode; if (color & 0xFF00) { @@ -1542,7 +1543,7 @@ void V_DrawFadeScreen(UINT16 color, UINT8 strength) b = std::clamp((fstrength - (2.f / 3.f)) * 3.f, 0.f, 1.f); a = 1; - blendmode = hwr2::Draw2dBlend::kReverseSubtractive; + blendmode = hwr2::BlendMode::kReverseSubtractive; } else { @@ -1553,7 +1554,7 @@ void V_DrawFadeScreen(UINT16 color, UINT8 strength) g = bc.green / 255.f; b = bc.blue / 255.f; a = softwaretranstohwr[std::clamp(static_cast(strength), 0, 10)] / 255.f; - blendmode = hwr2::Draw2dBlend::kAlphaTransparent; + blendmode = hwr2::BlendMode::kAlphaTransparent; } g_2d.begin_quad() @@ -1629,7 +1630,7 @@ void V_DrawFadeConsBack(INT32 plines) float a = 0.5f; g_2d.begin_quad() .rect(0, 0, vid.width, plines) - .blend(hwr2::Draw2dBlend::kAlphaTransparent) + .blend(hwr2::BlendMode::kAlphaTransparent) .color(r, g, b, a) .done(); } @@ -1649,7 +1650,7 @@ void V_EncoreInvertScreen(void) #endif g_2d.begin_quad() - .blend(hwr2::Draw2dBlend::kInvertDest) + .blend(hwr2::BlendMode::kInvertDest) .color(1, 1, 1, 1) .rect(0, 0, vid.width, vid.height) .done(); From 7e4d61730e24d01aadfbdeee75dbb1db4c99539c Mon Sep 17 00:00:00 2001 From: Eidolon Date: Fri, 19 May 2023 18:41:43 -0500 Subject: [PATCH 3/5] rhi: Fix handle std::hash specialization This prevented hashing of non-const qualified types. The contract for std::hash already implies that it is a callable for an lvalue reference of the template type. --- src/rhi/gl3_core/gl3_core_rhi.hpp | 4 ++-- src/rhi/handle.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rhi/gl3_core/gl3_core_rhi.hpp b/src/rhi/gl3_core/gl3_core_rhi.hpp index 31e85ad1d..b5f499422 100644 --- a/src/rhi/gl3_core/gl3_core_rhi.hpp +++ b/src/rhi/gl3_core/gl3_core_rhi.hpp @@ -46,11 +46,11 @@ struct std::hash { std::size_t operator()(const srb2::rhi::GlCoreFramebufferKey& key) const { - std::size_t color_hash = std::hash>()(key.color); + std::size_t color_hash = std::hash>()(key.color); std::size_t depth_stencil_hash = 0; if (key.depth_stencil) { - depth_stencil_hash = std::hash>()(*key.depth_stencil); + depth_stencil_hash = std::hash>()(*key.depth_stencil); } return color_hash ^ (depth_stencil_hash << 1); } diff --git a/src/rhi/handle.hpp b/src/rhi/handle.hpp index df160c564..282a924da 100644 --- a/src/rhi/handle.hpp +++ b/src/rhi/handle.hpp @@ -314,7 +314,7 @@ namespace std { template -struct hash> +struct hash> { std::size_t operator()(const srb2::rhi::Handle& e) const { From aa48237928ffcb947088cff6d83fb26701d165b3 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Fri, 19 May 2023 18:45:12 -0500 Subject: [PATCH 4/5] hwr2: Track colormaps and lighttables tex globally This will allow hardware to reuse the same colormap textures objects as twodee. --- src/hwr2/pass_resource_managers.cpp | 150 ++++++++++++++++++++++++++-- src/hwr2/pass_resource_managers.hpp | 21 ++++ src/hwr2/pass_twodee.cpp | 59 +---------- 3 files changed, 164 insertions(+), 66 deletions(-) diff --git a/src/hwr2/pass_resource_managers.cpp b/src/hwr2/pass_resource_managers.cpp index 58ea993eb..a6f2a52cc 100644 --- a/src/hwr2/pass_resource_managers.cpp +++ b/src/hwr2/pass_resource_managers.cpp @@ -12,6 +12,7 @@ #include #include +#include "../r_state.h" #include "../v_video.h" #include "../z_zone.h" @@ -139,28 +140,148 @@ void FramebufferManager::postpass(Rhi& rhi) { } -MainPaletteManager::MainPaletteManager() : Pass() -{ -} - +MainPaletteManager::MainPaletteManager() = default; MainPaletteManager::~MainPaletteManager() = default; +constexpr std::size_t kPaletteSize = 256; +constexpr std::size_t kLighttableRows = LIGHTLEVELS; + void MainPaletteManager::prepass(Rhi& rhi) { if (!palette_) { - palette_ = rhi.create_texture({TextureFormat::kRGBA, 256, 1, TextureWrapMode::kClamp, TextureWrapMode::kClamp}); + palette_ = rhi.create_texture({TextureFormat::kRGBA, kPaletteSize, 1, TextureWrapMode::kClamp, TextureWrapMode::kClamp}); } + + if (!lighttable_) + { + lighttable_ = rhi.create_texture({TextureFormat::kLuminance, kPaletteSize, kLighttableRows, TextureWrapMode::kClamp, TextureWrapMode::kClamp}); + } + + if (!encore_lighttable_) + { + encore_lighttable_ = rhi.create_texture({TextureFormat::kLuminance, kPaletteSize, kLighttableRows, TextureWrapMode::kClamp, TextureWrapMode::kClamp}); + } + + if (!default_colormap_) + { + default_colormap_ = rhi.create_texture({TextureFormat::kLuminance, kPaletteSize, 1, TextureWrapMode::kClamp, TextureWrapMode::kClamp}); + } +} + +void MainPaletteManager::upload_palette(Rhi& rhi, Handle ctx) +{ + std::array palette_32; + for (std::size_t i = 0; i < kPaletteSize; i++) + { + palette_32[i] = V_GetColor(i).s; + } + rhi.update_texture(ctx, palette_, {0, 0, kPaletteSize, 1}, PixelFormat::kRGBA8, tcb::as_bytes(tcb::span(palette_32))); +} + +void MainPaletteManager::upload_lighttables(Rhi& rhi, Handle ctx) +{ + tcb::span colormap_bytes = tcb::as_bytes(tcb::span(colormaps, kPaletteSize * kLighttableRows)); + rhi.update_texture(ctx, lighttable_, {0, 0, kPaletteSize, kLighttableRows}, PixelFormat::kR8, colormap_bytes); + + if (encoremap != nullptr) + { + tcb::span encoremap_bytes = tcb::as_bytes(tcb::span(encoremap, kPaletteSize * kLighttableRows)); + rhi.update_texture(ctx, lighttable_, {0, 0, kPaletteSize, kLighttableRows}, PixelFormat::kR8, encoremap_bytes); + } + + if (!lighttables_to_upload_.empty()) + { + for (auto lighttable : lighttables_to_upload_) + { + Handle lighttable_tex = find_extra_lighttable(lighttable); + SRB2_ASSERT(lighttable_tex != kNullHandle); + tcb::span lighttable_bytes = tcb::as_bytes(tcb::span(lighttable, kPaletteSize * kLighttableRows)); + rhi.update_texture(ctx, lighttable_tex, {0, 0, kPaletteSize, kLighttableRows}, PixelFormat::kR8, lighttable_bytes); + } + lighttables_to_upload_.clear(); + } +} + +void MainPaletteManager::upload_default_colormap(Rhi& rhi, Handle ctx) +{ + std::array data; + for (std::size_t i = 0; i < kPaletteSize; i++) + { + data[i] = i; + } + rhi.update_texture(ctx, default_colormap_, {0, 0, kPaletteSize, 1}, PixelFormat::kR8, tcb::as_bytes(tcb::span(data))); +} + +void MainPaletteManager::upload_colormaps(Rhi& rhi, Handle ctx) +{ + for (auto to_upload : colormaps_to_upload_) + { + SRB2_ASSERT(to_upload != nullptr); + SRB2_ASSERT(colormaps_.find(to_upload) != colormaps_.end()); + + rhi::Handle map_texture = colormaps_.at(to_upload); + + tcb::span map_bytes = tcb::as_bytes(tcb::span(to_upload, kPaletteSize)); + rhi.update_texture(ctx, map_texture, {0, 0, kPaletteSize, 1}, PixelFormat::kR8, map_bytes); + } + colormaps_to_upload_.clear(); +} + +rhi::Handle MainPaletteManager::find_or_create_colormap(rhi::Rhi& rhi, srb2::NotNull colormap) +{ + if (colormaps_.find(colormap) != colormaps_.end()) + { + return colormaps_[colormap]; + } + + Handle texture = rhi.create_texture({TextureFormat::kLuminance, kPaletteSize, 1, TextureWrapMode::kClamp, TextureWrapMode::kClamp}); + + colormaps_.insert_or_assign(colormap, texture); + colormaps_to_upload_.push_back(colormap); + return texture; +} + +rhi::Handle MainPaletteManager::find_colormap(srb2::NotNull colormap) const +{ + if (colormaps_.find(colormap) == colormaps_.end()) + { + return kNullHandle; + } + + return colormaps_.at(colormap); +} + +rhi::Handle MainPaletteManager::find_or_create_extra_lighttable(Rhi& rhi, srb2::NotNull lighttable) +{ + if (lighttables_.find(lighttable) != lighttables_.end()) + { + return lighttables_[lighttable]; + } + + Handle texture = rhi.create_texture({TextureFormat::kLuminance, kPaletteSize, kLighttableRows, TextureWrapMode::kClamp, TextureWrapMode::kClamp}); + + lighttables_.insert_or_assign(lighttable, texture); + lighttables_to_upload_.push_back(lighttable); + return texture; +} + +rhi::Handle MainPaletteManager::find_extra_lighttable(srb2::NotNull lighttable) const +{ + if (lighttables_.find(lighttable) == lighttables_.end()) + { + return kNullHandle; + } + + return lighttables_.at(lighttable); } void MainPaletteManager::transfer(Rhi& rhi, Handle ctx) { - std::array palette_32; - for (std::size_t i = 0; i < 256; i++) - { - palette_32[i] = V_GetColor(i).s; - } - rhi.update_texture(ctx, palette_, {0, 0, 256, 1}, PixelFormat::kRGBA8, tcb::as_bytes(tcb::span(palette_32))); + upload_palette(rhi, ctx); + upload_lighttables(rhi, ctx); + upload_default_colormap(rhi, ctx); + upload_colormaps(rhi, ctx); } void MainPaletteManager::graphics(Rhi& rhi, Handle ctx) @@ -169,6 +290,13 @@ void MainPaletteManager::graphics(Rhi& rhi, Handle ctx) void MainPaletteManager::postpass(Rhi& rhi) { + // Delete all colormap textures so they'll be recreated next frame + // Unfortunately the 256x1 translation colormaps are sometimes freed at runtime + for (auto& cm : colormaps_) + { + rhi.destroy_texture(cm.second); + } + colormaps_.clear(); } CommonResourcesManager::CommonResourcesManager() = default; diff --git a/src/hwr2/pass_resource_managers.hpp b/src/hwr2/pass_resource_managers.hpp index 30c158572..11e7fad95 100644 --- a/src/hwr2/pass_resource_managers.hpp +++ b/src/hwr2/pass_resource_managers.hpp @@ -74,6 +74,19 @@ public: class MainPaletteManager final : public Pass { rhi::Handle palette_; + rhi::Handle lighttable_; + rhi::Handle encore_lighttable_; + rhi::Handle default_colormap_; + + std::unordered_map> colormaps_; + std::unordered_map> lighttables_; + std::vector colormaps_to_upload_; + std::vector lighttables_to_upload_; + + void upload_palette(rhi::Rhi& rhi, rhi::Handle ctx); + void upload_lighttables(rhi::Rhi& rhi, rhi::Handle ctx); + void upload_default_colormap(rhi::Rhi& rhi, rhi::Handle ctx); + void upload_colormaps(rhi::Rhi& rhi, rhi::Handle ctx); public: MainPaletteManager(); @@ -85,6 +98,14 @@ public: virtual void postpass(rhi::Rhi& rhi) override; rhi::Handle palette() const noexcept { return palette_; } + rhi::Handle lighttable() const noexcept { return lighttable_; } + rhi::Handle encore_lighttable() const noexcept { return encore_lighttable_; } + rhi::Handle default_colormap() const noexcept { return default_colormap_; } + + rhi::Handle find_or_create_colormap(rhi::Rhi& rhi, srb2::NotNull colormap); + rhi::Handle find_colormap(srb2::NotNull colormap) const; + rhi::Handle find_or_create_extra_lighttable(rhi::Rhi& rhi, srb2::NotNull lighttable); + rhi::Handle find_extra_lighttable(srb2::NotNull lighttable) const; }; class CommonResourcesManager final : public Pass diff --git a/src/hwr2/pass_twodee.cpp b/src/hwr2/pass_twodee.cpp index acac41bd0..ed30706df 100644 --- a/src/hwr2/pass_twodee.cpp +++ b/src/hwr2/pass_twodee.cpp @@ -26,9 +26,6 @@ using namespace srb2::rhi; struct srb2::hwr2::TwodeePassData { Handle default_tex; - Handle default_colormap_tex; - std::unordered_map> colormaps; - std::vector colormaps_to_upload; std::unordered_map> pipelines; bool upload_default_tex = false; }; @@ -286,17 +283,6 @@ void TwodeePass::prepass(Rhi& rhi) }); data_->upload_default_tex = true; } - if (!data_->default_colormap_tex) - { - data_->default_colormap_tex = rhi.create_texture({ - TextureFormat::kLuminance, - 256, - 1, - TextureWrapMode::kClamp, - TextureWrapMode::kClamp - }); - data_->upload_default_tex = true; - } if (!render_pass_) { render_pass_ = rhi.create_render_pass( @@ -314,7 +300,6 @@ void TwodeePass::prepass(Rhi& rhi) // Stage 1 - command list patch detection std::unordered_set found_patches; - std::unordered_set found_colormaps; for (const auto& list : *ctx_) { for (const auto& cmd : list.cmds) @@ -328,7 +313,7 @@ void TwodeePass::prepass(Rhi& rhi) } if (cmd.colormap != nullptr) { - found_colormaps.insert(cmd.colormap); + palette_manager_->find_or_create_colormap(rhi, cmd.colormap); } }, [&](const Draw2dVertices& cmd) {}}; @@ -342,17 +327,6 @@ void TwodeePass::prepass(Rhi& rhi) } patch_atlas_cache_->pack(rhi); - for (auto colormap : found_colormaps) - { - if (data_->colormaps.find(colormap) == data_->colormaps.end()) - { - Handle colormap_tex = rhi.create_texture({TextureFormat::kLuminance, 256, 1}); - data_->colormaps.insert({colormap, colormap_tex}); - } - - data_->colormaps_to_upload.push_back(colormap); - } - size_t list_index = 0; for (auto& list : *ctx_) { @@ -536,34 +510,9 @@ void TwodeePass::transfer(Rhi& rhi, Handle ctx) std::array data = {0, 255, 0, 255}; rhi.update_texture(ctx, data_->default_tex, {0, 0, 2, 1}, PixelFormat::kRG8, tcb::as_bytes(tcb::span(data))); - std::array colormap_data; - for (size_t i = 0; i < 256; i++) - { - colormap_data[i] = i; - } - rhi.update_texture( - ctx, - data_->default_colormap_tex, - {0, 0, 256, 1}, - PixelFormat::kR8, - tcb::as_bytes(tcb::span(colormap_data)) - ); - data_->upload_default_tex = false; } - for (auto colormap : data_->colormaps_to_upload) - { - rhi.update_texture( - ctx, - data_->colormaps[colormap], - {0, 0, 256, 1}, - rhi::PixelFormat::kR8, - tcb::as_bytes(tcb::span(colormap, 256)) - ); - } - data_->colormaps_to_upload.clear(); - Handle palette_tex = palette_manager_->palette(); // Update the buffers for each list @@ -607,11 +556,11 @@ void TwodeePass::transfer(Rhi& rhi, Handle ctx) } const uint8_t* colormap = mcmd.colormap; - Handle colormap_h = data_->default_colormap_tex; + Handle colormap_h = palette_manager_->default_colormap(); if (colormap) { - SRB2_ASSERT(data_->colormaps.find(colormap) != data_->colormaps.end()); - colormap_h = data_->colormaps[colormap]; + colormap_h = palette_manager_->find_colormap(colormap); + SRB2_ASSERT(colormap_h != kNullHandle); } tx[2] = {SamplerName::kSampler2, colormap_h}; mcmd.binding_set = From be869aefc6c959d31e6b5b3a2bcb1dd326241286 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Fri, 19 May 2023 18:52:47 -0500 Subject: [PATCH 5/5] rhi gl: Allow create_pipeline in gfx ctx This function impl does not change any GL drawing state, and neither should Vulkan, so the constraint that this function not be called while a graphics context is active is not necessary. --- src/rhi/gl3_core/gl3_core_rhi.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rhi/gl3_core/gl3_core_rhi.cpp b/src/rhi/gl3_core/gl3_core_rhi.cpp index eab28f852..7d6481f6d 100644 --- a/src/rhi/gl3_core/gl3_core_rhi.cpp +++ b/src/rhi/gl3_core/gl3_core_rhi.cpp @@ -886,7 +886,6 @@ void GlCoreRhi::destroy_renderbuffer(rhi::Handle handle) rhi::Handle GlCoreRhi::create_pipeline(const PipelineDesc& desc) { - SRB2_ASSERT(graphics_context_active_ == false); SRB2_ASSERT(platform_ != nullptr); // TODO assert compatibility of pipeline description with program using ProgramRequirements