#pragma once #include #include "common/dma/gs.h" #include "common/math/Vector.h" #include "common/util/SmallVector.h" #include "game/graphics/opengl_renderer/BucketRenderer.h" #include "game/graphics/pipelines/opengl.h" /*! * The direct renderer will handle rendering GIFtags directly. * It's named after the DIRECT VIFCode which sends data directly to the GS. * * It should mostly be used for debugging/text stuff as this rendering style does all the math on * the EE and just sends geometry directly to the GS without using the VUs. * * It can be used as a BucketRenderer, or as a subcomponent of another renderer. */ class DirectRenderer : public BucketRenderer { public: DirectRenderer(const std::string& name, int my_id, int batch_size); void init_shaders(ShaderLibrary& sl) override; void draw_debug_window() override; void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override; virtual void pre_render() {} virtual void post_render() {} ~DirectRenderer(); /*! * Render directly from _VIF_ data. * You can optionally provide two vif tags that come in front of data. * These can be set to 0 if you don't have these. */ void render_vif(u32 vif0, u32 vif1, const u8* data, u32 size, SharedRenderState* render_state, ScopedProfilerNode& prof); /*! * Render directly from _GIF_ data. */ void render_gif(const u8* data, u32 size, SharedRenderState* render_state, ScopedProfilerNode& prof); void reset_state(); /*! * If you don't use the render interface, call this at the very end. */ void flush_pending(SharedRenderState* render_state, ScopedProfilerNode& prof); void hack_disable_blend() { m_blend_state.a = GsAlpha::BlendMode::SOURCE; m_blend_state.b = GsAlpha::BlendMode::SOURCE; m_blend_state.c = GsAlpha::BlendMode::SOURCE; m_blend_state.d = GsAlpha::BlendMode::SOURCE; } void set_mipmap(bool en) { m_debug_state.disable_mipmap = !en; } void handle_prim(u64 val, SharedRenderState* render_state, ScopedProfilerNode& prof); void handle_ad(const u8* data, SharedRenderState* render_state, ScopedProfilerNode& prof); void handle_st_packed(const u8* data); void handle_rgbaq_packed(const u8* data); void handle_xyzf2_packed(const u8* data, SharedRenderState* render_state, ScopedProfilerNode& prof); void handle_xyz2_packed(const u8* data, SharedRenderState* render_state, ScopedProfilerNode& prof); void handle_prim_packed(const u8* data, SharedRenderState* render_state, ScopedProfilerNode& prof); void handle_tex0_1_packed(const u8* data); void handle_uv_packed(const u8* data); void handle_rgbaq(u64 val); void handle_xyzf2(u64 val, SharedRenderState* render_state, ScopedProfilerNode& prof); void lookup_textures_again(SharedRenderState* render_state); void reinitialize_gl_state() { m_prim_gl_state_needs_gl_update = true; m_test_state_needs_gl_update = true; m_blend_state_needs_gl_update = true; } protected: virtual void handle_frame(u64 val, SharedRenderState* render_state, ScopedProfilerNode& prof); void handle_scissor(u64 val); void handle_zbuf1(u64 val, SharedRenderState* render_state, ScopedProfilerNode& prof); void handle_test1(u64 val, SharedRenderState* render_state, ScopedProfilerNode& prof); void handle_alpha1(u64 val, SharedRenderState* render_state, ScopedProfilerNode& prof); void handle_pabe(u64 val); void handle_clamp1(u64 val); void handle_tex0_1(u64 val); void handle_tex1_1(u64 val); void handle_texa(u64 val, SharedRenderState* render_state, ScopedProfilerNode& prof); void handle_xyoffset(u64 val); void handle_bitbltbuf(u64 val); void handle_trxpos(u64 val); void handle_trxreg(u64 val); void handle_trxdir(u64 dir, SharedRenderState* render_state, ScopedProfilerNode& prof); void handle_xyzf2_common(u32 x, u32 y, u32 z, u8 f, SharedRenderState* render_state, ScopedProfilerNode& prof, bool advance); void update_gl_prim(SharedRenderState* render_state); void update_gl_blend(); void update_gl_test(); void update_gl_texture(SharedRenderState* render_state, int unit); bool m_offscreen_mode = false; struct TestState { void from_register(GsTest reg); GsTest current_register; bool alpha_test_enable = false; bool prim_alpha_enable = false; GsTest::AlphaTest alpha_test = GsTest::AlphaTest::NOTEQUAL; u8 aref = 0; GsTest::AlphaFail afail = GsTest::AlphaFail::KEEP; bool date = false; bool datm = false; bool zte = true; GsTest::ZTest ztst = GsTest::ZTest::GEQUAL; bool write_rgb = true; bool depth_writes = true; } m_test_state; struct BlendState { void from_register(GsAlpha reg); GsAlpha current_register; GsAlpha::BlendMode a = GsAlpha::BlendMode::SOURCE; GsAlpha::BlendMode b = GsAlpha::BlendMode::DEST; GsAlpha::BlendMode c = GsAlpha::BlendMode::SOURCE; GsAlpha::BlendMode d = GsAlpha::BlendMode::DEST; bool alpha_blend_enable = false; u8 fix = 0; } m_blend_state; // state set through the prim register that requires changing GL stuff. struct PrimGlState { void from_register(GsPrim reg); GsPrim current_register; bool gouraud_enable = false; bool texture_enable = false; bool fogging_enable = false; bool aa_enable = false; bool use_uv = false; // todo: might not require a gl state change bool ctxt = false; // do they ever use ctxt2? bool fix = false; // what does this even do? u32 ta0 = 0; } m_prim_gl_state; static constexpr int TEXTURE_STATE_COUNT = 1; struct TextureState { GsTex0 current_register; u32 texture_base_ptr = 0; bool using_mt4hh = false; bool tcc = false; bool decal = false; bool enable_tex_filt = true; struct ClampState { void from_register(u64 value) { current_register = value; } u64 current_register = 0b101; bool clamp_s = true; bool clamp_t = true; } m_clamp_state; bool used = false; bool compatible_with(const TextureState& other) { return current_register == other.current_register && m_clamp_state.current_register == other.m_clamp_state.current_register && enable_tex_filt == other.enable_tex_filt; } }; // vertices will reference these texture states TextureState m_buffered_tex_state[TEXTURE_STATE_COUNT]; bool m_buffered_tex_state_currently_bound[TEXTURE_STATE_COUNT] = {0}; int m_next_free_tex_state = 0; // this texture state mirrors the current GS register. TextureState m_tex_state_from_reg; // if this is not -1, then it is the index of a texture state in m_buffered_tex_state that // matches m_tex_state_from_reg. int m_current_tex_state_idx = -1; int get_texture_unit_for_current_reg(SharedRenderState* render_state, ScopedProfilerNode& prof); // state set through the prim/rgbaq register that doesn't require changing GL stuff struct PrimBuildState { GsPrim::Kind kind = GsPrim::Kind::PRIM_7; math::Vector rgba_reg = math::Vector{0, 0, 0, 0}; math::Vector st_reg; std::array, 3> building_rgba; std::array, 3> building_vert; std::array, 3> building_stq; int building_idx = 0; int tri_strip_startup = 0; float Q = 1.0; } m_prim_building; struct Vertex { math::Vector xyzf; math::Vector stq; math::Vector rgba; u8 tex_unit; u8 tcc; u8 decal; u8 fog_enable; u8 use_uv; math::Vector __pad; // this can be simplified to use gs coords, if needed math::Vector scissor; }; static_assert(sizeof(Vertex) == 64); static_assert(offsetof(Vertex, tex_unit) == 32); struct PrimitiveBuffer { PrimitiveBuffer(int max_triangles); std::vector vertices; int vert_count = 0; int max_verts = 0; float x_off = 0; float y_off = 0; // leave 6 free on the end so we always have room to flush one last primitive. bool is_full() { return max_verts < (vert_count + 18); } void push(const math::Vector& rgba, const math::Vector& vert, const math::Vector& stq, const math::Vector& scissor, int unit, bool tcc, bool decal, bool fog_enable, bool use_uv); } m_prim_buffer; // the scissor state tends to be shared across buckets, so it is static here static struct ScissorState { u16 scax0 = 0, scay0 = 0; u16 scax1 = 0, scay1 = 0; } m_scissor; // however the toggle for it is per-bucket bool m_scissor_enable = false; struct BufferBlitState { // used to keep track of blit progress u8 expect = 0; // blit buffer source+dest settings u16 sbp = 0, dbp = 0; u8 sbw = 0, dbw = 0; u8 spsm = 0, dpsm = 0; // transfer pos u16 ssax = 0, dsax = 0; u16 ssay = 0, dsay = 0; // transfer region u16 width = 0, height = 0; // transfer dir u8 pixel_dir = 0; // gif IMAGE transfer size u16 qwc = 0; } m_blit_buf_state; struct { GLuint vertex_buffer; GLuint vao; u32 vertex_buffer_bytes = 0; u32 vertex_buffer_max_verts = 0; float color_mult = 1.0; float alpha_mult = 1.0; } m_ogl; struct { GLint alpha_min, alpha_max; GLint normal_shader_id = -1; } m_uniforms; struct { bool disable_texture = false; bool wireframe = false; bool red = false; bool always_draw = false; bool disable_mipmap = true; } m_debug_state; struct { int triangles = 0; int draw_calls = 0; int flush_from_tex_0 = 0; int flush_from_tex_1 = 0; int flush_from_zbuf = 0; int flush_from_test = 0; int flush_from_ta0 = 0; int flush_from_alpha = 0; int flush_from_clamp = 0; int flush_from_prim = 0; int flush_from_state_exhaust = 0; } m_stats; bool m_prim_gl_state_needs_gl_update = true; bool m_test_state_needs_gl_update = true; bool m_test_state_needs_double_draw = false; float m_double_draw_aref = 0; bool m_blend_state_needs_gl_update = true; struct SpriteMode { bool do_first_draw = true; } m_sprite_mode; };