From 0fcc7eb8e91e5138c45713ec5ece1a3309e19175 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sun, 22 Jan 2023 18:30:31 -0500 Subject: [PATCH] [merc2] Support emerc (#2147) This adds environment mapping support to `Merc2`, and turns it on for Jak 1 and Jak 2. - The performance is much better - Jak 1 can be toggled back to the old behavior with `(set! *emerc-hack* #f)`. The new environment mapping is identical to the old one everywhere I checked. - Jak 1 still falls back to generic for ripple/texscroll/blerc/eyes - there's still no dynamic texture or vertex updating support. The eye detection stuff will sometimes flag stuff as eyes which is not eyes, which is fine, but means that generic will be used in some places where emerc could be used. For example, the shiny plates on jak's arm will be drawn with generic because jak has eyes. - Jak 2 hasn't been checked super carefully against PCSX2 yet. - Jak 2 still isn't technically using emerc, but instead putting emerc models in the merc bucket. - The interface to merc is a lot different now and totally custom OpenGOAL DMA code. The original merc drawing asm doesn't run anymore. - The FR3 format changed - Something funky going on with foreground lighting in escape, but doesn't seem to be related to this change? Performance comparison, jak 1, in likely the most generic-merc heavy spot: ![image](https://user-images.githubusercontent.com/48171810/213882718-feb2ab59-95a9-44a2-b0e5-95fba860c7b0.png) ![image](https://user-images.githubusercontent.com/48171810/213882736-8dbbf4c9-6bbf-4d0b-96ce-78d63274660c.png) --- common/custom_data/TFrag3Data.cpp | 4 + common/custom_data/Tfrag3Data.h | 5 +- common/dma/dma.h | 1 + common/dma/gs.h | 2 +- decompiler/level_extractor/MercData.cpp | 88 +- decompiler/level_extractor/MercData.h | 6 + decompiler/level_extractor/extract_level.cpp | 2 +- decompiler/level_extractor/extract_merc.cpp | 153 +- decompiler/level_extractor/extract_merc.h | 3 +- docs/progress-notes/jak1/scratch/merc_asm.asm | 2 +- docs/progress-notes/jak2/emerc.md | 2190 +++++++++++++++++ .../opengl_renderer/OpenGLRenderer.cpp | 1 + game/graphics/opengl_renderer/Shader.cpp | 1 + game/graphics/opengl_renderer/Shader.h | 1 + .../background/background_common.cpp | 8 +- .../background/background_common.h | 1 + .../opengl_renderer/foreground/Generic2.cpp | 1 + .../foreground/Generic2_OpenGL.cpp | 3 + .../opengl_renderer/foreground/Merc2.cpp | 456 ++-- .../opengl_renderer/foreground/Merc2.h | 62 +- .../opengl_renderer/shaders/emerc.frag | 32 + .../opengl_renderer/shaders/emerc.vert | 132 + .../opengl_renderer/shaders/merc2.frag | 16 +- .../opengl_renderer/shaders/merc2.vert | 18 +- game/mips2c/jak1_functions/bones.cpp | 17 + game/mips2c/jak2_functions/foreground.cpp | 15 + goal_src/jak1/engine/gfx/foreground/bones.gc | 178 +- .../jak2/engine/gfx/foreground/foreground.gc | 203 +- 28 files changed, 3168 insertions(+), 433 deletions(-) create mode 100644 docs/progress-notes/jak2/emerc.md create mode 100644 game/graphics/opengl_renderer/shaders/emerc.frag create mode 100644 game/graphics/opengl_renderer/shaders/emerc.vert diff --git a/common/custom_data/TFrag3Data.cpp b/common/custom_data/TFrag3Data.cpp index 72f774793a..4ae886e973 100644 --- a/common/custom_data/TFrag3Data.cpp +++ b/common/custom_data/TFrag3Data.cpp @@ -265,6 +265,10 @@ void MercEffect::serialize(Serializer& ser) { for (auto& draw : draws) { draw.serialize(ser); } + + ser.from_ptr(&envmap_mode); + ser.from_ptr(&envmap_texture); + ser.from_ptr(&has_envmap); } void MercModel::serialize(Serializer& ser) { diff --git a/common/custom_data/Tfrag3Data.h b/common/custom_data/Tfrag3Data.h index 9243fc3016..e4dec9ef00 100644 --- a/common/custom_data/Tfrag3Data.h +++ b/common/custom_data/Tfrag3Data.h @@ -53,7 +53,7 @@ enum MemoryUsageCategory { NUM_CATEGORIES }; -constexpr int TFRAG3_VERSION = 21; +constexpr int TFRAG3_VERSION = 22; // These vertices should be uploaded to the GPU at load time and don't change struct PreloadedVertex { @@ -370,6 +370,9 @@ struct MercDraw { struct MercEffect { std::vector draws; + DrawMode envmap_mode; + u32 envmap_texture; + bool has_envmap = false; void serialize(Serializer& ser); }; diff --git a/common/dma/dma.h b/common/dma/dma.h index 6b50e66000..ef320cc10d 100644 --- a/common/dma/dma.h +++ b/common/dma/dma.h @@ -113,6 +113,7 @@ struct VifCode { ITOP = 0b100, STMOD = 0b101, PC_PORT = 0b1000, // not a valid PS2 VIF code, but we use this to signal PC-PORT specific stuff + PC_PORT2 = 0b1001, MSK3PATH = 0b110, MARK = 0b111, FLUSHE = 0b10000, diff --git a/common/dma/gs.h b/common/dma/gs.h index 73b7a35464..f5171d3fd7 100644 --- a/common/dma/gs.h +++ b/common/dma/gs.h @@ -424,7 +424,7 @@ class DrawMode { SRC_DST_FIX_DST = 4, // fix = 64 ZERO_SRC_SRC_DST = 5, SRC_SRC_SRC_SRC = 6, - SRC_0_DST_DST = 7 + SRC_0_DST_DST = 7 // Note: requires color_mult tricks }; enum class AlphaTest { diff --git a/decompiler/level_extractor/MercData.cpp b/decompiler/level_extractor/MercData.cpp index 64382057ce..5d932ebdaf 100644 --- a/decompiler/level_extractor/MercData.cpp +++ b/decompiler/level_extractor/MercData.cpp @@ -2,6 +2,7 @@ #include "common/dma/gs.h" +#include "decompiler/ObjectFile/LinkedObjectFile.h" #include "decompiler/util/DecompilerTypeSystem.h" #include "third-party/fmt/core.h" @@ -199,6 +200,44 @@ std::string MercShader::print() const { return result; } +MercShader make_shader(Ref& ref, bool expected_eop) { + // adgif0 + MercShader shader; + u8 adgif0_addr = deref_u8(ref, 8); + ASSERT(adgif0_addr == (u8)GsRegisterAddress::TEX0_1); + shader.output_offset = deref_u32(ref, 3); + shader.tex0 = GsTex0(deref_u64(ref, 0)); + ref.byte_offset += 16; + + // adgif1 + u8 adgif1_addr = deref_u8(ref, 8); + ASSERT(adgif1_addr == (u8)GsRegisterAddress::TEX1_1); + shader.tex1 = GsTex1(deref_u64(ref, 0)); + u16 stash = deref_u32(ref, 3); + shader.original_tex = deref_u32(ref, 2); + shader.next_strip_nloop = stash & 0x7fff; + ASSERT((!!(stash & 0x8000)) == expected_eop); // set eop on last + ref.byte_offset += 16; + + // adgif2 + u8 adgif2_addr = deref_u8(ref, 8); + ASSERT(adgif2_addr == (u8)GsRegisterAddress::MIPTBP1_1); + ref.byte_offset += 16; + + // adgif3 + u8 adgif3_addr = deref_u8(ref, 8); + ASSERT(adgif3_addr == (u8)GsRegisterAddress::CLAMP_1); + shader.clamp = deref_u64(ref, 0); + ref.byte_offset += 16; + + // adgif4 + u8 adgif4_addr = deref_u8(ref, 8); + ASSERT(adgif4_addr == (u8)GsRegisterAddress::ALPHA_1); + shader.alpha = GsAlpha(deref_u64(ref, 0)); + ref.byte_offset += 16; + return shader; +} + TypedRef MercFragment::from_ref(TypedRef tr, const DecompilerTypeSystem& dts, const MercFragmentControl& control, @@ -265,40 +304,8 @@ TypedRef MercFragment::from_ref(TypedRef tr, // fp shaders for (int i = 0; i < fp_header.shader_cnt; i++) { - auto& shader = shaders.emplace_back(); - // adgif0 - u8 adgif0_addr = deref_u8(fp_ref, 8); - ASSERT(adgif0_addr == (u8)GsRegisterAddress::TEX0_1); - shader.output_offset = deref_u32(fp_ref, 3); - shader.tex0 = GsTex0(deref_u64(fp_ref, 0)); - fp_ref.byte_offset += 16; - - // adgif1 - u8 adgif1_addr = deref_u8(fp_ref, 8); - ASSERT(adgif1_addr == (u8)GsRegisterAddress::TEX1_1); - shader.tex1 = GsTex1(deref_u64(fp_ref, 0)); - u16 stash = deref_u32(fp_ref, 3); - shader.original_tex = deref_u32(fp_ref, 2); - shader.next_strip_nloop = stash & 0x7fff; - ASSERT((!!(stash & 0x8000)) == (i == fp_header.shader_cnt - 1)); // set eop on last - fp_ref.byte_offset += 16; - - // adgif2 - u8 adgif2_addr = deref_u8(fp_ref, 8); - ASSERT(adgif2_addr == (u8)GsRegisterAddress::MIPTBP1_1); - fp_ref.byte_offset += 16; - - // adgif3 - u8 adgif3_addr = deref_u8(fp_ref, 8); - ASSERT(adgif3_addr == (u8)GsRegisterAddress::CLAMP_1); - shader.clamp = deref_u64(fp_ref, 0); - fp_ref.byte_offset += 16; - - // adgif4 - u8 adgif4_addr = deref_u8(fp_ref, 8); - ASSERT(adgif4_addr == (u8)GsRegisterAddress::ALPHA_1); - shader.alpha = GsAlpha(deref_u64(fp_ref, 0)); - fp_ref.byte_offset += 16; + bool expected_eop = (i == fp_header.shader_cnt - 1); + shaders.push_back(make_shader(fp_ref, expected_eop)); } tr.ref.byte_offset += (header.mm_quadword_size) * 16; @@ -361,6 +368,19 @@ void MercEffect::from_ref(TypedRef tr, for (u32 i = 0; i < frag_count; i++) { f = frag_geo.emplace_back().from_ref(f, dts, frag_ctrl.at(i), main_control); } + + // do extra info + auto fr = get_field_ref(tr, "extra-info", dts); + const auto& word = fr.data->words_by_seg.at(fr.seg).at(fr.byte_offset / 4); + if (word.kind() == LinkedWord::PTR) { + TypedRef mei(deref_label(fr), dts.ts.lookup_type("merc-extra-info")); + u8 shader_offset = read_plain_data_field(mei, "shader-offset", dts); + if (shader_offset) { + Ref r = mei.ref; + r.byte_offset += 16 * shader_offset; + extra_info.shader = make_shader(r, false); + } + } } std::string MercEffect::print() { diff --git a/decompiler/level_extractor/MercData.h b/decompiler/level_extractor/MercData.h index 670dc84618..146fa16809 100644 --- a/decompiler/level_extractor/MercData.h +++ b/decompiler/level_extractor/MercData.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -161,6 +162,10 @@ struct MercFragment { std::string print() const; }; +struct MercExtraInfo { + std::optional shader; +}; + struct MercEffect { //((frag-geo merc-fragment :offset-assert 0) ;; ? std::vector frag_geo; @@ -177,6 +182,7 @@ struct MercEffect { // (dummy1 uint8 :offset-assert 26) ?? u8 envmap_or_effect_usage; // (extra-info merc-extra-info :offset-assert 28) ?? + MercExtraInfo extra_info; void from_ref(TypedRef tr, const DecompilerTypeSystem& dts, const MercCtrlHeader& main_control); std::string print(); diff --git a/decompiler/level_extractor/extract_level.cpp b/decompiler/level_extractor/extract_level.cpp index 1c2c25e5a3..e3676508f3 100644 --- a/decompiler/level_extractor/extract_level.cpp +++ b/decompiler/level_extractor/extract_level.cpp @@ -119,7 +119,7 @@ void extract_art_groups_from_level(const ObjectFileDB& db, for (const auto& file : files) { if (file.name.length() > 3 && !file.name.compare(file.name.length() - 3, 3, "-ag")) { const auto& ag_file = db.lookup_record(file); - extract_merc(ag_file, tex_db, db.dts, tex_remap, level_data, false); + extract_merc(ag_file, tex_db, db.dts, tex_remap, level_data, false, db.version()); } } } diff --git a/decompiler/level_extractor/extract_merc.cpp b/decompiler/level_extractor/extract_merc.cpp index b2b99be68d..f44ed749f9 100644 --- a/decompiler/level_extractor/extract_merc.cpp +++ b/decompiler/level_extractor/extract_merc.cpp @@ -107,6 +107,9 @@ struct ConvertedMercEffect { // draws from all fragments. std::vector draws; std::vector vertices; + bool has_envmap = false; + DrawMode envmap_mode; + u32 envmap_texture; }; /*! @@ -195,6 +198,10 @@ void update_mode_from_alpha1(GsAlpha reg, DrawMode& mode) { reg.b_mode() == GsAlpha::BlendMode::SOURCE && reg.c_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED && reg.d_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED) { + } else if (reg.a_mode() == GsAlpha::BlendMode::SOURCE && + reg.b_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED && + reg.c_mode() == GsAlpha::BlendMode::DEST && reg.d_mode() == GsAlpha::BlendMode::DEST) { + mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_DST_DST); } else { @@ -209,7 +216,7 @@ void update_mode_from_alpha1(GsAlpha reg, DrawMode& mode) { /*! * Convert merc shader to PC draw mode */ -DrawMode process_draw_mode(const MercShader& info) { +DrawMode process_draw_mode(const MercShader& info, bool enable_alpha_test) { DrawMode mode; /* * (new 'static 'gs-test @@ -220,7 +227,7 @@ DrawMode process_draw_mode(const MercShader& info) { :ztst (gs-ztest greater-equal) ) */ - mode.enable_at(); + mode.set_at(enable_alpha_test); mode.set_alpha_test(DrawMode::AlphaTest::GEQUAL); mode.set_aref(0x26); mode.set_alpha_fail(GsTest::AlphaFail::KEEP); @@ -234,6 +241,10 @@ DrawMode process_draw_mode(const MercShader& info) { mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_SRC_DST); mode.set_tcc(info.tex0.tcc()); mode.set_decal(info.tex0.tfx() == GsTex0::TextureFunction::DECAL); + if (info.tex0.tfx() != GsTex0::TextureFunction::DECAL) { + ASSERT(info.tex0.tfx() == GsTex0::TextureFunction::MODULATE); + } + mode.set_filt_enable(info.tex1.mmag()); // the alpha matters (maybe?) @@ -652,16 +663,106 @@ std::string debug_dump_to_ply(const std::vector& draws, return result; } +int find_or_add_texture_to_level(tfrag3::Level& out, + const TextureDB& tex_db, + const std::string& debug_name, + u32 pc_combo_tex_id) { + u32 idx_in_level_texture = UINT32_MAX; + for (u32 i = 0; i < out.textures.size(); i++) { + if (out.textures[i].combo_id == pc_combo_tex_id) { + idx_in_level_texture = i; + break; + } + } + + if (idx_in_level_texture == UINT32_MAX) { + // not added to level, add it + auto tex_it = tex_db.textures.find(pc_combo_tex_id); + if (tex_it == tex_db.textures.end()) { + lg::error("merc failed to find texture: 0x{:x} for {}. Should be in tpage {}", + pc_combo_tex_id, debug_name, pc_combo_tex_id >> 16); + idx_in_level_texture = 0; + } else { + idx_in_level_texture = out.textures.size(); + auto& new_tex = out.textures.emplace_back(); + new_tex.combo_id = pc_combo_tex_id; + new_tex.w = tex_it->second.w; + new_tex.h = tex_it->second.h; + new_tex.debug_name = tex_it->second.name; + new_tex.debug_tpage_name = tex_db.tpage_names.at(tex_it->second.page); + new_tex.data = tex_it->second.rgba_bytes; + } + } + + return idx_in_level_texture; +} + ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect, const MercCtrlHeader& ctrl_header, const std::vector& map, const std::string& debug_name, size_t ctrl_idx, size_t effect_idx, - bool dump) { + bool dump, + const TextureDB& tex_db, + tfrag3::Level& out, + GameVersion version) { ConvertedMercEffect result; result.ctrl_idx = ctrl_idx; result.effect_idx = effect_idx; + if (input_effect.extra_info.shader) { + result.has_envmap = true; + result.envmap_mode = process_draw_mode(*input_effect.extra_info.shader, false); + result.envmap_mode.set_ab(true); + u32 new_tex = remap_texture(input_effect.extra_info.shader->original_tex, map); + ASSERT(result.envmap_mode.get_tcc_enable()); + ASSERT(result.envmap_mode.get_alpha_blend() == DrawMode::AlphaBlend::SRC_0_DST_DST); + + // texture the texture page/texture index, and convert to a PC port texture ID + u32 tpage = new_tex >> 20; + u32 tidx = (new_tex >> 8) & 0b1111'1111'1111; + u32 tex_combo = (((u32)tpage) << 16) | tidx; + result.envmap_texture = find_or_add_texture_to_level(out, tex_db, "envmap", tex_combo); + } else if (input_effect.envmap_or_effect_usage) { + u32 tex_combo = 0; + switch (version) { + case GameVersion::Jak1: { + u32 env = 0x10000000; // jak 1, check for jak 2. + u32 tpage = env >> 20; + u32 tidx = (env >> 8) & 0b1111'1111'1111; + tex_combo = (((u32)tpage) << 16) | tidx; + } break; + case GameVersion::Jak2: { + u32 tpage = 0x1f; + u32 tidx = 2; + tex_combo = (((u32)tpage) << 16) | tidx; + } break; + default: + ASSERT_NOT_REACHED(); + } + + result.envmap_texture = find_or_add_texture_to_level(out, tex_db, "envmap-default", tex_combo); + + DrawMode mode; + mode.set_at(false); + mode.enable_zt(); + mode.enable_depth_write(); + mode.set_depth_test(GsTest::ZTest::GEQUAL); + + // check these + mode.disable_ab(); + mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_DST_DST); + mode.set_tcc(true); + mode.set_decal(false); + mode.set_filt_enable(true); + + mode.set_clamp_s_enable(true); + mode.set_clamp_t_enable(true); + + result.has_envmap = true; + result.envmap_mode = mode; + result.envmap_mode.set_ab(true); + } // full reset of state per effect. // we have no idea what the previous effect draw will be - it might be given to // mercneric. @@ -718,7 +819,10 @@ ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect, for (size_t i = 0; i < frag.fp_header.shader_cnt; i++) { const auto& shader = frag.shaders.at(i); // update merc state from shader (will hold over to next fragment, if needed) - merc_state.merc_draw_mode.mode = process_draw_mode(shader); + merc_state.merc_draw_mode.mode = process_draw_mode(shader, result.has_envmap); + if (!merc_state.merc_draw_mode.mode.get_tcc_enable()) { + ASSERT(false); + } u32 new_tex = remap_texture(shader.original_tex, map); // texture the texture page/texture index, and convert to a PC port texture ID @@ -860,7 +964,8 @@ void extract_merc(const ObjectFileData& ag_data, const DecompilerTypeSystem& dts, const std::vector& map, tfrag3::Level& out, - bool dump_level) { + bool dump_level, + GameVersion version) { if (dump_level) { file_util::create_dir_if_needed(file_util::get_file_path({"debug_out/merc"})); } @@ -880,7 +985,8 @@ void extract_merc(const ObjectFileData& ag_data, auto& effects_in_ctrl = all_effects.emplace_back(); for (size_t ei = 0; ei < ctrls[ci].effects.size(); ei++) { effects_in_ctrl.push_back(convert_merc_effect(ctrls[ci].effects[ei], ctrls[ci].header, map, - ctrls[ci].name, ci, ei, dump_level)); + ctrls[ci].name, ci, ei, dump_level, tex_db, out, + version)); } } @@ -901,6 +1007,9 @@ void extract_merc(const ObjectFileData& ag_data, indices_temp[ci].emplace_back(); auto& pc_effect = pc_ctrl.effects.emplace_back(); auto& effect = all_effects[ci][ei]; + pc_effect.has_envmap = effect.has_envmap; + pc_effect.envmap_texture = effect.envmap_texture; + pc_effect.envmap_mode = effect.envmap_mode; u32 first_vertex = out.merc_data.vertices.size(); for (auto& vtx : effect.vertices) { auto cvtx = convert_vertex(vtx, ctrl.header.xyz_scale); @@ -925,36 +1034,8 @@ void extract_merc(const ObjectFileData& ag_data, draw_mode_dedup[draw.state.merc_draw_mode.as_u64()] = pc_draw_idx; pc_draw = &pc_effect.draws.emplace_back(); pc_draw->mode = draw.state.merc_draw_mode.mode; - - u32 idx_in_level_texture = UINT32_MAX; - for (u32 i = 0; i < out.textures.size(); i++) { - if (out.textures[i].combo_id == draw.state.merc_draw_mode.pc_combo_tex_id) { - idx_in_level_texture = i; - break; - } - } - - if (idx_in_level_texture == UINT32_MAX) { - // not added to level, add it - auto tex_it = tex_db.textures.find(draw.state.merc_draw_mode.pc_combo_tex_id); - if (tex_it == tex_db.textures.end()) { - lg::error("merc failed to find texture: 0x{:x} for {}. Should be in tpage {}", - draw.state.merc_draw_mode.pc_combo_tex_id, ctrl.name, - draw.state.merc_draw_mode.pc_combo_tex_id >> 16); - idx_in_level_texture = 0; - } else { - idx_in_level_texture = out.textures.size(); - auto& new_tex = out.textures.emplace_back(); - new_tex.combo_id = draw.state.merc_draw_mode.pc_combo_tex_id; - new_tex.w = tex_it->second.w; - new_tex.h = tex_it->second.h; - new_tex.debug_name = tex_it->second.name; - new_tex.debug_tpage_name = tex_db.tpage_names.at(tex_it->second.page); - new_tex.data = tex_it->second.rgba_bytes; - } - } - - pc_draw->tree_tex_id = idx_in_level_texture; + pc_draw->tree_tex_id = find_or_add_texture_to_level( + out, tex_db, ctrl.name, draw.state.merc_draw_mode.pc_combo_tex_id); } else { pc_draw_idx = existing->second; pc_draw = &pc_effect.draws.at(pc_draw_idx); diff --git a/decompiler/level_extractor/extract_merc.h b/decompiler/level_extractor/extract_merc.h index c6947747a4..0332c88e57 100644 --- a/decompiler/level_extractor/extract_merc.h +++ b/decompiler/level_extractor/extract_merc.h @@ -13,5 +13,6 @@ void extract_merc(const ObjectFileData& ag_data, const DecompilerTypeSystem& dts, const std::vector& map, tfrag3::Level& out, - bool dump_level); + bool dump_level, + GameVersion version); } // namespace decompiler \ No newline at end of file diff --git a/docs/progress-notes/jak1/scratch/merc_asm.asm b/docs/progress-notes/jak1/scratch/merc_asm.asm index 7ef16356f7..529dd51fa8 100644 --- a/docs/progress-notes/jak1/scratch/merc_asm.asm +++ b/docs/progress-notes/jak1/scratch/merc_asm.asm @@ -9,7 +9,7 @@ ;; vf05 = [lt1_color] ;; vf06 = [lt2_color] ;; vf07 = [lt_ambient] -;; vf17 = [2048, 255, -65537, xyz-add.x] (the ?? is set per fragment) +;; vf17 = [2048, 255, -65537, xyz-add.x] ;; vf18 = [st-out-X, st-out-X, -65537, xyz-add.y] (X = a if xtop = 0, X = b otherwise) ;; vf19 = [st-magic, st-magic, -65537, xyz-add.z] ;; vf22 = hvdf-offset (does get set to others, but is always restored) diff --git a/docs/progress-notes/jak2/emerc.md b/docs/progress-notes/jak2/emerc.md new file mode 100644 index 0000000000..35f73c5d80 --- /dev/null +++ b/docs/progress-notes/jak2/emerc.md @@ -0,0 +1,2190 @@ +# Emerc + +## Outline +It's one of two renderers used for foreground + environment mapping. There's also a generc + merc (mercneric) renderer. + +As far as I know, the supported effects are: +- skinning, with up to 3 bones influencing each vertex, and per-vertex specification of bone weights +- up to 3 directional lights, plus an ambient light +- vertex colors +- texturing +- texture-based environment mapping (done per vertex, not fragment) + +Our hope is to port the emerc renderer to PC, then use it for all rendering for envmapped foreground objects. I believe that `emerc` will be easier to understand than `mercneric`. The hope is that either `emerc` can be used for all models, or once we understand `emerc`, it will be straightforward to convert `mercneric`-only models to work with PC `emerc`. + +The mercneric renderer handles partially offscreen stuff, and is believed to be slower than emerc. However, mercneric may use less VU1 time, in exchange for more EE time. + +As far as I can tell, the way the game decides to use emerc only if all three of these conditions are true: +- `emerc` effect bit is set in the model, indicating it can use `emerc`. +- we're an actor spawned by scene-player +- we're not in a frame range specified by `scissor-frame` in the scene info + +The `emerc` bit is only there on high-resolution cutscene models. +Most of the time, there are no frames specified in `scissor-frame`. This makes sense, usually the actors are onscreen during cutscenes, and `emerc` seems quite tolerant of partially offscreen characters. (similar story in jak 1 - they were aggressive at letting merc draw offscreen instead of clipping triangles, likely because the clipping pipeline is so much slower). + +In very rare cases, they manually specified a frame range for a character who is mostly offscreen (like daxter's feet are visible in frame 2324 of `city-krew-collection-intro`), and then the character is rendered with `mercneric`. + +My guess is that they just used emerc by default everywhere. If a cutscene character is partially offscreen/behind the camera in a bad way that causes GS coordinates to overflow, this would draw garbage triangles, and they would manually annotate the frame range where this happened. + +## Review of how all this gets called + +### Setup +- A level containing `entity-actor`s is loaded +- The `level-update` method (called once per frame) in `entity.gc` calls `birth!` on `entity-actor`s that are visible and eligible to be spawned +- The newly created actor process is initialized by calling `init-from-entity!`, which is a method that all objects must implement. +- This method will eventually call `initialize-skeleton`, a method of the parent `process-drawable` class. +- This method creates a `draw-control` with `skeleton-group->draw-control` +- This method calls `setup-cspace-and-add` +- This method adds the process drawable to `*foreground-draw-engine*`, a list of processes to be drawn. +- The connection uses function `add-process-drawable`, which just calls the `dma-add-func` of the `draw-control`, which is `dma-add-process-drawable` by default + +### Per-Frame Draw +- Game-objects are responsible for calling `ja-post`, or adding themselves to the matrix-engine list, or somehow coming up with `joint` transforms. + +- main loop in `main.gc` calls `(*draw-hook*)`, which points to `real-main-draw-hook`. This function generates all DMA data for drawing. +- `foreground-engine-execute` + - `foreground-init` (doesn't do anything emerc-related) + - calls `execute-connections` on the engine, the `dma-add-process-drawable` for each object + - various stuff for shadows/picking lights + - generates `vu-lights` (light values in VU-friendly format) + - picks LOD based on distances + - sets texture masks to indidate to texture system which LODs of which textures will be used + - determines if `close-to-screen` culling is needed. + - call `foreground-draw` + - add an entry to the `*bone-calculation-list*` to tell it to compute skinning matrices. + - rotate lights to camera frame (note that merc only gets a perspective transform, transforming to camera frame is done in skinning calc to avoid a full affine transform on VU1) + - there's some confusing logic for the renderer selection, but in the end it populates `merc-effect-bucket-info` including a color and a few flags. + - calls `foreground-emerc`, which generates DMA data for `emerc` (asm func) +- `foreground-execute-cpu-vu0-engines` + - runs bones, modifying the above DMA data to contain skinning matrices computed from joints. +- `display-frame-finish` called after all drawing + - Calls `emerc-vu1-init-buffers`, which adds some init data to all used `emerc` buckets. + +### Emerc DMA Generation +The call in GOAL: +``` +(set! dma-ptr (foreground-emerc dc (-> (scratchpad-object foreground-work) regs mtxs) dma-ptr 29 19)) +``` +The arguments are: +- `draw-control`, which contains settings for drawing, and the actual merc geometry (called `geo`) +- a pointer to the "matrix area", which will contain skinning matrices computed by `bones` +- `dma-ptr`, a pointer to the DMA buffer to write data to +- 29, 19, likely addresses in the VU1 microprogram to start execution. Typically there is one program for the first run of the renderer, which initializes some VU1 registers/data memory, and then a slightly shorter program that skips the init step. + +Before the asm, the rough breakdown is: +- a `draw-control` stores 4 geos, one for each lod (some may be unpopulated) +- Each `geo` is a `merc-ctrl`, which is an entire model +- Each `merc-ctrl` is made up `merc-effect`s +- Each `merc-ctrl` is made up of "fragment"s. Each fragment has a `frag-geo` (actual data needed in VU1) and `frag-ctrl` (metadata describing how to upload data to VU1) +- Each fragment has a few types of data: + - `unsigned-four`: containing weights (u8), rgba (u8), addresses for crosscopy/samecopy. Unpacked [u8x4] to [u32x4] by VIF on upload to VU1. + - `lump-four`: containing vertex data. Unpacked [u8x4] to [u32x4 + some_magic_constant] by VIF on upload to VU1. This unpack magically converts integers to floats. + - `fp` data: containing a header, and "shaders" (giftags for setting up textures/settings). Copied directly by VIF. + +The calling function `foreground-draw` sets flags (per effect) in the `merc-bucket-info` array. All emerc stuff gets `merc-path` set to 1. + +High-level description of what it does. +Note that this is simplified from the assembly version, which combines some dma transfers shown here. +Also - this does not actually run any DMA or microprograms, it just generates a DMA chain that will do this later +On the next game frame, the giant DMA chain generated by all renderers will be submitted, and all these will run. +```cpp +// get the merc control for our level of detail (selected in drawable.gc) +MercControl& mc = draw_control.lod_set[draw_control.cur_lod].geo; + +// loop over each "effect" in the merc control. +// The "effect" is the grouping for what can be sent to one renderer or another +for (int effect_idx = 0; effect_idx < mc.header.effect_count; effect_idx++) { + MercEffect& merc_effect = mc.effect[effect_idx]; // merc data in the art group + MercBuckedInfo& merc_effect_info = gForeground.merc_bucket_info[effect_idx]; // settings generated by foreground-draw + + if (merc_effect_info.disable_draw) { + continue; // skip if disabled + } + + if (merc_effect_info.merc_path != 1) { + continue; // skip if not emerc (1 means emerc here) + } + + // where we started writing dma for this effect + u8* effect_dma_start = dma_ptr; + + // the source data (stored in the art group) that we'll be sending. + u8* source_ptr = merc_effect.frag_geo; + + // loop over fragments + for (int frag_idx = 0; frag_idx < merc_effect.frag_count; frag_idx++) { + MercFragmentControl& frag_ctrl = merc_effect.frag_ctrl[frag_idx]; + // set the ROW register of the VIF. + // when kRowAdd flag is given, the VIF will add these 4 values to each component of each quadword it writes out. + // This is used as part of the process to go from u8's to floats + // (they do some cool magic where they don't actually do int->float, they just add integers with VIF and + // do float math on VU1 and it works out somehow) + dma_ptr = generate_vif_strow(dma_ptr, mc.header.st_vif_add, mc.header.st_vif_add, 0x47800000, 0x4b010000); + + // number of quadwords (16-byte words) in EE memory of unsigned_four data to send + // unsigned_four data is stored as [u8, u8, u8, u8] and unpacked to [u32, u32, u32, u32]. + // the count variable is in units of 4 values. (4 bytes in EE memory, 16 bytes in VU1 memory) + int u4_qwc_in_ee_mem = (frag_ctrl.unsigned_four_count + 3) / 4; + + int dest_addr_qw = 140; + + dma_ptr = generate_vif_unpack(dma_ptr + kUnpackV4_8, // unpack [u8, u8, u8, u8] to [u32, u32, u32, u32] + kUnsigned, // zero extend when unpacking + dest_addr_qw, // VU1 data address (in quadwords) + kUseTop, // add value of TOP register to destination (VU1 program controls destination) + source_ptr, // source pointer + u4_qwc_in_ee_mem, // number of QW to transfer from EE memory + frag_ctrl.unsigned_four_count, // number of QW written to VU1 memory + kNoRow, // do not add row + ); + // note: to write 7 QW of data, the would have this in EE memory: + // [v0, v1, v2, v3] (4 bytes) + // [v4, v5, v6, XX] (4 bytes) + // they would transfer 2 QW to vif (including 1 padding byte) + // but you can tell VIF to unpack only 7 QW, and it will discard the padding. + + // advance source pointer to the next data (lump data) + source_ptr += u4_qwc_in_ee_mem * 16; + + // advance dest pointer. + dest_addr_qw += frag_ctrl.unsigned_four_count; + + // lump 4 is unpacked from [u8, u8, u8, u8] to [u32 + rx, u32 + ry, u32 + rz, u32 + rw] + // where [rx, ry, rz, rw] are specified in ROW set above. + int l4_qwc_in_ee_mem = (frag_ctrl.lump_four_count + 3) / 4; + + dma_ptr = generate_vif_unpack(dma_ptr + kUnpackV4_8, // unpack [u8, u8, u8, u8] to [u32, u32, u32, u32] + kUnsigned, // zero extend when unpacking + dest_addr_qw, // VU1 data address (in quadwords) + kUseTop, // add value of TOP register to destination (VU1 program controls destination) + source_ptr, // source pointer + l4_qwc_in_ee_mem, // number of QW to transfer from EE memory + frag_ctrl.lump_four_count, // number of QW written to VU1 memory + kAddRow // add the row value + ); + + // advance source pointer to the next data (lump data) + source_ptr += l4_qwc_in_ee_mem * 16; + + // advance dest pointer. + dest_addr_qw += frag_ctrl.unsigned_four_count; + + // send fp data. + dma_ptr = generate_vif_unpack(dma_ptr + kUnpackV4_32, // just plain memcpy to VU1 memory + kSigned, // no effect? they set it explicitly always, not sure why. + dest_addr_qw, // VU1 data address (in quadwords) + kUseTop, // add value of TOP register to destination (VU1 program controls destination) + source_ptr, // source pointer + fp_qwc, // number of QW to transfer from EE memory + frag_ctrl.fp_qwc, // number of QW written to VU1 memory + kNoRow // don't add the row value + ); + + // adavne source pointer + source_ptr += frag_ctrl.fp_qwc * 16; + + // there's some special data shared between all fragments. We put this DMA after the DMA + // for the first fragment as an optimization. We can write the first fragment of this effect + // to VU1 data memory while VU1 is processing the last fragment of the previous effect. + // This is ok because the per-fragment data is double buffered (controlled with the TOP register) + // However, the shared data is not double buffered, and we must wait for the previous effect + // to be fully done before transferring. We want to delay this as long as possible, so we + // transfer the first per-fragment data of this effect before this part. + if (frag_idx == 0) { + // sneak some more data in lights + auto lights = gForeground.merc_bucket_info.lights; + lights.qws[1].w = ignore_alpha ? 0x3f85026b : 0x3f85026a; + // copy the 7 qw of lights to the dma buffer now, setting up a transfer for them to go + // to address 140 in VU1 (no TOP). + // the previous code sets up these lights in VU format (vu-lights). + + dma_ptr = dma_memcpy_to_buffer_then_vu1(dma_ptr, 132, &lights, 7); + // copy these 4 values to address 139 (copying them to the dma-buffer now) + dma_ptr = dma_copy_to_buffer_then_vu1(dma_ptr, 139, merc_ctrl.header.xyz_scale, merc_ctrl.header.st_magic, merc_ctrl.header.st_out_a, merc_ctrl.header.st_out_b); + + // emerc new transfer - copying 1 qw color_fade (u8's unpacked to u32) + dma_ptr = dma_copy_to_buffer_then_vu1(dma_ptr, 118, unpack_u8_to_u32(merc_effect_info.color_fade)); + + AdgifShader* envmap_shader = DefaultEnvmapShader; + if (merc_effect.extra_info && merc_effect.extra_info.shader_offset) { // nonzero check + envmap_shader = ((u8*)&merc_effect.extra_info) + 16 * merc_effect.extra_info.shader_offset; + } + + // 5 qw envmap shader + dma_ptr = dma_copy_to_buffer_then_vu1(dma_ptr, 119, envmap_shader, 5 * 16); + } + + + // fragments will (most of the time) need new matrix data. + // there are some cases where they can reuse some matrix data from previous fragments in the same + // effect, so it's possible for there to be no matrices to transfer. But usually there are some + for (int mat_xfer = 0; mat_xfer < frag_ctrl.max_xfer_count; mat_xfer++) { + auto& info = frag_ctrl.mat_dest_data[mat_xfer]; + dma_ptr = dma_transfer_matrix(dma_ptr, info.matrix_dest, matrix_mem + sizeof(MercMatrix) * info.mattrix_number); + } + + // finally, call program. + dma_ptr = dma_mscal(frag_idx == 0 ? program_addr_1 : program_addr_2); + + + } + + // a bunch of bucket patching crap +} +``` + +The actual asm: +``` +L101: ;; function prologue + daddiu sp, sp, -128 + sd ra, 0(sp) + sq s0, 16(sp) + sq s1, 32(sp) + sq s2, 48(sp) + sq s3, 64(sp) + sq s4, 80(sp) + sq s5, 96(sp) + sq gp, 112(sp) + + ;; one-time setup for this "merc-control". A merc-control is a model (at a particular lod) + ;; for a process-drawable. + ;; using dc as the input draw-control (a constant) + ;; using mc as (-> dc lod-set (-> dc cur-lod) geo), the merc-control we're drawing (a constant) + ;; using t8 = mep as (-> mc effect ), one of the merc-effects in the merc-control (variable) + ;; using t7 = mec (merc-effect counter), the number of remaining merc-counters + ;; using t9 = mebp as (-> *foreground* merc-bucket-info effect ), one of the merc-bucket-info's filled out by + ;; the calling function, containing per-effect settings. +B0: + or t7, a3, r0 ;; t7 = program-addr-1 + or v1, t0, r0 ;; v1 = program-addr-2 + lui t0, 4096 ;; t0 = 0x10000000 + lui t1, 18304 ;; t1 = 0x47800000 + daddiu t0, t0, 1 ;; t0 = 0x10000001 + dsll32 t1, t1, 0 ;; t1 = 0x47800000'00000000 + lui a3, 12288 ;; a3 = 0x30000000 + lui t8, 19201 ;; t8 = 0x4b010000 + pcpyld t0, a3, t0 ;; t0 = 0x00000000'30000000'00000000'10000001 (STROW) + lbu a3, 78(a0) ;; a3 = (-> dc cur-lod) + pcpyld t1, t8, t1 ;; t1 = 0x00000000'4b010000'47800000'00000000 + lui t2, 28160 ;; t2 = 0x6e000000 + addiu t8, r0, 8 ;; t8 = 8 + multu3 a3, a3, t8 ;; a3 = (* 8 (-> dc cur-lod)) + lui t3, 1280 ;; t3 = 0x05000000 + lui t4, 27648 ;; t4 = 0x6c000000 + dsll32 t2, t2, 0 ;; t2 = 0x6e000000'00000000 + dsll32 t4, t4, 0 ;; t4 = 0x6c000000'00000000 + daddu t4, t4, t3 ;; t4 = 0x6c000000'05000000 + daddu t3, t2, t3 ;; t3 = 0x6e000000'05000000 + daddiu t3, t3, 1 ;; t3 = 0x6e000000'05000001 + daddu a0, a3, a0 ;; a0 = (+ dc (* 8 (-> dc cur-lod))) + pcpyld t2, t2, r0 ;; t2 = 0x6e000000'00000000'00000000'00000000 (unpack-v4-8, no change to row) + lw a0, 28(a0) ;; a0 = (-> dc lod-set (-> dc cur-lod) geo) ;; a merc-ctrl + pcpyld t3, t3, r0 ;; t3 = 0x6e000000'05000001'00000000'00000000 (unpack-v4-8, row add) + pcpyld t4, t4, r0 ;; t4 = 0x6c000000'05000000'00000000'00000000 (unpack-v4-32, disable row add) + lui t5, 12288 ;; t5 = 0x30000000 + lui t6, 4096 ;; t6 = 0x10000000 + daddiu t5, t5, 7 ;; t5 = 0x30000007 + lui t8, 5120 ;; t8 = 0x14000000 + lui a3, 27655 ;; a3 = 0x6c070000 + daddu t7, t8, t7 ;; t7 = 0x14000000 + program-addr-1 + dsll32 a3, a3, 0 ;; a3 = 0x6c070000'00000000 + dsll32 t8, t7, 0 ;; t8 = (0x14000000 + program-addr-1) << 32 + pcpyld t5, a3, t5 ;; t5 = 0x6c070000'00000000'00000000'30000007 + lwu t7, 52(a0) ;; t7 = (-> mc effect-count) + pcpyld t6, t8, t6 ;; t6 = ((0x14000000 + program-addr-1) << 32) << 64 + 0x00000000'10000000 + daddiu t8, a0, 156 ;; t8 = (-> mc effect 0) = mep "merc effect pointer" + beq t7, r0, L109 ;; branch if there's no effects (I think this is buggy and jumps to the wrong spot) + lw a3, *foreground*(s7) ;; a3 = *foreground* + +B1: + daddiu t9, a3, 2508 ;; t9 = (-> *foreground* merc-bucket-info effect 0) +B2: + + ;; TOP of per-effect loop + ;; (I've marked lines with stats if they are just for computing statistics) +L102: + lbu a3, 6(t9) ;; a3 = (-> mebp disable-draw) + or ra, a2, r0 ;; ra = start-of-dma-for-this-effect + lbu gp, 4(t9) ;; gp = (-> mebp merc-path) + bne a3, r0, L109 ;; jump to next effect if this is disabled. + lw a3, *merc-global-stats*(s7) ;; a3 = mgs + +B3: + daddiu a3, a3, 16 ;; a3 = (-> *merc-global-stats* emerc) + daddiu gp, gp, -1 ;; check if `merc-path` is 1, skip this fragment if it's something else + sll r0, r0, 0 + bne gp, r0, L109 + lhu s4, 2(a3) ;; stats.fragments + +B4: + lhu s3, 18(t8) ;; s3 = (-> mep frag-count) + lwu gp, 4(a3) ;; stats + lhu s5, 22(t8) ;; s5 = (-> mep tri-count) + daddu s4, s4, s3 ;; stats + lwu s3, 8(a3) ;; stats + lhu s2, 24(t8) ;; s2 = (-> mep dvert-count) + daddu gp, gp, s5 ;; stats + sh s4, 2(a3) ;; stats + sw gp, 4(a3) ;; stats + daddu s5, s3, s2 ;; stats + lwu t2, 0(t8) ;; t2 = (-> mep frag-geo) + lwu gp, 4(t8) ;; gp = (-> mep frag-ctrl) + lui s4, 12288 ;; 0x30000000 + dsll32 t2, t2, 0 ;; (-> mep frag-geo) << 32 + sw s5, 8(a3) ;; stats + or t2, t2, s4 ;; t2 = ((-> mep frag-geo) << 32) + 0x30000000 (upper 64-bits still have dma tmpl) + lhu s5, 18(t8) ;; s5 = (-> mep frag-count) + addiu s4, r0, 0 ;; s4 = 0 + beq s5, r0, L109 ;; skip to next effect if no frags in this effect. + sll r0, r0, 0 + +B5: + sll r0, r0, 0 + + ;; top of per-fragment loop. + ;; s4 = current-frag-idx + ;; s5 = num-frags + ;; a2 = dma-ptr + ;; DMA memory layout + ;; lower-bits higher bits + ;; 0 [dmatag-lower, dmatag-upper, strow-viftag, ROW_X ] ;; transfer 1 qw, immediately after this + ;; 1 [ROW_Y , ROW_Z , ROW_W , nop-viftag ] ;; the qw transferred by 0 + ;; 2 [dmatag-lower, dmatag-upper, nop , unpack-v4-8] ;; (unsigned4's) + ;; 3 [dmatag-lower, dmatag-upper, strow 1 , unpack-v4-8] ;; lumps + ;; 4 [dmatag-lower, dmatag-upper, strow 0 , unpack-v4-32] +B6: +L103: + lbu s0, 0(gp) ;; s0 = frag-ctrl.unsigned-four-count (number of 4xu8's in memory) + sll r0, r0, 0 + lbu s2, 1(gp) ;; s2 = frag-ctrl.lump-four-count + xori s1, r0, 49292 ;; s1 = 0xc08c + lbu s3, 2(gp) ;; s3 = frag-ctrl.fp-qwc + daddiu v0, s0, 3 ;; v0 = u4count + 3 + lw a3, 44(a0) ;; a3 = header.st-vif-add + srl v0, v0, 2 ;; v0 = (u4count + 3) / 4 + sq t0, 0(a2) ;; set DMA qw 0 (dmatag-strow only) + xor t2, t2, v0 ;; set dma qwc + sq t2, 32(a2) ;; store dma line 2. + xor t2, t2, v0 ;; unset dma qwc + sh s1, 44(a2) ;; set addr for unpack (tops + unsigned bits) + daddu s1, s1, s0 ;; unpdate qwc for next unpack + sb s0, 46(a2) ;; set qwc for unpack + dsll32 s0, v0, 4 ;; v0 = (u4-ee-qwc << 36) + daddu t3, t2, s0 ;; t3 = dma-tag templ + daddiu s0, s2, 3 ;; s0 = l4c + 3 + sw a3, 12(a2) ;; ROW_X = header.st-vif-add + srl s0, s0, 2 ;; s0 /= 4 + sq t1, 16(a2) ;; ROW_Z, W + xor t3, t3, s0 ;; set dma qwc + sq t3, 48(a2) ;; store dma templ 3 + xor t3, t3, s0 ;; unset dma qwc + sh s1, 60(a2) ;; set vif unpack + daddu s1, s1, s2 ;; next dest + sb s2, 62(a2) ;; store. + dsll32 s2, s0, 4 ;; s2 = dma-src-inc shifted + sw a3, 16(a2) ;; ROW Y + daddu t4, t3, s2 ;; unpack-v4-32 tmpl + xor t4, t4, s3 ;; set qwc in dma tmpl + xori a3, s1, 16384 ;; turn off sign extension in unpack + sq t4, 64(a2) ;; store dma 4 + xor t4, t4, s3 ;; unset qwc + sb s3, 78(a2) ;; set qwc in unpack + dsll32 s3, s3, 4 ;; qwc -> bytes + sh a3, 76(a2) ;; set unpack + daddu t2, t4, s3 ;; ?? (maybe reset t2 tmpl) + lbu s3, 3(gp) ;; s3 = mat-xfer-count + daddiu gp, gp, 4 ;; next fragment control + bne s4, r0, L105 ;; do B7, B8, B9, B10 only on first fragment + daddiu a2, a2, 80 ;; advance DMA ptr. + +B7: + sd t6, 0(a2) ;; weirdo dma generation code (somebody had too much fun here) + addiu s2, r0, 8 ;; transfer 8 qw + sd t6, 8(a2) ;; more weird crap + lui a3, 27656 ;; 0x6c08 + sb s2, 0(a2) ;; transfer 8 qw + daddiu a3, a3, 132 ;; to 140 + lw s2, *foreground*(s7) ;; fg + daddiu s2, s2, 2384 ;; s2 = merc-bucket-info array + sw a3, 12(a2) ;; unpack to 140 + lq a3, 0(s2) ;; a3 = lights 0 + lq s1, 16(s2) ;; s1 = lights 1 + lq s0, 32(s2) ;; s0 = lights 2 + lq v0, 48(s2) ;; v0 = lights 3 + sq a3, 16(a2) ;; store lights + sq s1, 32(a2) + sq s0, 48(a2) + sq v0, 64(a2) + lq a3, 64(s2) ;; lights again + lq s1, 80(s2) + lq s0, 96(s2) ;; lights 6 + lui v0, 16261 + lq s2, 28(a0) + daddiu v0, v0, 619 ;; v0 = 0x3f85026b + sq a3, 80(a2) ;; light store + lbu a3, 5(t9) ;; a3 = ignore-alpha + sq s1, 96(a2) ;; lights + sq s0, 112(a2) ;; last lights + dsubu a3, v0, a3 ;; compute ignore alpha + sq s2, 128(a2) ;; header + sw a3, 28(a2) ;; light[1].w + daddiu a2, a2, 144 ;; inc dma + sd t6, 0(a2) + addiu s2, r0, 6 + sd t6, 8(a2) + lui a3, 27654 ;; 0x6C06 + sb s2, 0(a2) + daddiu a3, a3, 118 + sw a3, 12(a2) ;; to 124 + lw a3, 0(t9) ;; a3 = color fade + pextlb a3, r0, a3 ;; unpack u8 to u32's + pextlh a3, r0, a3 + sq a3, 16(a2) ;; store color fade + lw a3, *default-envmap-shader*(s7) ;; envmap ptr. + lw s2, 28(t8) ;; merc-extra-info + beq s2, r0, L104 + sll r0, r0, 0 + +B8: + lbu s1, 1(s2) + beq s1, r0, L104 + sll r0, r0, 0 + +B9: + sll a3, s1, 4 + addu a3, s2, a3 +B10: +L104: + lq s2, 0(a3) ;; copy shader to dma buff + lq s1, 16(a3) + lq s0, 32(a3) + lq v0, 48(a3) + lq a3, 64(a3) + sq s2, 32(a2) + sq s1, 48(a2) + sq s0, 64(a2) + sq v0, 80(a2) + sq a3, 96(a2) + daddiu a2, a2, 112 + + ;; after first time per-effect stuff +B11: +L105: + beq s3, r0, L107 + addiu s2, r0, 128 ;; s2 = 128 (matrix size) + +B12: + lbu a3, 0(gp) ;; get mat number + sll r0, r0, 0 +B13: +L106: + multu3 s1, a3, s2 ;; s1 = matrix offset in ee world + sq t5, 0(a2) ;; mat transfer tmplate + lbu s0, 1(gp) ;; mat dest + daddiu gp, gp, 2 ;; gp = next mat transfer + lbu a3, 0(gp) ;; a3 = next matrix offset + daddiu s3, s3, -1 ;; dec remaining + sb s0, 12(a2) ;; store dest + daddiu a2, a2, 16 ;; inc dma + daddu s1, s1, a1 ;; compute matrix pointer + sll r0, r0, 0 + bne s3, r0, L106 + sw s1, -12(a2) + +B14: +L107: + sq t6, 0(a2) + daddiu a2, a2, 16 + bne s4, r0, L108 + daddiu s4, s4, 1 + +B15: + or a3, v1, r0 ;; execute program (1 for first round, 2 for later ones) + sb a3, -4(a2) +B16: +L108: + bne s4, s5, L103 ;; loop frag + sll r0, r0, 0 + +B17: ;; patching crap, based on texture index now. should document eventually... + lui s5, 28672 + lbu a3, 26(t8) + addiu gp, r0, 48 + lw s5, 52(s5) + mult3 a3, a3, gp + sll r0, r0, 0 + daddu a3, s5, a3 + sll r0, r0, 0 + lw gp, 12(a3) + sll r0, r0, 0 + lw s5, 16(a3) + lui s4, 8192 + sq r0, 0(a2) + movz gp, ra, gp + sw s4, 0(a2) + or s4, a2, r0 + sw gp, 12(a3) + daddiu a2, a2, 16 + beq s5, r0, L109 + sw s4, 16(a3) + +B18: + sll r0, r0, 0 + sw ra, 4(s5) +B19: +L109: + daddiu t8, t8, 32 + daddiu t9, t9, 8 + daddiu t7, t7, -1 + bne t7, r0, L102 ;; loop effect + sll r0, r0, 0 + +B20: + or v0, a2, r0 + ld ra, 0(sp) + lq gp, 112(sp) + lq s5, 96(sp) + lq s4, 80(sp) + lq s3, 64(sp) + lq s2, 48(sp) + lq s1, 32(sp) + lq s0, 16(sp) + jr ra + daddiu sp, sp, 128 + + sll r0, r0, 0 + sll r0, r0, 0 +``` + +### Summary of above +Overall, it's very similar to merc. There's some extra data transfered: +- the "low memory" stuff setup in `emerc.gc` is 1 QW longer (an extra "`unperspect` QW") +- the `rgba color-fade` is transferred to non-double-buffered memory (like lights) (1 Qw, unpack to u32's) +- 5 QW shader for envmapping (either `*default-envmap-shader*` or one provided in merc extra info) + +emerc data appears backward compatible with merc, which makes sense: +- emerc falls back to merc if it's too far away to envmap +- we put emerc stuff through merc code (blending and stuff is wrong, but the geometry comes out right) + +The promising thing is that we don't seem to need much extra information to do environment mapping. +I kinda though we'd need another set of texture coordinates, but I don't see where that enters yet. + +If all we need is the shader, plus tint values, it would be easy to do this for any model that succeeds with merc. + +## EMERC VU1 constants +Triangle strip giftag - same as normal merc exactly (in the normal no-alpha case) +```lisp +(set! (-> s5-0 tri-strip-gif tag) + (new 'static 'gif-tag64 + :pre #x1 + :prim (new 'static 'gs-prim :prim (gs-prim-type tri-strip) :iip #x1 :tme #x1 :fge #x1) + :nreg #x3 + ) + ) +(set! (-> s5-0 tri-strip-gif regs) + (new 'static 'gif-tag-regs :regs0 (gif-reg-id st) :regs1 (gif-reg-id rgbaq) :regs2 (gif-reg-id xyzf2)) + ) +;; word 3 gets set to #x303e4000 +``` + +### Program list +- `0`: per-frame init +- `19`: effect init +- `29`: process frag + +### Memory map +All in 16-byte quadword addresses. +``` +Low memory: after DMA +[0 ] : tri-strip-gif (st, rgbaq, xyzf2), no abe, 0x303e4000 in word 3, same as merc. +[1 ] : adgif-shader giftag (giftag for 5 a+d's) +[2 ] : hvdf-offset +[3 - 7] : perspective matrix (only perspective project, no rotation/translation). 3 gets set to persp_vector +[7 ] : fog (pfog0, fog-min, fog-max, 0.0) +[8 ] : unperspect (1/P(0, 0), 1/P(1, 1), 0.5, 1/P(2, 3)) + +Low memory: after inits (both frame and effect) +[0 ] : tri-strip-gif (st, rgbaq, xyzf2), no abe, 0x303e4000 in word 3, same as merc. +[1 ] : adgif-shader giftag (giftag for 5 a+d's) +[2 ] : hvdf-offset +[3 ] : P_mult = [low.P(0, 0), low.P(1, 1), low.P(2, 2), low.P(2, 3)] +[4 ] : P_add = [low.P(3, 0), low.P(3, 1), low.P(3, 2), low.P(3, 3)] +[5 ] : P_mult_scale = P_mult * header.xyz-scale +[7 ] : fog (pfog0, fog-min, fog-max, 0.0) +[8 ] : unperspect (1/P(0, 0), 1/P(1, 1), 0.5, 1/P(2, 3)) + +``` + +### Summary of math + +The "transformed vertex" refers to the vertex before perspective divide, and pfog0 multiply. +The "transformed normal" is the rotated normal, after normalization. +``` +vf08 = transformed +vf23 = unperspect +vf14 = rgba-fade +vf24 = normal st + +mul.xyzw vf09, vf08, vf23 ;; do unperspect + +subw.z vf10, vf10, vf00 ;; subtract 1 from z + +addw.z vf09, vf00, vf09 ;; xyww the unperspected thing + +mul.xyz vf15, vf09, vf10 ;; + +adday.xyzw vf15, vf15 + +maddz.x vf15, vf21, vf15 + +div Q, vf15.x, vf10.z + +mulaw.xyzw ACC, vf09, vf00 + +mul.xyzw vf09, vf08, vf23 + +madd.xyzw vf10, vf10, Q + +eleng.xyz P, vf10 + +mfp.w vf10, P + +div Q, vf23.z, vf10.w + +addaz.xyzw vf00, vf23 + +madd.xyzw vf10, vf10, Q + +mulz.xy vf24, vf10, vf24 ;; mul tex by q + +;; new rgba +sq.xyzw vf14, 443(vi10) + +;; +vf24 +``` + + +### VU1 Program: init (per frame) +``` + lq.xyzw vf01, 7(vi00) | nop + lq.xyzw vf25, 3(vi00) | nop + lq.xyzw vf26, 4(vi00) | nop + lq.xyzw vf27, 5(vi00) | nop + lq.xyzw vf28, 6(vi00) | nop + lq.xyzw vf08, 8(vi00) | nop + mr32.xyzw vf01, vf01 | nop + move.y vf25, vf26 | nop + move.zw vf25, vf27 | nop + sq.xyzw vf25, 3(vi00) | nop + sq.xyzw vf08, 124(vi00) | nop + 2048.0 | nop :i + 255.0 | maxi.x vf17, vf00, I :i + -65537.0 | maxi.y vf17, vf00, I :i + mr32.xyzw vf02, vf01 | minii.z vf17, vf00, I + lq.xyzw vf22, 2(vi00) | minii.z vf18, vf00, I + 0.003921569 | minii.z vf19, vf00, I :i + sq.xyzw vf28, 4(vi00) | minii.w vf29, vf00, I :e + mr32.xyzw vf03, vf02 | nop +``` + +Simplified code (`??`'s are either garbage, or some value that isn't important later on). Leaving out stores to low memory documented in the Memory Map section. +``` +vf01 = [??, ??, ??, low.pfog0] +vf02 = [??, ??, ??, low.fog_min] +vf03 = [??, ??, ??, low.fog_max] +vf17 = [2048., 255., -65537., ??] +vf22 = low_in.hvdf_offset +``` + +### VU1 Program: init (per effect) +Note that this continues directly into the per-frag program, to match the note in frag == 0 case in the dma generation part. + +``` + lq.xyzw vf25, 139(vi00) | nop + lq.xyzw vf26, 3(vi00) | nop + lq.xyz vf01, 132(vi00) | nop + lq.xyz vf02, 133(vi00) | nop + lq.xyz vf03, 134(vi00) | addy.xy vf19, vf00, vf25 + lq.xyzw vf04, 135(vi00) | mulx.xyzw vf26, vf26, vf25 + lq.xyzw vf05, 136(vi00) | nop + lq.xyzw vf06, 137(vi00) | nop + lq.xyzw vf07, 138(vi00) | nop + sq.xyzw vf26, 5(vi00) | nop ;; P_mult_scale store. +``` + +Simplified code (note: some of this stuff set later) +``` +vf25 = [xyz-scale, st-magic, st-out-a, st-out-b]; +vf26 = low.P_mult * xyz-scale; +vf01 = [lt0.xyz, pfog0] +vf02 = [lt1.xyz, fog-min] +vf03 = [lt2.xyz, fog-max] +vf19 = [st-magic, st-magic, -65537, xyz-add.z]; +vf04 = lt0_color; +vf05 = lt1_color; +vf06 = lt2_color; +``` + +### VU1 Program: per-fragment, pre-looping init +``` +;; reg setup stuff + lq.xyzw vf28, 139(vi00) | minix.xyzw vf15, vf00, vf00 ;; vf28 = merc-ctrl-header, vf15 = [0, 0, 0, 0] + xtop vi15 | nop ;; vi15 = 0 (output buffer) + iaddiu vi12, vi15, 0x8c | nop ;; vi12 = xtop + 140 (merc-byte-header, u4) + nop | nop ;; in merc was a branch for st-a/st-b select. + ilwr.w vi03, vi12 | maxz.xy vf18, vf00, vf28 ;; set vf18.xy = [st-out-a, st-out-a] (for a buffer) + iaddiu vi15, vi00, 0x173 | nop ;; vi15 = xtop + 371 + lq.xyzw vf14, 0(vi00) | nop ;; vf14 = tri-strip-gif-tag + nop | nop ;; in merc was fadeout + iadd vi03, vi03, vi12 | nop ;; st-output location = st-out-a + xtop + 140 + ilwr.w vi09, vi03 | nop ;; vi09 = fp-header u8's [shader-cnt, kick-off, kick-step, hword-cnt] + lqi.xyzw vf27, vi03 | nop ;; vf27 = xyz-add + ilw.x vi04, 1(vi12) | nop ;; vi04 = mat1-cnt + iaddiu vi05, vi00, 0x7f | addw.xyz vf15, vf15, vf00 ;; vf15 = [1, 1, 1, 0], vi05 = 0x7f + iand vi09, vi09, vi05 | nop ;; mask to get vi09 = shader-cnt + ilw.y vi06, 1(vi12) | miniz.w vf19, vf00, vf27 ;; setup vf19, vi06 = mat2-cnt + nop | miniy.w vf18, vf00, vf27 ;; setup vf18, merc had branch for no strips. + ilwr.z vi01, vi12 | minix.w vf17, vf00, vf27 ;; vi01 = lump-off + +;; vf17 = [2048, 255, -65537, xyz-add.x] +;; vf18 = [st-out-X, st-out-X, -65537, xyz-add.y] (X = a if xtop = 0, X = b otherwise) +;; vf19 = [st-magic, st-magic, -65537, xyz-add.z] + +;; shader setup (not envmap) + lq.xyzw vf13, 1(vi00) | nop ;; vf13 = adgif gif tag. + ilwr.w vi02, vi03 | nop ;; vi02 = shader control word 0 (dest offset) + lqi.xyzw vf08, vi03 | nop ;; load shader data + lqi.xyzw vf09, vi03 | nop + lqi.xyzw vf10, vi03 | nop + lqi.xyzw vf11, vi03 | nop + lqi.xyzw vf12, vi03 | nop + iadd vi02, vi02, vi15 | nop ;; compute destination + mtir vi08, vf09.w | nop ;; eop stuff (not sure this makes sense in 1-shader emerc) + sqi.xyzw vf13, vi02 | nop ;; store adgif gif tag + sqi.xyzw vf08, vi02 | nop ;; shader store 1 + sqi.xyzw vf09, vi02 | nop ;; shader store 2 + mfir.x vf14, vi08 | nop ;; set eop bit in giftag template + sqi.xyzw vf10, vi02 | nop ;; shader store 3 + sqi.xyzw vf11, vi02 | nop ;; shader store 4 + sqi.xyzw vf12, vi02 | nop ;; shader store 5 + sq.xyzw vf14, 0(vi02) | nop ;; store end giftag + +;; matrix warmup + lq.xyzw vf28, 3(vi00) | nop ;; vf28 = persp-diag + ilw.y vi08, 3(vi12) | nop ;; vi08 = mat-slot.0 + lq.xyzw vf16, 5(vi00) | nop ;; vf16 = scaled-persp-diag + lq.xyzw vf20, 4(vi00) | nop ;; vf20 = persp-off + ilw.z vi09, 3(vi12) | mul.xyzw vf27, vf28, vf15 ;; vf27 = [pdx, pdy, pdz, 0], vi09 = mat-slot.1 + ior vi11, vi08, vi00 | mul.xyzw vf28, vf28, vf00 ;; vf28 = [0, 0, 0, pdw], vi11 = vi08 = mat-slot.0 + ibeq vi00, vi08, L2 | mul.xyzw vf15, vf16, vf15 ;; vf15 = [spdx, spdy, spdz, 0], skip if slot = 0 + iaddi vi13, vi12, 0x3 | mul.xyzw vf16, vf16, vf00 ;; vi13 = mat-slot-ptr, vf16 = [0, 0, 0, spdw] +``` +- mostly same as merc +- always picks `st-a`, merc had a branch here based on state of `xtop`. +- no fade out flag stuff + +### Matrix multiply loop +Premultiplies uploaded matrices by perspective. Only does matrices that were uploaded this time. +Same as merc, so skipping. + +### The rest of it +- Transformed vertex (before perspective divide and pfog0 multiply is store back over `lump[2]`) +- Transformed normal is stored over `rgba` +``` +L2: (L14 in og merc) +;; Pipelining Start for vertex transform + ilw.x vi02, 3(vi12) | nop ;; vi02 = perc-off + ibeq vi00, vi04, L13 | nop ;; goto L13 if mat1 count is 0 + iadd vi01, vi01, vi12 | nop ;; vi01 = lump. + +;; Pipelining start for matrix 1's + ilwr.x vi08, vi01 | nop ;; vi08 = lump[0].x = mat-0? + lqi.xyzw vf08, vi01 | nop + lqi.xyzw vf11, vi01 | nop + lqi.xyzw vf14, vi01 | nop ;; vf14 = lump[2] = [texs, text, nrmz, posz] + lq.xyz vf29, 4(vi08) | nop + lq.xyz vf30, 5(vi08) | add.zw vf08, vf08, vf17 + lq.xyzw vf31, 6(vi08) | add.xyzw vf11, vf11, vf18 + iaddi vi04, vi04, -0x1 | add.xyzw vf14, vf14, vf19 + iadd vi02, vi02, vi12 | nop + lqi.xyzw vf24, vi02 | mulaz.xyzw ACC, vf29, vf08 + mtir vi10, vf11.x | maddaz.xyzw ACC, vf30, vf11 + mtir vi13, vf11.y | maddz.xyz vf11, vf31, vf14 + lq.xyzw vf25, 0(vi08) | nop + lq.xyzw vf26, 1(vi08) | itof0.xyzw vf24, vf24 + lq.xyzw vf27, 2(vi08) | nop + erleng.xyz P, vf11 | nop + lq.xyzw vf28, 3(vi08) | mulaw.xyzw ACC, vf25, vf08 + nop | maddaw.xyzw ACC, vf26, vf11 ;; modified from merc, no mercprime crap + mr32.z vf14, vf00 | maddw.xyzw vf08, vf27, vf14 + lqi.xyzw vf09, vi01 | nop + ilwr.y vi03, vi12 | nop + ilw.z vi07, 1(vi12) | nop + lqi.xyzw vf12, vi01 | add.xyzw vf08, vf08, vf28 + lqi.xyzw vf15, vi01 | nop + mtir vi08, vf09.x | nop ;; mercprime stuff in og. + + ;; CHANGE: transformed vf08 (pre perspective divide, pfog mult) + ;; is stored back! over lop lump[2] (texs, text, nrmz, posz) + sq.xyzw vf08, -4(vi01) | miniw.w vf08, vf08, vf01 + + iadd vi03, vi03, vi12 | nop + div Q, vf01.w, vf08.w | add.zw vf09, vf09, vf17 + iadd vi04, vi04, vi03 | add.xyzw vf12, vf12, vf18 + lq.xyz vf29, 4(vi08) | add.xyzw vf15, vf15, vf19 + lq.xyz vf30, 5(vi08) | nop + iadd vi06, vi06, vi04 | nop + lq.xyzw vf31, 6(vi08) | nop + lq.xyzw vf25, 0(vi08) | nop + lq.xyzw vf26, 1(vi08) | mul.xyz vf08, vf08, Q + mtir vi11, vf12.x | mul.xyzw vf14, vf14, Q + mtir vi14, vf12.y | nop + lq.xyzw vf27, 2(vi08) | nop + lqi.xyzw vf23, vi03 | add.xyzw vf08, vf08, vf22 ;; load rgba, hvdf offset + iadd vi07, vi07, vi06 | mulaz.xyzw ACC, vf29, vf09 + lq.xyzw vf28, 3(vi08) | maddaz.xyzw ACC, vf30, vf12 + mfp.w vf20, P | maddz.xyz vf12, vf31, vf15 + nop | nop + 1024.0 | miniw.w vf08, vf08, vf03 :i + nop | mulaw.xyzw ACC, vf25, vf09 ;; modified, no mercprime branch + ilw.y vi09, -6(vi01) | mulw.xyzw vf11, vf11, vf20 ;; + erleng.xyz P, vf12 | maxi.xy vf08, vf08, I ;; like mercprimt path (L82 in og merc) + 3072.0 | nop :i + nop | minii.xy vf08, vf08, I + +;; CHANGE store back normal over RGBA. + sq.xyzw vf11, -1(vi03) | maddaw.xyzw ACC, vf26, vf12 + mr32.z vf15, vf00 | maddw.xyzw vf09, vf27, vf15 + lqi.xyzw vf10, vi01 | mulax.xyzw ACC, vf01, vf11 + ibne vi04, vi03, L4 | madday.xyzw ACC, vf02, vf11 ;; branch to L4, pipelined mat 1 + nop | maddz.xyzw vf11, vf03, vf11 + ibne vi06, vi03, L17 | nop + nop | nop + b L52 | nop + nop | nop + + ;; pipelined mat 1 loop start +L3: (L16 in og) + sq.xyzw vf11, -1(vi03) | nop ;; normal store back + 3072.0 | mulax.xyzw ACC, vf01, vf11 :i ;; mercprime crap + lqi.xyzw vf10, vi01 | minii.xy vf08, vf08, I + sq.xyzw vf13, 1(vi12) | madday.xyzw ACC, vf02, vf11 + sq.xyzw vf13, 1(vi15) | maddz.xyzw vf11, vf03, vf11 + ;; pipelined mat 1 entry point +L4: (L17 in og) + lqi.xyzw vf13, vi01 | add.xyzw vf09, vf09, vf28 + lqi.xyzw vf16, vi01 | maxw.w vf08, vf08, vf02 + mtir vi08, vf10.x | itof0.xyzw vf23, vf23 + ilw.y vi09, -9(vi01) | maxx.xyzw vf11, vf11, vf00 + sq.xyzw vf09, -4(vi01) | miniw.w vf09, vf09, vf01 + div Q, vf01.w, vf09.w | add.zw vf10, vf10, vf17 + move.xyzw vf21, vf08 | add.xyzw vf13, vf13, vf18 + lq.xyz vf29, 4(vi08) | add.xyzw vf16, vf16, vf19 + lq.xyz vf30, 5(vi08) | mulax.xyzw ACC, vf04, vf11 + ibgtz vi09, L5 | madday.xyzw ACC, vf05, vf11 + lq.xyzw vf31, 6(vi08) | maddaz.xyzw ACC, vf06, vf11 + nop | addx.w vf21, vf21, vf17 +L5: (L18 in og) + lq.xyzw vf25, 0(vi08) | maddw.xyzw vf11, vf07, vf00 + lq.xyzw vf26, 1(vi08) | mul.xyz vf09, vf09, Q + mtir vi12, vf13.x | mul.xyzw vf15, vf15, Q + mtir vi15, vf13.y | ftoi4.xyzw vf21, vf21 + lq.xyzw vf27, 2(vi08) | mul.xyzw vf11, vf11, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf09, vf09, vf22 + ibne vi00, vi09, L6 | mulaz.xyzw ACC, vf29, vf10 + sq.xyzw vf21, 2(vi10) | maddaz.xyzw ACC, vf30, vf13 + nop | ftoi4.xyzw vf21, vf08 +L6: (L19 in og) + mfp.w vf20, P | maddz.xyz vf13, vf31, vf16 + sq.xyzw vf14, 0(vi10) | miniy.xyzw vf11, vf11, vf17 + sq.xyzw vf14, 0(vi13) | miniw.w vf09, vf09, vf03 + sq.xyzw vf21, 2(vi13) | mulaw.xyzw ACC, vf25, vf10 + lq.xyzw vf28, 3(vi08) | mulw.xyzw vf12, vf12, vf20 + 1024.0 | ftoi0.xyzw vf11, vf11 :i + erleng.xyz P, vf13 | maxi.xy vf09, vf09, I + ibne vi04, vi03, L7 | maddaw.xyzw ACC, vf26, vf13 + mr32.z vf16, vf00 | maddw.xyzw vf10, vf27, vf16 + ibne vi06, vi03, L22 | nop + ilw.y vi09, -6(vi01) | nop + ibne vi07, vi03, L57 | nop + nop | nop + b L67 | nop + nop | nop +L7: (L20 in og) + sq.xyzw vf12, -1(vi03) | nop + 3072.0 | mulax.xyzw ACC, vf01, vf12 :i + lqi.xyzw vf08, vi01 | minii.xy vf09, vf09, I + sq.xyzw vf11, 1(vi10) | madday.xyzw ACC, vf02, vf12 + sq.xyzw vf11, 1(vi13) | maddz.xyzw vf12, vf03, vf12 + lqi.xyzw vf11, vi01 | add.xyzw vf10, vf10, vf28 + lqi.xyzw vf14, vi01 | maxw.w vf09, vf09, vf02 + mtir vi08, vf08.x | itof0.xyzw vf23, vf23 + ilw.y vi09, -9(vi01) | maxx.xyzw vf12, vf12, vf00 + sq.xyzw vf10, -4(vi01) | miniw.w vf10, vf10, vf01 + div Q, vf01.w, vf10.w | add.zw vf08, vf08, vf17 + move.xyzw vf21, vf09 | add.xyzw vf11, vf11, vf18 + lq.xyz vf29, 4(vi08) | add.xyzw vf14, vf14, vf19 + lq.xyz vf30, 5(vi08) | mulax.xyzw ACC, vf04, vf12 + ibgtz vi09, L8 | madday.xyzw ACC, vf05, vf12 + lq.xyzw vf31, 6(vi08) | maddaz.xyzw ACC, vf06, vf12 + nop | addx.w vf21, vf21, vf17 +L8: (L21 in og) + lq.xyzw vf25, 0(vi08) | maddw.xyzw vf12, vf07, vf00 + lq.xyzw vf26, 1(vi08) | mul.xyz vf10, vf10, Q + mtir vi10, vf11.x | mul.xyzw vf16, vf16, Q + mtir vi13, vf11.y | ftoi4.xyzw vf21, vf21 + lq.xyzw vf27, 2(vi08) | mul.xyzw vf12, vf12, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf10, vf10, vf22 + ibne vi00, vi09, L9 | mulaz.xyzw ACC, vf29, vf08 + sq.xyzw vf21, 2(vi11) | maddaz.xyzw ACC, vf30, vf11 + nop | ftoi4.xyzw vf21, vf09 +L9: (L22 in og) + mfp.w vf20, P | maddz.xyz vf11, vf31, vf14 + sq.xyzw vf15, 0(vi11) | miniy.xyzw vf12, vf12, vf17 + sq.xyzw vf15, 0(vi14) | miniw.w vf10, vf10, vf03 + sq.xyzw vf21, 2(vi14) | mulaw.xyzw ACC, vf25, vf08 + lq.xyzw vf28, 3(vi08) | mulw.xyzw vf13, vf13, vf20 + 1024.0 | ftoi0.xyzw vf12, vf12 :i + erleng.xyz P, vf11 | maxi.xy vf10, vf10, I + ibne vi04, vi03, L10 | maddaw.xyzw ACC, vf26, vf11 + mr32.z vf14, vf00 | maddw.xyzw vf08, vf27, vf14 + ibne vi06, vi03, L27 | nop + ilw.y vi09, -6(vi01) | nop + ibne vi07, vi03, L62 | nop + nop | nop + b L72 | nop + nop | nop +L10: (L23 in og) + sq.xyzw vf13, -1(vi03) | nop + 3072.0 | mulax.xyzw ACC, vf01, vf13 :i + lqi.xyzw vf09, vi01 | minii.xy vf10, vf10, I + sq.xyzw vf12, 1(vi11) | madday.xyzw ACC, vf02, vf13 + sq.xyzw vf12, 1(vi14) | maddz.xyzw vf13, vf03, vf13 + lqi.xyzw vf12, vi01 | add.xyzw vf08, vf08, vf28 + lqi.xyzw vf15, vi01 | maxw.w vf10, vf10, vf02 + mtir vi08, vf09.x | itof0.xyzw vf23, vf23 + ilw.y vi09, -9(vi01) | maxx.xyzw vf13, vf13, vf00 + sq.xyzw vf08, -4(vi01) | miniw.w vf08, vf08, vf01 + div Q, vf01.w, vf08.w | add.zw vf09, vf09, vf17 + move.xyzw vf21, vf10 | add.xyzw vf12, vf12, vf18 + lq.xyz vf29, 4(vi08) | add.xyzw vf15, vf15, vf19 + lq.xyz vf30, 5(vi08) | mulax.xyzw ACC, vf04, vf13 + ibgtz vi09, L11 | madday.xyzw ACC, vf05, vf13 + lq.xyzw vf31, 6(vi08) | maddaz.xyzw ACC, vf06, vf13 + nop | addx.w vf21, vf21, vf17 +L11: (L24 in og) + lq.xyzw vf25, 0(vi08) | maddw.xyzw vf13, vf07, vf00 + lq.xyzw vf26, 1(vi08) | mul.xyz vf08, vf08, Q + mtir vi11, vf12.x | mul.xyzw vf14, vf14, Q + mtir vi14, vf12.y | ftoi4.xyzw vf21, vf21 + lq.xyzw vf27, 2(vi08) | mul.xyzw vf13, vf13, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf08, vf08, vf22 + ibne vi00, vi09, L12 | mulaz.xyzw ACC, vf29, vf09 + sq.xyzw vf21, 2(vi12) | maddaz.xyzw ACC, vf30, vf12 + nop | ftoi4.xyzw vf21, vf10 +L12: (L25 in og) + mfp.w vf20, P | maddz.xyz vf12, vf31, vf15 + sq.xyzw vf16, 0(vi12) | miniy.xyzw vf13, vf13, vf17 + sq.xyzw vf16, 0(vi15) | miniw.w vf08, vf08, vf03 + sq.xyzw vf21, 2(vi15) | mulaw.xyzw ACC, vf25, vf09 + lq.xyzw vf28, 3(vi08) | mulw.xyzw vf11, vf11, vf20 + 1024.0 | ftoi0.xyzw vf13, vf13 :i + erleng.xyz P, vf12 | maxi.xy vf08, vf08, I + ibne vi04, vi03, L3 | maddaw.xyzw ACC, vf26, vf12 + mr32.z vf15, vf00 | maddw.xyzw vf09, vf27, vf15 + ibne vi06, vi03, L16 | nop + ilw.y vi09, -6(vi01) | nop + ibne vi07, vi03, L51 | nop + nop | nop + b L77 | nop + nop | nop + +L13 (L26 in og merc): + ;; pipeline startup for mat 2's (assuming you have no mat1's) + ibeq vi00, vi06, L47 | nop + iadd vi02, vi02, vi12 | nop + lqi.xyzw vf08, vi01 | nop + lqi.xyzw vf24, vi02 | nop + lqi.xyzw vf11, vi01 | nop + lqi.xyzw vf14, vi01 | nop + mtir vi10, vf08.x | nop + mtir vi13, vf08.y | itof0.xyzw vf24, vf24 + iaddi vi06, vi06, -0x1 | add.zw vf08, vf08, vf17 + nop | add.xyzw vf11, vf11, vf18 + iand vi10, vi10, vi05 | add.xyzw vf14, vf14, vf19 + nop | mulw.xyzw vf24, vf24, vf29 + iand vi13, vi13, vi05 | nop + lq.xyzw vf20, 0(vi10) | nop + lq.xyzw vf25, 0(vi13) | nop + lq.xyzw vf23, 1(vi10) | nop + lq.xyzw vf26, 1(vi13) | nop + lq.xyzw vf20, 2(vi10) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf27, 2(vi13) | maddy.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 3(vi10) | mulax.xyzw ACC, vf23, vf24 + lq.xyzw vf28, 3(vi13) | maddy.xyzw vf26, vf26, vf24 + lq.xyzw vf20, 4(vi10) | mulax.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi13) | maddy.xyzw vf27, vf27, vf24 + lq.xyzw vf23, 5(vi10) | mulax.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi13) | maddy.xyzw vf28, vf28, vf24 + lq.xyzw vf20, 6(vi10) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf31, 6(vi13) | maddy.xyz vf29, vf29, vf24 + mtir vi10, vf11.x | mulax.xyzw ACC, vf23, vf24 + mtir vi13, vf11.y | maddy.xyz vf30, vf30, vf24 + nop | mulax.xyzw ACC, vf20, vf24 + nop | maddy.xyzw vf31, vf31, vf24 + nop | mulaz.xyzw ACC, vf29, vf08 + nop | maddaz.xyzw ACC, vf30, vf11 + nop | maddz.xyz vf11, vf31, vf14 + nop | nop + nop | nop + nop | mulaw.xyzw ACC, vf25, vf08 + nop | nop + erleng.xyz P, vf11 | nop + nop | maddaw.xyzw ACC, vf26, vf11 + mr32.z vf14, vf00 | maddw.xyzw vf08, vf27, vf14 + lqi.xyzw vf09, vi01 | nop + ilwr.y vi03, vi12 | nop + ilw.z vi07, 1(vi12) | nop + lqi.xyzw vf12, vi01 | add.xyzw vf08, vf08, vf28 + lqi.xyzw vf15, vi01 | nop + mtir vi11, vf09.x | nop + mtir vi14, vf09.y | nop + sq.xyzw vf08, -4(vi01) | miniw.w vf08, vf08, vf01 + div Q, vf01.w, vf08.w | add.zw vf09, vf09, vf17 + iadd vi03, vi03, vi12 | add.xyzw vf12, vf12, vf18 + iand vi11, vi11, vi05 | add.xyzw vf15, vf15, vf19 + iadd vi06, vi06, vi03 | nop + iadd vi07, vi07, vi06 | nop + iand vi14, vi14, vi05 | nop + ibne vi05, vi11, L14 | nop + iaddiu vi08, vi00, 0x23a | mul.xyz vf08, vf08, Q + mtir vi11, vf12.x | mul.xyzw vf14, vf14, Q + mtir vi14, vf12.y | nop + b L15 | nop + lqi.xyzw vf23, vi03 | add.xyzw vf08, vf08, vf22 +L14: (L28 in og) + lq.xyzw vf20, 0(vi11) | mul.xyzw vf14, vf14, Q + lq.xyzw vf25, 0(vi14) | nop + lq.xyzw vf23, 1(vi11) | nop + lq.xyzw vf26, 1(vi14) | add.xyzw vf08, vf08, vf22 + lq.xyzw vf20, 2(vi11) | mulaz.xyzw ACC, vf20, vf24 + lq.xyzw vf27, 2(vi14) | maddw.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 3(vi11) | mulaz.xyzw ACC, vf23, vf24 + lq.xyzw vf28, 3(vi14) | maddw.xyzw vf26, vf26, vf24 + lq.xyzw vf20, 4(vi11) | mulaz.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi14) | maddw.xyzw vf27, vf27, vf24 + lq.xyzw vf23, 5(vi11) | mulaz.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi14) | maddw.xyzw vf28, vf28, vf24 + lq.xyzw vf20, 6(vi11) | mulaz.xyzw ACC, vf20, vf24 + lq.xyzw vf31, 6(vi14) | maddw.xyz vf29, vf29, vf24 + lqi.xyzw vf23, vi02 | mulaz.xyzw ACC, vf23, vf24 + mtir vi11, vf12.x | maddw.xyz vf30, vf30, vf24 + mtir vi14, vf12.y | mulaz.xyzw ACC, vf20, vf24 + iaddiu vi08, vi00, 0x18c | maddw.xyzw vf31, vf31, vf24 + lqi.xyzw vf23, vi03 | itof0.xyzw vf24, vf23 +L15: (L29 in og) + nop | mulaz.xyzw ACC, vf29, vf09 + nop | maddaz.xyzw ACC, vf30, vf12 + mfp.w vf20, P | maddz.xyz vf12, vf31, vf15 + nop | nop + 1024.0 | miniw.w vf08, vf08, vf03 :i + nop | mulaw.xyzw ACC, vf25, vf09 + ilw.y vi09, -6(vi01) | mulw.xyzw vf11, vf11, vf20 + erleng.xyz P, vf12 | maxi.xy vf08, vf08, I + 3072.0 | nop :i + sq.xyzw vf11, -1(vi03) | minii.xy vf08, vf08, I + ibeq vi06, vi03, L50 | maddaw.xyzw ACC, vf26, vf12 + mr32.z vf15, vf00 | maddw.xyzw vf09, vf27, vf15 + lqi.xyzw vf10, vi01 | mulax.xyzw ACC, vf01, vf11 + jr vi08 | madday.xyzw ACC, vf02, vf11 + nop | maddz.xyzw vf11, vf03, vf11 +L16: (L30 in og) + sq.xyzw vf11, -1(vi03) | nop + 3072.0 | mulax.xyzw ACC, vf01, vf11 :i + lqi.xyzw vf10, vi01 | minii.xy vf08, vf08, I + sq.xyzw vf13, 1(vi12) | madday.xyzw ACC, vf02, vf11 + sq.xyzw vf13, 1(vi15) | maddz.xyzw vf11, vf03, vf11 +L17: (L31 in og) + lqi.xyzw vf13, vi01 | add.xyzw vf09, vf09, vf28 + lqi.xyzw vf16, vi01 | maxw.w vf08, vf08, vf02 + mtir vi12, vf10.x | itof0.xyzw vf23, vf23 + mtir vi15, vf10.y | maxx.xyzw vf11, vf11, vf00 + sq.xyzw vf09, -4(vi01) | miniw.w vf09, vf09, vf01 + div Q, vf01.w, vf09.w | add.zw vf10, vf10, vf17 + move.xyzw vf21, vf08 | add.xyzw vf13, vf13, vf18 + iand vi12, vi12, vi05 | add.xyzw vf16, vf16, vf19 + nop | mulax.xyzw ACC, vf04, vf11 + ibgtz vi09, L18 | madday.xyzw ACC, vf05, vf11 + iand vi15, vi15, vi05 | maddaz.xyzw ACC, vf06, vf11 + nop | addx.w vf21, vf21, vf17 +L18: (L32 in og) + ibne vi05, vi12, L19 | maddw.xyzw vf11, vf07, vf00 + ilw.x vi09, -9(vi01) | mul.xyz vf09, vf09, Q + mtir vi12, vf13.x | mul.xyzw vf15, vf15, Q + mtir vi15, vf13.y | ftoi4.xyzw vf21, vf21 + b L20 | mul.xyzw vf11, vf11, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf09, vf09, vf22 +L19: (L33 in og) + lq.xyzw vf20, 0(vi12) | mul.xyzw vf15, vf15, Q + nop | mulw.xyzw vf24, vf24, vf29 + lq.xyzw vf25, 0(vi15) | ftoi4.xyzw vf21, vf21 + lq.xyzw vf23, 1(vi12) | mul.xyzw vf11, vf11, vf23 + lq.xyzw vf26, 1(vi15) | add.xyzw vf09, vf09, vf22 + lq.xyzw vf20, 2(vi12) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf27, 2(vi15) | maddy.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 3(vi12) | mulax.xyzw ACC, vf23, vf24 + lq.xyzw vf28, 3(vi15) | maddy.xyzw vf26, vf26, vf24 + lq.xyzw vf20, 4(vi12) | mulax.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi15) | maddy.xyzw vf27, vf27, vf24 + lq.xyzw vf23, 5(vi12) | mulax.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi15) | maddy.xyzw vf28, vf28, vf24 + lq.xyzw vf20, 6(vi12) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf31, 6(vi15) | maddy.xyz vf29, vf29, vf24 + mtir vi12, vf13.x | mulax.xyzw ACC, vf23, vf24 + mtir vi15, vf13.y | maddy.xyz vf30, vf30, vf24 + b L35 | mulax.xyzw ACC, vf20, vf24 + lqi.xyzw vf23, vi03 | maddy.xyzw vf31, vf31, vf24 +L20: (L34 in og) + ibgez vi09, L21 | mulaz.xyzw ACC, vf29, vf10 + sq.xyzw vf21, 2(vi10) | maddaz.xyzw ACC, vf30, vf13 + nop | ftoi4.xyzw vf21, vf08 +L21: (L35 in og) + mfp.w vf20, P | maddz.xyz vf13, vf31, vf16 + sq.xyzw vf14, 0(vi10) | miniy.xyzw vf11, vf11, vf17 + sq.xyzw vf14, 0(vi13) | miniw.w vf09, vf09, vf03 + sq.xyzw vf21, 2(vi13) | mulaw.xyzw ACC, vf25, vf10 + ilw.y vi09, -6(vi01) | mulw.xyzw vf12, vf12, vf20 + 1024.0 | ftoi0.xyzw vf11, vf11 :i + erleng.xyz P, vf13 | maxi.xy vf09, vf09, I + ibne vi06, vi03, L22 | maddaw.xyzw ACC, vf26, vf13 + mr32.z vf16, vf00 | maddw.xyzw vf10, vf27, vf16 + ibne vi07, vi03, L57 | nop + nop | nop + b L67 | nop + nop | nop +L22: (L36 in og) + sq.xyzw vf12, -1(vi03) | nop + 3072.0 | mulax.xyzw ACC, vf01, vf12 :i + lqi.xyzw vf08, vi01 | minii.xy vf09, vf09, I + sq.xyzw vf11, 1(vi10) | madday.xyzw ACC, vf02, vf12 + sq.xyzw vf11, 1(vi13) | maddz.xyzw vf12, vf03, vf12 + lqi.xyzw vf11, vi01 | add.xyzw vf10, vf10, vf28 + lqi.xyzw vf14, vi01 | maxw.w vf09, vf09, vf02 + mtir vi10, vf08.x | itof0.xyzw vf23, vf23 + mtir vi13, vf08.y | maxx.xyzw vf12, vf12, vf00 + sq.xyzw vf10, -4(vi01) | miniw.w vf10, vf10, vf01 + div Q, vf01.w, vf10.w | add.zw vf08, vf08, vf17 + move.xyzw vf21, vf09 | add.xyzw vf11, vf11, vf18 + iand vi10, vi10, vi05 | add.xyzw vf14, vf14, vf19 + nop | mulax.xyzw ACC, vf04, vf12 + ibgtz vi09, L23 | madday.xyzw ACC, vf05, vf12 + iand vi13, vi13, vi05 | maddaz.xyzw ACC, vf06, vf12 + nop | addx.w vf21, vf21, vf17 +L23: (L37 in og) + ibne vi05, vi10, L24 | maddw.xyzw vf12, vf07, vf00 + ilw.x vi09, -9(vi01) | mul.xyz vf10, vf10, Q + mtir vi10, vf11.x | mul.xyzw vf16, vf16, Q + mtir vi13, vf11.y | ftoi4.xyzw vf21, vf21 + b L25 | mul.xyzw vf12, vf12, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf10, vf10, vf22 +L24: (L38 in og) + lq.xyzw vf20, 0(vi10) | mul.xyzw vf16, vf16, Q + nop | mulw.xyzw vf24, vf24, vf29 + lq.xyzw vf25, 0(vi13) | ftoi4.xyzw vf21, vf21 + lq.xyzw vf23, 1(vi10) | mul.xyzw vf12, vf12, vf23 + lq.xyzw vf26, 1(vi13) | add.xyzw vf10, vf10, vf22 + lq.xyzw vf20, 2(vi10) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf27, 2(vi13) | maddy.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 3(vi10) | mulax.xyzw ACC, vf23, vf24 + lq.xyzw vf28, 3(vi13) | maddy.xyzw vf26, vf26, vf24 + lq.xyzw vf20, 4(vi10) | mulax.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi13) | maddy.xyzw vf27, vf27, vf24 + lq.xyzw vf23, 5(vi10) | mulax.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi13) | maddy.xyzw vf28, vf28, vf24 + lq.xyzw vf20, 6(vi10) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf31, 6(vi13) | maddy.xyz vf29, vf29, vf24 + mtir vi10, vf11.x | mulax.xyzw ACC, vf23, vf24 + mtir vi13, vf11.y | maddy.xyz vf30, vf30, vf24 + b L40 | mulax.xyzw ACC, vf20, vf24 + lqi.xyzw vf23, vi03 | maddy.xyzw vf31, vf31, vf24 +L25: (L39 in og) + ibgez vi09, L26 | mulaz.xyzw ACC, vf29, vf08 + sq.xyzw vf21, 2(vi11) | maddaz.xyzw ACC, vf30, vf11 + nop | ftoi4.xyzw vf21, vf09 +L26: (L40 in og) + mfp.w vf20, P | maddz.xyz vf11, vf31, vf14 + sq.xyzw vf15, 0(vi11) | miniy.xyzw vf12, vf12, vf17 + sq.xyzw vf15, 0(vi14) | miniw.w vf10, vf10, vf03 + sq.xyzw vf21, 2(vi14) | mulaw.xyzw ACC, vf25, vf08 + ilw.y vi09, -6(vi01) | mulw.xyzw vf13, vf13, vf20 + 1024.0 | ftoi0.xyzw vf12, vf12 :i + erleng.xyz P, vf11 | maxi.xy vf10, vf10, I + ibne vi06, vi03, L27 | maddaw.xyzw ACC, vf26, vf11 + mr32.z vf14, vf00 | maddw.xyzw vf08, vf27, vf14 + ibne vi07, vi03, L62 | nop + nop | nop + b L72 | nop + nop | nop +L27: (L41 in og) + sq.xyzw vf13, -1(vi03) | nop + 3072.0 | mulax.xyzw ACC, vf01, vf13 :i + lqi.xyzw vf09, vi01 | minii.xy vf10, vf10, I + sq.xyzw vf12, 1(vi11) | madday.xyzw ACC, vf02, vf13 + sq.xyzw vf12, 1(vi14) | maddz.xyzw vf13, vf03, vf13 + lqi.xyzw vf12, vi01 | add.xyzw vf08, vf08, vf28 + lqi.xyzw vf15, vi01 | maxw.w vf10, vf10, vf02 + mtir vi11, vf09.x | itof0.xyzw vf23, vf23 + mtir vi14, vf09.y | maxx.xyzw vf13, vf13, vf00 + sq.xyzw vf08, -4(vi01) | miniw.w vf08, vf08, vf01 + div Q, vf01.w, vf08.w | add.zw vf09, vf09, vf17 + move.xyzw vf21, vf10 | add.xyzw vf12, vf12, vf18 + iand vi11, vi11, vi05 | add.xyzw vf15, vf15, vf19 + nop | mulax.xyzw ACC, vf04, vf13 + ibgtz vi09, L28 | madday.xyzw ACC, vf05, vf13 + iand vi14, vi14, vi05 | maddaz.xyzw ACC, vf06, vf13 + nop | addx.w vf21, vf21, vf17 +L28: (L42 in og) + ibne vi05, vi11, L29 | maddw.xyzw vf13, vf07, vf00 + ilw.x vi09, -9(vi01) | mul.xyz vf08, vf08, Q + mtir vi11, vf12.x | mul.xyzw vf14, vf14, Q + mtir vi14, vf12.y | ftoi4.xyzw vf21, vf21 + b L30 | mul.xyzw vf13, vf13, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf08, vf08, vf22 +L29: (L43 in og) + lq.xyzw vf20, 0(vi11) | mul.xyzw vf14, vf14, Q + nop | mulw.xyzw vf24, vf24, vf29 + lq.xyzw vf25, 0(vi14) | ftoi4.xyzw vf21, vf21 + lq.xyzw vf23, 1(vi11) | mul.xyzw vf13, vf13, vf23 + lq.xyzw vf26, 1(vi14) | add.xyzw vf08, vf08, vf22 + lq.xyzw vf20, 2(vi11) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf27, 2(vi14) | maddy.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 3(vi11) | mulax.xyzw ACC, vf23, vf24 + lq.xyzw vf28, 3(vi14) | maddy.xyzw vf26, vf26, vf24 + lq.xyzw vf20, 4(vi11) | mulax.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi14) | maddy.xyzw vf27, vf27, vf24 + lq.xyzw vf23, 5(vi11) | mulax.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi14) | maddy.xyzw vf28, vf28, vf24 + lq.xyzw vf20, 6(vi11) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf31, 6(vi14) | maddy.xyz vf29, vf29, vf24 + mtir vi11, vf12.x | mulax.xyzw ACC, vf23, vf24 + mtir vi14, vf12.y | maddy.xyz vf30, vf30, vf24 + b L45 | mulax.xyzw ACC, vf20, vf24 + lqi.xyzw vf23, vi03 | maddy.xyzw vf31, vf31, vf24 +L30: (L44 in og) + ibgez vi09, L31 | mulaz.xyzw ACC, vf29, vf09 + sq.xyzw vf21, 2(vi12) | maddaz.xyzw ACC, vf30, vf12 + nop | ftoi4.xyzw vf21, vf10 +L31: (L45 in og) + mfp.w vf20, P | maddz.xyz vf12, vf31, vf15 + sq.xyzw vf16, 0(vi12) | miniy.xyzw vf13, vf13, vf17 + sq.xyzw vf16, 0(vi15) | miniw.w vf08, vf08, vf03 + sq.xyzw vf21, 2(vi15) | mulaw.xyzw ACC, vf25, vf09 + ilw.y vi09, -6(vi01) | mulw.xyzw vf11, vf11, vf20 + 1024.0 | ftoi0.xyzw vf13, vf13 :i + erleng.xyz P, vf12 | maxi.xy vf08, vf08, I + ibne vi06, vi03, L16 | maddaw.xyzw ACC, vf26, vf12 + mr32.z vf15, vf00 | maddw.xyzw vf09, vf27, vf15 + ibne vi07, vi03, L51 | nop + nop | nop + b L77 | nop + nop | nop +L32: (L46 in og) + sq.xyzw vf11, -1(vi03) | nop + 3072.0 | mulax.xyzw ACC, vf01, vf11 :i + lqi.xyzw vf10, vi01 | minii.xy vf08, vf08, I + sq.xyzw vf13, 1(vi12) | madday.xyzw ACC, vf02, vf11 + sq.xyzw vf13, 1(vi15) | maddz.xyzw vf11, vf03, vf11 + lqi.xyzw vf13, vi01 | add.xyzw vf09, vf09, vf28 + lqi.xyzw vf16, vi01 | maxw.w vf08, vf08, vf02 + mtir vi12, vf10.x | itof0.xyzw vf23, vf23 + mtir vi15, vf10.y | maxx.xyzw vf11, vf11, vf00 + sq.xyzw vf09, -4(vi01) | miniw.w vf09, vf09, vf01 + div Q, vf01.w, vf09.w | add.zw vf10, vf10, vf17 + move.xyzw vf21, vf08 | add.xyzw vf13, vf13, vf18 + iand vi12, vi12, vi05 | add.xyzw vf16, vf16, vf19 + nop | mulax.xyzw ACC, vf04, vf11 + ibgtz vi09, L33 | madday.xyzw ACC, vf05, vf11 + iand vi15, vi15, vi05 | maddaz.xyzw ACC, vf06, vf11 + nop | addx.w vf21, vf21, vf17 +L33: (L47 in og) + ibne vi05, vi12, L34 | maddw.xyzw vf11, vf07, vf00 + ilw.x vi09, -9(vi01) | mul.xyz vf09, vf09, Q + mtir vi12, vf13.x | mul.xyzw vf15, vf15, Q + mtir vi15, vf13.y | ftoi4.xyzw vf21, vf21 + b L35 | mul.xyzw vf11, vf11, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf09, vf09, vf22 +L34: (L48 in og) + lq.xyzw vf20, 0(vi12) | mul.xyzw vf15, vf15, Q + lq.xyzw vf25, 0(vi15) | ftoi4.xyzw vf21, vf21 + lq.xyzw vf23, 1(vi12) | mul.xyzw vf11, vf11, vf23 + lq.xyzw vf26, 1(vi15) | add.xyzw vf09, vf09, vf22 + lq.xyzw vf20, 2(vi12) | mulaz.xyzw ACC, vf20, vf24 + lq.xyzw vf27, 2(vi15) | maddw.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 3(vi12) | mulaz.xyzw ACC, vf23, vf24 + lq.xyzw vf28, 3(vi15) | maddw.xyzw vf26, vf26, vf24 + lq.xyzw vf20, 4(vi12) | mulaz.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi15) | maddw.xyzw vf27, vf27, vf24 + lq.xyzw vf23, 5(vi12) | mulaz.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi15) | maddw.xyzw vf28, vf28, vf24 + lq.xyzw vf20, 6(vi12) | mulaz.xyzw ACC, vf20, vf24 + lq.xyzw vf31, 6(vi15) | maddw.xyz vf29, vf29, vf24 + lqi.xyzw vf23, vi02 | mulaz.xyzw ACC, vf23, vf24 + mtir vi12, vf13.x | maddw.xyz vf30, vf30, vf24 + mtir vi15, vf13.y | mulaz.xyzw ACC, vf20, vf24 + b L20 | maddw.xyzw vf31, vf31, vf24 + lqi.xyzw vf23, vi03 | itof0.xyzw vf24, vf23 +L35: (L49 in og) + ibgez vi09, L36 | mulaz.xyzw ACC, vf29, vf10 + sq.xyzw vf21, 2(vi10) | maddaz.xyzw ACC, vf30, vf13 + nop | ftoi4.xyzw vf21, vf08 +L36: (L50 in og) + mfp.w vf20, P | maddz.xyz vf13, vf31, vf16 + sq.xyzw vf14, 0(vi10) | miniy.xyzw vf11, vf11, vf17 + sq.xyzw vf14, 0(vi13) | miniw.w vf09, vf09, vf03 + sq.xyzw vf21, 2(vi13) | mulaw.xyzw ACC, vf25, vf10 + ilw.y vi09, -6(vi01) | mulw.xyzw vf12, vf12, vf20 + 1024.0 | ftoi0.xyzw vf11, vf11 :i + erleng.xyz P, vf13 | maxi.xy vf09, vf09, I + ibne vi06, vi03, L37 | maddaw.xyzw ACC, vf26, vf13 + mr32.z vf16, vf00 | maddw.xyzw vf10, vf27, vf16 + ibne vi07, vi03, L57 | nop + nop | nop + b L67 | nop + nop | nop +L37: (L51 in og) + sq.xyzw vf12, -1(vi03) | nop + 3072.0 | mulax.xyzw ACC, vf01, vf12 :i + lqi.xyzw vf08, vi01 | minii.xy vf09, vf09, I + sq.xyzw vf11, 1(vi10) | madday.xyzw ACC, vf02, vf12 + sq.xyzw vf11, 1(vi13) | maddz.xyzw vf12, vf03, vf12 + lqi.xyzw vf11, vi01 | add.xyzw vf10, vf10, vf28 + lqi.xyzw vf14, vi01 | maxw.w vf09, vf09, vf02 + mtir vi10, vf08.x | itof0.xyzw vf23, vf23 + mtir vi13, vf08.y | maxx.xyzw vf12, vf12, vf00 + sq.xyzw vf10, -4(vi01) | miniw.w vf10, vf10, vf01 + div Q, vf01.w, vf10.w | add.zw vf08, vf08, vf17 + move.xyzw vf21, vf09 | add.xyzw vf11, vf11, vf18 + iand vi10, vi10, vi05 | add.xyzw vf14, vf14, vf19 + nop | mulax.xyzw ACC, vf04, vf12 + ibgtz vi09, L38 | madday.xyzw ACC, vf05, vf12 + iand vi13, vi13, vi05 | maddaz.xyzw ACC, vf06, vf12 + nop | addx.w vf21, vf21, vf17 +L38: (L52 in og) + ibne vi05, vi10, L39 | maddw.xyzw vf12, vf07, vf00 + ilw.x vi09, -9(vi01) | mul.xyz vf10, vf10, Q + mtir vi10, vf11.x | mul.xyzw vf16, vf16, Q + mtir vi13, vf11.y | ftoi4.xyzw vf21, vf21 + b L40 | mul.xyzw vf12, vf12, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf10, vf10, vf22 +L39: (L53 in og) + lq.xyzw vf20, 0(vi10) | mul.xyzw vf16, vf16, Q + lq.xyzw vf25, 0(vi13) | ftoi4.xyzw vf21, vf21 + lq.xyzw vf23, 1(vi10) | mul.xyzw vf12, vf12, vf23 + lq.xyzw vf26, 1(vi13) | add.xyzw vf10, vf10, vf22 + lq.xyzw vf20, 2(vi10) | mulaz.xyzw ACC, vf20, vf24 + lq.xyzw vf27, 2(vi13) | maddw.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 3(vi10) | mulaz.xyzw ACC, vf23, vf24 + lq.xyzw vf28, 3(vi13) | maddw.xyzw vf26, vf26, vf24 + lq.xyzw vf20, 4(vi10) | mulaz.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi13) | maddw.xyzw vf27, vf27, vf24 + lq.xyzw vf23, 5(vi10) | mulaz.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi13) | maddw.xyzw vf28, vf28, vf24 + lq.xyzw vf20, 6(vi10) | mulaz.xyzw ACC, vf20, vf24 + lq.xyzw vf31, 6(vi13) | maddw.xyz vf29, vf29, vf24 + lqi.xyzw vf23, vi02 | mulaz.xyzw ACC, vf23, vf24 + mtir vi10, vf11.x | maddw.xyz vf30, vf30, vf24 + mtir vi13, vf11.y | mulaz.xyzw ACC, vf20, vf24 + b L25 | maddw.xyzw vf31, vf31, vf24 + lqi.xyzw vf23, vi03 | itof0.xyzw vf24, vf23 +L40: (L54 in og) + ibgez vi09, L41 | mulaz.xyzw ACC, vf29, vf08 + sq.xyzw vf21, 2(vi11) | maddaz.xyzw ACC, vf30, vf11 + nop | ftoi4.xyzw vf21, vf09 +L41: (L55 in og) + mfp.w vf20, P | maddz.xyz vf11, vf31, vf14 + sq.xyzw vf15, 0(vi11) | miniy.xyzw vf12, vf12, vf17 + sq.xyzw vf15, 0(vi14) | miniw.w vf10, vf10, vf03 + sq.xyzw vf21, 2(vi14) | mulaw.xyzw ACC, vf25, vf08 + ilw.y vi09, -6(vi01) | mulw.xyzw vf13, vf13, vf20 + 1024.0 | ftoi0.xyzw vf12, vf12 :i + erleng.xyz P, vf11 | maxi.xy vf10, vf10, I + ibne vi06, vi03, L42 | maddaw.xyzw ACC, vf26, vf11 + mr32.z vf14, vf00 | maddw.xyzw vf08, vf27, vf14 + ibne vi07, vi03, L62 | nop + nop | nop + b L72 | nop + nop | nop +L42: (L56 in og) + sq.xyzw vf13, -1(vi03) | nop + 3072.0 | mulax.xyzw ACC, vf01, vf13 :i + lqi.xyzw vf09, vi01 | minii.xy vf10, vf10, I + sq.xyzw vf12, 1(vi11) | madday.xyzw ACC, vf02, vf13 + sq.xyzw vf12, 1(vi14) | maddz.xyzw vf13, vf03, vf13 + lqi.xyzw vf12, vi01 | add.xyzw vf08, vf08, vf28 + lqi.xyzw vf15, vi01 | maxw.w vf10, vf10, vf02 + mtir vi11, vf09.x | itof0.xyzw vf23, vf23 + mtir vi14, vf09.y | maxx.xyzw vf13, vf13, vf00 + sq.xyzw vf08, -4(vi01) | miniw.w vf08, vf08, vf01 + div Q, vf01.w, vf08.w | add.zw vf09, vf09, vf17 + move.xyzw vf21, vf10 | add.xyzw vf12, vf12, vf18 + iand vi11, vi11, vi05 | add.xyzw vf15, vf15, vf19 + nop | mulax.xyzw ACC, vf04, vf13 + ibgtz vi09, L43 | madday.xyzw ACC, vf05, vf13 + iand vi14, vi14, vi05 | maddaz.xyzw ACC, vf06, vf13 + nop | addx.w vf21, vf21, vf17 +L43: (L57 in og) + ibne vi05, vi11, L44 | maddw.xyzw vf13, vf07, vf00 + ilw.x vi09, -9(vi01) | mul.xyz vf08, vf08, Q + mtir vi11, vf12.x | mul.xyzw vf14, vf14, Q + mtir vi14, vf12.y | ftoi4.xyzw vf21, vf21 + b L45 | mul.xyzw vf13, vf13, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf08, vf08, vf22 +L44: (L58 in og) + lq.xyzw vf20, 0(vi11) | mul.xyzw vf14, vf14, Q + lq.xyzw vf25, 0(vi14) | ftoi4.xyzw vf21, vf21 + lq.xyzw vf23, 1(vi11) | mul.xyzw vf13, vf13, vf23 + lq.xyzw vf26, 1(vi14) | add.xyzw vf08, vf08, vf22 + lq.xyzw vf20, 2(vi11) | mulaz.xyzw ACC, vf20, vf24 + lq.xyzw vf27, 2(vi14) | maddw.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 3(vi11) | mulaz.xyzw ACC, vf23, vf24 + lq.xyzw vf28, 3(vi14) | maddw.xyzw vf26, vf26, vf24 + lq.xyzw vf20, 4(vi11) | mulaz.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi14) | maddw.xyzw vf27, vf27, vf24 + lq.xyzw vf23, 5(vi11) | mulaz.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi14) | maddw.xyzw vf28, vf28, vf24 + lq.xyzw vf20, 6(vi11) | mulaz.xyzw ACC, vf20, vf24 + lq.xyzw vf31, 6(vi14) | maddw.xyz vf29, vf29, vf24 + lqi.xyzw vf23, vi02 | mulaz.xyzw ACC, vf23, vf24 + mtir vi11, vf12.x | maddw.xyz vf30, vf30, vf24 + mtir vi14, vf12.y | mulaz.xyzw ACC, vf20, vf24 + b L30 | maddw.xyzw vf31, vf31, vf24 + lqi.xyzw vf23, vi03 | itof0.xyzw vf24, vf23 +L45: (L59 in og) + ibgez vi09, L46 | mulaz.xyzw ACC, vf29, vf09 + sq.xyzw vf21, 2(vi12) | maddaz.xyzw ACC, vf30, vf12 + nop | ftoi4.xyzw vf21, vf10 +L46: (L60 in og) + mfp.w vf20, P | maddz.xyz vf12, vf31, vf15 + sq.xyzw vf16, 0(vi12) | miniy.xyzw vf13, vf13, vf17 + sq.xyzw vf16, 0(vi15) | miniw.w vf08, vf08, vf03 + sq.xyzw vf21, 2(vi15) | mulaw.xyzw ACC, vf25, vf09 + ilw.y vi09, -6(vi01) | mulw.xyzw vf11, vf11, vf20 + 1024.0 | ftoi0.xyzw vf13, vf13 :i + erleng.xyz P, vf12 | maxi.xy vf08, vf08, I + ibne vi06, vi03, L32 | maddaw.xyzw ACC, vf26, vf12 + mr32.z vf15, vf00 | maddw.xyzw vf09, vf27, vf15 + ibne vi07, vi03, L57 | nop + nop | nop + b L77 | nop + nop | nop + +;; mat 3 +L47: + lqi.xyzw vf08, vi01 | nop + lqi.xyzw vf24, vi02 | nop + lqi.xyzw vf11, vi01 | nop + lqi.xyzw vf14, vi01 | nop + mtir vi10, vf08.x | nop + mtir vi13, vf08.y | itof0.xyzw vf24, vf24 + nop | add.zw vf08, vf08, vf17 + nop | add.xyzw vf11, vf11, vf18 + iand vi10, vi10, vi05 | add.xyzw vf14, vf14, vf19 + ilw.w vi08, -1(vi02) | mulw.xyzw vf24, vf24, vf29 + iand vi13, vi13, vi05 | nop + lq.xyzw vf20, 0(vi10) | nop + lq.xyzw vf31, 0(vi13) | nop + lq.xyzw vf25, 0(vi08) | nop + lq.xyzw vf23, 1(vi10) | nop + lq.xyzw vf20, 1(vi13) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf26, 1(vi08) | madday.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 2(vi10) | maddz.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 2(vi13) | mulax.xyzw ACC, vf23, vf24 + lq.xyzw vf27, 2(vi08) | madday.xyzw ACC, vf20, vf24 + lq.xyzw vf20, 3(vi10) | maddz.xyzw vf26, vf26, vf24 + lq.xyzw vf31, 3(vi13) | mulax.xyzw ACC, vf31, vf24 + lq.xyzw vf28, 3(vi08) | madday.xyzw ACC, vf23, vf24 + lq.xyzw vf23, 4(vi10) | maddz.xyzw vf27, vf27, vf24 + lq.xyzw vf20, 4(vi13) | mulax.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi08) | madday.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 5(vi10) | maddz.xyzw vf28, vf28, vf24 + lq.xyzw vf23, 5(vi13) | mulax.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi08) | madday.xyzw ACC, vf20, vf24 + lq.xyzw vf20, 6(vi10) | maddz.xyz vf29, vf29, vf24 + lq.xyzw vf22, 6(vi13) | mulax.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 6(vi08) | madday.xyzw ACC, vf23, vf24 + lqi.xyzw vf23, vi02 | maddz.xyz vf30, vf30, vf24 + mtir vi10, vf11.x | mulax.xyzw ACC, vf20, vf24 + mtir vi13, vf11.y | madday.xyzw ACC, vf22, vf24 + lq.xyzw vf22, 2(vi00) | maddz.xyzw vf31, vf31, vf24 + nop | itof0.xyzw vf24, vf23 + nop | mulaz.xyzw ACC, vf29, vf08 + nop | maddaz.xyzw ACC, vf30, vf11 + nop | maddz.xyz vf11, vf31, vf14 + nop | nop + nop | nop + nop | mulaw.xyzw ACC, vf25, vf08 + nop | nop + erleng.xyz P, vf11 | nop + nop | maddaw.xyzw ACC, vf26, vf11 + mr32.z vf14, vf00 | maddw.xyzw vf08, vf27, vf14 + lqi.xyzw vf09, vi01 | nop + ilwr.y vi03, vi12 | nop + ilw.z vi07, 1(vi12) | nop + lqi.xyzw vf12, vi01 | add.xyzw vf08, vf08, vf28 + lqi.xyzw vf15, vi01 | nop + mtir vi11, vf09.x | nop + mtir vi14, vf09.y | nop + sq.xyzw vf08, -4(vi01) | miniw.w vf08, vf08, vf01 + div Q, vf01.w, vf08.w | add.zw vf09, vf09, vf17 + iadd vi03, vi03, vi12 | add.xyzw vf12, vf12, vf18 + iand vi11, vi11, vi05 | add.xyzw vf15, vf15, vf19 + ilw.w vi08, -1(vi02) | nop + iadd vi07, vi07, vi03 | nop + iand vi14, vi14, vi05 | nop + ibne vi05, vi11, L48 | nop + iaddi vi07, vi07, -0x1 | mul.xyz vf08, vf08, Q + mtir vi11, vf12.x | mul.xyzw vf14, vf14, Q + mtir vi14, vf12.y | nop + b L49 | nop + lqi.xyzw vf23, vi03 | add.xyzw vf08, vf08, vf22 +L48: + lq.xyzw vf20, 0(vi11) | mul.xyzw vf14, vf14, Q + nop | mulw.xyzw vf24, vf24, vf29 + lq.xyzw vf31, 0(vi14) | nop + lq.xyzw vf25, 0(vi08) | nop + lq.xyzw vf23, 1(vi11) | add.xyzw vf08, vf08, vf22 + lq.xyzw vf20, 1(vi14) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf26, 1(vi08) | madday.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 2(vi11) | maddz.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 2(vi14) | mulax.xyzw ACC, vf23, vf24 + lq.xyzw vf27, 2(vi08) | madday.xyzw ACC, vf20, vf24 + lq.xyzw vf20, 3(vi11) | maddz.xyzw vf26, vf26, vf24 + lq.xyzw vf31, 3(vi14) | mulax.xyzw ACC, vf31, vf24 + lq.xyzw vf28, 3(vi08) | madday.xyzw ACC, vf23, vf24 + lq.xyzw vf23, 4(vi11) | maddz.xyzw vf27, vf27, vf24 + lq.xyzw vf20, 4(vi14) | mulax.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi08) | madday.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 5(vi11) | maddz.xyzw vf28, vf28, vf24 + lq.xyzw vf23, 5(vi14) | mulax.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi08) | madday.xyzw ACC, vf20, vf24 + lq.xyzw vf20, 6(vi11) | maddz.xyz vf29, vf29, vf24 + lq.xyzw vf22, 6(vi14) | mulax.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 6(vi08) | madday.xyzw ACC, vf23, vf24 + lqi.xyzw vf23, vi02 | maddz.xyz vf30, vf30, vf24 + mtir vi11, vf12.x | mulax.xyzw ACC, vf20, vf24 + mtir vi14, vf12.y | madday.xyzw ACC, vf22, vf24 + lq.xyzw vf22, 2(vi00) | maddz.xyzw vf31, vf31, vf24 + lqi.xyzw vf23, vi03 | itof0.xyzw vf24, vf23 +L49: + nop | mulaz.xyzw ACC, vf29, vf09 + nop | maddaz.xyzw ACC, vf30, vf12 + mfp.w vf20, P | maddz.xyz vf12, vf31, vf15 + nop | nop + 1024.0 | miniw.w vf08, vf08, vf03 :i + nop | mulaw.xyzw ACC, vf25, vf09 + ilw.y vi09, -6(vi01) | mulw.xyzw vf11, vf11, vf20 + erleng.xyz P, vf12 | maxi.xy vf08, vf08, I + 3072.0 | nop :i + sq.xyzw vf11, -1(vi03) | minii.xy vf08, vf08, I + nop | maddaw.xyzw ACC, vf26, vf12 + mr32.z vf15, vf00 | maddw.xyzw vf09, vf27, vf15 +L50: + lqi.xyzw vf10, vi01 | mulax.xyzw ACC, vf01, vf11 + b L52 | madday.xyzw ACC, vf02, vf11 + nop | maddz.xyzw vf11, vf03, vf11 +L51: + sq.xyzw vf11, -1(vi03) | nop + 3072.0 | mulax.xyzw ACC, vf01, vf11 :i + lqi.xyzw vf10, vi01 | minii.xy vf08, vf08, I + sq.xyzw vf13, 1(vi12) | madday.xyzw ACC, vf02, vf11 + sq.xyzw vf13, 1(vi15) | maddz.xyzw vf11, vf03, vf11 +L52: + lqi.xyzw vf13, vi01 | add.xyzw vf09, vf09, vf28 + lqi.xyzw vf16, vi01 | maxw.w vf08, vf08, vf02 + mtir vi12, vf10.x | itof0.xyzw vf23, vf23 + mtir vi15, vf10.y | maxx.xyzw vf11, vf11, vf00 + sq.xyzw vf09, -4(vi01) | miniw.w vf09, vf09, vf01 + div Q, vf01.w, vf09.w | add.zw vf10, vf10, vf17 + move.xyzw vf21, vf08 | add.xyzw vf13, vf13, vf18 + iand vi12, vi12, vi05 | add.xyzw vf16, vf16, vf19 + ilw.w vi08, -1(vi02) | mulax.xyzw ACC, vf04, vf11 + ibgtz vi09, L53 | madday.xyzw ACC, vf05, vf11 + iand vi15, vi15, vi05 | maddaz.xyzw ACC, vf06, vf11 + nop | addx.w vf21, vf21, vf17 +L53: + ibne vi05, vi12, L54 | maddw.xyzw vf11, vf07, vf00 + ilw.x vi09, -9(vi01) | mul.xyz vf09, vf09, Q + mtir vi12, vf13.x | mul.xyzw vf15, vf15, Q + mtir vi15, vf13.y | ftoi4.xyzw vf21, vf21 + b L55 | mul.xyzw vf11, vf11, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf09, vf09, vf22 +L54: + lq.xyzw vf20, 0(vi12) | mul.xyzw vf15, vf15, Q + nop | mulw.xyzw vf24, vf24, vf29 + lq.xyzw vf31, 0(vi15) | ftoi4.xyzw vf21, vf21 + lq.xyzw vf25, 0(vi08) | mul.xyzw vf11, vf11, vf23 + lq.xyzw vf23, 1(vi12) | add.xyzw vf09, vf09, vf22 + lq.xyzw vf20, 1(vi15) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf26, 1(vi08) | madday.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 2(vi12) | maddz.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 2(vi15) | mulax.xyzw ACC, vf23, vf24 + lq.xyzw vf27, 2(vi08) | madday.xyzw ACC, vf20, vf24 + lq.xyzw vf20, 3(vi12) | maddz.xyzw vf26, vf26, vf24 + lq.xyzw vf31, 3(vi15) | mulax.xyzw ACC, vf31, vf24 + lq.xyzw vf28, 3(vi08) | madday.xyzw ACC, vf23, vf24 + lq.xyzw vf23, 4(vi12) | maddz.xyzw vf27, vf27, vf24 + lq.xyzw vf20, 4(vi15) | mulax.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi08) | madday.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 5(vi12) | maddz.xyzw vf28, vf28, vf24 + lq.xyzw vf23, 5(vi15) | mulax.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi08) | madday.xyzw ACC, vf20, vf24 + lq.xyzw vf20, 6(vi12) | maddz.xyz vf29, vf29, vf24 + lq.xyzw vf22, 6(vi15) | mulax.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 6(vi08) | madday.xyzw ACC, vf23, vf24 + lqi.xyzw vf23, vi02 | maddz.xyz vf30, vf30, vf24 + mtir vi12, vf13.x | mulax.xyzw ACC, vf20, vf24 + mtir vi15, vf13.y | madday.xyzw ACC, vf22, vf24 + lq.xyzw vf22, 2(vi00) | maddz.xyzw vf31, vf31, vf24 + lqi.xyzw vf23, vi03 | itof0.xyzw vf24, vf23 +L55: (L70 in og) + ibgez vi09, L56 | mulaz.xyzw ACC, vf29, vf10 + sq.xyzw vf21, 2(vi10) | maddaz.xyzw ACC, vf30, vf13 + nop | ftoi4.xyzw vf21, vf08 +L56: + mfp.w vf20, P | maddz.xyz vf13, vf31, vf16 + sq.xyzw vf14, 0(vi10) | miniy.xyzw vf11, vf11, vf17 + sq.xyzw vf14, 0(vi13) | miniw.w vf09, vf09, vf03 + sq.xyzw vf21, 2(vi13) | mulaw.xyzw ACC, vf25, vf10 + ilw.y vi09, -6(vi01) | mulw.xyzw vf12, vf12, vf20 + 1024.0 | ftoi0.xyzw vf11, vf11 :i + erleng.xyz P, vf13 | maxi.xy vf09, vf09, I + ibeq vi07, vi03, L67 | maddaw.xyzw ACC, vf26, vf13 + mr32.z vf16, vf00 | maddw.xyzw vf10, vf27, vf16 +L57: + sq.xyzw vf12, -1(vi03) | nop + 3072.0 | mulax.xyzw ACC, vf01, vf12 :i + lqi.xyzw vf08, vi01 | minii.xy vf09, vf09, I + sq.xyzw vf11, 1(vi10) | madday.xyzw ACC, vf02, vf12 + sq.xyzw vf11, 1(vi13) | maddz.xyzw vf12, vf03, vf12 + lqi.xyzw vf11, vi01 | add.xyzw vf10, vf10, vf28 + lqi.xyzw vf14, vi01 | maxw.w vf09, vf09, vf02 + mtir vi10, vf08.x | itof0.xyzw vf23, vf23 + mtir vi13, vf08.y | maxx.xyzw vf12, vf12, vf00 + sq.xyzw vf10, -4(vi01) | miniw.w vf10, vf10, vf01 + div Q, vf01.w, vf10.w | add.zw vf08, vf08, vf17 + move.xyzw vf21, vf09 | add.xyzw vf11, vf11, vf18 + iand vi10, vi10, vi05 | add.xyzw vf14, vf14, vf19 + ilw.w vi08, -1(vi02) | mulax.xyzw ACC, vf04, vf12 + ibgtz vi09, L58 | madday.xyzw ACC, vf05, vf12 + iand vi13, vi13, vi05 | maddaz.xyzw ACC, vf06, vf12 + nop | addx.w vf21, vf21, vf17 +L58: + ibne vi05, vi10, L59 | maddw.xyzw vf12, vf07, vf00 + ilw.x vi09, -9(vi01) | mul.xyz vf10, vf10, Q + mtir vi10, vf11.x | mul.xyzw vf16, vf16, Q + mtir vi13, vf11.y | ftoi4.xyzw vf21, vf21 + b L60 | mul.xyzw vf12, vf12, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf10, vf10, vf22 +L59: + lq.xyzw vf20, 0(vi10) | mul.xyzw vf16, vf16, Q + nop | mulw.xyzw vf24, vf24, vf29 + lq.xyzw vf31, 0(vi13) | ftoi4.xyzw vf21, vf21 + lq.xyzw vf25, 0(vi08) | mul.xyzw vf12, vf12, vf23 + lq.xyzw vf23, 1(vi10) | add.xyzw vf10, vf10, vf22 + lq.xyzw vf20, 1(vi13) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf26, 1(vi08) | madday.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 2(vi10) | maddz.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 2(vi13) | mulax.xyzw ACC, vf23, vf24 + lq.xyzw vf27, 2(vi08) | madday.xyzw ACC, vf20, vf24 + lq.xyzw vf20, 3(vi10) | maddz.xyzw vf26, vf26, vf24 + lq.xyzw vf31, 3(vi13) | mulax.xyzw ACC, vf31, vf24 + lq.xyzw vf28, 3(vi08) | madday.xyzw ACC, vf23, vf24 + lq.xyzw vf23, 4(vi10) | maddz.xyzw vf27, vf27, vf24 + lq.xyzw vf20, 4(vi13) | mulax.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi08) | madday.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 5(vi10) | maddz.xyzw vf28, vf28, vf24 + lq.xyzw vf23, 5(vi13) | mulax.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi08) | madday.xyzw ACC, vf20, vf24 + lq.xyzw vf20, 6(vi10) | maddz.xyz vf29, vf29, vf24 + lq.xyzw vf22, 6(vi13) | mulax.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 6(vi08) | madday.xyzw ACC, vf23, vf24 + lqi.xyzw vf23, vi02 | maddz.xyz vf30, vf30, vf24 + mtir vi10, vf11.x | mulax.xyzw ACC, vf20, vf24 + mtir vi13, vf11.y | madday.xyzw ACC, vf22, vf24 + lq.xyzw vf22, 2(vi00) | maddz.xyzw vf31, vf31, vf24 + lqi.xyzw vf23, vi03 | itof0.xyzw vf24, vf23 +L60: + ibgez vi09, L61 | mulaz.xyzw ACC, vf29, vf08 + sq.xyzw vf21, 2(vi11) | maddaz.xyzw ACC, vf30, vf11 + nop | ftoi4.xyzw vf21, vf09 +L61: + mfp.w vf20, P | maddz.xyz vf11, vf31, vf14 + sq.xyzw vf15, 0(vi11) | miniy.xyzw vf12, vf12, vf17 + sq.xyzw vf15, 0(vi14) | miniw.w vf10, vf10, vf03 + sq.xyzw vf21, 2(vi14) | mulaw.xyzw ACC, vf25, vf08 + ilw.y vi09, -6(vi01) | mulw.xyzw vf13, vf13, vf20 + 1024.0 | ftoi0.xyzw vf12, vf12 :i + erleng.xyz P, vf11 | maxi.xy vf10, vf10, I + ibeq vi07, vi03, L72 | maddaw.xyzw ACC, vf26, vf11 + mr32.z vf14, vf00 | maddw.xyzw vf08, vf27, vf14 +L62: + sq.xyzw vf13, -1(vi03) | nop + 3072.0 | mulax.xyzw ACC, vf01, vf13 :i + lqi.xyzw vf09, vi01 | minii.xy vf10, vf10, I + sq.xyzw vf12, 1(vi11) | madday.xyzw ACC, vf02, vf13 + sq.xyzw vf12, 1(vi14) | maddz.xyzw vf13, vf03, vf13 + lqi.xyzw vf12, vi01 | add.xyzw vf08, vf08, vf28 + lqi.xyzw vf15, vi01 | maxw.w vf10, vf10, vf02 + mtir vi11, vf09.x | itof0.xyzw vf23, vf23 + mtir vi14, vf09.y | maxx.xyzw vf13, vf13, vf00 + sq.xyzw vf08, -4(vi01) | miniw.w vf08, vf08, vf01 + div Q, vf01.w, vf08.w | add.zw vf09, vf09, vf17 + move.xyzw vf21, vf10 | add.xyzw vf12, vf12, vf18 + iand vi11, vi11, vi05 | add.xyzw vf15, vf15, vf19 + ilw.w vi08, -1(vi02) | mulax.xyzw ACC, vf04, vf13 + ibgtz vi09, L63 | madday.xyzw ACC, vf05, vf13 + iand vi14, vi14, vi05 | maddaz.xyzw ACC, vf06, vf13 + nop | addx.w vf21, vf21, vf17 +L63: + ibne vi05, vi11, L64 | maddw.xyzw vf13, vf07, vf00 + ilw.x vi09, -9(vi01) | mul.xyz vf08, vf08, Q + mtir vi11, vf12.x | mul.xyzw vf14, vf14, Q + mtir vi14, vf12.y | ftoi4.xyzw vf21, vf21 + b L65 | mul.xyzw vf13, vf13, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf08, vf08, vf22 +L64: + lq.xyzw vf20, 0(vi11) | mul.xyzw vf14, vf14, Q + nop | mulw.xyzw vf24, vf24, vf29 + lq.xyzw vf31, 0(vi14) | ftoi4.xyzw vf21, vf21 + lq.xyzw vf25, 0(vi08) | mul.xyzw vf13, vf13, vf23 + lq.xyzw vf23, 1(vi11) | add.xyzw vf08, vf08, vf22 + lq.xyzw vf20, 1(vi14) | mulax.xyzw ACC, vf20, vf24 + lq.xyzw vf26, 1(vi08) | madday.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 2(vi11) | maddz.xyzw vf25, vf25, vf24 + lq.xyzw vf23, 2(vi14) | mulax.xyzw ACC, vf23, vf24 + lq.xyzw vf27, 2(vi08) | madday.xyzw ACC, vf20, vf24 + lq.xyzw vf20, 3(vi11) | maddz.xyzw vf26, vf26, vf24 + lq.xyzw vf31, 3(vi14) | mulax.xyzw ACC, vf31, vf24 + lq.xyzw vf28, 3(vi08) | madday.xyzw ACC, vf23, vf24 + lq.xyzw vf23, 4(vi11) | maddz.xyzw vf27, vf27, vf24 + lq.xyzw vf20, 4(vi14) | mulax.xyzw ACC, vf20, vf24 + lq.xyz vf29, 4(vi08) | madday.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 5(vi11) | maddz.xyzw vf28, vf28, vf24 + lq.xyzw vf23, 5(vi14) | mulax.xyzw ACC, vf23, vf24 + lq.xyz vf30, 5(vi08) | madday.xyzw ACC, vf20, vf24 + lq.xyzw vf20, 6(vi11) | maddz.xyz vf29, vf29, vf24 + lq.xyzw vf22, 6(vi14) | mulax.xyzw ACC, vf31, vf24 + lq.xyzw vf31, 6(vi08) | madday.xyzw ACC, vf23, vf24 + lqi.xyzw vf23, vi02 | maddz.xyz vf30, vf30, vf24 + mtir vi11, vf12.x | mulax.xyzw ACC, vf20, vf24 + mtir vi14, vf12.y | madday.xyzw ACC, vf22, vf24 + lq.xyzw vf22, 2(vi00) | maddz.xyzw vf31, vf31, vf24 + lqi.xyzw vf23, vi03 | itof0.xyzw vf24, vf23 +L65: + ibgez vi09, L66 | mulaz.xyzw ACC, vf29, vf09 + sq.xyzw vf21, 2(vi12) | maddaz.xyzw ACC, vf30, vf12 + nop | ftoi4.xyzw vf21, vf10 +L66: (L80 in og) + mfp.w vf20, P | maddz.xyz vf12, vf31, vf15 + sq.xyzw vf16, 0(vi12) | miniy.xyzw vf13, vf13, vf17 + sq.xyzw vf16, 0(vi15) | miniw.w vf08, vf08, vf03 + sq.xyzw vf21, 2(vi15) | mulaw.xyzw ACC, vf25, vf09 + ilw.y vi09, -6(vi01) | mulw.xyzw vf11, vf11, vf20 + 1024.0 | ftoi0.xyzw vf13, vf13 :i + erleng.xyz P, vf12 | maxi.xy vf08, vf08, I + ibne vi07, vi03, L51 | maddaw.xyzw ACC, vf26, vf12 + mr32.z vf15, vf00 | maddw.xyzw vf09, vf27, vf15 + b L77 | nop + nop | nop + +;;;;;;;;;;; OG merc has a bunch of merc prime alternate paths here. + +;;;; next we have 3x pipeline exits. +;; + +L67: + 3072.0 | mulax.xyzw ACC, vf01, vf12 :i + sq.xyzw vf12, -1(vi03) | minii.xy vf09, vf09, I + sq.xyzw vf11, 1(vi10) | madday.xyzw ACC, vf02, vf12 + sq.xyzw vf11, 1(vi13) | maddz.xyzw vf12, vf03, vf12 + iaddiu vi05, vi00, 0x173 | add.xyzw vf10, vf10, vf28 + lq.xyzw vf26, 1(vi00) | maxw.w vf09, vf09, vf02 + iaddi vi08, vi00, 0x1 | itof0.xyzw vf23, vf23 + isw.x vi08, -2(vi05) | maxx.xyzw vf12, vf12, vf00 + sq.xyzw vf10, -1(vi01) | miniw.w vf10, vf10, vf01 + div Q, vf01.w, vf10.w | nop + move.xyzw vf21, vf09 | nop + iaddiu vi08, vi00, 0x42 | nop + isw.z vi08, -1(vi05) | mulax.xyzw ACC, vf04, vf12 + ibgtz vi09, L68 | madday.xyzw ACC, vf05, vf12 + isw.x vi00, -1(vi05) | maddaz.xyzw ACC, vf06, vf12 + nop | addx.w vf21, vf21, vf17 +L68: + sq.yzw vf26, -2(vi05) | maddw.xyzw vf12, vf07, vf00 + ilw.x vi09, -6(vi01) | mul.xyz vf10, vf10, Q + iaddiu vi08, vi00, 0x171 | mul.xyzw vf16, vf16, Q + nop | ftoi4.xyzw vf21, vf21 + nop | mul.xyzw vf12, vf12, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf10, vf10, vf22 + ibgez vi09, L69 | nop + sq.xyzw vf21, 2(vi11) | nop + nop | ftoi4.xyzw vf21, vf09 +L69: + mfp.w vf20, P | nop + sq.xyzw vf15, 0(vi11) | miniy.xyzw vf12, vf12, vf17 + sq.xyzw vf15, 0(vi14) | miniw.w vf10, vf10, vf03 + sq.xyzw vf21, 2(vi14) | nop + ilw.y vi09, -3(vi01) | mulw.xyzw vf13, vf13, vf20 + 1024.0 | ftoi0.xyzw vf12, vf12 :i + nop | maxi.xy vf10, vf10, I + nop | nop + 3072.0 | mulax.xyzw ACC, vf01, vf13 :i + sq.xyzw vf13, -1(vi03) | minii.xy vf10, vf10, I + sq.xyzw vf12, 1(vi11) | madday.xyzw ACC, vf02, vf13 + sq.xyzw vf12, 1(vi14) | maddz.xyzw vf13, vf03, vf13 + nop | nop + nop | maxw.w vf10, vf10, vf02 + nop | itof0.xyzw vf23, vf23 + nop | maxx.xyzw vf13, vf13, vf00 + nop | nop + move.xyzw vf21, vf10 | nop + nop | nop + nop | mulax.xyzw ACC, vf04, vf13 + ibgtz vi09, L70 | madday.xyzw ACC, vf05, vf13 + nop | maddaz.xyzw ACC, vf06, vf13 + nop | addx.w vf21, vf21, vf17 +L70: + nop | maddw.xyzw vf13, vf07, vf00 + ilw.x vi09, -3(vi01) | nop + xtop vi05 | nop + iaddiu vi05, vi05, 0x8c | ftoi4.xyzw vf21, vf21 + ilwr.z vi01, vi05 | mul.xyzw vf13, vf13, vf23 + ilwr.y vi03, vi05 | nop + ibgez vi09, L71 | nop + sq.xyzw vf21, 2(vi12) | nop + nop | ftoi4.xyzw vf21, vf10 +L71: + nop | nop + sq.xyzw vf16, 0(vi12) | miniy.xyzw vf13, vf13, vf17 + sq.xyzw vf16, 0(vi15) | nop + sq.xyzw vf21, 2(vi15) | nop + nop | nop + nop | ftoi0.xyzw vf13, vf13 + lq.xyzw vf23, 124(vi00) | nop + iadd vi01, vi01, vi05 | nop + iadd vi03, vi03, vi05 | nop + sq.xyzw vf13, 1(vi12) | nop + b L82 | nop + sq.xyzw vf13, 1(vi15) | + + +L72: + 3072.0 | mulax.xyzw ACC, vf01, vf13 :i + sq.xyzw vf13, -1(vi03) | minii.xy vf10, vf10, I + sq.xyzw vf12, 1(vi11) | madday.xyzw ACC, vf02, vf13 + sq.xyzw vf12, 1(vi14) | maddz.xyzw vf13, vf03, vf13 + iaddiu vi05, vi00, 0x173 | add.xyzw vf08, vf08, vf28 + lq.xyzw vf26, 1(vi00) | maxw.w vf10, vf10, vf02 + iaddi vi08, vi00, 0x1 | itof0.xyzw vf23, vf23 + isw.x vi08, -2(vi05) | maxx.xyzw vf13, vf13, vf00 + sq.xyzw vf08, -1(vi01) | miniw.w vf08, vf08, vf01 + div Q, vf01.w, vf08.w | nop + move.xyzw vf21, vf10 | nop + iaddiu vi08, vi00, 0x42 | nop + isw.z vi08, -1(vi05) | mulax.xyzw ACC, vf04, vf13 + ibgtz vi09, L73 | madday.xyzw ACC, vf05, vf13 + isw.x vi00, -1(vi05) | maddaz.xyzw ACC, vf06, vf13 + nop | addx.w vf21, vf21, vf17 +L73: + sq.yzw vf26, -2(vi05) | maddw.xyzw vf13, vf07, vf00 + ilw.x vi09, -6(vi01) | mul.xyz vf08, vf08, Q + iaddiu vi08, vi00, 0x171 | mul.xyzw vf14, vf14, Q + nop | ftoi4.xyzw vf21, vf21 + nop | mul.xyzw vf13, vf13, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf08, vf08, vf22 + ibgez vi09, L74 | nop + sq.xyzw vf21, 2(vi12) | nop + nop | ftoi4.xyzw vf21, vf10 +L74: + mfp.w vf20, P | nop + sq.xyzw vf16, 0(vi12) | miniy.xyzw vf13, vf13, vf17 + sq.xyzw vf16, 0(vi15) | miniw.w vf08, vf08, vf03 + sq.xyzw vf21, 2(vi15) | nop + ilw.y vi09, -3(vi01) | mulw.xyzw vf11, vf11, vf20 + 1024.0 | ftoi0.xyzw vf13, vf13 :i + nop | maxi.xy vf08, vf08, I + nop | nop + 3072.0 | mulax.xyzw ACC, vf01, vf11 :i + sq.xyzw vf11, -1(vi03) | minii.xy vf08, vf08, I + sq.xyzw vf13, 1(vi12) | madday.xyzw ACC, vf02, vf11 + sq.xyzw vf13, 1(vi15) | maddz.xyzw vf11, vf03, vf11 + nop | nop + nop | maxw.w vf08, vf08, vf02 + nop | itof0.xyzw vf23, vf23 + nop | maxx.xyzw vf11, vf11, vf00 + nop | nop + move.xyzw vf21, vf08 | nop + nop | nop + nop | mulax.xyzw ACC, vf04, vf11 + ibgtz vi09, L75 | madday.xyzw ACC, vf05, vf11 + nop | maddaz.xyzw ACC, vf06, vf11 + nop | addx.w vf21, vf21, vf17 +L75: + nop | maddw.xyzw vf11, vf07, vf00 + ilw.x vi09, -3(vi01) | nop + xtop vi05 | nop + iaddiu vi05, vi05, 0x8c | ftoi4.xyzw vf21, vf21 + ilwr.z vi01, vi05 | mul.xyzw vf11, vf11, vf23 + ilwr.y vi03, vi05 | nop + ibgez vi09, L76 | nop + sq.xyzw vf21, 2(vi10) | nop + nop | ftoi4.xyzw vf21, vf08 +L76: + nop | nop + sq.xyzw vf14, 0(vi10) | miniy.xyzw vf11, vf11, vf17 + sq.xyzw vf14, 0(vi13) | nop + sq.xyzw vf21, 2(vi13) | nop + nop | nop + nop | ftoi0.xyzw vf11, vf11 + lq.xyzw vf23, 124(vi00) | nop + iadd vi01, vi01, vi05 | nop + iadd vi03, vi03, vi05 | nop + sq.xyzw vf11, 1(vi10) | nop + b L82 | nop + sq.xyzw vf11, 1(vi13) | nop + + + +L77: + 3072.0 | mulax.xyzw ACC, vf01, vf11 :i + sq.xyzw vf11, -1(vi03) | minii.xy vf08, vf08, I + sq.xyzw vf13, 1(vi12) | madday.xyzw ACC, vf02, vf11 + sq.xyzw vf13, 1(vi15) | maddz.xyzw vf11, vf03, vf11 + iaddiu vi05, vi00, 0x173 | add.xyzw vf09, vf09, vf28 + lq.xyzw vf26, 1(vi00) | maxw.w vf08, vf08, vf02 + iaddi vi08, vi00, 0x1 | itof0.xyzw vf23, vf23 + isw.x vi08, -2(vi05) | maxx.xyzw vf11, vf11, vf00 + sq.xyzw vf09, -1(vi01) | miniw.w vf09, vf09, vf01 + div Q, vf01.w, vf09.w | nop + move.xyzw vf21, vf08 | nop + iaddiu vi08, vi00, 0x42 | nop + isw.z vi08, -1(vi05) | mulax.xyzw ACC, vf04, vf11 + ibgtz vi09, L78 | madday.xyzw ACC, vf05, vf11 + isw.x vi00, -1(vi05) | maddaz.xyzw ACC, vf06, vf11 + nop | addx.w vf21, vf21, vf17 +L78: + sq.yzw vf26, -2(vi05) | maddw.xyzw vf11, vf07, vf00 + ilw.x vi09, -6(vi01) | mul.xyz vf09, vf09, Q + iaddiu vi08, vi00, 0x171 | mul.xyzw vf15, vf15, Q ;; vi08 = 0x171: output location (fixed?) + nop | ftoi4.xyzw vf21, vf21 + nop | mul.xyzw vf11, vf11, vf23 + lqi.xyzw vf23, vi03 | add.xyzw vf09, vf09, vf22 + ibgez vi09, L79 | nop + sq.xyzw vf21, 2(vi10) | nop + nop | ftoi4.xyzw vf21, vf08 +L79: + mfp.w vf20, P | nop + sq.xyzw vf14, 0(vi10) | miniy.xyzw vf11, vf11, vf17 + sq.xyzw vf14, 0(vi13) | miniw.w vf09, vf09, vf03 + sq.xyzw vf21, 2(vi13) | nop + ilw.y vi09, -3(vi01) | mulw.xyzw vf12, vf12, vf20 + 1024.0 | ftoi0.xyzw vf11, vf11 :i + nop | maxi.xy vf09, vf09, I + nop | nop + 3072.0 | mulax.xyzw ACC, vf01, vf12 :i + sq.xyzw vf12, -1(vi03) | minii.xy vf09, vf09, I + sq.xyzw vf11, 1(vi10) | madday.xyzw ACC, vf02, vf12 + sq.xyzw vf11, 1(vi13) | maddz.xyzw vf12, vf03, vf12 + nop | nop + nop | maxw.w vf09, vf09, vf02 + nop | itof0.xyzw vf23, vf23 + nop | maxx.xyzw vf12, vf12, vf00 + nop | nop + move.xyzw vf21, vf09 | nop + nop | nop + nop | mulax.xyzw ACC, vf04, vf12 + ibgtz vi09, L80 | madday.xyzw ACC, vf05, vf12 + nop | maddaz.xyzw ACC, vf06, vf12 + nop | addx.w vf21, vf21, vf17 +L80: + nop | maddw.xyzw vf12, vf07, vf00 + ilw.x vi09, -3(vi01) | nop + xtop vi05 | nop + iaddiu vi05, vi05, 0x8c | ftoi4.xyzw vf21, vf21 ;; vi05 = byte-header + ilwr.z vi01, vi05 | mul.xyzw vf12, vf12, vf23 ;; vi01 = lump + ilwr.y vi03, vi05 | nop ;; vi03 = rgba + ibgez vi09, L81 | nop + sq.xyzw vf21, 2(vi11) | nop + nop | ftoi4.xyzw vf21, vf09 +L81: + nop | nop + sq.xyzw vf15, 0(vi11) | miniy.xyzw vf12, vf12, vf17 + sq.xyzw vf15, 0(vi14) | nop + sq.xyzw vf21, 2(vi14) | nop + nop | nop + nop | ftoi0.xyzw vf12, vf12 + lq.xyzw vf23, 124(vi00) | nop ;; unperspect + iadd vi01, vi01, vi05 | nop ;; lump + iadd vi03, vi03, vi05 | nop ;; rgba + sq.xyzw vf12, 1(vi11) | nop + sq.xyzw vf12, 1(vi14) | nop + +;; COMMON finish part + +L82: + xgkick vi08 | nop ;; normal draw? + +;; pipeline startup for envmap math + lq.xyzw vf08, 2(vi01) | nop ;; vf08 = transformed vert + lqi.xyzw vf10, vi03 | nop ;; vf10 = transformed normal + ilw.x vi04, 1(vi05) | nop ;; vi04 = mat1-cnt + ilw.y vi06, 1(vi05) | nop ;; vi06 = mat2-cnt + ilw.z vi07, 1(vi05) | mul.xyzw vf09, vf08, vf23 ;; vi07 = mat3-cnt, unperspect the vert + iadd vi04, vi04, vi06 | subw.z vf10, vf10, vf00 ;; vi04 = mat1-cnt + mat2-cnt, refl1 + iaddi vi01, vi01, 0x3 | nop ;; step lump + iadd vi04, vi04, vi07 | nop ;; vi04 = mat1 + mat2 + mat3 counts + iadd vi02, vi03, vi04 | addw.z vf09, vf00, vf09 ;; vi02 = end rgba, vert1 + iaddi vi02, vi02, 0x2 | nop ;; end rgba more + lq.xyzw vf14, 118(vi00) | maxw.xyzw vf21, vf00, vf00 ;; vf14 = rgba-fade, vf21 = [1, 1, 1, 1] + lq.xyzw vf26, 371(vi00) | nop ;; vf26 = the giftag + nop | mul.xyz vf15, vf09, vf10 ;; multiply + lq.xyzw vf27, 119(vi00) | nop ;; vf27 = e-adgif0 + nop | nop + lq.xyzw vf28, 120(vi00) | nop ;; vf28 = e-adgif1 + nop | adday.xyzw vf15, vf15 + lq.xyzw vf31, 121(vi00) | maddz.x vf15, vf21, vf15 ;; vf31 = e-adgif2 + nop | nop + sq.xyzw vf26, 813(vi00) | nop ;; store giftag + lq.xyzw vf08, 2(vi01) | nop ;; pipe + lqi.xyzw vf11, vi03 | nop ;; pipe + div Q, vf15.x, vf10.z | nop ;; div + sq.xyzw vf27, 814(vi00) | mulaw.xyzw ACC, vf09, vf00 ;; store e-ad0, mul + nop | mul.xyzw vf09, vf08, vf23 ;; pipe + sq.xyzw vf28, 815(vi00) | subw.z vf11, vf11, vf00 ;; store e-ad1, pipe + iaddi vi01, vi01, 0x3 | nop ;; pipe + sq.xyzw vf31, 816(vi00) | nop ;; store e-ad2 + nop | addw.z vf09, vf00, vf09 ;; pipe + lq.xyzw vf26, 0(vi00) | madd.xyzw vf10, vf10, Q ;; vf26 = tristrip giftag, madd + nop | nop + lq.xyzw vf27, 122(vi00) | nop ;; vf27 = e-ad3 + nop | mul.xyz vf15, vf09, vf11 ;; pipe + eleng.xyz P, vf10 | nop ;; len + lq.xyzw vf28, 123(vi00) | nop ;; vf28 = e-ad4 + nop | nop + lq.xyzw vf31, 377(vi00) | adday.xyzw vf15, vf15 ;; vf31 = old tristrip??? + nop | maddz.x vf15, vf21, vf15 ;; pipe + mr32.xyzw vf26, vf26 | nop ;; rotate tristrip template + nop | nop + lq.xyzw vf08, 2(vi01) | nop ;; pipe + lqi.xyzw vf12, vi03 | nop ;; pipe + div Q, vf15.x, vf11.z | nop ;; pipe + mr32.xyzw vf26, vf26 | mulaw.xyzw ACC, vf09, vf00 ;; rotate | pipe + sq.xyzw vf27, 817(vi00) | mul.xyzw vf09, vf08, vf23 ;; store adgif3 | pipe + lq.xyzw vf25, -5(vi01) | subw.z vf12, vf12, vf00 ;; vf25 = lump[1] | pipe + iaddi vi01, vi01, 0x3 | nop ;; pipe + sq.xyzw vf28, 818(vi00) | nop ;; e-ad4 store + nop | addw.z vf09, vf00, vf09 ;; pipe + sq.xyzw vf31, 819(vi00) | madd.xyzw vf11, vf11, Q ;; tristrip store | pipe + nop | nop + mfp.w vf10, P | nop + sq.y vf26, 819(vi00) | mul.xyz vf15, vf09, vf12 ;; set abe | pipe + eleng.xyz P, vf11 | nop + nop | nop + div Q, vf23.z, vf10.w | nop ;; NOT PIPE (!) + nop | adday.xyzw vf15, vf15 ;; pipe + nop | maddz.x vf15, vf21, vf15 ;; pipe + nop | nop + nop | add.xyzw vf25, vf25, vf18 ;; lump dest stuff +L83: + lq.xyzw vf08, 2(vi01) | nop ;; pipe + lqi.xyzw vf13, vi03 | addaz.xyzw vf00, vf23 + div Q, vf15.x, vf12.z | madd.xyzw vf10, vf10, Q + mtir vi10, vf25.x | mulaw.xyzw ACC, vf09, vf00 + mtir vi13, vf25.y | mul.xyzw vf09, vf08, vf23 + lq.xyzw vf25, -5(vi01) | subw.z vf13, vf13, vf00 + ;; + iaddi vi01, vi01, 0x3 | nop + lq.xyzw vf24, 0(vi10) | nop + lq.xyzw vf16, 2(vi10) | addw.z vf09, vf00, vf09 + lq.xyzw vf20, 2(vi13) | madd.xyzw vf12, vf12, Q + sq.xyzw vf14, 443(vi10) | nop + mfp.w vf11, P | nop + sq.xyzw vf14, 443(vi13) | mul.xyz vf15, vf09, vf13 + eleng.xyz P, vf12 | mulz.xy vf24, vf10, vf24 + sq.xyzw vf16, 444(vi10) | nop + div Q, vf23.z, vf11.w | nop + sq.xyzw vf20, 444(vi13) | adday.xyzw vf15, vf15 + sq.xyzw vf24, 442(vi10) | maddz.x vf15, vf21, vf15 + ibeq vi02, vi03, L84 | nop + sq.xyzw vf24, 442(vi13) | add.xyzw vf25, vf25, vf18 + lq.xyzw vf08, 2(vi01) | nop + lqi.xyzw vf10, vi03 | addaz.xyzw vf00, vf23 + div Q, vf15.x, vf13.z | madd.xyzw vf11, vf11, Q + mtir vi10, vf25.x | mulaw.xyzw ACC, vf09, vf00 + mtir vi13, vf25.y | mul.xyzw vf09, vf08, vf23 + lq.xyzw vf25, -5(vi01) | subw.z vf10, vf10, vf00 + iaddi vi01, vi01, 0x3 | nop + lq.xyzw vf24, 0(vi10) | nop + lq.xyzw vf16, 2(vi10) | addw.z vf09, vf00, vf09 + lq.xyzw vf20, 2(vi13) | madd.xyzw vf13, vf13, Q + sq.xyzw vf14, 443(vi10) | nop + mfp.w vf12, P | nop + sq.xyzw vf14, 443(vi13) | mul.xyz vf15, vf09, vf10 + eleng.xyz P, vf13 | mulz.xy vf24, vf11, vf24 + sq.xyzw vf16, 444(vi10) | nop + div Q, vf23.z, vf12.w | nop + sq.xyzw vf20, 444(vi13) | adday.xyzw vf15, vf15 + sq.xyzw vf24, 442(vi10) | maddz.x vf15, vf21, vf15 + ibeq vi02, vi03, L84 | nop + sq.xyzw vf24, 442(vi13) | add.xyzw vf25, vf25, vf18 + lq.xyzw vf08, 2(vi01) | nop + lqi.xyzw vf11, vi03 | addaz.xyzw vf00, vf23 + div Q, vf15.x, vf10.z | madd.xyzw vf12, vf12, Q + mtir vi10, vf25.x | mulaw.xyzw ACC, vf09, vf00 + mtir vi13, vf25.y | mul.xyzw vf09, vf08, vf23 + lq.xyzw vf25, -5(vi01) | subw.z vf11, vf11, vf00 + iaddi vi01, vi01, 0x3 | nop + lq.xyzw vf24, 0(vi10) | nop + lq.xyzw vf16, 2(vi10) | addw.z vf09, vf00, vf09 + lq.xyzw vf20, 2(vi13) | madd.xyzw vf10, vf10, Q + sq.xyzw vf14, 443(vi10) | nop + mfp.w vf13, P | nop + sq.xyzw vf14, 443(vi13) | mul.xyz vf15, vf09, vf11 + eleng.xyz P, vf10 | mulz.xy vf24, vf12, vf24 + sq.xyzw vf16, 444(vi10) | nop + div Q, vf23.z, vf13.w | nop + sq.xyzw vf20, 444(vi13) | adday.xyzw vf15, vf15 + sq.xyzw vf24, 442(vi10) | maddz.x vf15, vf21, vf15 + ibeq vi02, vi03, L84 | nop + sq.xyzw vf24, 442(vi13) | add.xyzw vf25, vf25, vf18 + lq.xyzw vf08, 2(vi01) | nop + lqi.xyzw vf12, vi03 | addaz.xyzw vf00, vf23 + div Q, vf15.x, vf11.z | madd.xyzw vf13, vf13, Q + mtir vi10, vf25.x | mulaw.xyzw ACC, vf09, vf00 + mtir vi13, vf25.y | mul.xyzw vf09, vf08, vf23 + lq.xyzw vf25, -5(vi01) | subw.z vf12, vf12, vf00 + iaddi vi01, vi01, 0x3 | nop + lq.xyzw vf24, 0(vi10) | nop + lq.xyzw vf16, 2(vi10) | addw.z vf09, vf00, vf09 + lq.xyzw vf20, 2(vi13) | madd.xyzw vf11, vf11, Q + sq.xyzw vf14, 443(vi10) | nop + mfp.w vf10, P | nop + sq.xyzw vf14, 443(vi13) | mul.xyz vf15, vf09, vf12 + eleng.xyz P, vf11 | mulz.xy vf24, vf13, vf24 + sq.xyzw vf16, 444(vi10) | nop + div Q, vf23.z, vf10.w | nop + sq.xyzw vf20, 444(vi13) | adday.xyzw vf15, vf15 + sq.xyzw vf24, 442(vi10) | maddz.x vf15, vf21, vf15 + ibne vi02, vi03, L83 | nop + sq.xyzw vf24, 442(vi13) | add.xyzw vf25, vf25, vf18 +L84: + iaddiu vi08, vi00, 0x32d | nop + xgkick vi08 | nop + nop | nop :e + nop | nop + +``` diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.cpp b/game/graphics/opengl_renderer/OpenGLRenderer.cpp index b0f9edcc39..f435f8c2bb 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.cpp +++ b/game/graphics/opengl_renderer/OpenGLRenderer.cpp @@ -88,6 +88,7 @@ void OpenGLRenderer::init_bucket_renderers_jak2() { using namespace jak2; m_bucket_renderers.resize((int)BucketId::MAX_BUCKETS); m_bucket_categories.resize((int)BucketId::MAX_BUCKETS, BucketCategory::OTHER); + // 0 init_bucket_renderer("vis", BucketCategory::OTHER, BucketId::SPECIAL_BUCKET_2); init_bucket_renderer("tex-lcom-sky-pre", BucketCategory::TEX, diff --git a/game/graphics/opengl_renderer/Shader.cpp b/game/graphics/opengl_renderer/Shader.cpp index bbb854035a..60b00dca28 100644 --- a/game/graphics/opengl_renderer/Shader.cpp +++ b/game/graphics/opengl_renderer/Shader.cpp @@ -94,6 +94,7 @@ ShaderLibrary::ShaderLibrary(GameVersion version) { at(ShaderId::SPRITE_DISTORT_INSTANCED) = {"sprite_distort_instanced", version}; at(ShaderId::POST_PROCESSING) = {"post_processing", version}; at(ShaderId::DEPTH_CUE) = {"depth_cue", version}; + at(ShaderId::EMERC) = {"emerc", version}; for (auto& shader : m_shaders) { ASSERT_MSG(shader.okay(), "error compiling shader"); diff --git a/game/graphics/opengl_renderer/Shader.h b/game/graphics/opengl_renderer/Shader.h index 2480af2f17..02a400116f 100644 --- a/game/graphics/opengl_renderer/Shader.h +++ b/game/graphics/opengl_renderer/Shader.h @@ -47,6 +47,7 @@ enum class ShaderId { SPRITE_DISTORT_INSTANCED = 21, POST_PROCESSING = 22, DEPTH_CUE = 23, + EMERC = 24, MAX_SHADERS }; diff --git a/game/graphics/opengl_renderer/background/background_common.cpp b/game/graphics/opengl_renderer/background/background_common.cpp index 2fe62be6e8..1fa9e7b809 100644 --- a/game/graphics/opengl_renderer/background/background_common.cpp +++ b/game/graphics/opengl_renderer/background/background_common.cpp @@ -34,6 +34,8 @@ DoubleDraw setup_opengl_from_draw_mode(DrawMode mode, u32 tex_unit, bool mipmap) glDisable(GL_DEPTH_TEST); } + DoubleDraw double_draw; + if (mode.get_ab_enable() && mode.get_alpha_blend() != DrawMode::AlphaBlend::DISABLED) { glEnable(GL_BLEND); switch (mode.get_alpha_blend()) { @@ -61,6 +63,11 @@ DoubleDraw setup_opengl_from_draw_mode(DrawMode mode, u32 tex_unit, bool mipmap) glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ZERO); glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); break; + case DrawMode::AlphaBlend::SRC_0_DST_DST: + glBlendFunc(GL_DST_ALPHA, GL_ONE); + glBlendEquation(GL_FUNC_ADD); + double_draw.color_mult = 0.5f; + break; default: ASSERT(false); } @@ -91,7 +98,6 @@ DoubleDraw setup_opengl_from_draw_mode(DrawMode mode, u32 tex_unit, bool mipmap) // for some reason, they set atest NEVER + FB_ONLY to disable depth writes bool alpha_hack_to_disable_z_write = false; - DoubleDraw double_draw; float alpha_min = 0.; if (mode.get_at_enable()) { diff --git a/game/graphics/opengl_renderer/background/background_common.h b/game/graphics/opengl_renderer/background/background_common.h index 2f24f7646a..6d4c1049e1 100644 --- a/game/graphics/opengl_renderer/background/background_common.h +++ b/game/graphics/opengl_renderer/background/background_common.h @@ -34,6 +34,7 @@ struct DoubleDraw { DoubleDrawKind kind = DoubleDrawKind::NONE; float aref_first = 0.; float aref_second = 0.; + float color_mult = 1.; }; DoubleDraw setup_tfrag_shader(SharedRenderState* render_state, DrawMode mode, ShaderId shader); diff --git a/game/graphics/opengl_renderer/foreground/Generic2.cpp b/game/graphics/opengl_renderer/foreground/Generic2.cpp index ce5c6a4029..22a4580ea1 100644 --- a/game/graphics/opengl_renderer/foreground/Generic2.cpp +++ b/game/graphics/opengl_renderer/foreground/Generic2.cpp @@ -31,6 +31,7 @@ void Generic2::draw_debug_window() { ImGui::Checkbox("Alpha 4", &m_alpha_draw_enable[3]); ImGui::Checkbox("Alpha 5", &m_alpha_draw_enable[4]); ImGui::Checkbox("Alpha 6", &m_alpha_draw_enable[5]); + ImGui::Checkbox("Alpha 7", &m_alpha_draw_enable[6]); ImGui::Text("Max Seen:"); ImGui::Text(" frag: %d/%d %.1f%%", m_max_frags_seen, (int)m_fragments.size(), diff --git a/game/graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp b/game/graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp index 5b17e13780..1ea2638e02 100644 --- a/game/graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp +++ b/game/graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp @@ -268,6 +268,9 @@ void Generic2::do_draws_for_alpha(SharedRenderState* render_state, setup_opengl_for_draw_mode(first.mode, first.fix, render_state); setup_opengl_tex(0, first.tbp, first.mode.get_filt_enable(), first.mode.get_clamp_s_enable(), first.mode.get_clamp_t_enable(), render_state); + // if (alpha == DrawMode::AlphaBlend::SRC_0_DST_DST) { + // glBindTexture(GL_TEXTURE_2D, render_state->texture_pool->get_placeholder_texture()); + // } glDrawElements(GL_TRIANGLE_STRIP, bucket.idx_count, GL_UNSIGNED_INT, (void*)(sizeof(u32) * bucket.idx_idx)); prof.add_draw_call(); diff --git a/game/graphics/opengl_renderer/foreground/Merc2.cpp b/game/graphics/opengl_renderer/foreground/Merc2.cpp index eac0589bd5..bd5801140d 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.cpp +++ b/game/graphics/opengl_renderer/foreground/Merc2.cpp @@ -31,6 +31,7 @@ Merc2::Merc2(const std::string& name, int my_id) : BucketRenderer(name, my_id) { for (int i = 0; i < MAX_LEVELS; i++) { auto& draws = m_level_draw_buckets.emplace_back(); draws.draws.resize(MAX_DRAWS_PER_LEVEL); + draws.envmap_draws.resize(MAX_ENVMAP_DRAWS_PER_LEVEL); } } @@ -38,42 +39,72 @@ Merc2::Merc2(const std::string& name, int my_id) : BucketRenderer(name, my_id) { * Handle the merc renderer switching to a different model. */ void Merc2::init_pc_model(const DmaTransfer& setup, SharedRenderState* render_state) { - // determine the name. We've packed this in a separate PC-port specific packet. + // ;; name (128 char, 8 qw) + // ;; lights (7 qw x 1) + // ;; matrix slot string (128 char, 8 qw) + // ;; matrices (7 qw x N) + // ;; flags (num-effects, effect-alpha-ignore, effect-disable) + // ;; fades (u32 x N), padding to qw aligned + + // Part 1: name + const u8* input_data = setup.data; char name[128]; strcpy(name, (const char*)setup.data); - - // get the model from the loader m_current_model = render_state->loader->get_merc_model(name); + input_data += 128; - // update stats - m_stats.num_models++; - if (m_current_model) { - for (const auto& effect : m_current_model->model->effects) { - m_stats.num_effects++; - m_stats.num_predicted_draws += effect.draws.size(); - for (const auto& draw : effect.draws) { - m_stats.num_predicted_tris += draw.num_triangles; - } + // Part 2: lights + memcpy(&m_current_lights, input_data, sizeof(VuLights)); + input_data += sizeof(VuLights); + + // Part 3: matrix slot string + auto* matrix_array = (const u32*)(input_data + 128); + int i; + for (i = 0; i < 128; i++) { + if (input_data[i] == 0xff) { + break; + } + u32 addr; + memcpy(&addr, &matrix_array[i * 4], 4); + const u8* real_addr = setup.data - setup.data_offset + addr; + memcpy(&m_skel_matrix_buffer[input_data[i]], real_addr, sizeof(MercMat)); + } + input_data += 128 + 16 * i; + + // Part 4: flags + auto* flags = (const u32*)input_data; + int num_effects = flags[0]; + m_current_ignore_alpha_bits = flags[1]; + m_current_effect_enable_bits = flags[2]; + input_data += 16; + + // Part 5: fades + for (int ei = 0; ei < num_effects; ei++) { + for (int j = 0; j < 4; j++) { + m_fade_buffer[ei * 4 + j] = input_data[ei * 4 + j]; } } -} -/*! - * Once-per-frame initialization - */ -void Merc2::init_for_frame(SharedRenderState* render_state) { - // reset state - m_current_model = std::nullopt; - m_stats = {}; - - // activate the merc shader used for all draws - render_state->shaders[ShaderId::MERC2].activate(); - - // set uniforms that we know from render_state - glUniform4f(m_uniforms.fog_color, render_state->fog_color[0] / 255.f, - render_state->fog_color[1] / 255.f, render_state->fog_color[2] / 255.f, - render_state->fog_intensity / 255); - glUniform1i(m_uniforms.gfx_hack_no_tex, Gfx::g_global_settings.hack_no_tex); + if (m_current_model) { + m_stats.num_models++; + for (const auto& effect : m_current_model->model->effects) { + bool envmap = effect.has_envmap; + m_stats.num_effects++; + m_stats.num_predicted_draws += effect.draws.size(); + if (envmap) { + m_stats.num_envmap_effects++; + m_stats.num_predicted_draws += effect.draws.size(); + } + for (const auto& draw : effect.draws) { + m_stats.num_predicted_tris += draw.num_triangles; + if (envmap) { + m_stats.num_predicted_tris += draw.num_triangles; + } + } + } + } else { + m_stats.num_missing_models++; + } } void Merc2::draw_debug_window() { @@ -84,40 +115,67 @@ void Merc2::draw_debug_window() { ImGui::Text("Bones : %d", m_stats.num_bones_uploaded); ImGui::Text("Lights : %d", m_stats.num_lights); ImGui::Text("Dflush : %d", m_stats.num_draw_flush); + + ImGui::Text("EEffects : %d", m_stats.num_envmap_effects); + ImGui::Text("ETris : %d", m_stats.num_envmap_tris); } void Merc2::init_shaders(ShaderLibrary& shaders) { - const auto& shader = shaders[ShaderId::MERC2]; + init_shader_common(shaders[ShaderId::MERC2], &m_merc_uniforms, true); + init_shader_common(shaders[ShaderId::EMERC], &m_emerc_uniforms, false); + m_emerc_uniforms.fade = glGetUniformLocation(shaders[ShaderId::EMERC].id(), "fade"); +} + +void Merc2::init_shader_common(Shader& shader, Uniforms* uniforms, bool include_lights) { auto id = shader.id(); - shaders[ShaderId::MERC2].activate(); - m_uniforms.light_direction[0] = glGetUniformLocation(id, "light_dir0"); - m_uniforms.light_direction[1] = glGetUniformLocation(id, "light_dir1"); - m_uniforms.light_direction[2] = glGetUniformLocation(id, "light_dir2"); - m_uniforms.light_color[0] = glGetUniformLocation(id, "light_col0"); - m_uniforms.light_color[1] = glGetUniformLocation(id, "light_col1"); - m_uniforms.light_color[2] = glGetUniformLocation(id, "light_col2"); - m_uniforms.light_ambient = glGetUniformLocation(id, "light_ambient"); + shader.activate(); + if (include_lights) { + uniforms->light_direction[0] = glGetUniformLocation(id, "light_dir0"); + uniforms->light_direction[1] = glGetUniformLocation(id, "light_dir1"); + uniforms->light_direction[2] = glGetUniformLocation(id, "light_dir2"); + uniforms->light_color[0] = glGetUniformLocation(id, "light_col0"); + uniforms->light_color[1] = glGetUniformLocation(id, "light_col1"); + uniforms->light_color[2] = glGetUniformLocation(id, "light_col2"); + uniforms->light_ambient = glGetUniformLocation(id, "light_ambient"); + } - m_uniforms.hvdf_offset = glGetUniformLocation(id, "hvdf_offset"); - m_uniforms.perspective[0] = glGetUniformLocation(id, "perspective0"); - m_uniforms.perspective[1] = glGetUniformLocation(id, "perspective1"); - m_uniforms.perspective[2] = glGetUniformLocation(id, "perspective2"); - m_uniforms.perspective[3] = glGetUniformLocation(id, "perspective3"); + uniforms->hvdf_offset = glGetUniformLocation(id, "hvdf_offset"); - m_uniforms.fog = glGetUniformLocation(id, "fog_constants"); - m_uniforms.decal = glGetUniformLocation(id, "decal_enable"); + uniforms->fog = glGetUniformLocation(id, "fog_constants"); + uniforms->decal = glGetUniformLocation(id, "decal_enable"); - m_uniforms.fog_color = glGetUniformLocation(id, "fog_color"); - m_uniforms.perspective_matrix = glGetUniformLocation(id, "perspective_matrix"); - m_uniforms.ignore_alpha = glGetUniformLocation(id, "ignore_alpha"); + uniforms->fog_color = glGetUniformLocation(id, "fog_color"); + uniforms->perspective_matrix = glGetUniformLocation(id, "perspective_matrix"); + uniforms->ignore_alpha = glGetUniformLocation(id, "ignore_alpha"); - m_uniforms.gfx_hack_no_tex = glGetUniformLocation(id, "gfx_hack_no_tex"); + uniforms->gfx_hack_no_tex = glGetUniformLocation(id, "gfx_hack_no_tex"); +} + +void Merc2::switch_to_merc2(SharedRenderState* render_state) { + render_state->shaders[ShaderId::MERC2].activate(); + + // set uniforms that we know from render_state + glUniform4f(m_merc_uniforms.fog_color, render_state->fog_color[0] / 255.f, + render_state->fog_color[1] / 255.f, render_state->fog_color[2] / 255.f, + render_state->fog_intensity / 255); + glUniform1i(m_merc_uniforms.gfx_hack_no_tex, Gfx::g_global_settings.hack_no_tex); +} + +void Merc2::switch_to_emerc(SharedRenderState* render_state) { + render_state->shaders[ShaderId::EMERC].activate(); + // set uniforms that we know from render_state + glUniform4f(m_emerc_uniforms.fog_color, render_state->fog_color[0] / 255.f, + render_state->fog_color[1] / 255.f, render_state->fog_color[2] / 255.f, + render_state->fog_intensity / 255); + glUniform1i(m_emerc_uniforms.gfx_hack_no_tex, Gfx::g_global_settings.hack_no_tex); } /*! * Main merc2 rendering. */ void Merc2::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) { + m_stats = {}; + // skip if disabled if (!m_enabled) { while (dma.current_tag_offset() != render_state->next_bucket) { @@ -125,8 +183,8 @@ void Merc2::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProf } return; } - - init_for_frame(render_state); + m_current_model = std::nullopt; + switch_to_merc2(render_state); // iterate through the dma chain, filling buckets handle_all_dma(dma, render_state, prof); @@ -146,25 +204,12 @@ u32 Merc2::alloc_lights(const VuLights& lights) { return light_idx; } -/*! - * Store light values - */ -void Merc2::set_lights(const DmaTransfer& dma) { - memcpy(&m_current_lights, dma.data, sizeof(VuLights)); -} - std::string Merc2::ShaderMercMat::to_string() const { return fmt::format("tmat:\n{}\n{}\n{}\n{}\n", tmat[0].to_string_aligned(), tmat[1].to_string_aligned(), tmat[2].to_string_aligned(), tmat[3].to_string_aligned()); } -void Merc2::handle_matrix_dma(const DmaTransfer& dma) { - int slot = dma.vif0() & 0xff; - ASSERT(slot < MAX_SKEL_BONES); - memcpy(&m_skel_matrix_buffer[slot], dma.data, sizeof(MercMat)); -} - /*! * Main MERC2 function to handle DMA */ @@ -250,19 +295,17 @@ void Merc2::handle_setup_dma(DmaFollower& dma, SharedRenderState* render_state) // 8 qw's of low memory data memcpy(&m_low_memory, first.data + 16, sizeof(LowMemory)); - // fmt::print("low\nhvdf: {}\nfog: {}\nmat:\n{}\n{}\n{}\n{}\n\n", - // m_low_memory.hvdf_offset.to_string_aligned(), m_low_memory.fog.to_string_aligned(), - // m_low_memory.perspective[0].to_string_aligned(), - // m_low_memory.perspective[1].to_string_aligned(), - // m_low_memory.perspective[2].to_string_aligned(), - // m_low_memory.perspective[3].to_string_aligned()); - set_uniform(m_uniforms.hvdf_offset, m_low_memory.hvdf_offset); - set_uniform(m_uniforms.fog, m_low_memory.fog); - for (int i = 0; i < 4; i++) { - set_uniform(m_uniforms.perspective[i], m_low_memory.perspective[i]); - } - // todo rm. - glUniformMatrix4fv(m_uniforms.perspective_matrix, 1, GL_FALSE, &m_low_memory.perspective[0].x()); + + switch_to_merc2(render_state); + set_uniform(m_merc_uniforms.hvdf_offset, m_low_memory.hvdf_offset); + set_uniform(m_merc_uniforms.fog, m_low_memory.fog); + glUniformMatrix4fv(m_merc_uniforms.perspective_matrix, 1, GL_FALSE, + &m_low_memory.perspective[0].x()); + switch_to_emerc(render_state); + set_uniform(m_emerc_uniforms.hvdf_offset, m_low_memory.hvdf_offset); + set_uniform(m_emerc_uniforms.fog, m_low_memory.fog); + glUniformMatrix4fv(m_emerc_uniforms.perspective_matrix, 1, GL_FALSE, + &m_low_memory.perspective[0].x()); // 1 qw with another 4 vifcodes. u32 vifcode_final_data[4]; @@ -301,10 +344,6 @@ bool tag_is_nothing_next(const DmaFollower& dma) { return dma.current_tag().kind == DmaTag::Kind::NEXT && dma.current_tag().qwc == 0 && dma.current_tag_vif0() == 0 && dma.current_tag_vif1() == 0; } -bool tag_is_nothing_cnt(const DmaFollower& dma) { - return dma.current_tag().kind == DmaTag::Kind::CNT && dma.current_tag().qwc == 0 && - dma.current_tag_vif0() == 0 && dma.current_tag_vif1() == 0; -} } // namespace void Merc2::handle_merc_chain(DmaFollower& dma, @@ -322,107 +361,31 @@ void Merc2::handle_merc_chain(DmaFollower& dma, } auto init = dma.read_and_advance(); - - if (init.vifcode0().kind == VifCode::Kind::FLUSHA) { - while (dma.current_tag_offset() != render_state->next_bucket) { - dma.read_and_advance(); - } - return; + int skip_count = 2; + if (render_state->version == GameVersion::Jak2) { + skip_count = 1; } - if (init.vifcode1().kind == VifCode::Kind::PC_PORT) { - // we got a PC PORT packet. this contains some extra data to set up the model + while (init.vifcode1().kind == VifCode::Kind::PC_PORT) { flush_pending_model(render_state, prof); init_pc_model(init, render_state); - - ASSERT(tag_is_nothing_cnt(dma) || tag_is_nothing_next(dma)); - init = dma.read_and_advance(); // dummy tag in pc port - if (init.vifcode0().kind != VifCode::Kind::STROW) { - init = dma.read_and_advance(); - } - if (init.vifcode0().kind != VifCode::Kind::STROW) { - init = dma.read_and_advance(); - } - if (init.vifcode0().kind != VifCode::Kind::STROW) { - while (dma.current_tag_offset() != render_state->next_bucket) { - auto skip = dma.read_and_advance(); - ASSERT(skip.vifcode0().kind == VifCode::Kind::NOP); - ASSERT(skip.vifcode1().kind == VifCode::Kind::NOP); - } - return; + for (int i = 0; i < skip_count; i++) { + auto link = dma.read_and_advance(); + ASSERT(link.vifcode0().kind == VifCode::Kind::NOP); + ASSERT(link.vifcode1().kind == VifCode::Kind::NOP); + ASSERT(link.size_bytes == 0); } + init = dma.read_and_advance(); } - // row stuff. - ASSERT(init.vifcode0().kind == VifCode::Kind::STROW); - ASSERT(init.size_bytes == 16); - // m_vif.row[0] = init.vif1(); - // memcpy(m_vif.row + 1, init.data, 12); - u32 extra; - memcpy(&extra, init.data + 12, 4); - // ASSERT(extra == 0); - m_current_effect_enable_bits = extra; - m_current_ignore_alpha_bits = extra >> 16; - DmaTransfer next; - - bool setting_up = true; - u32 mscal_addr = -1; - while (setting_up) { - next = dma.read_and_advance(); - // fmt::print("next: {}", dma.current_tag().print()); - u32 offset_in_data = 0; - // fmt::print("START {} : {} {}\n", next.size_bytes, next.vifcode0().print(), - // next.vifcode1().print()); - auto vif0 = next.vifcode0(); - switch (vif0.kind) { - case VifCode::Kind::NOP: - case VifCode::Kind::FLUSHE: - break; - case VifCode::Kind::STMOD: - ASSERT(vif0.immediate == 0 || vif0.immediate == 1); - // m_vif.stmod = vif0.immediate; - break; - default: - ASSERT(false); - } - - auto vif1 = next.vifcode1(); - switch (vif1.kind) { - case VifCode::Kind::UNPACK_V4_8: { - VifCodeUnpack up(vif1); - offset_in_data += 4 * vif1.num; - } break; - case VifCode::Kind::UNPACK_V4_32: { - VifCodeUnpack up(vif1); - if (up.addr_qw == 132 && vif1.num == 8) { - set_lights(next); - } else if (vif1.num == 7) { - handle_matrix_dma(next); - } - offset_in_data += 16 * vif1.num; - } break; - case VifCode::Kind::MSCAL: - // fmt::print("cal\n"); - mscal_addr = vif1.immediate; - ASSERT(next.size_bytes == 0); - setting_up = false; - break; - default: - ASSERT(false); - } - - ASSERT(offset_in_data <= next.size_bytes); - if (offset_in_data < next.size_bytes) { - ASSERT((offset_in_data % 4) == 0); - u32 leftover = next.size_bytes - offset_in_data; - if (leftover < 16) { - for (u32 i = 0; i < leftover; i++) { - ASSERT(next.data[offset_in_data + i] == 0); - } - } else { - ASSERT(false); - } + if (init.vifcode0().kind == VifCode::Kind::FLUSHA) { + int num_skipped = 0; + while (dma.current_tag_offset() != render_state->next_bucket) { + dma.read_and_advance(); + num_skipped++; } + ASSERT(num_skipped < 4); + return; } } @@ -452,16 +415,6 @@ u32 Merc2::alloc_bones(int count) { shader_mat[bv++] = skel_mat.nmat[j]; } - // we could include the effect of the perspective matrix here. - // for (int j = 0; j < 3; j++) { - // tbone_buffer[i][j] = vf15.elementwise_multiply(bone_mat[j]); - // tbone_buffer[i][j].w() += p.w() * bone_mat[j].z(); - // tbone_buffer[i][j] *= scale; - // } - // - // tbone_buffer[i][3] = vf15.elementwise_multiply(bone_mat[3]) + - // m_low_memory.perspective[3]; tbone_buffer[i][3].w() += p.w() * bone_mat[3].z(); - m_next_free_bone_vector += 8; } @@ -531,6 +484,16 @@ void Merc2::flush_pending_model(SharedRenderState* render_state, ScopedProfilerN return; } + if (lev_bucket->next_free_envmap_draw + model->max_draws >= lev_bucket->envmap_draws.size()) { + // out of room, flush + fmt::print("MERC2 out of envmap draws, consider increasing MAX_ENVMAP_DRAWS_PER_LEVEL\n"); + // or, use a more accurate max_draws for envmap. + flush_draw_buckets(render_state, prof); + // and retry the whole thing. + flush_pending_model(render_state, prof); + return; + } + u32 first_bone = alloc_bones(bone_count); // allocate lights @@ -543,6 +506,31 @@ void Merc2::flush_pending_model(SharedRenderState* render_state, ScopedProfilerN u8 ignore_alpha = (m_current_ignore_alpha_bits & (1 << ei)); auto& effect = model->effects[ei]; + if (effect.has_envmap) { + bool nonzero_fade = false; + for (int i = 0; i < 4; i++) { + if (m_fade_buffer[4 * ei + i]) { + nonzero_fade = true; + break; + } + } + if (nonzero_fade) { + for (auto& mdraw : effect.draws) { + Draw* draw = &lev_bucket->envmap_draws[lev_bucket->next_free_envmap_draw++]; + draw->first_index = mdraw.first_index; + draw->index_count = mdraw.index_count; + draw->mode = effect.envmap_mode; + draw->texture = effect.envmap_texture; + draw->first_bone = first_bone; + draw->light_idx = lights; + draw->num_triangles = mdraw.num_triangles; + draw->ignore_alpha = false; + for (int i = 0; i < 4; i++) { + draw->fade[i] = m_fade_buffer[4 * ei + i]; + } + } + } + } for (auto& mdraw : effect.draws) { Draw* draw = &lev_bucket->draws[lev_bucket->next_free_draw++]; draw->first_index = mdraw.first_index; @@ -553,13 +541,16 @@ void Merc2::flush_pending_model(SharedRenderState* render_state, ScopedProfilerN draw->light_idx = lights; draw->num_triangles = mdraw.num_triangles; draw->ignore_alpha = ignore_alpha; + for (int i = 0; i < 4; i++) { + draw->fade[i] = 0; + } } } m_current_model = std::nullopt; } -void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfilerNode& prof) { +void Merc2::flush_draw_buckets(SharedRenderState* render_state, ScopedProfilerNode& prof) { m_stats.num_draw_flush++; for (u32 li = 0; li < m_next_free_level_bucket; li++) { @@ -613,7 +604,7 @@ void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfil ); glVertexAttribPointer(4, // location 1 in the shader - 3, // 3 values per vert + 4, // 3 values per vert GL_UNSIGNED_BYTE, // floats GL_TRUE, // normalized sizeof(tfrag3::MercVertex), // stride @@ -627,8 +618,6 @@ void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfil (void*)offsetof(tfrag3::MercVertex, mats[0]) // offset in array ); - int last_tex = -1; - int last_light = -1; m_stats.num_bones_uploaded += m_next_free_bone_vector; glBindBuffer(GL_UNIFORM_BUFFER, m_bones_buffer); @@ -636,38 +625,13 @@ void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfil m_shader_bone_vector_buffer); glBindBuffer(GL_UNIFORM_BUFFER, 0); - for (u32 di = 0; di < lev_bucket.next_free_draw; di++) { - auto& draw = lev_bucket.draws[di]; - glUniform1i(m_uniforms.ignore_alpha, draw.ignore_alpha); - if ((int)draw.texture != last_tex) { - if (draw.texture < lev->textures.size()) { - glBindTexture(GL_TEXTURE_2D, lev->textures.at(draw.texture)); - } else { - fmt::print("Invalid draw.texture is {}, would have crashed.\n", draw.texture); - } - last_tex = draw.texture; - } - - if ((int)draw.light_idx != last_light) { - set_uniform(m_uniforms.light_direction[0], m_lights_buffer[draw.light_idx].direction0); - set_uniform(m_uniforms.light_direction[1], m_lights_buffer[draw.light_idx].direction1); - set_uniform(m_uniforms.light_direction[2], m_lights_buffer[draw.light_idx].direction2); - set_uniform(m_uniforms.light_color[0], m_lights_buffer[draw.light_idx].color0); - set_uniform(m_uniforms.light_color[1], m_lights_buffer[draw.light_idx].color1); - set_uniform(m_uniforms.light_color[2], m_lights_buffer[draw.light_idx].color2); - set_uniform(m_uniforms.light_ambient, m_lights_buffer[draw.light_idx].ambient); - last_light = draw.light_idx; - } - setup_opengl_from_draw_mode(draw.mode, GL_TEXTURE0, true); - - glUniform1i(m_uniforms.decal, draw.mode.get_decal()); - - prof.add_draw_call(); - prof.add_tri(draw.num_triangles); - glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_bones_buffer, - sizeof(math::Vector4f) * draw.first_bone, 128 * sizeof(ShaderMercMat)); - glDrawElements(GL_TRIANGLE_STRIP, draw.index_count, GL_UNSIGNED_INT, - (void*)(sizeof(u32) * draw.first_index)); + switch_to_merc2(render_state); + do_draws(lev_bucket.draws.data(), lev, lev_bucket.next_free_draw, m_merc_uniforms, prof, false, + render_state); + if (lev_bucket.next_free_envmap_draw) { + switch_to_emerc(render_state); + do_draws(lev_bucket.envmap_draws.data(), lev, lev_bucket.next_free_envmap_draw, + m_emerc_uniforms, prof, true, render_state); } } @@ -675,3 +639,55 @@ void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfil m_next_free_bone_vector = 0; m_next_free_level_bucket = 0; } + +void Merc2::do_draws(const Draw* draw_array, + const LevelData* lev, + u32 num_draws, + const Uniforms& uniforms, + ScopedProfilerNode& prof, + bool set_fade, + SharedRenderState* render_state) { + int last_tex = -1; + int last_light = -1; + for (u32 di = 0; di < num_draws; di++) { + auto& draw = draw_array[di]; + glUniform1i(uniforms.ignore_alpha, draw.ignore_alpha); + if ((int)draw.texture != last_tex) { + if (draw.texture < lev->textures.size()) { + glBindTexture(GL_TEXTURE_2D, lev->textures.at(draw.texture)); + } else { + fmt::print("Invalid draw.texture is {}, would have crashed.\n", draw.texture); + } + last_tex = draw.texture; + } + + if ((int)draw.light_idx != last_light && !set_fade) { + set_uniform(uniforms.light_direction[0], m_lights_buffer[draw.light_idx].direction0); + set_uniform(uniforms.light_direction[1], m_lights_buffer[draw.light_idx].direction1); + set_uniform(uniforms.light_direction[2], m_lights_buffer[draw.light_idx].direction2); + set_uniform(uniforms.light_color[0], m_lights_buffer[draw.light_idx].color0); + set_uniform(uniforms.light_color[1], m_lights_buffer[draw.light_idx].color1); + set_uniform(uniforms.light_color[2], m_lights_buffer[draw.light_idx].color2); + set_uniform(uniforms.light_ambient, m_lights_buffer[draw.light_idx].ambient); + last_light = draw.light_idx; + } + setup_opengl_from_draw_mode(draw.mode, GL_TEXTURE0, true); + + glUniform1i(uniforms.decal, draw.mode.get_decal()); + + if (set_fade) { + math::Vector4f fade = + math::Vector4f(draw.fade[0], draw.fade[1], draw.fade[2], draw.fade[3]) / 255.f; + set_uniform(uniforms.fade, fade); + ASSERT(draw.mode.get_alpha_blend() == DrawMode::AlphaBlend::SRC_0_DST_DST); + // glBindTexture(GL_TEXTURE_2D, render_state->texture_pool->get_placeholder_texture()); + } + + prof.add_draw_call(); + prof.add_tri(draw.num_triangles); + glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_bones_buffer, + sizeof(math::Vector4f) * draw.first_bone, 128 * sizeof(ShaderMercMat)); + glDrawElements(GL_TRIANGLE_STRIP, draw.index_count, GL_UNSIGNED_INT, + (void*)(sizeof(u32) * draw.first_index)); + } +} \ No newline at end of file diff --git a/game/graphics/opengl_renderer/foreground/Merc2.h b/game/graphics/opengl_renderer/foreground/Merc2.h index e9f578cb44..4b79aa9460 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.h +++ b/game/graphics/opengl_renderer/foreground/Merc2.h @@ -7,9 +7,6 @@ class Merc2 : public BucketRenderer { void draw_debug_window() override; void init_shaders(ShaderLibrary& shaders) override; void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override; - void handle_merc_chain(DmaFollower& dma, - SharedRenderState* render_state, - ScopedProfilerNode& prof); private: enum MercDataMemory { @@ -35,30 +32,22 @@ class Merc2 : public BucketRenderer { u32 w1; math::Vector3f direction2; u32 w2; - math::Vector3f color0; - u32 w3; - math::Vector3f color1; - u32 w4; - math::Vector3f color2; - u32 w5; - math::Vector3f ambient; - u32 w6; + math::Vector4f color0; + math::Vector4f color1; + math::Vector4f color2; + math::Vector4f ambient; }; - void init_for_frame(SharedRenderState* render_state); void init_pc_model(const DmaTransfer& setup, SharedRenderState* render_state); - void handle_all_dma(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof); - void handle_setup_dma(DmaFollower& dma, SharedRenderState* render_state); u32 alloc_lights(const VuLights& lights); - void set_lights(const DmaTransfer& dma); - void handle_matrix_dma(const DmaTransfer& dma); - void flush_pending_model(SharedRenderState* render_state, ScopedProfilerNode& prof); u32 alloc_bones(int count); std::optional m_current_model = std::nullopt; u16 m_current_effect_enable_bits = 0; u16 m_current_ignore_alpha_bits = 0; + static constexpr int kMaxEffect = 16; + u8 m_fade_buffer[4 * kMaxEffect]; struct MercMat { math::Vector4f tmat[4]; @@ -69,7 +58,6 @@ class Merc2 : public BucketRenderer { math::Vector4f tmat[4]; math::Vector4f nmat[3]; math::Vector4f pad; - std::string to_string() const; }; @@ -79,17 +67,17 @@ class Merc2 : public BucketRenderer { static constexpr int MAX_LEVELS = 3; static constexpr int MAX_DRAWS_PER_LEVEL = 1024; + static constexpr int MAX_ENVMAP_DRAWS_PER_LEVEL = 1024; math::Vector4f m_shader_bone_vector_buffer[MAX_SHADER_BONE_VECTORS]; ShaderMercMat m_skel_matrix_buffer[MAX_SKEL_BONES]; - struct { + struct Uniforms { GLuint light_direction[3]; GLuint light_color[3]; GLuint light_ambient; GLuint hvdf_offset; - GLuint perspective[4]; GLuint fog; GLuint tbone; @@ -102,7 +90,23 @@ class Merc2 : public BucketRenderer { GLuint decal; GLuint gfx_hack_no_tex; - } m_uniforms; + + GLuint fade; + }; + + Uniforms m_merc_uniforms, m_emerc_uniforms; + + void init_shader_common(Shader& shader, Uniforms* uniforms, bool include_lights); + void init_for_frame(SharedRenderState* render_state, ShaderId shader); + void handle_setup_dma(DmaFollower& dma, SharedRenderState* render_state); + void handle_all_dma(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof); + void flush_pending_model(SharedRenderState* render_state, ScopedProfilerNode& prof); + void handle_merc_chain(DmaFollower& dma, + SharedRenderState* render_state, + ScopedProfilerNode& prof); + + void switch_to_merc2(SharedRenderState* render_state); + void switch_to_emerc(SharedRenderState* render_state); GLuint m_vao; @@ -110,6 +114,7 @@ class Merc2 : public BucketRenderer { struct Stats { int num_models = 0; + int num_missing_models = 0; int num_chains = 0; int num_effects = 0; int num_predicted_draws = 0; @@ -117,6 +122,9 @@ class Merc2 : public BucketRenderer { int num_bones_uploaded = 0; int num_lights = 0; int num_draw_flush = 0; + + int num_envmap_effects = 0; + int num_envmap_tris = 0; } m_stats; struct Draw { @@ -128,19 +136,31 @@ class Merc2 : public BucketRenderer { u16 first_bone; u16 light_idx; u8 ignore_alpha; + u8 fade[4]; }; struct LevelDrawBucket { const LevelData* level = nullptr; std::vector draws; + std::vector envmap_draws; u32 next_free_draw = 0; + u32 next_free_envmap_draw = 0; void reset() { level = nullptr; next_free_draw = 0; + next_free_envmap_draw = 0; } }; + void do_draws(const Draw* draw_array, + const LevelData* lev, + u32 num_draws, + const Uniforms& uniforms, + ScopedProfilerNode& prof, + bool set_fade, + SharedRenderState* render_state); + static constexpr int MAX_LIGHTS = 1024; VuLights m_lights_buffer[MAX_LIGHTS]; u32 m_next_free_light = 0; diff --git a/game/graphics/opengl_renderer/shaders/emerc.frag b/game/graphics/opengl_renderer/shaders/emerc.frag new file mode 100644 index 0000000000..a6f3f9fe5d --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/emerc.frag @@ -0,0 +1,32 @@ +#version 430 core + +out vec4 color; +in vec3 vtx_color; +in vec2 vtx_st; +in float fog; + + +uniform sampler2D tex_T0; + +uniform vec4 fog_color; +uniform int ignore_alpha; + +uniform int decal_enable; + +uniform int gfx_hack_no_tex; + + +void main() { + if (gfx_hack_no_tex == 0) { + vec4 T0 = texture(tex_T0, vtx_st); + + color.a = T0.a; + color.rgb = T0.rgb * vtx_color; + color *= 2; + } else { + color.rgb = vtx_color; + color.a = 1; + } + + color.xyz *= 0.5; +} diff --git a/game/graphics/opengl_renderer/shaders/emerc.vert b/game/graphics/opengl_renderer/shaders/emerc.vert new file mode 100644 index 0000000000..5a058e6d83 --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/emerc.vert @@ -0,0 +1,132 @@ +#version 430 core + +// merc vertex definition +layout (location = 0) in vec3 position_in; +layout (location = 1) in vec3 normal_in; +layout (location = 2) in vec3 weights_in; +layout (location = 3) in vec2 st_in; +layout (location = 4) in vec3 rgba; +layout (location = 5) in uvec3 mats; + +// camera control +uniform vec4 hvdf_offset; +uniform vec4 fog_constants; + +uniform mat4 perspective_matrix; + +uniform vec4 fade; + +const float SCISSOR_ADJUST = HEIGHT_SCALE * 512.0/448.0; + +// output +out vec3 vtx_color; +out vec2 vtx_st; + +out float fog; + +struct MercMatrixData { + mat4 X; + mat3 R; + vec4 pad; +}; + +layout (std140, binding = 1) uniform ub_bones { + MercMatrixData bones[128]; +}; + + +void main() { + + + vec4 p = vec4(position_in, 1); + vec4 vtx_pos = -bones[mats[0]].X * p * weights_in[0]; + vec3 rotated_nrm = bones[mats[0]].R * normal_in * weights_in[0]; + + // game may send garbage bones if the weight is 0, don't let NaNs sneak in. + if (weights_in[1] > 0) { + vtx_pos += -bones[mats[1]].X * p * weights_in[1]; + rotated_nrm += bones[mats[1]].R * normal_in * weights_in[1]; + } + if (weights_in[2] > 0) { + vtx_pos += -bones[mats[2]].X * p * weights_in[2]; + rotated_nrm += bones[mats[2]].R * normal_in * weights_in[2]; + } + + vec4 transformed = perspective_matrix * vtx_pos; + + rotated_nrm = normalize(rotated_nrm); + + float Q = fog_constants.x / transformed[3]; + fog = 255 - clamp(-transformed.w + hvdf_offset.w, fog_constants.y, fog_constants.z); + + // emerc + vec2 st_mod = st_in; + { + vec4 vf10 = vec4(rotated_nrm, 1); // ?? + // vf08 = transformed + vec4 vf08 = transformed; + // vf23 = unperspect + // unperspect (1/P(0, 0), 1/P(1, 1), 0.5, 1/P(2, 3)) + vec4 vf23 = vec4(1. / perspective_matrix[0][0], + 1. / perspective_matrix[1][1], + 0.5, + 1. / perspective_matrix[2][3]); + // vf14 = rgba-fade + vec4 vf14 = fade; + // vf24 = normal st + // mul.xyzw vf09, vf08, vf23 ;; do unperspect + vec4 vf09 = vf08 * vf23; + //subw.z vf10, vf10, vf00 ;; subtract 1 from z + vf10.z -= 1; + //addw.z vf09, vf00, vf09 ;; xyww the unperspected thing + vf09.z = vf09.w; + //mul.xyz vf15, vf09, vf10 ;; + vec3 vf15 = vf09.xyz * vf10.xyz; + //adday.xyzw vf15, vf15 + //maddz.x vf15, vf21, vf15 + float vf15_x = vf15.x + vf15.y + vf15.z; + //div Q, vf15.x, vf10.z + float qq = vf15_x / vf10.z; + //mulaw.xyzw ACC, vf09, vf00 + vec4 ACC = vf09; + //mul.xyzw vf09, vf08, vf23 + vf09 = vf08 * vf23; + //madd.xyzw vf10, vf10, Q + vf10 = ACC + vf10 * qq; + //eleng.xyz P, vf10 + float P = length(vf10.xyz); + //mfp.w vf10, P + //div Q, vf23.z, vf10.w + float qqq = vf23.z / P; + //addaz.xyzw vf00, vf23 + ACC = vec4(vf23.z, vf23.z, vf23.z, vf23.z + 1.); + //madd.xyzw vf10, vf10, Q + vf10 = ACC + vf10 * qqq; + st_mod = vf10.xy; + + // this is required to make jak 1's envmapping look right + // otherwise it behaves like the envmap texture is mirrored. + // TODO: see if this is right for jak 2 or not. + // It _might_ make sense that this exists because we skip the multiply by Q + // below, and Q is negative (no idea how that works out with clamp). + st_mod.x = 1 - vf10.x; + st_mod.y = 1 - vf10.y; + + //mulz.xy vf24, vf10, vf24 ;; mul tex by q + } + + transformed.xyz *= Q; + transformed.xyz += hvdf_offset.xyz; + transformed.xy -= (2048.); + transformed.z /= (8388608); + transformed.z -= 1; + transformed.x /= (256); + transformed.y /= -(128); + transformed.xyz *= transformed.w; + transformed.y *= SCISSOR_ADJUST; + gl_Position = transformed; + + + vtx_color = fade.xyz; + vtx_st = st_mod; +} diff --git a/game/graphics/opengl_renderer/shaders/merc2.frag b/game/graphics/opengl_renderer/shaders/merc2.frag index 1a261ae134..c5f2b9c223 100644 --- a/game/graphics/opengl_renderer/shaders/merc2.frag +++ b/game/graphics/opengl_renderer/shaders/merc2.frag @@ -1,7 +1,7 @@ #version 430 core out vec4 color; -in vec3 vtx_color; +in vec4 vtx_color; in vec2 vtx_st; in float fog; @@ -18,16 +18,16 @@ uniform int gfx_hack_no_tex; void main() { if (gfx_hack_no_tex == 0) { vec4 T0 = texture(tex_T0, vtx_st); - + // all merc is tcc=rgba and modulate if (decal_enable == 0) { - color.rgb = vtx_color * T0.rgb; + color = vtx_color * T0 * 2; } else { - color.rgb = T0.rgb * 0.5; + color = T0; } - color.a = T0.a; - color *= 2; + color.a *= 2; + //color.a = T0.a * 4; } else { - color.rgb = vtx_color; + color.rgb = vtx_color.rgb; color.a = 1; } @@ -36,5 +36,5 @@ void main() { discard; } - color.xyz = mix(color.xyz, fog_color.rgb, clamp(fog_color.a * fog, 0, 1)); + color.xyz = mix(color.xyz, fog_color.rgb, clamp(fog_color.a * fog, 0, 1)); } diff --git a/game/graphics/opengl_renderer/shaders/merc2.vert b/game/graphics/opengl_renderer/shaders/merc2.vert index 9a068b058e..4aa0634db6 100644 --- a/game/graphics/opengl_renderer/shaders/merc2.vert +++ b/game/graphics/opengl_renderer/shaders/merc2.vert @@ -5,24 +5,20 @@ layout (location = 0) in vec3 position_in; layout (location = 1) in vec3 normal_in; layout (location = 2) in vec3 weights_in; layout (location = 3) in vec2 st_in; -layout (location = 4) in vec3 rgba; +layout (location = 4) in vec4 rgba; layout (location = 5) in uvec3 mats; // light control uniform vec3 light_dir0; uniform vec3 light_dir1; uniform vec3 light_dir2; -uniform vec3 light_col0; -uniform vec3 light_col1; -uniform vec3 light_col2; -uniform vec3 light_ambient; +uniform vec4 light_col0; +uniform vec4 light_col1; +uniform vec4 light_col2; +uniform vec4 light_ambient; // camera control uniform vec4 hvdf_offset; -uniform vec4 perspective0; -uniform vec4 perspective1; -uniform vec4 perspective2; -uniform vec4 perspective3; uniform vec4 fog_constants; uniform mat4 perspective_matrix; @@ -30,7 +26,7 @@ uniform mat4 perspective_matrix; const float SCISSOR_ADJUST = HEIGHT_SCALE * 512.0/448.0; // output -out vec3 vtx_color; +out vec4 vtx_color; out vec2 vtx_st; out float fog; @@ -93,7 +89,7 @@ void main() { vec3 light_intensity = light_dir0 * rotated_nrm.x + light_dir1 * rotated_nrm.y + light_dir2 * rotated_nrm.z; light_intensity = max(light_intensity, vec3(0, 0, 0)); - vec3 light_color = light_ambient + vec4 light_color = light_ambient + light_intensity.x * light_col0 + light_intensity.y * light_col1 + light_intensity.z * light_col2; diff --git a/game/mips2c/jak1_functions/bones.cpp b/game/mips2c/jak1_functions/bones.cpp index 2201bf5027..72ade06777 100644 --- a/game/mips2c/jak1_functions/bones.cpp +++ b/game/mips2c/jak1_functions/bones.cpp @@ -803,6 +803,7 @@ u64 execute(void* ctxt) { const MercBucketInfo* mbi = (const MercBucketInfo*)(g_ee_main_mem + c->sgpr64(a3)); u16 use_pc_merc_bits = 0; u16 ignore_alpha_bits = 0; + u32 fade = 0; for (int i = 0; i < 16; i++) { if (!mbi->effects[i].use_mercneric) { use_pc_merc_bits |= (1 << i); @@ -949,6 +950,22 @@ block_3: c->sw(a3, 28, a2); // sw a3, 28(a2) c->daddiu(a2, a2, 144); // daddiu a2, a2, 144 + // PC ADD BONUS DATA (bonus!) + { + // 10 qw test + u64 dmatag = 5 | (1 << 28); + memcpy(g_ee_main_mem + c->sgpr64(a2), &dmatag, 8); + u32 vif = (0b1001 << 24); + memcpy(g_ee_main_mem + c->sgpr64(a2) + 8, &vif, 4); + + for (int i = 0; i < 16; i++) { + memcpy(g_ee_main_mem + c->sgpr64(a2) + 16 + i * 4, mbi->effects[i].color_fade, 4); + } + + c->gprs[a2].du32[0] += 6 * 16; + } + + // after first frag setup block_5: bc = c->sgpr64(s3) == 0; // beq s3, r0, L75 diff --git a/game/mips2c/jak2_functions/foreground.cpp b/game/mips2c/jak2_functions/foreground.cpp index 8208d41fd4..9b2089e193 100644 --- a/game/mips2c/jak2_functions/foreground.cpp +++ b/game/mips2c/jak2_functions/foreground.cpp @@ -558,6 +558,21 @@ u64 execute(void* ctxt) { c->sw(a3, 28, a2); // sw a3, 28(a2) c->daddiu(a2, a2, 144); // daddiu a2, a2, 144 + // PC ADD BONUS DATA (bonus!) + { + // 10 qw test + u64 dmatag = 5 | (1 << 28); + memcpy(g_ee_main_mem + c->sgpr64(a2), &dmatag, 8); + u32 vif = (0b1001 << 24); + memcpy(g_ee_main_mem + c->sgpr64(a2) + 8, &vif, 4); + + for (int i = 0; i < 16; i++) { + memcpy(g_ee_main_mem + c->sgpr64(a2) + 16 + i * 4, mbi->effects[i].color_fade, 4); + } + + c->gprs[a2].du32[0] += 6 * 16; + } + block_8: bc = c->sgpr64(s3) == 0; // beq s3, r0, L115 c->addiu(s2, r0, 128); // addiu s2, r0, 128 diff --git a/goal_src/jak1/engine/gfx/foreground/bones.gc b/goal_src/jak1/engine/gfx/foreground/bones.gc index 8afb69881b..e59f13031d 100644 --- a/goal_src/jak1/engine/gfx/foreground/bones.gc +++ b/goal_src/jak1/engine/gfx/foreground/bones.gc @@ -937,35 +937,134 @@ `(set! (-> (the-as (pointer uint32) ,addr)) ,val) ) -(defun pc-draw-bones ((dc draw-control) (dma-buf pointer)) - "Add a dma packet to tell the PC renderer which model we are renderering." - (let ((packet (the-as dma-packet dma-buf))) +;; name (128 char, 8 qw) +;; lights (7 qw x 1) +;; matrix slot string (128 char, 8 qw) +;; matrices (7 qw x N) +;; flags (num-effects, effect-alpha-ignore, effect-disable) +;; fades (u32 x N), padding to qw aligned + +(defun pc-merc-draw-request ((dc draw-control) (dma-buf pointer) (matrix-buf pointer)) + (let ((start-packet (the-as dma-packet dma-buf)) + (qwc-total 0)) ;; merc draw asm will check this. (when (zero? (-> (scratchpad-object terrain-context) work foreground bone-mem work next-merc)) - (set! (-> (scratchpad-object terrain-context) work foreground bone-mem work next-merc) packet) + (set! (-> (scratchpad-object terrain-context) work foreground bone-mem work next-merc) start-packet) ) - (set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 10)) - (set! (-> packet vif0) (new 'static 'vif-tag)) - (set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port))) - (set! dma-buf (the pointer (&+ packet 16))) - ) + (set! (-> start-packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) + (set! (-> start-packet vif0) (new 'static 'vif-tag)) + (set! (-> start-packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port))) + (set! dma-buf (the pointer (&+ start-packet 16))) - (let ((data-ptr (the-as (pointer uint128) dma-buf))) - (charp<-string (the (pointer uint8) (&-> data-ptr 0)) (-> dc mgeo name)) - ) - (&+! dma-buf (* 16 10)) + ;; NAME: 128 char, 8 qw + (let ((data-ptr (the-as (pointer uint128) dma-buf))) + (charp<-string (the (pointer uint8) (&-> data-ptr 0)) (-> dc mgeo name)) + ) + (&+! dma-buf (* 16 8)) + (+! qwc-total 8) - ;; merc linking needs this. - (let ((packet (the-as dma-packet dma-buf))) - (set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 0)) - (set! (-> packet vif0) (new 'static 'vif-tag)) - (set! (-> packet vif1) (new 'static 'vif-tag)) - (set! dma-buf (the pointer (&+ packet 16))) - ) + ;; LIGHTS: + (quad-copy! dma-buf (the pointer (-> *merc-bucket-info* light)) 7) + (&+! dma-buf (* 16 7)) + (+! qwc-total 7) - dma-buf + + (let ((matrix-slot-string (the (pointer uint8) dma-buf)) ;; matrix slot list (so PC knows what order they come in) + (enable-mask 0) + (ignore-alpha-mask 0) + (matrix-out-idx 0) + ) + (&+! dma-buf (* 16 8)) + (+! qwc-total 8) + ;; flag of matrices we've already transferred and can de-dup + (let ((transferred-matrices (new 'stack-no-clear 'array 'uint8 128))) + (dotimes (i 128) (set! (-> transferred-matrices i) 0)) + + (let ((merc-ctrl (-> dc lod (-> dc cur-lod) geo))) + + ;; loop to: grab matrices and populate flags + (dotimes (effect-idx (-> merc-ctrl header effect-count)) + (when (zero? (-> *merc-bucket-info* effect effect-idx use-mercneric)) + (logior! enable-mask (ash 1 effect-idx)) + (when (nonzero? (-> *merc-bucket-info* effect effect-idx ignore-alpha)) + (logior! ignore-alpha-mask (ash 1 effect-idx)) + ) + (let* ((effect (-> merc-ctrl effect effect-idx)) + (frag (-> effect frag-ctrl)) + ) + (dotimes (frag-idx (-> effect frag-count)) + (dotimes (mat-xfer-idx (-> frag mat-xfer-count)) + (let ((mat-idx (-> frag mat-dest-data mat-xfer-idx matrix-number))) + (if (>= mat-idx 128) + (break!) + ) + (when (zero? (-> transferred-matrices mat-idx)) + ;; transfer it! + (set! (-> transferred-matrices mat-idx) 1) + (set! (-> matrix-slot-string matrix-out-idx) mat-idx) + (+! matrix-out-idx 1) + (set! (-> (the (pointer pointer) dma-buf)) (&+ matrix-buf (* 128 mat-idx))) + ;(quad-copy! dma-buf (&+ matrix-buf (* 128 mat-idx)) 7) + (&+! dma-buf 16) + (+! qwc-total 1) + ) + ) + ) + (&+! frag (* 2 (-> frag mat-xfer-count))) + (&+! frag (size-of merc-fragment-control)) + ) + ) + ) + ) ;; end effect loop + + ;; end matrix string + (while (< matrix-out-idx 128) + (set! (-> matrix-slot-string matrix-out-idx) #xff) + (+! matrix-out-idx 1) + ) + + ;; flags + (let ((flags (the (pointer uint32) dma-buf))) + (set! (-> flags 0) (-> merc-ctrl header effect-count)) + (set! (-> flags 1) ignore-alpha-mask) + (set! (-> flags 2) enable-mask) + ) + (&+! dma-buf (* 16 1)) + (+! qwc-total 1) + + ;; fades + (let ((fades (the (pointer uint32) dma-buf))) + (dotimes (i (-> merc-ctrl header effect-count)) + (set! (-> fades i) (the-as uint (-> *merc-bucket-info* effect i color-fade))) + ) + ) + + (let ((num-fades (/ (+ (-> merc-ctrl header effect-count) 3) 4))) + (&+! dma-buf (* 16 num-fades)) + (+! qwc-total num-fades) + ) + ) + ) + ) + + (set! (-> start-packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc qwc-total)) + + ;; merc linking needs this. + (let ((packet (the-as dma-packet dma-buf))) + (set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 0)) + (set! (-> packet vif0) (new 'static 'vif-tag)) + (set! (-> packet vif1) (new 'static 'vif-tag)) + (set! dma-buf (the pointer (&+ packet 16))) + ) + + dma-buf + ) ) +;; when set, render some environment mapped stuff with jak 2's emerc. +;; this is much faster, and does significantly speed up the game thread on finalboss. +(define *emerc-hack* #t) + (defun draw-bones ((arg0 draw-control) (dma-buf dma-buffer) (arg2 float)) "Main draw function for all bone-related renderers. Will set up merc, generic and shadow. and also add the bones to the calculation list." @@ -1256,6 +1355,13 @@ ) ) + ;; additional in pc to make envmap fade 0 when envmap is not used + (let ((a0-36 (the-as object (-> *merc-bucket-info* effect effect-idx)))) + (dotimes (a2-15 4) + (set! (-> (the-as (pointer int8) a0-36) a2-15) 0) + ) + ) + ;; final mercneric checks (cond @@ -1321,10 +1427,20 @@ ) (set! (-> (the-as (pointer uint8) a0-36) 3) (the-as uint 0)) ) - ;; env mapping, so use mercneric. - (set! (-> *merc-bucket-info* effect effect-idx use-mercneric) (the-as uint 1)) - (set! used-mercneric 1) - used-mercneric + (cond + ((and *emerc-hack* (not pc-force-mercneric)) + (set! (-> *merc-bucket-info* effect effect-idx use-mercneric) (the-as uint 0)) + (set! (-> *merc-bucket-info* effect effect-idx ignore-alpha) (the-as uint 1)) + (set! used-merc 1) + ) + (else + ;; env mapping, so use mercneric. + (set! (-> *merc-bucket-info* effect effect-idx use-mercneric) (the-as uint 1)) + (set! used-mercneric 1) + used-mercneric + ) + ) + ) (else ;; no env map, don't use mercneric. @@ -1399,12 +1515,12 @@ ) ) ) - (set! s2-0 (pc-draw-bones arg0 (the pointer s2-0))) - (if (nonzero? (-> *merc-bucket-info* need-mercprime-if-merc)) - (set! (-> dma-buf base) (draw-bones-merc arg0 matrix-data s2-0 32 17)) - (set! (-> dma-buf base) (draw-bones-merc arg0 matrix-data s2-0 35 20)) - ) - (set! s2-0 (-> dma-buf base)) + (set! s2-0 (pc-merc-draw-request arg0 (the pointer s2-0) (the pointer matrix-data))) + ; (if (nonzero? (-> *merc-bucket-info* need-mercprime-if-merc)) + ; (set! (-> dma-buf base) (draw-bones-merc arg0 matrix-data s2-0 32 17)) + ; (set! (-> dma-buf base) (draw-bones-merc arg0 matrix-data s2-0 35 20)) + ; ) + ; (set! s2-0 (-> dma-buf base)) ) ) ) ;; end geom processing diff --git a/goal_src/jak2/engine/gfx/foreground/foreground.gc b/goal_src/jak2/engine/gfx/foreground/foreground.gc index 6a05015d4b..366e34237b 100644 --- a/goal_src/jak2/engine/gfx/foreground/foreground.gc +++ b/goal_src/jak2/engine/gfx/foreground/foreground.gc @@ -629,7 +629,125 @@ ) ) -(defun pc-draw-bones ((dc draw-control) (dma-buf pointer)) +(defun pc-merc-draw-request ((dc draw-control) (dma-buf pointer) (matrix-buf pointer) (tex-idx int)) + "Send a request to PC Merc2 to draw the given object. + Only draws the effects which match this texture index. + Just places a single big dma packet, you have to patch the end yourself." + (let ((start-packet (the-as dma-packet dma-buf)) + (qwc-total 0)) + (set! (-> start-packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) + (set! (-> start-packet vif0) (new 'static 'vif-tag)) + (set! (-> start-packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port))) + (set! dma-buf (the pointer (&+ start-packet 16))) + + ;; NAME: 128 char, 8 qw + (let ((data-ptr (the-as (pointer uint128) dma-buf))) + (charp<-string (the (pointer uint8) (&-> data-ptr 0)) (-> dc mgeo name)) + ) + (&+! dma-buf (* 16 8)) + (+! qwc-total 8) + + ;; LIGHTS: + (quad-copy! dma-buf (the pointer (-> (-> *foreground* merc-bucket-info) light)) 7) + (&+! dma-buf (* 16 7)) + (+! qwc-total 7) + + + (let ((matrix-slot-string (the (pointer uint8) dma-buf)) ;; matrix slot list (so PC knows what order they come in) + (enable-mask 0) + (ignore-alpha-mask 0) + (matrix-out-idx 0) + ) + (&+! dma-buf (* 16 8)) + (+! qwc-total 8) + ;; flag of matrices we've already transferred and can de-dup + (let ((transferred-matrices (new 'stack-no-clear 'array 'uint8 128))) + (dotimes (i 128) (set! (-> transferred-matrices i) 0)) + + (let ((merc-ctrl (-> dc lod-set lod (-> dc cur-lod) geo))) + + ;; loop to: grab matrices and populate flags + (dotimes (effect-idx (-> merc-ctrl header effect-count)) + + ;; check to skip fragment. only draw if: + ;; We want to use "merc" + ;; We aren't disabled (new feature in jak 2) + ;; We match the texture index for the bucket. + (when (and (zero? (-> (-> *foreground* merc-bucket-info) effect effect-idx merc-path)) + (zero? (-> (-> *foreground* merc-bucket-info) effect effect-idx disable-draw)) + (= tex-idx (-> merc-ctrl effect effect-idx texture-index)) + ) + ;; set enable bit for pc render + (logior! enable-mask (ash 1 effect-idx)) + ;; set alpha bit for pc render + (when (nonzero? (-> (-> *foreground* merc-bucket-info) effect effect-idx ignore-alpha)) + (logior! ignore-alpha-mask (ash 1 effect-idx)) + ) + (let* ((effect (-> merc-ctrl effect effect-idx)) + (frag (-> effect frag-ctrl)) + ) + ;; iterate over fragments + (dotimes (frag-idx (-> effect frag-count)) + ;; matrices + (dotimes (mat-xfer-idx (-> frag mat-xfer-count)) + (let ((mat-idx (-> frag mat-dest-data mat-xfer-idx matrix-number))) + (if (>= mat-idx 128) ;; pc merc2 limit + (break!) + ) + (when (zero? (-> transferred-matrices mat-idx)) + ;; transfer it. + (set! (-> transferred-matrices mat-idx) 1) + (set! (-> matrix-slot-string matrix-out-idx) mat-idx) + (+! matrix-out-idx 1) + (set! (-> (the (pointer pointer) dma-buf)) (&+ matrix-buf (* 128 mat-idx))) + (&+! dma-buf 16) + (+! qwc-total 1) + ) + ) + ) + (&+! frag (* 2 (-> frag mat-xfer-count))) + (&+! frag (size-of merc-fragment-control)) + ) + ) + ) + ) ;; end effect loop + + ;; end matrix string + (while (< matrix-out-idx 128) + (set! (-> matrix-slot-string matrix-out-idx) #xff) + (+! matrix-out-idx 1) + ) + + ;; flags + (let ((flags (the (pointer uint32) dma-buf))) + (set! (-> flags 0) (-> merc-ctrl header effect-count)) + (set! (-> flags 1) ignore-alpha-mask) + (set! (-> flags 2) enable-mask) + ) + (&+! dma-buf (* 16 1)) + (+! qwc-total 1) + + ;; fades + (let ((fades (the (pointer uint32) dma-buf))) + (dotimes (i (-> merc-ctrl header effect-count)) + (set! (-> fades i) (the-as uint (-> (-> *foreground* merc-bucket-info) effect i color-fade))) + ) + ) + + (let ((num-fades (/ (+ (-> merc-ctrl header effect-count) 3) 4))) + (&+! dma-buf (* 16 num-fades)) + (+! qwc-total num-fades) + ) + ) + ) + ) + + (set! (-> start-packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc qwc-total)) + dma-buf + ) + ) + +(defun pc-draw-bones ((dc draw-control) (dma-buf pointer) (matrix-buf pointer)) "Add a dma packet to tell the PC renderer which model we are renderering." (let ((use-flags (new 'stack-no-clear 'array 'uint8 7)) (mctrl (-> dc mgeo)) @@ -650,15 +768,7 @@ ;; this one is used, update the model for pc. ;; create dma-packet to send the name: (let ((packet (the-as dma-packet dma-buf))) - (set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 10)) - (set! (-> packet vif0) (new 'static 'vif-tag)) - (set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port))) - (set! dma-buf (the pointer (&+ packet 16))) - ;; send the name - (let ((data-ptr (the-as (pointer uint128) dma-buf))) - (charp<-string (the (pointer uint8) (&-> data-ptr 0)) (-> dc mgeo name)) - ) - (&+! dma-buf (* 16 10)) + (set! dma-buf (pc-merc-draw-request dc dma-buf matrix-buf i)) ;; create a patch packet (let ((patch-packet (the-as dma-packet dma-buf))) @@ -690,56 +800,8 @@ ) ) - - - - ) dma-buf - ; (let ((packet (the-as dma-packet dma-buf))) - ; ;; merc draw asm will check this. - ; (when (zero? (-> (scratchpad-object terrain-context) work foreground bone-mem work next-merc)) - ; (set! (-> (scratchpad-object terrain-context) work foreground bone-mem work next-merc) packet) - ; ) - ; (set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 10)) - ; (set! (-> packet vif0) (new 'static 'vif-tag)) - ; (set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port))) - ; (set! dma-buf (the pointer (&+ packet 16))) - ; ) - - ; (let ((data-ptr (the-as (pointer uint128) dma-buf))) - ; (charp<-string (the (pointer uint8) (&-> data-ptr 0)) (-> dc mgeo name)) - ; ) - ; (&+! dma-buf (* 16 10)) - - ; ;; merc linking needs this. - ; (let ((packet (the-as dma-packet dma-buf))) - ; (set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 0)) - ; (set! (-> packet vif0) (new 'static 'vif-tag)) - ; (set! (-> packet vif1) (new 'static 'vif-tag)) - ; (set! dma-buf (the pointer (&+ packet 16))) - ; ) - - ; dma-buf - - ;; ra is start packet - ; lw s4, 0(a3) ;; s4 = first - ; lw gp, 4(a3) ;; gp = patch - ; movz s4, ra, s4 ;; check first, if zero, set s4 to this packet - ; sq r0, 0(a2) - ; lui s5, 8192 - ; sw s4, 0(a3) ;; store first - ; or s4, a2, r0 - ; sw s5, 0(a2) - ; daddiu a2, a2, 16 - ; beq gp, r0, L117 - ; sw s4, 4(a3) - ; branch non-likely - - ; sll r0, r0, 0 - ; sw ra, 4(gp) - ;; L117 - ) (defun foreground-draw ((dc draw-control) (dma-buf dma-buffer) (dist-in float)) @@ -991,6 +1053,7 @@ ) (set! (-> (the-as merc-effect-bucket-info v1-74) alpha) (the-as uint 128)) ) + ;(format 0 "envmap stat: ~A ~A~%" (-> dc process name) (logtest? (-> geo effect effect-idx effect-bits) (effect-bits emerc))) (cond ((and (logtest? (-> dc status) (draw-control-status force-vu1)) (logtest? (-> geo effect effect-idx effect-bits) (effect-bits emerc)) @@ -1006,6 +1069,7 @@ ) ) ) + (set! (-> bucket-info effect effect-idx ignore-alpha) (the-as uint (if (logtest? (-> geo effect effect-idx effect-bits) (effect-bits ignore-alpha)) 1 @@ -1033,7 +1097,8 @@ ) ) ) - ) + ) ;; end if envmap + (else (cond ((logtest? (-> geo effect effect-idx effect-bits) (effect-bits cross-fade)) @@ -1070,7 +1135,8 @@ (set! effect-mask (shr effect-mask 1)) ) ) - ;; massive hack + + ;; massive hack use merc for everything.. (when (or (nonzero? (-> (scratchpad-object foreground-work) regs mercneric-used)) (nonzero? (-> (scratchpad-object foreground-work) regs emerc-used)) ) @@ -1079,6 +1145,10 @@ (set! (-> (scratchpad-object foreground-work) regs merc-used) 1) (dotimes (effect-idx (the-as int (-> geo header effect-count))) (set! (-> bucket-info effect effect-idx merc-path) 0) + (when (logtest? (-> geo effect effect-idx effect-usage) 1) + (set! (-> bucket-info effect effect-idx ignore-alpha) 1) + ) + ) ) @@ -1128,12 +1198,12 @@ ) ;; added - (set! dma-ptr (pc-draw-bones dc dma-ptr)) + (set! dma-ptr (pc-draw-bones dc dma-ptr (the pointer (-> (scratchpad-object foreground-work) regs mtxs)))) - (if (nonzero? (-> bucket-info need-mercprime-if-merc)) - (set! dma-ptr (foreground-merc dc (-> (scratchpad-object foreground-work) regs mtxs) dma-ptr 32 17 bucket-info)) - (set! dma-ptr (foreground-merc dc (-> (scratchpad-object foreground-work) regs mtxs) dma-ptr 35 20 bucket-info)) - ) + ; (if (nonzero? (-> bucket-info need-mercprime-if-merc)) + ; (set! dma-ptr (foreground-merc dc (-> (scratchpad-object foreground-work) regs mtxs) dma-ptr 32 17 bucket-info)) + ; (set! dma-ptr (foreground-merc dc (-> (scratchpad-object foreground-work) regs mtxs) dma-ptr 35 20 bucket-info)) + ; ) ) ) ) @@ -1180,4 +1250,5 @@ ;; todo foreground-draw-hud -(define *foreground-draw-engine* (new 'global 'engine 'draw 768 connection)) \ No newline at end of file +;; changed to perm. +(define-perm *foreground-draw-engine* engine (new 'global 'engine 'draw 768 connection)) \ No newline at end of file