2022-05-11 22:53:53 -04:00
|
|
|
#include "MercData.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
|
|
|
|
#include "common/dma/gs.h"
|
|
|
|
|
2023-01-22 18:30:31 -05:00
|
|
|
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
2022-05-11 22:53:53 -04:00
|
|
|
#include "decompiler/util/DecompilerTypeSystem.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
|
2022-05-11 22:53:53 -04:00
|
|
|
#include "third-party/fmt/core.h"
|
|
|
|
|
|
|
|
namespace decompiler {
|
2023-03-08 18:18:35 -05:00
|
|
|
|
|
|
|
void MercEyeCtrl::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) {
|
|
|
|
eye_slot = read_plain_data_field<s8>(tr, "eye-slot", dts);
|
|
|
|
}
|
|
|
|
|
2022-05-11 22:53:53 -04:00
|
|
|
void MercCtrlHeader::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) {
|
2022-05-28 19:28:19 -04:00
|
|
|
st_magic = read_plain_data_field<u32>(tr, "st-magic", dts);
|
2022-05-11 22:53:53 -04:00
|
|
|
xyz_scale = read_plain_data_field<float>(tr, "xyz-scale", dts);
|
|
|
|
st_out_a = read_plain_data_field<u32>(tr, "st-out-a", dts);
|
|
|
|
st_out_b = read_plain_data_field<u32>(tr, "st-out-b", dts);
|
|
|
|
st_vif_add = read_plain_data_field<u32>(tr, "st-vif-add", dts);
|
|
|
|
st_int_off = read_plain_data_field<u16>(tr, "st-int-off", dts);
|
|
|
|
st_int_scale = read_plain_data_field<u16>(tr, "st-int-scale", dts);
|
|
|
|
effect_count = read_plain_data_field<u32>(tr, "effect-count", dts);
|
|
|
|
blend_target_count = read_plain_data_field<u32>(tr, "blend-target-count", dts);
|
|
|
|
fragment_count = read_plain_data_field<u16>(tr, "fragment-count", dts);
|
|
|
|
tri_count = read_plain_data_field<u16>(tr, "tri-count", dts);
|
|
|
|
matrix_count = read_plain_data_field<u8>(tr, "matrix-count", dts);
|
|
|
|
shader_count = read_plain_data_field<u8>(tr, "shader-count", dts);
|
|
|
|
transform_vertex_count = read_plain_data_field<u16>(tr, "transform-vertex-count", dts);
|
|
|
|
dvert_count = read_plain_data_field<u16>(tr, "dvert-count", dts);
|
|
|
|
one_mat_count = read_plain_data_field<u16>(tr, "one-mat-count", dts);
|
|
|
|
two_mat_count = read_plain_data_field<u16>(tr, "two-mat-count", dts);
|
|
|
|
two_mat_reuse_count = read_plain_data_field<u16>(tr, "two-mat-reuse-count", dts);
|
|
|
|
three_mat_count = read_plain_data_field<u16>(tr, "three-mat-count", dts);
|
|
|
|
three_mat_reuse_count = read_plain_data_field<u16>(tr, "three-mat-reuse-count", dts);
|
|
|
|
shader_upload_count = read_plain_data_field<u8>(tr, "shader-upload-count", dts);
|
|
|
|
matrix_upload_count = read_plain_data_field<u8>(tr, "matrix-upload-count", dts);
|
|
|
|
same_copy_count = read_plain_data_field<u16>(tr, "same-copy-count", dts);
|
|
|
|
cross_copy_count = read_plain_data_field<u16>(tr, "cross-copy-count", dts);
|
|
|
|
num_verts = read_plain_data_field<u16>(tr, "num-verts", dts);
|
|
|
|
longest_edge = read_plain_data_field<float>(tr, "longest-edge", dts);
|
2023-03-08 18:18:35 -05:00
|
|
|
auto fr = get_field_ref(tr, "eye-ctrl", dts);
|
|
|
|
const auto& word = fr.data->words_by_seg.at(fr.seg).at(fr.byte_offset / 4);
|
|
|
|
if (word.kind() == LinkedWord::PTR) {
|
|
|
|
eye_ctrl.emplace();
|
|
|
|
eye_ctrl->from_ref(TypedRef(deref_label(fr), dts.ts.lookup_type("merc-eye-ctrl")), dts);
|
|
|
|
}
|
|
|
|
|
|
|
|
// todo masks
|
2022-05-11 22:53:53 -04:00
|
|
|
envmap_tint = read_plain_data_field<u32>(tr, "envmap-tint", dts);
|
|
|
|
needs_clip = read_plain_data_field<u8>(tr, "needs-clip", dts);
|
|
|
|
use_isometric = read_plain_data_field<u8>(tr, "use-isometric", dts);
|
|
|
|
use_attached_shader = read_plain_data_field<u8>(tr, "use-attached-shader", dts);
|
|
|
|
display_triangles = read_plain_data_field<u8>(tr, "display-triangles", dts);
|
|
|
|
death_vertex_skip = read_plain_data_field<u16>(tr, "death-vertex-skip", dts);
|
|
|
|
death_start_vertex = read_plain_data_field<u16>(tr, "death-start-vertex", dts);
|
|
|
|
death_effect = read_plain_data_field<u32>(tr, "death-effect", dts);
|
|
|
|
use_translucent = read_plain_data_field<u8>(tr, "use-translucent", dts);
|
|
|
|
display_this_fragment = read_plain_data_field<u8>(tr, "display-this-fragment", dts);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string MercCtrlHeader::print() const {
|
|
|
|
std::string result;
|
|
|
|
result += fmt::format(" xyz_scale: {}\n", xyz_scale);
|
|
|
|
result += fmt::format(" st_out_a: 0x{:x}\n", st_out_a);
|
|
|
|
result += fmt::format(" st_out_b: 0x{:x}\n", st_out_b);
|
|
|
|
result += fmt::format(" st_vif_add: 0x{:x}\n", st_vif_add);
|
|
|
|
result += fmt::format(" st_int_off: 0x{:x}\n", st_int_off);
|
|
|
|
result += fmt::format(" st_int_scale: {}\n", st_int_scale);
|
|
|
|
result += fmt::format(" effect_count: {}\n", effect_count);
|
|
|
|
result += fmt::format(" blend_target_count: {}\n", blend_target_count);
|
|
|
|
result += fmt::format(" fragment_count: {}\n", fragment_count);
|
|
|
|
result += fmt::format(" tri_count: {}\n", tri_count);
|
|
|
|
result += fmt::format(" matrix_count: {}\n", matrix_count);
|
|
|
|
result += fmt::format(" shader_count: {}\n", shader_count);
|
|
|
|
result += fmt::format(" transform_vertex_count: {}\n", transform_vertex_count);
|
|
|
|
result += fmt::format(" dvert_count: {}\n", dvert_count);
|
|
|
|
result += fmt::format(" one_mat_count: {}\n", one_mat_count);
|
|
|
|
result += fmt::format(" two_mat_count: {}\n", two_mat_count);
|
|
|
|
result += fmt::format(" two_mat_reuse_count: {}\n", two_mat_reuse_count);
|
|
|
|
result += fmt::format(" three_mat_count: {}\n", three_mat_count);
|
|
|
|
result += fmt::format(" three_mat_reuse_count: {}\n", three_mat_reuse_count);
|
|
|
|
result += fmt::format(" shader_upload_count: {}\n", shader_upload_count);
|
|
|
|
result += fmt::format(" matrix_upload_count: {}\n", matrix_upload_count);
|
|
|
|
result += fmt::format(" same_copy_count: {}\n", same_copy_count);
|
|
|
|
result += fmt::format(" cross_copy_count: {}\n", cross_copy_count);
|
|
|
|
result += fmt::format(" num_verts: {}\n", num_verts);
|
|
|
|
result += fmt::format(" longest_edge: {}\n", longest_edge);
|
|
|
|
result += fmt::format(" envmap_tint: {}\n", envmap_tint);
|
|
|
|
result += fmt::format(" needs_clip: {}\n", needs_clip);
|
|
|
|
result += fmt::format(" use_isometric: {}\n", use_isometric);
|
|
|
|
result += fmt::format(" use_attached_shader: {}\n", use_attached_shader);
|
|
|
|
result += fmt::format(" display_triangles: {}\n", display_triangles);
|
|
|
|
result += fmt::format(" death_vertex_skip: {}\n", death_vertex_skip);
|
|
|
|
result += fmt::format(" death_start_vertex: {}\n", death_start_vertex);
|
|
|
|
result += fmt::format(" death_effect: {}\n", death_effect);
|
|
|
|
result += fmt::format(" use_translucent: {}\n", use_translucent);
|
|
|
|
result += fmt::format(" display_this_fragment: {}\n", display_this_fragment);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypedRef MercFragmentControl::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) {
|
|
|
|
unsigned_four_count = read_plain_data_field<u8>(tr, "unsigned-four-count", dts);
|
|
|
|
lump_four_count = read_plain_data_field<u8>(tr, "lump-four-count", dts);
|
|
|
|
fp_qwc = read_plain_data_field<u8>(tr, "fp-qwc", dts);
|
|
|
|
mat_xfer_count = read_plain_data_field<u8>(tr, "mat-xfer-count", dts);
|
|
|
|
ASSERT(mat_xfer_count < 10);
|
|
|
|
Ref dest_data_ref = get_field_ref(tr, "mat-dest-data", dts);
|
|
|
|
for (u8 i = 0; i < mat_xfer_count; i++) {
|
|
|
|
auto& entry = mat_dest_data.emplace_back();
|
|
|
|
entry.matrix_number = deref_u8(dest_data_ref, i * 2);
|
|
|
|
entry.matrix_dest = deref_u8(dest_data_ref, i * 2 + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
tr.ref.byte_offset += (4 + 2 * mat_dest_data.size());
|
|
|
|
return tr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MercFpHeader::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) {
|
|
|
|
x_add = read_plain_data_field<float>(tr, "x-add", dts);
|
|
|
|
y_add = read_plain_data_field<float>(tr, "y-add", dts);
|
|
|
|
z_add = read_plain_data_field<float>(tr, "z-add", dts);
|
|
|
|
|
|
|
|
shader_cnt = read_plain_data_field<u8>(tr, "shader-cnt", dts);
|
|
|
|
kick_info_offset = read_plain_data_field<u8>(tr, "kick-info-offset", dts);
|
|
|
|
kick_info_step = read_plain_data_field<u8>(tr, "kick-info-step", dts);
|
|
|
|
hword_cnt = read_plain_data_field<u8>(tr, "hword-cnt", dts);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string MercFpHeader::print() const {
|
|
|
|
std::string result;
|
|
|
|
result += fmt::format(" x_add: {}\n", x_add);
|
|
|
|
result += fmt::format(" y_add: {}\n", y_add);
|
|
|
|
result += fmt::format(" z_add: {}\n", z_add);
|
|
|
|
result += fmt::format(" shader_cnt: {}\n", shader_cnt);
|
|
|
|
result += fmt::format(" kick_info_offset: {}\n", kick_info_offset);
|
|
|
|
result += fmt::format(" kick_info_step: {}\n", kick_info_step);
|
|
|
|
result += fmt::format(" hword_cnt: {}\n", hword_cnt);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MercByteHeader::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) {
|
|
|
|
srcdest_off = read_plain_data_field<u8>(tr, "srcdest-off", dts);
|
|
|
|
rgba_off = read_plain_data_field<u8>(tr, "rgba-off", dts);
|
|
|
|
lump_off = read_plain_data_field<u8>(tr, "lump-off", dts);
|
|
|
|
fp_off = read_plain_data_field<u8>(tr, "fp-off", dts);
|
|
|
|
mat1_cnt = read_plain_data_field<u8>(tr, "mat1-cnt", dts);
|
|
|
|
mat2_cnt = read_plain_data_field<u8>(tr, "mat2-cnt", dts);
|
|
|
|
mat3_cnt = read_plain_data_field<u8>(tr, "mat3-cnt", dts);
|
|
|
|
samecopy_cnt = read_plain_data_field<u8>(tr, "samecopy-cnt", dts);
|
|
|
|
crosscopy_cnt = read_plain_data_field<u8>(tr, "crosscopy-cnt", dts);
|
|
|
|
strip_len = read_plain_data_field<u8>(tr, "strip-len", dts);
|
|
|
|
mm_quadword_fp_off = read_plain_data_field<u8>(tr, "mm-quadword-fp-off", dts);
|
|
|
|
mm_quadword_size = read_plain_data_field<u8>(tr, "mm-quadword-size", dts);
|
|
|
|
perc_off = read_plain_data_field<u8>(tr, "perc-off", dts);
|
|
|
|
|
|
|
|
auto ms = get_field_ref(tr, "perc-off", dts);
|
|
|
|
bool got_end = false;
|
|
|
|
for (int i = 0; i < MAT_SLOTS; i++) {
|
|
|
|
ms.byte_offset++;
|
|
|
|
mat_slot[i] = deref_u8(ms, 0);
|
|
|
|
if (mat_slot[i] == 128) {
|
|
|
|
got_end = true;
|
|
|
|
} else {
|
|
|
|
// ASSERT(!got_end);
|
|
|
|
if (got_end) {
|
2022-10-01 11:58:36 -04:00
|
|
|
// lg::print("got something after the end\n"); // todo, should investigate more
|
2022-05-11 22:53:53 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string MercByteHeader::print() const {
|
|
|
|
std::string result;
|
|
|
|
result += fmt::format(" srcdest_off: {}\n", srcdest_off);
|
|
|
|
result += fmt::format(" rgba_off: {}\n", rgba_off);
|
|
|
|
result += fmt::format(" lump_off: {}\n", lump_off);
|
|
|
|
result += fmt::format(" fp_off: {}\n", fp_off);
|
|
|
|
result += fmt::format(" mat1_cnt: {}\n", mat1_cnt);
|
|
|
|
result += fmt::format(" mat2_cnt: {}\n", mat2_cnt);
|
|
|
|
result += fmt::format(" mat3_cnt: {}\n", mat3_cnt);
|
|
|
|
result += fmt::format(" samecopy_cnt: {}\n", samecopy_cnt);
|
|
|
|
result += fmt::format(" crosscopy_cnt: {}\n", crosscopy_cnt);
|
|
|
|
result += fmt::format(" strip_len: {}\n", strip_len);
|
|
|
|
result += fmt::format(" mm_quadword_fp_off: {}\n", mm_quadword_fp_off);
|
|
|
|
result += fmt::format(" mm_quadword_size: {}\n", mm_quadword_size);
|
|
|
|
result += fmt::format(" perc_off: {}\n", perc_off);
|
|
|
|
for (int i = 0; i < MAT_SLOTS; i++) {
|
|
|
|
result += fmt::format(" mat_slot[{}]: {}\n", i, mat_slot[i]);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string MercFragmentControl::print() const {
|
|
|
|
std::string result;
|
|
|
|
result += fmt::format(" unsigned_four_count: {}\n", unsigned_four_count);
|
|
|
|
result += fmt::format(" lump_four_count: {}\n", lump_four_count);
|
|
|
|
result += fmt::format(" fp_qwc: {}\n", fp_qwc);
|
|
|
|
result += fmt::format(" mat_xfer_count: {}\n", mat_xfer_count);
|
|
|
|
for (u8 i = 0; i < mat_xfer_count; i++) {
|
|
|
|
result += fmt::format(" mat[{}] {} -> {}\n", i, mat_dest_data[i].matrix_number,
|
|
|
|
mat_dest_data[i].matrix_dest);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string MercShader::print() const {
|
|
|
|
std::string result;
|
|
|
|
result += fmt::format(" output_offset: {}\n", output_offset);
|
|
|
|
result += fmt::format(" strip_tag: 0x{:x}\n", next_strip_nloop);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-01-22 18:30:31 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-05-11 22:53:53 -04:00
|
|
|
TypedRef MercFragment::from_ref(TypedRef tr,
|
|
|
|
const DecompilerTypeSystem& dts,
|
|
|
|
const MercFragmentControl& control,
|
|
|
|
const MercCtrlHeader& main_control) {
|
2022-10-01 11:58:36 -04:00
|
|
|
// lg::print("frag::from_ref:\n{}\n", control.print());
|
2022-05-11 22:53:53 -04:00
|
|
|
TypedRef byte_hdr(get_field_ref(tr, "header", dts), dts.ts.lookup_type("merc-byte-header"));
|
|
|
|
header.from_ref(byte_hdr, dts);
|
2022-10-01 11:58:36 -04:00
|
|
|
// lg::print("{}\n", header.print());
|
2022-05-11 22:53:53 -04:00
|
|
|
|
|
|
|
// all these offsets are super confusing.
|
|
|
|
// the DMA transfers require source and dest addresses/sized to have alignment of 16 bytes.
|
|
|
|
// so the data is padded.
|
|
|
|
// the transfers increase size by 4x due to VIF unpacking
|
|
|
|
// But transferring this padding exactly would result in up to 63 bytes of wasted space.
|
|
|
|
// so they cheat the destination pointers to be slightly overlapping so the next transfer
|
|
|
|
// overlaps the padding of the previous.
|
|
|
|
// as a result, the "in VU" offsets are different from "in main memory"
|
|
|
|
|
|
|
|
// let's figure it out from bones.gc asm
|
|
|
|
// u4
|
|
|
|
// lbu s0, 0(gp) (fragment.control.unsigned-four-count)
|
|
|
|
// daddiu v0, s0, 3
|
|
|
|
// srl v0, v0, 2
|
|
|
|
// dsll32 s0, v0, 4
|
|
|
|
// daddu t3, t2, s0
|
|
|
|
u32 my_u4_count = ((control.unsigned_four_count + 3) / 4) * 16;
|
2022-10-01 11:58:36 -04:00
|
|
|
// lg::print("my u4: {} ({} qwc)\n", my_u4_count, my_u4_count / 16);
|
2022-05-11 22:53:53 -04:00
|
|
|
for (u32 w = 0; w < my_u4_count / 4; w++) {
|
|
|
|
u32 val = deref_u32(tr.ref, w);
|
|
|
|
memcpy(unsigned_four_including_header.emplace_back().data(), &val, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
// l4
|
|
|
|
// lbu s2, 1(gp)
|
|
|
|
// daddiu s0, s2, 3
|
|
|
|
// srl s0, s0, 2
|
|
|
|
// dsll32 s2, s0, 4
|
|
|
|
u32 my_l4_count = my_u4_count + ((control.lump_four_count + 3) / 4) * 16;
|
2022-10-01 11:58:36 -04:00
|
|
|
// lg::print("my l4: {} ({} qwc)\n", my_l4_count, my_l4_count / 16);
|
2022-05-11 22:53:53 -04:00
|
|
|
// end of lump should align with mm (main memory?) fp off. which
|
|
|
|
// is used for accessing the fp data in main memory.
|
|
|
|
ASSERT(my_l4_count / 16 == header.mm_quadword_fp_off);
|
|
|
|
|
|
|
|
// row.x/y is st-vif-add from the merc-ctrl-header.
|
|
|
|
// row.z = 0x47800000, row.w = 0x4b010000
|
|
|
|
math::Vector<u32, 4> row(main_control.st_vif_add, main_control.st_vif_add, 0x47800000,
|
|
|
|
0x4b010000);
|
|
|
|
for (u32 w = my_u4_count / 4; w < my_l4_count / 4; w++) {
|
|
|
|
ASSERT((w * 4) < header.mm_quadword_fp_off * 16);
|
|
|
|
u32 val = deref_u32(tr.ref, w);
|
|
|
|
|
|
|
|
math::Vector<u8, 4> as_u8s;
|
|
|
|
memcpy(as_u8s.data(), &val, 4);
|
|
|
|
math::Vector<u32, 4> as_u32s = as_u8s.cast<u32>();
|
|
|
|
as_u32s += row;
|
|
|
|
memcpy(lump4_unpacked.emplace_back().data(), as_u32s.data(), 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
// fp header
|
|
|
|
Ref fp_ref = tr.ref;
|
|
|
|
fp_ref.byte_offset += 16 * header.mm_quadword_fp_off;
|
|
|
|
fp_header.from_ref(TypedRef(fp_ref, dts.ts.lookup_type("merc-fp-header")), dts);
|
|
|
|
fp_ref.byte_offset += 16;
|
|
|
|
|
|
|
|
// fp shaders
|
|
|
|
for (int i = 0; i < fp_header.shader_cnt; i++) {
|
2023-01-22 18:30:31 -05:00
|
|
|
bool expected_eop = (i == fp_header.shader_cnt - 1);
|
|
|
|
shaders.push_back(make_shader(fp_ref, expected_eop));
|
2022-05-11 22:53:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
tr.ref.byte_offset += (header.mm_quadword_size) * 16;
|
|
|
|
|
|
|
|
// let's verify the matrix slots here.
|
|
|
|
int used_matrix_slots = 0;
|
|
|
|
for (auto x : header.mat_slot) {
|
|
|
|
if (x) {
|
|
|
|
used_matrix_slots++;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ASSERT(used_matrix_slots == control.mat_xfer_count);
|
|
|
|
for (int i = 0; i < used_matrix_slots; i++) {
|
|
|
|
ASSERT(header.mat_slot[i] == control.mat_dest_data.at(i).matrix_dest);
|
|
|
|
}
|
|
|
|
|
|
|
|
return tr;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string MercFragment::print() const {
|
|
|
|
std::string result;
|
|
|
|
result += fmt::format(" + BYTE-HEADER\n");
|
|
|
|
result += header.print();
|
|
|
|
result += fmt::format(" + FP-HEADER\n");
|
|
|
|
result += fp_header.print();
|
|
|
|
for (const auto& shader : shaders) {
|
|
|
|
result += fmt::format(" + SHADER\n");
|
|
|
|
result += shader.print();
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MercEffect::from_ref(TypedRef tr,
|
|
|
|
const DecompilerTypeSystem& dts,
|
|
|
|
const MercCtrlHeader& main_control) {
|
|
|
|
effect_bits = read_plain_data_field<u8>(tr, "effect-bits", dts);
|
|
|
|
frag_count = read_plain_data_field<u16>(tr, "frag-count", dts);
|
|
|
|
blend_frag_count = read_plain_data_field<u16>(tr, "blend-frag-count", dts);
|
|
|
|
tri_count = read_plain_data_field<u16>(tr, "tri-count", dts);
|
|
|
|
dvert_count = read_plain_data_field<u16>(tr, "dvert-count", dts);
|
2022-09-05 20:29:12 -04:00
|
|
|
auto* type = dynamic_cast<StructureType*>(dts.ts.lookup_type("merc-effect"));
|
|
|
|
Field temp;
|
|
|
|
if (type->lookup_field("envmap-usage", &temp)) {
|
|
|
|
envmap_or_effect_usage = read_plain_data_field<u8>(tr, "envmap-usage", dts);
|
|
|
|
} else {
|
|
|
|
envmap_or_effect_usage = read_plain_data_field<u8>(tr, "effect-usage", dts);
|
|
|
|
}
|
2022-05-11 22:53:53 -04:00
|
|
|
|
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179)
This PR adds a feature to merc2 to update vertices. This will be needed
to efficient do effects like blerc/ripple/texture scroll. It's enabled
for blerc in jak 1 and jak 2, but with a few disclaimers:
- currently we still use the mips2c blerc implementation, which is slow
and has some "jittering" because of integer precision. When porting to
PC, there was an additional synchronization problem because blerc
overwrites the merc data as its being read by the renderers. I _think_
this wasn't an issue on PS2 because the blerc dma is higher priority
than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex
for this on PC to avoid very slight flickering/gaps. This isn't ideal
for performance, but still beats generic by a significant amount in
every place I tested. If you see merc taking 2ms to draw, it is likely
because it is stuck waiting on blerc to finish. This will go away once
blerc itself is ported to C++.
- in jak 1, we end up using generic in some cases where we could use
merc. In particular maia in village3 hut. This will be fixed later once
we can use merc in more places. I don't want to mess with the
merc/generic selection logic when we're hopefully going to get rid of it
soon.
- There is no support for ripple or texture scroll. These use generic on
jak 1, and remain broken on jak 2.
- Like with `emerc`, jak 1 has a toggle to go back to the old behavior
`*blerc-hack*`.
- In most cases, toggling this causes no visual differences. One
exception is Gol's teeth. I believe this is caused by texture coordinate
rounding issues, where generic has an additional float -> int -> float
compared to PC merc. It is very hard to notice so I'm not going to worry
about it.
2023-01-31 18:23:39 -05:00
|
|
|
if (type->lookup_field("texture-index", &temp)) {
|
|
|
|
texture_index = read_plain_data_field<u8>(tr, "texture-index", dts);
|
|
|
|
}
|
|
|
|
|
2022-05-11 22:53:53 -04:00
|
|
|
// do frag-ctrls
|
|
|
|
TypedRef fc(deref_label(get_field_ref(tr, "frag-ctrl", dts)),
|
|
|
|
dts.ts.lookup_type("merc-fragment-control"));
|
|
|
|
for (u32 i = 0; i < frag_count; i++) {
|
|
|
|
fc = frag_ctrl.emplace_back().from_ref(fc, dts);
|
|
|
|
}
|
|
|
|
|
|
|
|
// do actual frags
|
|
|
|
TypedRef f(deref_label(get_field_ref(tr, "frag-geo", dts)), dts.ts.lookup_type("merc-fragment"));
|
|
|
|
for (u32 i = 0; i < frag_count; i++) {
|
|
|
|
f = frag_geo.emplace_back().from_ref(f, dts, frag_ctrl.at(i), main_control);
|
|
|
|
}
|
2023-01-22 18:30:31 -05:00
|
|
|
|
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179)
This PR adds a feature to merc2 to update vertices. This will be needed
to efficient do effects like blerc/ripple/texture scroll. It's enabled
for blerc in jak 1 and jak 2, but with a few disclaimers:
- currently we still use the mips2c blerc implementation, which is slow
and has some "jittering" because of integer precision. When porting to
PC, there was an additional synchronization problem because blerc
overwrites the merc data as its being read by the renderers. I _think_
this wasn't an issue on PS2 because the blerc dma is higher priority
than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex
for this on PC to avoid very slight flickering/gaps. This isn't ideal
for performance, but still beats generic by a significant amount in
every place I tested. If you see merc taking 2ms to draw, it is likely
because it is stuck waiting on blerc to finish. This will go away once
blerc itself is ported to C++.
- in jak 1, we end up using generic in some cases where we could use
merc. In particular maia in village3 hut. This will be fixed later once
we can use merc in more places. I don't want to mess with the
merc/generic selection logic when we're hopefully going to get rid of it
soon.
- There is no support for ripple or texture scroll. These use generic on
jak 1, and remain broken on jak 2.
- Like with `emerc`, jak 1 has a toggle to go back to the old behavior
`*blerc-hack*`.
- In most cases, toggling this causes no visual differences. One
exception is Gol's teeth. I believe this is caused by texture coordinate
rounding issues, where generic has an additional float -> int -> float
compared to PC merc. It is very hard to notice so I'm not going to worry
about it.
2023-01-31 18:23:39 -05:00
|
|
|
// do blend ctrls
|
|
|
|
if (blend_frag_count) {
|
|
|
|
TypedRef bc(deref_label(get_field_ref(tr, "blend-ctrl", dts)),
|
|
|
|
dts.ts.lookup_type("merc-blend-ctrl"));
|
|
|
|
for (u32 i = 0; i < blend_frag_count; i++) {
|
|
|
|
bc = blend_ctrl.emplace_back().from_ref(bc, dts, main_control.blend_target_count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-22 18:30:31 -05:00
|
|
|
// 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<u8>(mei, "shader-offset", dts);
|
|
|
|
if (shader_offset) {
|
|
|
|
Ref r = mei.ref;
|
|
|
|
r.byte_offset += 16 * shader_offset;
|
|
|
|
extra_info.shader = make_shader(r, false);
|
|
|
|
}
|
|
|
|
}
|
2022-05-11 22:53:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string MercEffect::print() {
|
|
|
|
std::string result;
|
|
|
|
result += fmt::format(" effect_bits: {}\n", effect_bits);
|
|
|
|
result += fmt::format(" frag_count: {}\n", frag_count);
|
|
|
|
result += fmt::format(" blend_frag_count: {}\n", blend_frag_count);
|
|
|
|
result += fmt::format(" tri_count: {}\n", tri_count);
|
|
|
|
result += fmt::format(" dvert_count: {}\n", dvert_count);
|
2022-09-05 20:29:12 -04:00
|
|
|
result += fmt::format(" envmap_or_effect_usage: {}\n", envmap_or_effect_usage);
|
2022-05-11 22:53:53 -04:00
|
|
|
|
|
|
|
for (u32 i = 0; i < frag_count; i++) {
|
|
|
|
result += fmt::format(" +FRAGMENT {}\n", i);
|
|
|
|
result += fmt::format(" + CTRL\n");
|
|
|
|
result += frag_ctrl[i].print();
|
|
|
|
result += fmt::format(" + GEO\n");
|
|
|
|
result += frag_geo[i].print();
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MercCtrl::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) {
|
|
|
|
name = read_string_field(tr, "name", dts, false);
|
|
|
|
num_joints = read_plain_data_field<s32>(tr, "num-joints", dts);
|
|
|
|
auto merc_ctrl_header_ref =
|
|
|
|
TypedRef(get_field_ref(tr, "header", dts), dts.ts.lookup_type("merc-ctrl-header"));
|
|
|
|
header.from_ref(merc_ctrl_header_ref, dts);
|
|
|
|
|
|
|
|
auto eff_ref = TypedRef(get_field_ref(tr, "effect", dts), dts.ts.lookup_type("merc-effect"));
|
|
|
|
for (u32 i = 0; i < header.effect_count; i++) {
|
|
|
|
effects.emplace_back().from_ref(eff_ref, dts, header);
|
|
|
|
eff_ref.ref.byte_offset += 32; //
|
|
|
|
}
|
2023-03-08 18:18:35 -05:00
|
|
|
|
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179)
This PR adds a feature to merc2 to update vertices. This will be needed
to efficient do effects like blerc/ripple/texture scroll. It's enabled
for blerc in jak 1 and jak 2, but with a few disclaimers:
- currently we still use the mips2c blerc implementation, which is slow
and has some "jittering" because of integer precision. When porting to
PC, there was an additional synchronization problem because blerc
overwrites the merc data as its being read by the renderers. I _think_
this wasn't an issue on PS2 because the blerc dma is higher priority
than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex
for this on PC to avoid very slight flickering/gaps. This isn't ideal
for performance, but still beats generic by a significant amount in
every place I tested. If you see merc taking 2ms to draw, it is likely
because it is stuck waiting on blerc to finish. This will go away once
blerc itself is ported to C++.
- in jak 1, we end up using generic in some cases where we could use
merc. In particular maia in village3 hut. This will be fixed later once
we can use merc in more places. I don't want to mess with the
merc/generic selection logic when we're hopefully going to get rid of it
soon.
- There is no support for ripple or texture scroll. These use generic on
jak 1, and remain broken on jak 2.
- Like with `emerc`, jak 1 has a toggle to go back to the old behavior
`*blerc-hack*`.
- In most cases, toggling this causes no visual differences. One
exception is Gol's teeth. I believe this is caused by texture coordinate
rounding issues, where generic has an additional float -> int -> float
compared to PC merc. It is very hard to notice so I'm not going to worry
about it.
2023-01-31 18:23:39 -05:00
|
|
|
// debug_print_blerc();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MercCtrl::debug_print_blerc() {
|
|
|
|
int total_verts = 0;
|
|
|
|
int blerc_verts = 0;
|
|
|
|
int total_frags = 0;
|
|
|
|
int blerc_frags = 0;
|
|
|
|
int total_effects = effects.size();
|
|
|
|
int blerc_effects = 0;
|
|
|
|
|
|
|
|
for (auto& effect : effects) {
|
|
|
|
bool effect_has_blerc = false;
|
|
|
|
for (size_t frag_idx = 0; frag_idx < effect.frag_count; frag_idx++) {
|
|
|
|
total_frags++;
|
|
|
|
auto& fc = effect.frag_ctrl.at(frag_idx);
|
|
|
|
total_verts += fc.lump_four_count;
|
|
|
|
|
|
|
|
if (frag_idx < effect.blend_ctrl.size()) {
|
|
|
|
auto& bfc = effect.blend_ctrl.at(frag_idx);
|
|
|
|
if (bfc.blend_vtx_count) {
|
|
|
|
effect_has_blerc = true;
|
|
|
|
blerc_frags++;
|
|
|
|
blerc_verts += fc.lump_four_count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (effect_has_blerc) {
|
|
|
|
blerc_effects++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (blerc_effects) {
|
|
|
|
fmt::print("BLERC: {}, {}/{} e, {}/{} f, {}/{} v\n", name, blerc_effects, total_effects,
|
|
|
|
blerc_frags, total_frags, blerc_verts, total_verts);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TypedRef MercBlendCtrl::from_ref(TypedRef tr,
|
|
|
|
const DecompilerTypeSystem& dts,
|
|
|
|
int blend_target_count) {
|
|
|
|
blend_vtx_count = read_plain_data_field<u8>(tr, "blend-vtx-count", dts);
|
|
|
|
nonzero_index_count = read_plain_data_field<u8>(tr, "nonzero-index-count", dts);
|
|
|
|
tr.ref.byte_offset += 2;
|
|
|
|
for (int i = 0; i < blend_target_count; i++) {
|
|
|
|
bt_index.push_back(deref_u8(tr.ref, 0));
|
|
|
|
tr.ref.byte_offset += 1;
|
|
|
|
}
|
|
|
|
return tr;
|
2022-05-11 22:53:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string MercCtrl::print() {
|
|
|
|
std::string result;
|
|
|
|
result += fmt::format("name: {}\n", name);
|
|
|
|
result += fmt::format("num_joints: {}\n", num_joints);
|
|
|
|
result += "+ HEADER\n";
|
|
|
|
result += header.print();
|
|
|
|
result += "\n";
|
|
|
|
for (auto& eff : effects) {
|
|
|
|
result += fmt::format("+ EFFECT\n{}\n", eff.print());
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-10-01 11:58:36 -04:00
|
|
|
} // namespace decompiler
|