diff --git a/src/d_main.c b/src/d_main.c index 229de62b7..58df759e4 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -800,6 +800,9 @@ void D_SRB2Loop(void) HW3S_BeginFrameUpdate(); #endif + I_NewTwodeeFrame(); + I_NewImguiFrame(); + if (realtics > 0 || singletics) { // don't skip more than 10 frames at a time @@ -1491,6 +1494,9 @@ void D_SRB2Main(void) CONS_Printf("I_StartupGraphics()...\n"); I_StartupGraphics(); + I_NewTwodeeFrame(); + I_NewImguiFrame(); + #ifdef HWRENDER // Lactozilla: Add every hardware mode CVAR and CCMD. // Has to be done before the configuration file loads, diff --git a/src/f_finale.h b/src/f_finale.h index e2d4599a2..970bf0619 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -144,6 +144,7 @@ extern UINT8 g_wipetype; extern UINT8 g_wipeframe; extern boolean g_wipereverse; extern boolean g_wipeskiprender; +extern boolean g_wipeencorewiggle; extern boolean WipeStageTitle; extern INT32 lastwipetic; @@ -159,6 +160,14 @@ void F_WipeStageTitle(void); #define F_WipeColorFill(c) V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, c) tic_t F_GetWipeLength(UINT8 wipetype); boolean F_WipeExists(UINT8 wipetype); +/// @brief true if the wipetype is to-black +boolean F_WipeIsToBlack(UINT8 wipetype); +/// @brief true if the wipetype is to-white +boolean F_WipeIsToWhite(UINT8 wipetype); +/// @brief true if the wipetype is to-invert +boolean F_WipeIsToInvert(UINT8 wipetype); +/// @brief true if the wipetype is modulated from the previous frame +boolean F_WipeIsCrossfade(UINT8 wipetype); enum { diff --git a/src/f_wipe.c b/src/f_wipe.c index 98097b32c..435d79fda 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -85,6 +85,138 @@ UINT8 wipedefs[NUMWIPEDEFS] = { 99 // wipe_cutscene_final (hardcoded) }; +static boolean g_wipedef_toblack[NUMWIPEDEFS] = { + true, // wipe_credits_intermediate (0) + + true, // wipe_level_toblack + true, // wipe_intermission_toblack + true, // wipe_voting_toblack, + true, // wipe_continuing_toblack + true, // wipe_titlescreen_toblack + true, // wipe_menu_toblack + true, // wipe_credits_toblack + true, // wipe_evaluation_toblack + true, // wipe_gameend_toblack + true, // wipe_intro_toblack (hardcoded) + true, // wipe_ending_toblack (hardcoded) + true, // wipe_cutscene_toblack (hardcoded) + + false, // wipe_encore_toinvert + false, // wipe_encore_towhite + + true, // wipe_level_final + true, // wipe_intermission_final + true, // wipe_voting_final + true, // wipe_continuing_final + true, // wipe_titlescreen_final + true, // wipe_menu_final + true, // wipe_credits_final + true, // wipe_evaluation_final + true, // wipe_gameend_final + true, // wipe_intro_final (hardcoded) + true, // wipe_ending_final (hardcoded) + true // wipe_cutscene_final (hardcoded) +}; + +static boolean g_wipedef_toinvert[NUMWIPEDEFS] = { + false, // wipe_credits_intermediate (0) + + false, // wipe_level_toblack + false, // wipe_intermission_toblack + false, // wipe_voting_toblack, + false, // wipe_continuing_toblack + false, // wipe_titlescreen_toblack + false, // wipe_menu_toblack + false, // wipe_credits_toblack + false, // wipe_evaluation_toblack + false, // wipe_gameend_toblack + false, // wipe_intro_toblack (hardcoded) + false, // wipe_ending_toblack (hardcoded) + false, // wipe_cutscene_toblack (hardcoded) + + true, // wipe_encore_toinvert + false, // wipe_encore_towhite + + false, // wipe_level_final + false, // wipe_intermission_final + false, // wipe_voting_final + false, // wipe_continuing_final + false, // wipe_titlescreen_final + false, // wipe_menu_final + false, // wipe_credits_final + false, // wipe_evaluation_final + false, // wipe_gameend_final + false, // wipe_intro_final (hardcoded) + false, // wipe_ending_final (hardcoded) + false // wipe_cutscene_final (hardcoded) +}; + +static boolean g_wipedef_towhite[NUMWIPEDEFS] = { + false, // wipe_credits_intermediate (0) + + false, // wipe_level_toblack + false, // wipe_intermission_toblack + false, // wipe_voting_toblack, + false, // wipe_continuing_toblack + false, // wipe_titlescreen_toblack + false, // wipe_menu_toblack + false, // wipe_credits_toblack + false, // wipe_evaluation_toblack + false, // wipe_gameend_toblack + false, // wipe_intro_toblack (hardcoded) + false, // wipe_ending_toblack (hardcoded) + false, // wipe_cutscene_toblack (hardcoded) + + false, // wipe_encore_toinvert + true, // wipe_encore_towhite + + false, // wipe_level_final + false, // wipe_intermission_final + false, // wipe_voting_final + false, // wipe_continuing_final + false, // wipe_titlescreen_final + false, // wipe_menu_final + false, // wipe_credits_final + false, // wipe_evaluation_final + false, // wipe_gameend_final + false, // wipe_intro_final (hardcoded) + false, // wipe_ending_final (hardcoded) + false // wipe_cutscene_final (hardcoded) +}; + +static boolean g_wipedef_crossfade[NUMWIPEDEFS] = { + false, // wipe_credits_intermediate (0) + + false, // wipe_level_toblack + false, // wipe_intermission_toblack + false, // wipe_voting_toblack, + false, // wipe_continuing_toblack + false, // wipe_titlescreen_toblack + false, // wipe_menu_toblack + false, // wipe_credits_toblack + false, // wipe_evaluation_toblack + false, // wipe_gameend_toblack + false, // wipe_intro_toblack (hardcoded) + false, // wipe_ending_toblack (hardcoded) + false, // wipe_cutscene_toblack (hardcoded) + + false, // wipe_encore_toinvert + false, // wipe_encore_towhite + + true, // wipe_level_final + true, // wipe_intermission_final + true, // wipe_voting_final + true, // wipe_continuing_final + true, // wipe_titlescreen_final + true, // wipe_menu_final + true, // wipe_credits_final + true, // wipe_evaluation_final + true, // wipe_gameend_final + true, // wipe_intro_final (hardcoded) + true, // wipe_ending_final (hardcoded) + true // wipe_cutscene_final (hardcoded) +}; + //-------------------------------------------------------------------------- // SCREEN WIPE PACKAGE //-------------------------------------------------------------------------- @@ -94,6 +226,7 @@ UINT8 g_wipetype = 0; UINT8 g_wipeframe = 0; boolean g_wipereverse = false; boolean g_wipeskiprender = false; +boolean g_wipeencorewiggle = false; boolean WipeStageTitle = false; INT32 lastwipetic = 0; @@ -207,8 +340,9 @@ void F_WipeStartScreen(void) return; } #endif - wipe_scr_start = screens[3]; - I_ReadScreen(wipe_scr_start); + // wipe_scr_start = screens[3]; + // I_ReadScreen(wipe_scr_start); + I_FinishUpdateWipeStartScreen(); #endif } @@ -224,9 +358,10 @@ void F_WipeEndScreen(void) return; } #endif - wipe_scr_end = screens[4]; - I_ReadScreen(wipe_scr_end); - V_DrawBlock(0, 0, 0, vid.width, vid.height, wipe_scr_start); + // wipe_scr_end = screens[4]; + // I_ReadScreen(wipe_scr_end); + // V_DrawBlock(0, 0, 0, vid.width, vid.height, wipe_scr_start); + I_FinishUpdateWipeEndScreen(); #endif } @@ -360,10 +495,11 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu, const char *colormap, boolean r if (encorewiggle) { -#ifdef HWRENDER - if (rendermode != render_opengl) -#endif - F_DoEncoreWiggle(wipeframe); + g_wipeencorewiggle = wipeframe - 1; + } + else + { + g_wipeencorewiggle = 0; } } @@ -381,7 +517,7 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu, const char *colormap, boolean r #endif } - I_FinishUpdate(); // page flip or blit buffer + I_FinishUpdateWipe(); // page flip or blit buffer if (rendermode != render_none) { @@ -454,3 +590,24 @@ boolean F_WipeExists(UINT8 wipetype) return !(lumpnum == LUMPERROR); #endif } + +boolean F_WipeIsToBlack(UINT8 wipetype) +{ + return g_wipedef_toblack[wipetype]; +} + +boolean F_WipeIsToWhite(UINT8 wipetype) +{ + return g_wipedef_towhite[wipetype]; +} + +boolean F_WipeIsToInvert(UINT8 wipetype) +{ + return g_wipedef_toinvert[wipetype]; +} + +boolean F_WipeIsCrossfade(UINT8 wipetype) +{ + return g_wipedef_crossfade[wipetype]; +} + diff --git a/src/hwr2/pass_manager.cpp b/src/hwr2/pass_manager.cpp index e79ef2725..a6bc386a7 100644 --- a/src/hwr2/pass_manager.cpp +++ b/src/hwr2/pass_manager.cpp @@ -80,6 +80,8 @@ void LambdaPass::postpass(Rhi& rhi) } PassManager::PassManager() = default; +PassManager::PassManager(const PassManager&) = default; +PassManager& PassManager::operator=(const PassManager&) = default; void PassManager::insert(const std::string& name, std::shared_ptr pass) { @@ -124,13 +126,8 @@ std::weak_ptr PassManager::for_name(const std::string& name) return passes_[itr->second].pass; } -void PassManager::render(Rhi& rhi) const +void PassManager::prepass(Rhi& rhi) { - if (passes_.empty()) - { - return; - } - for (auto& pass : passes_) { if (pass.enabled) @@ -138,27 +135,32 @@ void PassManager::render(Rhi& rhi) const pass.pass->prepass(rhi); } } +} - Handle tc = rhi.begin_transfer(); +void PassManager::transfer(Rhi& rhi, Handle ctx) +{ for (auto& pass : passes_) { if (pass.enabled) { - pass.pass->transfer(rhi, tc); + pass.pass->transfer(rhi, ctx); } } - rhi.end_transfer(tc); +} - Handle gc = rhi.begin_graphics(); +void PassManager::graphics(Rhi& rhi, Handle ctx) +{ for (auto& pass : passes_) { if (pass.enabled) { - pass.pass->graphics(rhi, gc); + pass.pass->graphics(rhi, ctx); } } - rhi.end_graphics(gc); +} +void PassManager::postpass(Rhi& rhi) +{ for (auto& pass : passes_) { if (pass.enabled) @@ -167,3 +169,23 @@ void PassManager::render(Rhi& rhi) const } } } + +void PassManager::render(Rhi& rhi) +{ + if (passes_.empty()) + { + return; + } + + prepass(rhi); + + Handle tc = rhi.begin_transfer(); + transfer(rhi, tc); + rhi.end_transfer(tc); + + Handle gc = rhi.begin_graphics(); + graphics(rhi, gc); + rhi.end_graphics(gc); + + postpass(rhi); +} diff --git a/src/hwr2/pass_manager.hpp b/src/hwr2/pass_manager.hpp index 372db893e..c462dd395 100644 --- a/src/hwr2/pass_manager.hpp +++ b/src/hwr2/pass_manager.hpp @@ -23,7 +23,7 @@ namespace srb2::hwr2 { -class PassManager +class PassManager final : public Pass { struct PassManagerEntry { @@ -37,11 +37,16 @@ class PassManager public: PassManager(); - PassManager(const PassManager&) = delete; + PassManager(const PassManager&); PassManager(PassManager&&) = delete; - PassManager& operator=(const PassManager&) = delete; + PassManager& operator=(const PassManager&); PassManager& operator=(PassManager&&) = delete; + virtual void prepass(rhi::Rhi& rhi) override; + virtual void transfer(rhi::Rhi& rhi, rhi::Handle ctx) override; + virtual void graphics(rhi::Rhi& rhi, rhi::Handle ctx) override; + virtual void postpass(rhi::Rhi& rhi) override; + void insert(const std::string& name, std::shared_ptr pass); void insert(const std::string& name, std::function prepass_func); void insert( @@ -52,7 +57,7 @@ public: std::weak_ptr for_name(const std::string& name); void set_pass_enabled(const std::string& name, bool enabled); - void render(rhi::Rhi& rhi) const; + void render(rhi::Rhi& rhi); }; } // namespace srb2::hwr2 diff --git a/src/hwr2/pass_postprocess.cpp b/src/hwr2/pass_postprocess.cpp index 89b1ae005..333338159 100644 --- a/src/hwr2/pass_postprocess.cpp +++ b/src/hwr2/pass_postprocess.cpp @@ -46,8 +46,8 @@ static const PipelineDesc kWipePipelineDesc = { {VertexAttributeName::kPosition, 0, 0}, {VertexAttributeName::kTexCoord0, 0, 12}, }}, - {{{{UniformName::kProjection}}}}, - {{SamplerName::kSampler0, SamplerName::kSampler1}}, + {{{{UniformName::kProjection, UniformName::kWipeColorizeMode, UniformName::kWipeEncoreSwizzle}}}}, + {{SamplerName::kSampler0, SamplerName::kSampler1, SamplerName::kSampler2}}, std::nullopt, {PixelFormat::kRGBA8, std::nullopt, {true, true, true, true}}, PrimitiveType::kTriangles, @@ -55,7 +55,7 @@ static const PipelineDesc kWipePipelineDesc = { FaceWinding::kCounterClockwise, {0.f, 0.f, 0.f, 1.f}}; -PostprocessWipePass::PostprocessWipePass() : Pass() +PostprocessWipePass::PostprocessWipePass() { } @@ -89,6 +89,27 @@ void PostprocessWipePass::prepass(Rhi& rhi) uint32_t wipe_type = g_wipetype; uint32_t wipe_frame = g_wipeframe; bool wipe_reverse = g_wipereverse; + + wipe_color_mode_ = 0; // TODO 0 = modulate, 1 = invert, 2 = MD to black, 3 = MD to white + if (F_WipeIsToBlack(wipe_type)) + { + wipe_color_mode_ = 2; + } + else if (F_WipeIsToWhite(wipe_type)) + { + wipe_color_mode_ = 3; + } + else if (F_WipeIsToInvert(wipe_type)) + { + wipe_color_mode_ = 1; + } + else if (F_WipeIsCrossfade(wipe_type)) + { + wipe_color_mode_ = 0; + } + + wipe_swizzle_ = g_wipeencorewiggle; + if (wipe_type >= 100 || wipe_frame >= 100) { return; @@ -145,7 +166,7 @@ void PostprocessWipePass::transfer(Rhi& rhi, Handle ctx) return; } - if (source_ == kNullHandle) + if (start_ == kNullHandle || end_ == kNullHandle) { return; } @@ -166,13 +187,17 @@ void PostprocessWipePass::transfer(Rhi& rhi, Handle ctx) rhi.update_texture(ctx, wipe_tex_, {0, 0, mask_w_, mask_h_}, PixelFormat::kR8, data); UniformVariant uniforms[] = { - {// Projection - std::array, 4> { - {{2.f, 0.f, 0.f, 0.f}, {0.f, 2.f, 0.f, 0.f}, {0.f, 0.f, 1.f, 0.f}, {0.f, 0.f, 0.f, 1.f}}}}}; + {std::array, 4> { + {{2.f, 0.f, 0.f, 0.f}, {0.f, 2.f, 0.f, 0.f}, {0.f, 0.f, 1.f, 0.f}, {0.f, 0.f, 0.f, 1.f}}}}, + {static_cast(wipe_color_mode_)}, + {static_cast(wipe_swizzle_)}}; us_ = rhi.create_uniform_set(ctx, {tcb::span(uniforms)}); VertexAttributeBufferBinding vbos[] = {{0, vbo_}}; - TextureBinding tx[] = {{SamplerName::kSampler0, source_}, {SamplerName::kSampler1, wipe_tex_}}; + TextureBinding tx[] = { + {SamplerName::kSampler0, start_}, + {SamplerName::kSampler1, end_}, + {SamplerName::kSampler2, wipe_tex_}}; bs_ = rhi.create_binding_set(ctx, pipeline_, {vbos, tx}); } diff --git a/src/hwr2/pass_postprocess.hpp b/src/hwr2/pass_postprocess.hpp index 113d96296..ced81975c 100644 --- a/src/hwr2/pass_postprocess.hpp +++ b/src/hwr2/pass_postprocess.hpp @@ -19,6 +19,7 @@ namespace srb2::hwr2 class PostprocessWipePass final : public Pass { + // Internal RHI resources rhi::Handle render_pass_; rhi::Handle pipeline_; rhi::Handle vbo_; @@ -28,14 +29,17 @@ class PostprocessWipePass final : public Pass rhi::Handle us_; rhi::Handle bs_; rhi::Handle wipe_tex_; - rhi::Handle source_; - uint32_t source_w_ = 0; - uint32_t source_h_ = 0; + int wipe_color_mode_ = 0; + int wipe_swizzle_ = 0; + + // Pass parameters + rhi::Handle start_; rhi::Handle end_; rhi::Handle target_; uint32_t target_w_ = 0; uint32_t target_h_ = 0; + // Mask lump loading std::vector mask_data_; uint32_t mask_w_ = 0; uint32_t mask_h_ = 0; @@ -49,12 +53,7 @@ public: virtual void graphics(rhi::Rhi& rhi, rhi::Handle ctx) override; virtual void postpass(rhi::Rhi& rhi) override; - void set_source(rhi::Handle source, uint32_t width, uint32_t height) noexcept - { - source_ = source; - source_w_ = width; - source_h_ = height; - } + void set_start(rhi::Handle start) noexcept { start_ = start; } void set_end(rhi::Handle end) noexcept { end_ = end; } diff --git a/src/hwr2/pass_resource_managers.cpp b/src/hwr2/pass_resource_managers.cpp index 2f5e599c4..40efb0980 100644 --- a/src/hwr2/pass_resource_managers.cpp +++ b/src/hwr2/pass_resource_managers.cpp @@ -59,6 +59,16 @@ void FramebufferManager::prepass(Rhi& rhi) rhi.destroy_texture(post_colors_[1]); post_colors_[1] = kNullHandle; } + if (wipe_start_color_ != kNullHandle) + { + rhi.destroy_texture(wipe_start_color_); + wipe_start_color_ = kNullHandle; + } + if (wipe_end_color_ != kNullHandle) + { + rhi.destroy_texture(wipe_end_color_); + wipe_end_color_ = kNullHandle; + } } width_ = current_width; height_ = current_height; @@ -85,6 +95,15 @@ void FramebufferManager::prepass(Rhi& rhi) { post_colors_[1] = rhi.create_texture({TextureFormat::kRGBA, current_width, current_height}); } + + if (wipe_start_color_ == kNullHandle) + { + wipe_start_color_ = rhi.create_texture({TextureFormat::kRGBA, current_width, current_height}); + } + if (wipe_end_color_ == kNullHandle) + { + wipe_end_color_ = rhi.create_texture({TextureFormat::kRGBA, current_width, current_height}); + } } void FramebufferManager::transfer(Rhi& rhi, Handle ctx) @@ -131,6 +150,45 @@ void MainPaletteManager::postpass(Rhi& rhi) { } +CommonResourcesManager::CommonResourcesManager() = default; +CommonResourcesManager::~CommonResourcesManager() = default; + +void CommonResourcesManager::prepass(Rhi& rhi) +{ + if (!init_) + { + black_ = rhi.create_texture({TextureFormat::kRGBA, 1, 1}); + white_ = rhi.create_texture({TextureFormat::kRGBA, 1, 1}); + transparent_ = rhi.create_texture({TextureFormat::kRGBA, 1, 1}); + } +} + +void CommonResourcesManager::transfer(Rhi& rhi, Handle ctx) +{ + if (!init_) + { + uint8_t black[4] = {0, 0, 0, 255}; + tcb::span black_bytes = tcb::as_bytes(tcb::span(black, 4)); + uint8_t white[4] = {255, 255, 255, 255}; + tcb::span white_bytes = tcb::as_bytes(tcb::span(white, 4)); + uint8_t transparent[4] = {0, 0, 0, 0}; + tcb::span transparent_bytes = tcb::as_bytes(tcb::span(transparent, 4)); + + rhi.update_texture(ctx, black_, {0, 0, 1, 1}, PixelFormat::kRGBA8, black_bytes); + rhi.update_texture(ctx, white_, {0, 0, 1, 1}, PixelFormat::kRGBA8, white_bytes); + rhi.update_texture(ctx, transparent_, {0, 0, 1, 1}, PixelFormat::kRGBA8, transparent_bytes); + } +} + +void CommonResourcesManager::graphics(Rhi& rhi, Handle ctx) +{ +} + +void CommonResourcesManager::postpass(Rhi& rhi) +{ + init_ = true; +} + static uint32_t get_flat_size(lumpnum_t lump) { SRB2_ASSERT(lump != LUMPERROR); diff --git a/src/hwr2/pass_resource_managers.hpp b/src/hwr2/pass_resource_managers.hpp index 4eee36b8c..43e9dc56f 100644 --- a/src/hwr2/pass_resource_managers.hpp +++ b/src/hwr2/pass_resource_managers.hpp @@ -25,6 +25,8 @@ class FramebufferManager final : public Pass std::array, 2> main_colors_; rhi::Handle main_depth_; std::array, 2> post_colors_; + rhi::Handle wipe_start_color_; + rhi::Handle wipe_end_color_; std::size_t main_index_ = 0; std::size_t post_index_ = 0; std::size_t width_ = 0; @@ -67,6 +69,9 @@ public: return post_colors_[1 - post_index_]; }; + rhi::Handle wipe_start_color() const noexcept { return wipe_start_color_; } + rhi::Handle wipe_end_color() const noexcept { return wipe_end_color_; } + std::size_t width() const noexcept { return width_; } std::size_t height() const noexcept { return height_; } }; @@ -87,6 +92,27 @@ public: rhi::Handle palette() const noexcept { return palette_; } }; +class CommonResourcesManager final : public Pass +{ + bool init_ = false; + rhi::Handle black_; + rhi::Handle white_; + rhi::Handle transparent_; + +public: + CommonResourcesManager(); + virtual ~CommonResourcesManager(); + + virtual void prepass(rhi::Rhi& rhi) override; + virtual void transfer(rhi::Rhi& rhi, rhi::Handle ctx) override; + virtual void graphics(rhi::Rhi& rhi, rhi::Handle ctx) override; + virtual void postpass(rhi::Rhi& rhi) override; + + rhi::Handle black() const noexcept { return black_; } + rhi::Handle white() const noexcept { return white_; } + rhi::Handle transparent() const noexcept { return transparent_; } +}; + /* A note to the reader: diff --git a/src/i_video.h b/src/i_video.h index a80bd33dc..4da72efc0 100644 --- a/src/i_video.h +++ b/src/i_video.h @@ -130,10 +130,25 @@ extern boolean allow_fullscreen; */ void I_UpdateNoBlit(void); +/** \brief Begin a new Twodee frame. +*/ +void I_NewTwodeeFrame(void); + +/** \brief Begin a new dear imgui frame. +*/ +void I_NewImguiFrame(void); + /** \brief Update video system with updating frame */ void I_FinishUpdate(void); +void I_FinishUpdateWipeStartScreen(void); +void I_FinishUpdateWipeEndScreen(void); + +/** \brief Update video system during a wipe +*/ +void I_FinishUpdateWipe(void); + /** \brief I_FinishUpdate(), but vsync disabled */ void I_UpdateNoVsync(void); diff --git a/src/i_video_common.cpp b/src/i_video_common.cpp index 493faa821..f3371a344 100644 --- a/src/i_video_common.cpp +++ b/src/i_video_common.cpp @@ -40,13 +40,26 @@ using namespace srb2; using namespace srb2::hwr2; using namespace srb2::rhi; -static std::shared_ptr g_passmanager; +namespace +{ +struct InternalPassData +{ + std::shared_ptr resource_passmanager; + std::shared_ptr normal_rendering; + std::shared_ptr wipe_capture_start_rendering; + std::shared_ptr wipe_capture_end_rendering; + std::shared_ptr wipe_rendering; +}; +} // namespace + +static std::unique_ptr g_passes; +static Rhi* g_last_known_rhi = nullptr; Handle srb2::sys::g_current_rhi = kNullHandle; -static bool rhi_changed() +static bool rhi_changed(Rhi* rhi) { - return false; + return g_last_known_rhi != rhi; } #ifdef HWRENDER @@ -103,50 +116,49 @@ static void finish_legacy_ogl_update() } #endif -static std::shared_ptr build_pass_manager() +static InternalPassData build_pass_manager() { - std::shared_ptr manager = std::make_shared(); + auto framebuffer_manager = std::make_shared(); + auto palette_manager = std::make_shared(); + auto common_resources_manager = std::make_shared(); + auto flat_texture_manager = std::make_shared(); + auto resource_manager = std::make_shared(); - std::shared_ptr framebuffer_manager = std::make_shared(); - std::shared_ptr palette_manager = std::make_shared(); - std::shared_ptr flat_texture_manager = std::make_shared(); + resource_manager->insert("framebuffer_manager", framebuffer_manager); + resource_manager->insert("palette_manager", palette_manager); + resource_manager->insert("common_resources_manager", common_resources_manager); + resource_manager->insert("flat_texture_manager", flat_texture_manager); - std::shared_ptr software_pass = std::make_shared(); - std::shared_ptr blit_sw_pass = std::make_shared(palette_manager, true); - std::shared_ptr twodee = std::make_shared(); + // Basic Rendering is responsible for drawing 3d, 2d, and postprocessing the image. + // This is drawn to an alternating internal color buffer. + // Normal Rendering will output the result via final composite and present. + // Wipe Start Screen and Wipe End Screen will save to special color buffers used for Wipe Rendering. + auto basic_rendering = std::make_shared(); + + auto software_pass = std::make_shared(); + auto blit_sw_pass = std::make_shared(palette_manager, true); + auto twodee = std::make_shared(); twodee->flat_manager_ = flat_texture_manager; twodee->data_ = make_twodee_pass_data(); twodee->ctx_ = &g_2d; - std::shared_ptr pp_simple_blit_pass = std::make_shared(false); - std::shared_ptr pp_wipe_pass = std::make_shared(); - std::shared_ptr screenshot_pass = std::make_shared(); - std::shared_ptr imgui_pass = std::make_shared(); - std::shared_ptr final_composite_pass = std::make_shared(true); + auto pp_simple_blit_pass = std::make_shared(false); + auto screenshot_pass = std::make_shared(); + auto imgui_pass = std::make_shared(); + auto final_composite_pass = std::make_shared(true); - manager->insert("framebuffer_manager", framebuffer_manager); - manager->insert("palette_manager", palette_manager); - manager->insert("flat_texture_manager", flat_texture_manager); - - manager->insert( + basic_rendering->insert( "3d_prepare", [framebuffer_manager](PassManager& mgr, Rhi&) { const bool sw_enabled = rendermode == render_soft; - mgr.set_pass_enabled("software", !g_wipeskiprender && sw_enabled); - mgr.set_pass_enabled("blit_sw_prepare", !g_wipeskiprender && sw_enabled); - mgr.set_pass_enabled("blit_sw", !g_wipeskiprender && sw_enabled); - }, - [framebuffer_manager](PassManager&, Rhi&) - { - if (!WipeInAction) - { - framebuffer_manager->swap_main(); - } + mgr.set_pass_enabled("software", sw_enabled); + mgr.set_pass_enabled("blit_sw_prepare", sw_enabled); + mgr.set_pass_enabled("blit_sw", sw_enabled); } ); - manager->insert("software", software_pass); - manager->insert( + basic_rendering->insert("software", software_pass); + basic_rendering->insert( "blit_sw_prepare", [blit_sw_pass, software_pass, framebuffer_manager](PassManager&, Rhi&) { @@ -154,9 +166,9 @@ static std::shared_ptr build_pass_manager() blit_sw_pass->set_output(framebuffer_manager->current_main_color(), vid.width, vid.height, false, false); } ); - manager->insert("blit_sw", blit_sw_pass); + basic_rendering->insert("blit_sw", blit_sw_pass); - manager->insert( + basic_rendering->insert( "2d_prepare", [twodee, framebuffer_manager, palette_manager](PassManager& mgr, Rhi&) { @@ -166,74 +178,62 @@ static std::shared_ptr build_pass_manager() twodee->output_height_ = vid.height; } ); - manager->insert("2d", twodee); + basic_rendering->insert("2d", twodee); - manager->insert( - "pp_final_prepare", - [](PassManager& mgr, Rhi&) - { - mgr.set_pass_enabled("pp_final_wipe_prepare", WipeInAction); - mgr.set_pass_enabled("pp_final_wipe", WipeInAction); - mgr.set_pass_enabled("pp_final_wipe_flip", WipeInAction); - } - ); - manager->insert( + // basic_rendering->insert( + // "pp_final_prepare", + // [](PassManager& mgr, Rhi&) + // { + // mgr.set_pass_enabled("pp_final_wipe_prepare", WipeInAction); + // mgr.set_pass_enabled("pp_final_wipe", WipeInAction); + // mgr.set_pass_enabled("pp_final_wipe_flip", WipeInAction); + // } + // ); + basic_rendering->insert( "pp_final_simple_blit_prepare", [pp_simple_blit_pass, framebuffer_manager](PassManager&, Rhi&) { - Handle color = framebuffer_manager->current_main_color(); - if (WipeInAction && !g_wipereverse) - { - // Non-reverse wipes are "fade-outs" from the previous frame. - color = framebuffer_manager->previous_main_color(); - } - pp_simple_blit_pass->set_texture(color, vid.width, vid.height); + framebuffer_manager->swap_post(); + pp_simple_blit_pass->set_texture(framebuffer_manager->current_main_color(), vid.width, vid.height); pp_simple_blit_pass - ->set_output(framebuffer_manager->current_post_color(), vid.width, vid.height, false, false); + ->set_output(framebuffer_manager->current_post_color(), vid.width, vid.height, false, true); } ); - manager->insert("pp_final_simple_blit", pp_simple_blit_pass); - manager->insert( - "pp_final_simple_blit_flip", - [framebuffer_manager](PassManager&, Rhi&) { framebuffer_manager->swap_post(); } - ); - manager->insert( - "pp_final_wipe_prepare", - [pp_wipe_pass, framebuffer_manager](PassManager&, Rhi&) - { - pp_wipe_pass->set_source(framebuffer_manager->previous_post_color(), vid.width, vid.height); - pp_wipe_pass->set_end(framebuffer_manager->current_main_color()); - pp_wipe_pass->set_target(framebuffer_manager->current_post_color(), vid.width, vid.height); - } - ); - manager->insert("pp_final_wipe", pp_wipe_pass); - manager->insert( - "pp_final_wipe_flip", - [framebuffer_manager](PassManager&, Rhi&) { framebuffer_manager->swap_post(); } - ); + basic_rendering->insert("pp_final_simple_blit", pp_simple_blit_pass); + // basic_rendering->insert( + // "pp_final_simple_blit_flip", + // [framebuffer_manager](PassManager&, Rhi&) { framebuffer_manager->swap_post(); } + // ); - manager->insert( + // basic_rendering->insert( + // "pp_final_wipe_flip", + // [framebuffer_manager](PassManager&, Rhi&) { framebuffer_manager->swap_post(); } + // ); + + basic_rendering->insert( "screenshot_prepare", [screenshot_pass, framebuffer_manager](PassManager&, Rhi&) { - screenshot_pass->set_source(framebuffer_manager->previous_post_color(), vid.width, vid.height); + screenshot_pass->set_source(framebuffer_manager->current_post_color(), vid.width, vid.height); } ); - manager->insert("screenshot", screenshot_pass); + basic_rendering->insert("screenshot", screenshot_pass); - manager->insert( + // Composite-present takes the current postprocess result and outputs it to the default framebuffer. + // It also renders imgui and presents the screen. + auto composite_present_rendering = std::make_shared(); + + composite_present_rendering->insert( "final_composite_prepare", [final_composite_pass, framebuffer_manager](PassManager&, Rhi&) { - final_composite_pass->set_texture(framebuffer_manager->previous_post_color(), vid.width, vid.height); - final_composite_pass->set_output(kNullHandle, vid.realwidth, vid.realheight, true, false); + final_composite_pass->set_texture(framebuffer_manager->current_post_color(), vid.width, vid.height); + final_composite_pass->set_output(kNullHandle, vid.realwidth, vid.realheight, true, true); } ); - manager->insert("final_composite", final_composite_pass); - - manager->insert("imgui", imgui_pass); - - manager->insert( + composite_present_rendering->insert("final_composite", final_composite_pass); + composite_present_rendering->insert("imgui", imgui_pass); + composite_present_rendering->insert( "present", [](PassManager&, Rhi& rhi) {}, [framebuffer_manager](PassManager&, Rhi& rhi) @@ -241,25 +241,104 @@ static std::shared_ptr build_pass_manager() rhi.present(); rhi.finish(); framebuffer_manager->reset_post(); - - // TODO fix this: it's an ugly hack to work around issues with wipes - // Why this works: - // - Menus run F_RunWipe which is an inner update loop calling I_FinishUpdate, with this global set - // - After exiting F_RunWipe, g_2d should normally be cleared by I_FinishUpdate - // - Unfortunately, the menu has already run all its draw calls when exiting F_RunWipe - // - That causes a single-frame flash of no 2d content, which is an epilepsy risk. - // - By not clearing the 2d context, we are redrawing 2d every frame of the wipe - // - This "works" because we draw 2d to the normal color buffer, not the postprocessed screen. - // - It does result in the FPS counter being mangled during the wipe though. - // - To fix the issues around wipes, wipes need to be a "sub" game state, and eliminate the inner tic loops. - if (!WipeInAction) - { - g_2d = Twodee(); - } } ); - return manager; + // Normal rendering combines basic rendering and composite-present. + auto normal_rendering = std::make_shared(); + + normal_rendering->insert("resource_manager", resource_manager); + normal_rendering->insert("basic_rendering", basic_rendering); + normal_rendering->insert("composite_present_rendering", composite_present_rendering); + + // Wipe Start Screen Capture rendering + auto wipe_capture_start_rendering = std::make_shared(); + auto wipe_start_blit = std::make_shared(); + + wipe_capture_start_rendering->insert("resource_manager", resource_manager); + wipe_capture_start_rendering->insert("basic_rendering", basic_rendering); + wipe_capture_start_rendering->insert( + "wipe_capture_prepare", + [framebuffer_manager, wipe_start_blit](PassManager&, Rhi&) + { + wipe_start_blit->set_texture(framebuffer_manager->previous_post_color(), vid.width, vid.height); + wipe_start_blit->set_output(framebuffer_manager->wipe_start_color(), vid.width, vid.height, false, true); + } + ); + wipe_capture_start_rendering->insert("wipe_capture", wipe_start_blit); + + // Wipe End Screen Capture rendering + auto wipe_capture_end_rendering = std::make_shared(); + auto wipe_end_blit = std::make_shared(); + + wipe_capture_end_rendering->insert("resource_manager", resource_manager); + wipe_capture_end_rendering->insert("basic_rendering", basic_rendering); + wipe_capture_end_rendering->insert( + "wipe_capture_prepare", + [framebuffer_manager, wipe_end_blit](PassManager&, Rhi&) + { + wipe_end_blit->set_texture(framebuffer_manager->current_post_color(), vid.width, vid.height); + wipe_end_blit->set_output(framebuffer_manager->wipe_end_color(), vid.width, vid.height, false, true); + } + ); + wipe_capture_end_rendering->insert("wipe_capture", wipe_end_blit); + + // Wipe rendering only runs the wipe shader on the start and end screens, and adds composite-present. + auto wipe_rendering = std::make_shared(); + + auto pp_wipe_pass = std::make_shared(); + + wipe_rendering->insert("resource_manager", resource_manager); + wipe_rendering->insert( + "pp_final_wipe_prepare", + [pp_wipe_pass, framebuffer_manager, common_resources_manager](PassManager&, Rhi&) + { + framebuffer_manager->swap_post(); + Handle start = framebuffer_manager->wipe_start_color(); + Handle end = framebuffer_manager->wipe_end_color(); + if (g_wipereverse) + { + std::swap(start, end); + } + pp_wipe_pass->set_start(start); + pp_wipe_pass->set_end(end); + pp_wipe_pass->set_target(framebuffer_manager->current_post_color(), vid.width, vid.height); + } + ); + wipe_rendering->insert("pp_final_wipe", pp_wipe_pass); + wipe_rendering->insert("composite_present_rendering", composite_present_rendering); + + InternalPassData ret; + ret.resource_passmanager = resource_manager; + ret.normal_rendering = normal_rendering; + ret.wipe_capture_start_rendering = wipe_capture_start_rendering; + ret.wipe_capture_end_rendering = wipe_capture_end_rendering; + ret.wipe_rendering = wipe_rendering; + + return ret; +} + +void I_NewTwodeeFrame(void) +{ + g_2d = Twodee(); +} + +void I_NewImguiFrame(void) +{ + // TODO move this to srb2loop + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize.x = vid.realwidth; + io.DisplaySize.y = vid.realheight; + ImGui::NewFrame(); +} + +static void maybe_reinit_passes(Rhi* rhi) +{ + if (rhi_changed(rhi) || !g_passes) + { + g_last_known_rhi = rhi; + g_passes = std::make_unique(build_pass_manager()); + } } void I_FinishUpdate(void) @@ -277,17 +356,34 @@ void I_FinishUpdate(void) } #endif - // TODO move this to srb2loop - ImGuiIO& io = ImGui::GetIO(); - io.DisplaySize.x = vid.realwidth; - io.DisplaySize.y = vid.realheight; - ImGui::NewFrame(); + rhi::Rhi* rhi = sys::get_rhi(sys::g_current_rhi); - if (rhi_changed() || !g_passmanager) + if (rhi == nullptr) { - g_passmanager = build_pass_manager(); + // ??? + return; } + maybe_reinit_passes(rhi); + + g_passes->normal_rendering->render(*rhi); +} + +void I_FinishUpdateWipeStartScreen(void) +{ + if (rendermode == render_none) + { + return; + } + +#ifdef HWRENDER + if (rendermode == render_opengl) + { + finish_legacy_ogl_update(); + return; + } +#endif + rhi::Rhi* rhi = sys::get_rhi(sys::g_current_rhi); if (rhi == nullptr) @@ -296,5 +392,63 @@ void I_FinishUpdate(void) return; } - g_passmanager->render(*rhi); + maybe_reinit_passes(rhi); + + g_passes->wipe_capture_start_rendering->render(*rhi); +} + +void I_FinishUpdateWipeEndScreen(void) +{ + if (rendermode == render_none) + { + return; + } + +#ifdef HWRENDER + if (rendermode == render_opengl) + { + finish_legacy_ogl_update(); + return; + } +#endif + + rhi::Rhi* rhi = sys::get_rhi(sys::g_current_rhi); + + if (rhi == nullptr) + { + // ??? + return; + } + + maybe_reinit_passes(rhi); + + g_passes->wipe_capture_end_rendering->render(*rhi); +} + +void I_FinishUpdateWipe(void) +{ + if (rendermode == render_none) + { + return; + } + +#ifdef HWRENDER + if (rendermode == render_opengl) + { + finish_legacy_ogl_update(); + return; + } +#endif + + rhi::Rhi* rhi = sys::get_rhi(sys::g_current_rhi); + + if (rhi == nullptr) + { + // ??? + return; + } + + maybe_reinit_passes(rhi); + + g_passes->wipe_rendering->render(*rhi); } diff --git a/src/rhi/gl3_core/gl3_core_rhi.cpp b/src/rhi/gl3_core/gl3_core_rhi.cpp index c4f1dd57b..856913d53 100644 --- a/src/rhi/gl3_core/gl3_core_rhi.cpp +++ b/src/rhi/gl3_core/gl3_core_rhi.cpp @@ -334,6 +334,10 @@ constexpr const char* map_uniform_attribute_symbol_name(rhi::UniformName name) return "u_texcoord0_transform"; case rhi::UniformName::kSampler0IsIndexedAlpha: return "u_sampler0_is_indexed_alpha"; + case rhi::UniformName::kWipeColorizeMode: + return "u_wipe_colorize_mode"; + case rhi::UniformName::kWipeEncoreSwizzle: + return "u_wipe_encore_swizzle"; default: return nullptr; } @@ -353,6 +357,10 @@ constexpr const char* map_uniform_enable_define(rhi::UniformName name) return "ENABLE_U_TEXCOORD0_TRANSFORM"; case rhi::UniformName::kSampler0IsIndexedAlpha: return "ENABLE_U_SAMPLER0_IS_INDEXED_ALPHA"; + case rhi::UniformName::kWipeColorizeMode: + return "ENABLE_U_WIPE_COLORIZE_MODE"; + case rhi::UniformName::kWipeEncoreSwizzle: + return "ENABLE_U_WIPE_ENCORE_SWIZZLE"; default: return nullptr; } diff --git a/src/rhi/rhi.cpp b/src/rhi/rhi.cpp index 7e166246a..36c3c7c8e 100644 --- a/src/rhi/rhi.cpp +++ b/src/rhi/rhi.cpp @@ -44,8 +44,11 @@ const ProgramRequirements srb2::rhi::kProgramRequirementsPostprocessWipe = { ProgramVertexInputRequirements { {ProgramVertexInput {VertexAttributeName::kPosition, VertexAttributeFormat::kFloat3, true}, ProgramVertexInput {VertexAttributeName::kTexCoord0, VertexAttributeFormat::kFloat2, true}}}, - ProgramUniformRequirements {{{{{UniformName::kProjection, true}, {UniformName::kModelView, true}}}}}, - ProgramSamplerRequirements {{{SamplerName::kSampler0, true}, {SamplerName::kSampler1, true}}}}; + ProgramUniformRequirements { + {{{{UniformName::kProjection, true}, + {UniformName::kWipeColorizeMode, true}, + {UniformName::kWipeEncoreSwizzle, true}}}}}, + ProgramSamplerRequirements {{{SamplerName::kSampler0, true}, {SamplerName::kSampler1, true}, {SamplerName::kSampler2, true}}}}; const ProgramRequirements& rhi::program_requirements_for_program(PipelineProgram program) noexcept { diff --git a/src/rhi/rhi.hpp b/src/rhi/rhi.hpp index 2af19c118..a6186ba97 100644 --- a/src/rhi/rhi.hpp +++ b/src/rhi/rhi.hpp @@ -196,7 +196,9 @@ enum class UniformName kModelView, kProjection, kTexCoord0Transform, - kSampler0IsIndexedAlpha + kSampler0IsIndexedAlpha, + kWipeColorizeMode, + kWipeEncoreSwizzle }; enum class SamplerName @@ -306,6 +308,10 @@ inline constexpr const UniformFormat uniform_format(UniformName name) noexcept return UniformFormat::kMat3; case UniformName::kSampler0IsIndexedAlpha: return UniformFormat::kInt; + case UniformName::kWipeColorizeMode: + return UniformFormat::kInt; + case UniformName::kWipeEncoreSwizzle: + return UniformFormat::kInt; default: return UniformFormat::kFloat; }