diff --git a/game/graphics/opengl_renderer/sprite/GlowRenderer.cpp b/game/graphics/opengl_renderer/sprite/GlowRenderer.cpp index 5caf1ed2d3..1bf0f6fa15 100644 --- a/game/graphics/opengl_renderer/sprite/GlowRenderer.cpp +++ b/game/graphics/opengl_renderer/sprite/GlowRenderer.cpp @@ -617,7 +617,9 @@ void GlowRenderer::debug_draw_probe_copies(SharedRenderState* render_state, /*! * Draw all pending sprites. */ -void GlowRenderer::flush(SharedRenderState* render_state, ScopedProfilerNode& prof) { +void GlowRenderer::flush(SharedRenderState* render_state, + ScopedProfilerNode& prof, + bool update_depth_blit) { m_debug.num_sprites = m_next_sprite; if (!m_next_sprite) { // no sprites submitted. @@ -626,7 +628,9 @@ void GlowRenderer::flush(SharedRenderState* render_state, ScopedProfilerNode& pr // copy depth from framebuffer to a temporary buffer // (this is a bit wasteful) - blit_depth(render_state); + if (update_depth_blit) { + blit_depth(render_state); + } // generate vertex/index data for probes u32 probe_idx_start = m_next_index; diff --git a/game/graphics/opengl_renderer/sprite/GlowRenderer.h b/game/graphics/opengl_renderer/sprite/GlowRenderer.h index a3ba19734b..74d1c560f8 100644 --- a/game/graphics/opengl_renderer/sprite/GlowRenderer.h +++ b/game/graphics/opengl_renderer/sprite/GlowRenderer.h @@ -8,7 +8,7 @@ class GlowRenderer { SpriteGlowOutput* alloc_sprite(); void cancel_sprite(); - void flush(SharedRenderState* render_state, ScopedProfilerNode& prof); + void flush(SharedRenderState* render_state, ScopedProfilerNode& prof, bool update_depth_blit); void draw_debug_window(); // Vertex can hold all possible values for all passes. The total number of vertices is very small diff --git a/game/graphics/opengl_renderer/sprite/Sprite3.cpp b/game/graphics/opengl_renderer/sprite/Sprite3.cpp index 647ca106d7..375bb37990 100644 --- a/game/graphics/opengl_renderer/sprite/Sprite3.cpp +++ b/game/graphics/opengl_renderer/sprite/Sprite3.cpp @@ -556,6 +556,7 @@ void Sprite3::draw_debug_window() { m_debug_stats.count_2d_grp0); ImGui::Text("2D Group 1 (HUD) blocks: %d sprites: %d", m_debug_stats.blocks_2d_grp1, m_debug_stats.count_2d_grp1); + ImGui::Text("Glow: %d sprites, %d flush", m_debug_stats.count_glow, m_debug_stats.glow_flushes); ImGui::Checkbox("Culling", &m_enable_culling); ImGui::Checkbox("2d", &m_2d_enable); ImGui::Checkbox("Glow", &m_enable_glow); @@ -564,6 +565,9 @@ void Sprite3::draw_debug_window() { ImGui::Checkbox("Distort", &m_distort_enable); ImGui::Checkbox("Distort instancing", &m_enable_distort_instancing); ImGui::Separator(); + + const char* names[] = {"once", "always", "auto"}; + ImGui::ListBox("Glow flush mode", &m_glow_flush_mode, names, 3); m_glow_renderer.draw_debug_window(); } diff --git a/game/graphics/opengl_renderer/sprite/Sprite3.h b/game/graphics/opengl_renderer/sprite/Sprite3.h index a128a86173..54cb3ef06e 100644 --- a/game/graphics/opengl_renderer/sprite/Sprite3.h +++ b/game/graphics/opengl_renderer/sprite/Sprite3.h @@ -68,9 +68,26 @@ class Sprite3 : public BucketRenderer { void flush_sprites(SharedRenderState* render_state, ScopedProfilerNode& prof, bool double_draw); GlowRenderer m_glow_renderer; + enum GlowFlushMode { SINGLE = 0, ALWAYS = 1, AUTO = 2 }; + int m_glow_flush_mode = SINGLE; void glow_dma_and_draw(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof); + void glow_single_flush(DmaFollower& dma, + DmaTransfer xfer, + const SpriteGlowConsts& consts, + SharedRenderState* render_state, + ScopedProfilerNode& prof); + void glow_always_flush(DmaFollower& dma, + DmaTransfer xfer, + const SpriteGlowConsts& consts, + SharedRenderState* render_state, + ScopedProfilerNode& prof); + void glow_auto_flush(DmaFollower& dma, + DmaTransfer xfer, + const SpriteGlowConsts& consts, + SharedRenderState* render_state, + ScopedProfilerNode& prof); struct SpriteDistorterSetup { GifTag gif_tag; @@ -164,6 +181,8 @@ class Sprite3 : public BucketRenderer { int count_2d_grp0 = 0; int blocks_2d_grp1 = 0; int count_2d_grp1 = 0; + int count_glow = 0; + int glow_flushes = 0; } m_debug_stats; bool m_enable_distort_instancing = true; diff --git a/game/graphics/opengl_renderer/sprite/Sprite3_Glow.cpp b/game/graphics/opengl_renderer/sprite/Sprite3_Glow.cpp index da5665a8f6..7ef7d3e922 100644 --- a/game/graphics/opengl_renderer/sprite/Sprite3_Glow.cpp +++ b/game/graphics/opengl_renderer/sprite/Sprite3_Glow.cpp @@ -169,6 +169,174 @@ bool glow_math(const SpriteGlowConsts* consts, return true; } +void Sprite3::glow_always_flush(DmaFollower& dma, + DmaTransfer xfer, + const SpriteGlowConsts& consts, + SharedRenderState* render_state, + ScopedProfilerNode& prof) { + bool needs_blit = true; + while (xfer.size_bytes == 16) { + auto vecdata_xfer = dma.read_and_advance(); + auto shader_xfer = dma.read_and_advance(); + auto call = dma.read_and_advance(); + (void)call; + + u32 num_sprites; + memcpy(&num_sprites, xfer.data, 4); + ASSERT(num_sprites == 1); // always, for whatever reason. + + ASSERT(vecdata_xfer.size_bytes == 4 * 16); + ASSERT(shader_xfer.size_bytes == 5 * 16); + + if (m_enable_glow) { + auto* out = m_glow_renderer.alloc_sprite(); + if (!glow_math(&consts, vecdata_xfer.data, shader_xfer.data, out)) { + m_glow_renderer.cancel_sprite(); + } else { + m_debug_stats.count_glow++; + m_debug_stats.glow_flushes++; + m_glow_renderer.flush(render_state, prof, needs_blit); + needs_blit = false; + } + } + + xfer = dma.read_and_advance(); + while (xfer.size_bytes == 0 && xfer.vifcode0().kind == VifCode::Kind::NOP && + xfer.vifcode1().kind == VifCode::Kind::NOP) { + xfer = dma.read_and_advance(); + } + } +} + +bool overlap_test(const SpriteGlowOutput& a, const SpriteGlowOutput& b) { + if (a.first_clear_pos[0].x() > b.first_clear_pos[1].x() || + b.first_clear_pos[0].x() > a.first_clear_pos[1].x() || + a.first_clear_pos[0].y() > b.first_clear_pos[1].y() || + b.first_clear_pos[0].y() > a.first_clear_pos[1].y()) { + return false; + } else { + return true; + } +} + +struct Bucket { + std::vector sprites; +}; + +struct SpriteDivider { + void add_sprite(const SpriteGlowOutput& spr); + std::vector buckets; +}; + +void SpriteDivider::add_sprite(const SpriteGlowOutput& spr) { + bool added_to_existing = false; + + for (auto& bucket : buckets) { + + bool fail = false; + for (auto& s : bucket.sprites) { + if (overlap_test(s, spr)) { + fail = true; + break; + } + } + if (fail) { + continue; + } + + bucket.sprites.push_back(spr); + return; + } + + if (!added_to_existing) { + buckets.push_back({{spr}}); + } +} + +void Sprite3::glow_auto_flush(DmaFollower& dma, + DmaTransfer xfer, + const SpriteGlowConsts& consts, + SharedRenderState* render_state, + ScopedProfilerNode& prof) { + SpriteDivider divider; + while (xfer.size_bytes == 16) { + auto vecdata_xfer = dma.read_and_advance(); + auto shader_xfer = dma.read_and_advance(); + auto call = dma.read_and_advance(); + (void)call; + + u32 num_sprites; + memcpy(&num_sprites, xfer.data, 4); + ASSERT(num_sprites == 1); // always, for whatever reason. + + ASSERT(vecdata_xfer.size_bytes == 4 * 16); + ASSERT(shader_xfer.size_bytes == 5 * 16); + + if (m_enable_glow) { + SpriteGlowOutput out; + if (!glow_math(&consts, vecdata_xfer.data, shader_xfer.data, &out)) { + } else { + m_debug_stats.count_glow++; + divider.add_sprite(out); + } + } + + xfer = dma.read_and_advance(); + while (xfer.size_bytes == 0 && xfer.vifcode0().kind == VifCode::Kind::NOP && + xfer.vifcode1().kind == VifCode::Kind::NOP) { + xfer = dma.read_and_advance(); + } + } + + bool blit = true; + for (auto& bucket : divider.buckets) { + m_debug_stats.glow_flushes++; + for (auto& s : bucket.sprites) { + *m_glow_renderer.alloc_sprite() = s; + } + m_glow_renderer.flush(render_state, prof, blit); + blit = false; + } +} + +void Sprite3::glow_single_flush(DmaFollower& dma, + DmaTransfer xfer, + const SpriteGlowConsts& consts, + SharedRenderState* render_state, + ScopedProfilerNode& prof) { + while (xfer.size_bytes == 16) { + auto vecdata_xfer = dma.read_and_advance(); + auto shader_xfer = dma.read_and_advance(); + auto call = dma.read_and_advance(); + (void)call; + + u32 num_sprites; + memcpy(&num_sprites, xfer.data, 4); + ASSERT(num_sprites == 1); // always, for whatever reason. + + ASSERT(vecdata_xfer.size_bytes == 4 * 16); + ASSERT(shader_xfer.size_bytes == 5 * 16); + + if (m_enable_glow) { + auto* out = m_glow_renderer.alloc_sprite(); + if (!glow_math(&consts, vecdata_xfer.data, shader_xfer.data, out)) { + m_glow_renderer.cancel_sprite(); + } else { + m_debug_stats.count_glow++; + } + } + + xfer = dma.read_and_advance(); + while (xfer.size_bytes == 0 && xfer.vifcode0().kind == VifCode::Kind::NOP && + xfer.vifcode1().kind == VifCode::Kind::NOP) { + xfer = dma.read_and_advance(); + } + } + + m_debug_stats.glow_flushes++; + m_glow_renderer.flush(render_state, prof, true); +} + /*! * Handle glow dma and draw glow sprites using GlowRenderer */ @@ -200,32 +368,18 @@ void Sprite3::glow_dma_and_draw(DmaFollower& dma, control_xfer.vifcode1().kind == VifCode::Kind::NOP) { control_xfer = dma.read_and_advance(); } - while (control_xfer.size_bytes == 16) { - auto vecdata_xfer = dma.read_and_advance(); - auto shader_xfer = dma.read_and_advance(); - auto call = dma.read_and_advance(); - (void)call; - u32 num_sprites; - memcpy(&num_sprites, control_xfer.data, 4); - ASSERT(num_sprites == 1); // always, for whatever reason. - - ASSERT(vecdata_xfer.size_bytes == 4 * 16); - ASSERT(shader_xfer.size_bytes == 5 * 16); - - if (m_enable_glow) { - auto* out = m_glow_renderer.alloc_sprite(); - if (!glow_math(&consts, vecdata_xfer.data, shader_xfer.data, out)) { - m_glow_renderer.cancel_sprite(); - } - } - - control_xfer = dma.read_and_advance(); - while (control_xfer.size_bytes == 0 && control_xfer.vifcode0().kind == VifCode::Kind::NOP && - control_xfer.vifcode1().kind == VifCode::Kind::NOP) { - control_xfer = dma.read_and_advance(); - } + switch (m_glow_flush_mode) { + case SINGLE: + glow_single_flush(dma, control_xfer, consts, render_state, prof); + break; + case ALWAYS: + glow_always_flush(dma, control_xfer, consts, render_state, prof); + break; + case AUTO: + glow_auto_flush(dma, control_xfer, consts, render_state, prof); + break; + default: + ASSERT_NOT_REACHED(); } - - m_glow_renderer.flush(render_state, prof); }