From fc05db744194c0c83d8a305239c52bedbca61951 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sat, 26 Oct 2024 14:09:46 -0500 Subject: [PATCH] rhi: Use renderpasses as a stack This way when you pop a render pass, it will restore the previous renderpass state if there was one. Allows for rendering code to enter and leave the default render pass without interfering with other drawing code. --- src/hwr2/upscale_backbuffer.cpp | 2 +- src/i_video_common.cpp | 6 +-- src/rhi/gl2/gl2_rhi.cpp | 74 +++++++++++++++++++++------------ src/rhi/gl2/gl2_rhi.hpp | 12 ++++-- src/rhi/gles2/gles2_rhi.cpp | 6 +-- src/rhi/gles2/gles2_rhi.hpp | 6 +-- src/rhi/rhi.hpp | 6 +-- 7 files changed, 69 insertions(+), 43 deletions(-) diff --git a/src/hwr2/upscale_backbuffer.cpp b/src/hwr2/upscale_backbuffer.cpp index 9a71509b4..d7efb5c3e 100644 --- a/src/hwr2/upscale_backbuffer.cpp +++ b/src/hwr2/upscale_backbuffer.cpp @@ -68,5 +68,5 @@ void UpscaleBackbuffer::begin_pass(Rhi& rhi) begin_info.depth_store_op = rhi::AttachmentStoreOp::kStore; begin_info.stencil_load_op = rhi::AttachmentLoadOp::kLoad; begin_info.stencil_store_op = rhi::AttachmentStoreOp::kStore; - rhi.begin_render_pass(begin_info); + rhi.push_render_pass(begin_info); } diff --git a/src/i_video_common.cpp b/src/i_video_common.cpp index 1015ee593..2a0046b1c 100644 --- a/src/i_video_common.cpp +++ b/src/i_video_common.cpp @@ -273,9 +273,9 @@ void I_FinishUpdate(void) // better hope the drawing code left the context in a render pass, I guess g_hw_state.twodee_renderer->flush(*rhi, g_2d); - rhi->end_render_pass(); + rhi->pop_render_pass(); - rhi->begin_default_render_pass(true); + rhi->push_default_render_pass(true); // Upscale draw the backbuffer (with postprocessing maybe?) if (cv_scr_scale.value != FRACUNIT) @@ -323,7 +323,7 @@ void I_FinishUpdate(void) break; } - rhi->end_render_pass(); + rhi->pop_render_pass(); postframe_update(*rhi); diff --git a/src/rhi/gl2/gl2_rhi.cpp b/src/rhi/gl2/gl2_rhi.cpp index 70e4076f9..0c3e49516 100644 --- a/src/rhi/gl2/gl2_rhi.cpp +++ b/src/rhi/gl2/gl2_rhi.cpp @@ -985,11 +985,8 @@ void Gl2Rhi::present() platform_->present(); } -void Gl2Rhi::begin_default_render_pass(bool clear) +void Gl2Rhi::apply_default_framebuffer(bool clear) { - SRB2_ASSERT(platform_ != nullptr); - SRB2_ASSERT(current_render_pass_.has_value() == false); - const Rect fb_rect = platform_->get_default_framebuffer_dimensions(); gl_->BindFramebuffer(GL_FRAMEBUFFER, 0); @@ -1007,14 +1004,10 @@ void Gl2Rhi::begin_default_render_pass(bool clear) gl_->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); GL_ASSERT; } - - current_render_pass_ = Gl2Rhi::DefaultRenderPassState {}; } -void Gl2Rhi::begin_render_pass(const RenderPassBeginInfo& info) +void Gl2Rhi::apply_framebuffer(const RenderPassBeginInfo& info, bool allow_clear) { - SRB2_ASSERT(current_render_pass_.has_value() == false); - auto fb_itr = framebuffers_.find(Gl2FramebufferKey {info.color_attachment, info.depth_stencil_attachment}); if (fb_itr == framebuffers_.end()) { @@ -1070,26 +1063,55 @@ void Gl2Rhi::begin_render_pass(const RenderPassBeginInfo& info) clear_bits |= GL_STENCIL_BUFFER_BIT; } - if (clear_bits != 0) + if (clear_bits != 0 && allow_clear) { gl_->Clear(clear_bits); GL_ASSERT; } - - current_render_pass_ = info; } -void Gl2Rhi::end_render_pass() +void Gl2Rhi::push_default_render_pass(bool clear) { - SRB2_ASSERT(current_render_pass_.has_value() == true); + SRB2_ASSERT(platform_ != nullptr); + + render_pass_stack_.emplace_back(Gl2Rhi::DefaultRenderPassState { clear }); + apply_default_framebuffer(clear); +} + +void Gl2Rhi::push_render_pass(const RenderPassBeginInfo& info) +{ + render_pass_stack_.push_back(info); + apply_framebuffer(info, false); +} + +void Gl2Rhi::pop_render_pass() +{ + SRB2_ASSERT(render_pass_stack_.empty() == false); current_program_ = std::nullopt; - current_render_pass_ = std::nullopt; + + render_pass_stack_.pop_back(); + + if (!render_pass_stack_.empty()) + { + RenderPassState& state = *render_pass_stack_.rbegin(); + // We must not clear the framebuffer when restoring a previous framebuffer, + // even if the clear was previously requested. + auto visitor = srb2::Overload { + [this](const DefaultRenderPassState& s) { + apply_default_framebuffer(false); + }, + [this](const RenderPassBeginInfo& info) { + apply_framebuffer(info, false); + } + }; + std::visit(visitor, state); + } } void Gl2Rhi::bind_program(Handle program) { - SRB2_ASSERT(current_render_pass_.has_value() == true); + SRB2_ASSERT(render_pass_stack_.empty() == false); Gl2Program& prog = program_slab_[program]; gl_->UseProgram(prog.program); @@ -1276,7 +1298,7 @@ void Gl2Rhi::set_uniform(const char* name, glm::mat4 value) void Gl2Rhi::set_sampler(const char* name, uint32_t slot, Handle texture) { SRB2_ASSERT(slot >= 0 && slot < kMaxSamplers); - SRB2_ASSERT(current_program_.has_value() && current_render_pass_.has_value()); + SRB2_ASSERT(current_program_.has_value() && render_pass_stack_.empty() == false); Gl2Program& prog = program_slab_[*current_program_]; SRB2_ASSERT(texture_slab_.is_valid(texture)); @@ -1414,7 +1436,7 @@ void Gl2Rhi::set_rasterizer_state(const RasterizerStateDesc& desc) void Gl2Rhi::set_viewport(const Rect& rect) { - SRB2_ASSERT(current_render_pass_.has_value() == true); + SRB2_ASSERT(render_pass_stack_.empty() == false); gl_->Viewport(rect.x, rect.y, rect.w, rect.h); GL_ASSERT; @@ -1422,7 +1444,7 @@ void Gl2Rhi::set_viewport(const Rect& rect) void Gl2Rhi::draw(uint32_t vertex_count, uint32_t first_vertex) { - SRB2_ASSERT(current_render_pass_.has_value() == true); + SRB2_ASSERT(render_pass_stack_.empty() == false); gl_->DrawArrays(map_primitive_mode(current_primitive_type_), first_vertex, vertex_count); GL_ASSERT; @@ -1449,7 +1471,7 @@ void Gl2Rhi::draw_indexed(uint32_t index_count, uint32_t first_index) void Gl2Rhi::read_pixels(const Rect& rect, PixelFormat format, tcb::span out) { - SRB2_ASSERT(current_render_pass_.has_value()); + SRB2_ASSERT(render_pass_stack_.empty() == false); std::tuple gl_format = map_pixel_data_format(format); GLenum layout = std::get<0>(gl_format); @@ -1475,7 +1497,7 @@ void Gl2Rhi::read_pixels(const Rect& rect, PixelFormat format, tcb::span= 0); SRB2_ASSERT(rect.y >= 0); @@ -1493,7 +1515,7 @@ void Gl2Rhi::read_pixels(const Rect& rect, PixelFormat format, tcb::span= 0); SRB2_ASSERT(src_region.y >= 0); diff --git a/src/rhi/gl2/gl2_rhi.hpp b/src/rhi/gl2/gl2_rhi.hpp index 0d32aca36..0863ed311 100644 --- a/src/rhi/gl2/gl2_rhi.hpp +++ b/src/rhi/gl2/gl2_rhi.hpp @@ -125,9 +125,10 @@ class Gl2Rhi final : public Rhi struct DefaultRenderPassState { + bool clear = false; }; using RenderPassState = std::variant; - std::optional current_render_pass_; + std::vector render_pass_stack_; std::optional> current_program_; PrimitiveType current_primitive_type_ = PrimitiveType::kPoints; uint32_t index_buffer_offset_ = 0; @@ -141,6 +142,9 @@ class Gl2Rhi final : public Rhi CompareFunc stencil_front_func_; CompareFunc stencil_back_func_; + void apply_default_framebuffer(bool clear); + void apply_framebuffer(const RenderPassBeginInfo& info, bool allow_clear); + public: Gl2Rhi(std::unique_ptr&& platform, GlLoadFunc load_func); virtual ~Gl2Rhi(); @@ -179,9 +183,9 @@ public: ) override; // Graphics context functions - virtual void begin_default_render_pass(bool clear) override; - virtual void begin_render_pass(const RenderPassBeginInfo& info) override; - virtual void end_render_pass() override; + virtual void push_default_render_pass(bool clear) override; + virtual void push_render_pass(const RenderPassBeginInfo& info) override; + virtual void pop_render_pass() override; virtual void bind_program(Handle program) override; virtual void bind_vertex_attrib( const char* name, diff --git a/src/rhi/gles2/gles2_rhi.cpp b/src/rhi/gles2/gles2_rhi.cpp index 3bdfff0f5..0faf3b5d6 100644 --- a/src/rhi/gles2/gles2_rhi.cpp +++ b/src/rhi/gles2/gles2_rhi.cpp @@ -923,7 +923,7 @@ void Gles2Rhi::present() platform_->present(); } -void Gles2Rhi::begin_default_render_pass(Handle ctx) +void Gles2Rhi::push_default_render_pass(Handle ctx) { SRB2_ASSERT(platform_ != nullptr); SRB2_ASSERT(graphics_context_active_ == true); @@ -942,7 +942,7 @@ void Gles2Rhi::begin_default_render_pass(Handle ctx) current_render_pass_ = Gles2Rhi::DefaultRenderPassState {}; } -void Gles2Rhi::begin_render_pass(Handle ctx, const RenderPassBeginInfo& info) +void Gles2Rhi::push_render_pass(Handle ctx, const RenderPassBeginInfo& info) { SRB2_ASSERT(graphics_context_active_ == true && graphics_context_generation_ == ctx.generation()); SRB2_ASSERT(current_render_pass_.has_value() == false); @@ -981,7 +981,7 @@ void Gles2Rhi::begin_render_pass(Handle ctx, const RenderPassBe current_render_pass_ = info; } -void Gles2Rhi::end_render_pass(Handle ctx) +void Gles2Rhi::pop_render_pass(Handle ctx) { SRB2_ASSERT(graphics_context_active_ == true && graphics_context_generation_ == ctx.generation()); SRB2_ASSERT(current_render_pass_.has_value() == true); diff --git a/src/rhi/gles2/gles2_rhi.hpp b/src/rhi/gles2/gles2_rhi.hpp index d9e7a63cb..7c3829e1e 100644 --- a/src/rhi/gles2/gles2_rhi.hpp +++ b/src/rhi/gles2/gles2_rhi.hpp @@ -133,9 +133,9 @@ public: virtual void end_graphics(Handle&& ctx) override; // Graphics context functions - virtual void begin_default_render_pass(Handle ctx) override; - virtual void begin_render_pass(Handle ctx, const RenderPassBeginInfo& info) override; - virtual void end_render_pass(Handle ctx) override; + virtual void push_default_render_pass(Handle ctx) override; + virtual void push_render_pass(Handle ctx, const RenderPassBeginInfo& info) override; + virtual void pop_render_pass(Handle ctx) override; virtual void bind_pipeline(Handle ctx, Handle pipeline) override; virtual void update_bindings(Handle ctx, const UpdateBindingsInfo& info) override; virtual void update_uniforms(Handle ctx, tcb::span uniforms) override; diff --git a/src/rhi/rhi.hpp b/src/rhi/rhi.hpp index cab068ff6..4f141574b 100644 --- a/src/rhi/rhi.hpp +++ b/src/rhi/rhi.hpp @@ -606,9 +606,9 @@ struct Rhi ) = 0; // Graphics context functions - virtual void begin_default_render_pass(bool clear) = 0; - virtual void begin_render_pass(const RenderPassBeginInfo& info) = 0; - virtual void end_render_pass() = 0; + virtual void push_default_render_pass(bool clear) = 0; + virtual void push_render_pass(const RenderPassBeginInfo& info) = 0; + virtual void pop_render_pass() = 0; virtual void bind_program(Handle program) = 0; virtual void bind_vertex_attrib( const char* name,