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