Files
jak-project/game/graphics/opengl_renderer/SpriteRenderer.cpp
T
water111 7292cb5765 tfrag improvements (#970)
* tfrag improvements

* cleanup

* one last fix

* clang
2021-11-15 20:07:10 -05:00

1085 lines
40 KiB
C++

#include "third-party/fmt/core.h"
#include "third-party/imgui/imgui.h"
#include "SpriteRenderer.h"
#include "game/graphics/opengl_renderer/dma_helpers.h"
namespace {
/*!
* Does the next DMA transfer look like it could be the start of a 2D group?
*/
bool looks_like_2d_chunk_start(const DmaFollower& dma) {
return dma.current_tag().qwc == 1 && dma.current_tag().kind == DmaTag::Kind::CNT;
}
/*!
* Read the header. Asserts if it's bad.
* Returns the number of sprites.
* Advances 1 dma transfer
*/
u32 process_sprite_chunk_header(DmaFollower& dma) {
auto transfer = dma.read_and_advance();
// note that flg = true, this should use double buffering
bool ok = verify_unpack_with_stcycl(transfer, VifCode::Kind::UNPACK_V4_32, 4, 4, 1,
SpriteDataMem::Header, false, true);
assert(ok);
u32 header[4];
memcpy(header, transfer.data, 16);
assert(header[0] <= SpriteRenderer::SPRITES_PER_CHUNK);
return header[0];
}
} // namespace
SpriteRenderer::SpriteRenderer(const std::string& name, BucketId my_id)
: BucketRenderer(name, my_id),
m_sprite_renderer(fmt::format("{}.sprites", name),
my_id,
100,
DirectRenderer::Mode::SPRITE_CPU),
m_direct_renderer(fmt::format("{}.direct", name), my_id, 100, DirectRenderer::Mode::NORMAL) {}
/*!
* Run the sprite distorter. Currently nothing uses sprite-distorter so this just skips through
* the table upload stuff that runs every frame, even if there are no sprites.
*/
void SpriteRenderer::render_distorter(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
// Next thing should be the sprite-distorter setup
m_direct_renderer.reset_state();
while (dma.current_tag().qwc != 7) {
auto direct_data = dma.read_and_advance();
m_direct_renderer.render_vif(direct_data.vif0(), direct_data.vif1(), direct_data.data,
direct_data.size_bytes, render_state, prof);
}
m_direct_renderer.flush_pending(render_state, prof);
auto sprite_distorter_direct_setup = dma.read_and_advance();
assert(sprite_distorter_direct_setup.vifcode0().kind == VifCode::Kind::NOP);
assert(sprite_distorter_direct_setup.vifcode1().kind == VifCode::Kind::DIRECT);
assert(sprite_distorter_direct_setup.vifcode1().immediate == 7);
memcpy(m_sprite_distorter_setup, sprite_distorter_direct_setup.data, 7 * 16);
// Next thing should be the sprite-distorter tables
auto sprite_distorter_tables = dma.read_and_advance();
assert(sprite_distorter_tables.size_bytes == 0x8b * 16);
assert(sprite_distorter_tables.vifcode0().kind == VifCode::Kind::STCYCL);
VifCodeStcycl distorter_table_transfer(sprite_distorter_tables.vifcode0());
assert(distorter_table_transfer.cl == 4);
assert(distorter_table_transfer.wl == 4);
// TODO: check unpack cmd (vif1)
// TODO: do something with the table
// next would be the program, but we don't have it.
// TODO: next is the sprite-distorter (currently not used)
}
/*!
* Handle DMA data that does the per-frame setup.
* This should get the dma chain immediately after the call to sprite-draw-distorters.
* It ends right before the sprite-add-matrix-data for the 3d's
*/
void SpriteRenderer::handle_sprite_frame_setup(DmaFollower& dma) {
// first is some direct data
auto direct_data = dma.read_and_advance();
assert(direct_data.size_bytes == 3 * 16);
memcpy(m_sprite_direct_setup, direct_data.data, 3 * 16);
// next would be the program, but it's 0 size on the PC and isn't sent.
// next is the "frame data"
auto frame_data = dma.read_and_advance();
assert(frame_data.size_bytes == (int)sizeof(SpriteFrameData)); // very cool
assert(frame_data.vifcode0().kind == VifCode::Kind::STCYCL);
VifCodeStcycl frame_data_stcycl(frame_data.vifcode0());
assert(frame_data_stcycl.cl == 4);
assert(frame_data_stcycl.wl == 4);
assert(frame_data.vifcode1().kind == VifCode::Kind::UNPACK_V4_32);
VifCodeUnpack frame_data_unpack(frame_data.vifcode1());
assert(frame_data_unpack.addr_qw == SpriteDataMem::FrameData);
assert(frame_data_unpack.use_tops_flag == false);
memcpy(&m_frame_data, frame_data.data, sizeof(SpriteFrameData));
// next, a MSCALF.
auto mscalf = dma.read_and_advance();
assert(mscalf.size_bytes == 0);
assert(mscalf.vifcode0().kind == VifCode::Kind::MSCALF);
assert(mscalf.vifcode0().immediate == SpriteProgMem::Init);
assert(mscalf.vifcode1().kind == VifCode::Kind::FLUSHE);
// next base and offset
auto base_offset = dma.read_and_advance();
assert(base_offset.size_bytes == 0);
assert(base_offset.vifcode0().kind == VifCode::Kind::BASE);
assert(base_offset.vifcode0().immediate == SpriteDataMem::Buffer0);
assert(base_offset.vifcode1().kind == VifCode::Kind::OFFSET);
assert(base_offset.vifcode1().immediate == SpriteDataMem::Buffer1);
}
void SpriteRenderer::render_3d(DmaFollower& dma) {
// one time matrix data
auto matrix_data = dma.read_and_advance();
assert(matrix_data.size_bytes == sizeof(Sprite3DMatrixData));
bool unpack_ok = verify_unpack_with_stcycl(matrix_data, VifCode::Kind::UNPACK_V4_32, 4, 4, 5,
SpriteDataMem::Matrix, false, false);
assert(unpack_ok);
static_assert(sizeof(m_3d_matrix_data) == 5 * 16);
memcpy(&m_3d_matrix_data, matrix_data.data, sizeof(m_3d_matrix_data));
// TODO
}
void SpriteRenderer::render_2d_group0(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
(void)dma;
while (looks_like_2d_chunk_start(dma)) {
m_debug_stats.blocks_2d_grp0++;
// 4 packets per chunk
// first is the header
u32 sprite_count = process_sprite_chunk_header(dma);
m_debug_stats.count_2d_grp0 += sprite_count;
// second is the vector data
u32 expected_vec_size = sizeof(SpriteVecData2d) * sprite_count;
auto vec_data = dma.read_and_advance();
assert(expected_vec_size <= sizeof(m_vec_data_2d));
unpack_to_no_stcycl(&m_vec_data_2d, vec_data, VifCode::Kind::UNPACK_V4_32, expected_vec_size,
SpriteDataMem::Vector, false, true);
// third is the adgif data
u32 expected_adgif_size = sizeof(AdGifData) * sprite_count;
auto adgif_data = dma.read_and_advance();
assert(expected_adgif_size <= sizeof(m_adgif));
unpack_to_no_stcycl(&m_adgif, adgif_data, VifCode::Kind::UNPACK_V4_32, expected_adgif_size,
SpriteDataMem::Adgif, false, true);
// fourth is the actual run!!!!!
auto run = dma.read_and_advance();
assert(run.vifcode0().kind == VifCode::Kind::NOP);
assert(run.vifcode1().kind == VifCode::Kind::MSCAL);
// HACK: this renderers 3D sprites with the 2D renderer. amazingly, it almost works.
// assert(run.vifcode1().immediate == SpriteProgMem::Sprites2dGrp0);
if (m_enabled) {
do_2d_group0_block_cpu(sprite_count, render_state, prof);
}
}
}
void SpriteRenderer::render_fake_shadow(DmaFollower& dma) {
// TODO
// nop + flushe
auto nop_flushe = dma.read_and_advance();
assert(nop_flushe.vifcode0().kind == VifCode::Kind::NOP);
assert(nop_flushe.vifcode1().kind == VifCode::Kind::FLUSHE);
}
/*!
* Handle DMA data for group1 2d's (HUD)
*/
void SpriteRenderer::render_2d_group1(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
// one time matrix data upload
auto mat_upload = dma.read_and_advance();
bool mat_ok = verify_unpack_with_stcycl(mat_upload, VifCode::Kind::UNPACK_V4_32, 4, 4, 80,
SpriteDataMem::Matrix, false, false);
assert(mat_ok);
assert(mat_upload.size_bytes == sizeof(m_hud_matrix_data));
memcpy(&m_hud_matrix_data, mat_upload.data, sizeof(m_hud_matrix_data));
// loop through chunks.
while (looks_like_2d_chunk_start(dma)) {
m_debug_stats.blocks_2d_grp1++;
// 4 packets per chunk
// first is the header
u32 sprite_count = process_sprite_chunk_header(dma);
m_debug_stats.count_2d_grp1 += sprite_count;
// second is the vector data
u32 expected_vec_size = sizeof(SpriteVecData2d) * sprite_count;
auto vec_data = dma.read_and_advance();
assert(expected_vec_size <= sizeof(m_vec_data_2d));
unpack_to_no_stcycl(&m_vec_data_2d, vec_data, VifCode::Kind::UNPACK_V4_32, expected_vec_size,
SpriteDataMem::Vector, false, true);
// third is the adgif data
u32 expected_adgif_size = sizeof(AdGifData) * sprite_count;
auto adgif_data = dma.read_and_advance();
assert(expected_adgif_size <= sizeof(m_adgif));
unpack_to_no_stcycl(&m_adgif, adgif_data, VifCode::Kind::UNPACK_V4_32, expected_adgif_size,
SpriteDataMem::Adgif, false, true);
// fourth is the actual run!!!!!
auto run = dma.read_and_advance();
assert(run.vifcode0().kind == VifCode::Kind::NOP);
assert(run.vifcode1().kind == VifCode::Kind::MSCAL);
assert(run.vifcode1().immediate == SpriteProgMem::Sprites2dHud);
if (m_enabled) {
do_2d_group1_block_cpu(sprite_count, render_state, prof);
}
}
}
void SpriteRenderer::render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
m_debug_stats = {};
// First thing should be a NEXT with two nops. this is a jump from buckets to sprite data
auto data0 = dma.read_and_advance();
assert(data0.vif1() == 0);
assert(data0.vif0() == 0);
assert(data0.size_bytes == 0);
if (dma.current_tag().kind == DmaTag::Kind::CALL) {
// sprite renderer didn't run, let's just get out of here.
for (int i = 0; i < 4; i++) {
dma.read_and_advance();
}
assert(dma.current_tag_offset() == render_state->next_bucket);
return;
}
// First is the distorter
{
auto child = prof.make_scoped_child("distorter");
render_distorter(dma, render_state, child);
}
// next, sprite frame setup.
handle_sprite_frame_setup(dma);
// 3d sprites
render_3d(dma);
// 2d draw
m_sprite_renderer.reset_state();
{
auto child = prof.make_scoped_child("2d-group0");
render_2d_group0(dma, render_state, child);
}
// shadow draw
render_fake_shadow(dma);
// 2d draw (HUD)
{
auto child = prof.make_scoped_child("2d-group1");
render_2d_group1(dma, render_state, child);
m_sprite_renderer.flush_pending(render_state, child);
}
// TODO finish this up.
// fmt::print("next bucket is 0x{}\n", render_state->next_bucket);
while (dma.current_tag_offset() != render_state->next_bucket) {
// auto tag = dma.current_tag();
// fmt::print("@ 0x{:x} tag: {}", dma.current_tag_offset(), tag.print());
auto data = dma.read_and_advance();
VifCode code(data.vif0());
// fmt::print(" vif: {}\n", code.print());
if (code.kind == VifCode::Kind::NOP) {
// fmt::print(" vif: {}\n", VifCode(data.vif1()).print());
}
}
}
void SpriteRenderer::draw_debug_window() {
ImGui::Separator();
ImGui::Text("2D Group 0 (World) blocks: %d sprites: %d", m_debug_stats.blocks_2d_grp0,
m_debug_stats.count_2d_grp0);
ImGui::Text("2D Group 1 (HUD) blocks: %d sprites: %d", m_debug_stats.blocks_2d_grp1,
m_debug_stats.count_2d_grp1);
ImGui::Checkbox("Extra Debug", &m_extra_debug);
if (ImGui::TreeNode("direct")) {
m_sprite_renderer.draw_debug_window();
ImGui::TreePop();
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Render (for real)
namespace {
Vector4f matrix_transform(const Matrix4f& mat, const Vector4f& pt) {
// mulaw.xyzw ACC, vf28, vf00
// maddax.xyzw ACC, vf25, vf01
// madday.xyzw ACC, vf26, vf01
// maddz.xyzw vf02, vf27, vf01
return mat.col(3) + (mat.col(0) * pt[0]) + (mat.col(1) * pt[1]) + (mat.col(2) * pt[2]);
}
bool clip_xyz_plus_minus(const Vector4f& pt) {
float pw = std::abs(pt.w());
float mw = -pw;
for (int i = 0; i < 3; i++) {
if (pt[i] > pw) {
return true;
}
if (pt[i] < mw) {
return true;
}
}
return false;
}
void imgui_vec(const Vector4f& vec, const char* name = nullptr, int indent = 0) {
std::string spacing(indent, ' ');
if (name) {
ImGui::Text("%s%s: %f, %f, %f, %f", spacing.c_str(), name, vec.x(), vec.y(), vec.z(), vec.w());
} else {
ImGui::Text("%s%f, %f, %f, %f", spacing.c_str(), vec.x(), vec.y(), vec.z(), vec.w());
}
}
} // namespace
/*!
* Render the sprites!
* This is a somewhat inefficient way to do it:
* The VU program is (poorly) translated to C, then the gs packet is sent to a DirectRenderer.
* In the future we should make a sprite-specific renderer which would have some benefits:
* - do this math on the GPU
* - special case the primitive buffer stuff
*/
void SpriteRenderer::do_2d_group1_block_cpu(u32 count,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
if (m_extra_debug) {
ImGui::Begin("Sprite Extra Debug 2d_1");
}
// set up double buffering
// xtop vi02 | nop
// nop | nop
// load sprite count from header
// vi04 = count
// ilwr.x vi04, vi02 | nop
// vi02 = m_vec_data_2d
// iaddi vi02, vi02, 0x1 | nop
// vi03 = m_adgif
// iaddiu vi03, vi02, 0x90 | nop
// this VU program uses "software pipelining"
// it's a little bit tricky to use software pipelining in a case like
// this where sometimes you want to reject a sprite entirely and jump ahead
// so sometimes they reset back to L7 on rejection.
// The approach in this translation is to assume we loop back to L7 every time
// and not worry about the pipeline stuff that shows up in L8 and on.
// you can enter from L7 at anytime, they are not assumed to only run on the first go.
// (though if their implementation has bugs we will not replicate them correctly...)
Matrix4f camera_matrix = m_hud_matrix_data.matrix; // vf25, vf26, vf27, vf28
for (u32 sprite_idx = 0; sprite_idx < count; sprite_idx++) {
if (m_extra_debug) {
ImGui::Text("Sprite: %d", sprite_idx);
}
SpriteHud2DPacket packet;
memset(&packet, 0, sizeof(packet));
// L7 (prologue, and early abort)
// ilw.y vi08, 1(vi02) | nop vi08 = matrix
u32 offset_selector = m_vec_data_2d[sprite_idx].matrix();
// moved this out of the loop.
// lq.xyzw vf25, 900(vi00) | nop vf25 = cam_mat
// lq.xyzw vf26, 901(vi00) | nop
// lq.xyzw vf27, 902(vi00) | nop
// lq.xyzw vf28, 903(vi00) | nop
// lq.xyzw vf30, 904(vi08) | nop vf30 = hvdf_offset
// vf30
Vector4f hvdf_offset = offset_selector == 0 ? m_hud_matrix_data.hvdf_offset
: m_hud_matrix_data.user_hvdf[offset_selector - 1];
// lqi.xyzw vf01, vi02 | nop
Vector4f pos_vf01 = m_vec_data_2d[sprite_idx].xyz_sx;
if (m_extra_debug) {
imgui_vec(pos_vf01, "POS", 2);
}
// lqi.xyzw vf05, vi02 | nop
Vector4f flags_vf05 = m_vec_data_2d[sprite_idx].flag_rot_sy;
// lqi.xyzw vf11, vi02 | nop
Vector4f color_vf11 = m_vec_data_2d[sprite_idx].rgba;
// multiplications from the right column
Vector4f transformed_pos_vf02 = matrix_transform(camera_matrix, pos_vf01);
Vector4f scales_vf01 = pos_vf01; // now used for something else.
// lq.xyzw vf12, 1020(vi00) | mulaw.xyzw ACC, vf28, vf00
// vf12 is fog consts
Vector4f fog_consts_vf12(m_frame_data.fog_min, m_frame_data.fog_max, m_frame_data.max_scale,
m_frame_data.bonus);
// ilw.y vi08, 1(vi02) | maddax.xyzw ACC, vf25, vf01
// load offset selector for the next round.
// nop | madday.xyzw ACC, vf26, vf01
// nop | maddz.xyzw vf02, vf27, vf01
// move.w vf05, vf00 | addw.z vf01, vf00, vf05
// scales_vf01.z = sy
scales_vf01.z() = flags_vf05.w(); // start building the scale vector
flags_vf05.w() = 1.f; // what are we building in flags right now??
// nop | nop
// div Q, vf31.x, vf02.w | muly.z vf05, vf05, vf31
float Q = m_frame_data.pfog0 / transformed_pos_vf02.w();
flags_vf05.z() *= m_frame_data.deg_to_rad;
// nop | mul.xyzw vf03, vf02, vf29
Vector4f scaled_pos_vf03 = transformed_pos_vf02.elementwise_multiply(m_frame_data.hmge_scale);
// nop | nop
// nop | nop
// nop | mulz.z vf04, vf05, vf05 (ts)
// fmt::print("rot is {} degrees\n", flags_vf05.z() * 360.0 / (2.0 * M_PI));
// the load is for rotation stuff,
// lq.xyzw vf14, 1001(vi00) | clipw.xyz vf03, vf03 (used for fcand)
// iaddi vi06, vi00, 0x1 | adda.xyzw ACC, vf11, vf11 (used for fmand)
// upcoming fcand with 0x3f, that checks all of them.
bool fcand_result = clip_xyz_plus_minus(scaled_pos_vf03);
bool fmand_result = color_vf11.w() == 0; // (really w+w, but I don't think it matters?)
// L8:
// xgkick double buffer setup
// ior vi05, vi15, vi00 | mul.zw vf01, vf01, Q
scales_vf01.z() *= Q; // sy
scales_vf01.w() *= Q; // sx
// lq.xyzw vf06, 998(vi00) | mulz.xyzw vf15, vf05, vf04 (ts)
auto adgif_vf06 = m_frame_data.adgif_giftag;
// lq.xyzw vf14, 1002(vi00) ts| mula.xyzw ACC, vf05, vf14 (ts)
// fmand vi01, vi06 | mul.xyz vf02, vf02, Q
transformed_pos_vf02.x() *= Q;
transformed_pos_vf02.y() *= Q;
transformed_pos_vf02.z() *= Q;
// if (m_extra_debug) {
// imgui_vec(transformed_pos_vf02, "scaled xf");
// }
// ibne vi00, vi01, L10 | addz.x vf01, vf00, vf01
scales_vf01.x() = scales_vf01.z(); // = sy
if (fmand_result) {
if (m_extra_debug) {
ImGui::TextColored(ImVec4(0.8, 0.2, 0.2, 1.0), "fmand reject");
ImGui::Separator();
}
continue; // reject!
}
// lqi.xyzw vf07, vi03 | mulz.xyzw vf16, vf15, vf04 (ts)
// vf07 is first use adgif
// lq.xyzw vf14, 1003(vi00) | madda.xyzw ACC, vf15, vf14 (ts both)
// lqi.xyzw vf08, vi03 | add.xyzw vf10, vf02, vf30
// vf08 is second user adgif
Vector4f offset_pos_vf10 = transformed_pos_vf02 + hvdf_offset;
// if (m_extra_debug) {
// ImGui::Text("sel %d", offset_selector);
// //ImGui::Text("hvdf off z: %f tf/w z: %f", hvdf_offset.z(), transformed_pos_vf02.z());
// imgui_vec(hvdf_offset, "hvdf");
// imgui_vec(transformed_pos_vf02, "tf'd");
// }
// lqi.xyzw vf09, vi03 | mulw.x vf01, vf01, vf01
// vf09 is third user adgif
scales_vf01.x() *= scales_vf01.w(); // x = sx * sy
// sqi.xyzw vf06, vi05 | mulz.xyzw vf15, vf16, vf04 (ts)
// FIRST ADGIF IS adgif_vf06
packet.adgif_giftag = adgif_vf06;
// lq.xyzw vf14, 1004(vi00) | madda.xyzw ACC, vf16, vf14 (ts both)
// sqi.xyzw vf07, vi05 | maxx.w vf10, vf10, vf12
// SECOND ADGIF is first user
// just do all 5 now.
packet.user_adgif = m_adgif[sprite_idx];
offset_pos_vf10.w() = std::max(offset_pos_vf10.w(), m_frame_data.fog_max);
// sqi.xyzw vf08, vi05 | maxz.zw vf01, vf01, vf31
// THIRD ADGIF is second user
scales_vf01.z() = std::max(scales_vf01.z(), m_frame_data.min_scale);
scales_vf01.w() = std::max(scales_vf01.w(), m_frame_data.min_scale);
// sqi.xyzw vf09, vi05 | mulz.xyzw vf16, vf15, vf04 (ts)
// FOURTH ADGIF is third user
// lq.xyzw vf14, 1005(vi00) | madda.xyzw ACC, vf15, vf14 (ts both)
// lqi.xyzw vf06, vi03 | mulw.x vf01, vf01, vf31
// vf06 is fourth user adgif
scales_vf01.x() *= m_frame_data.inv_area; // x = sx * sy * inv_area (area ratio)
// lqi.xyzw vf07, vi03 | miniy.w vf10, vf10, vf12
// vf07 is fifth user adgif
offset_pos_vf10.w() = std::min(offset_pos_vf10.w(), m_frame_data.fog_min);
// lq.xyzw vf08, 1000(vi00) | nop
// vf08 is 2d giftag 2
// ilw.x vi07, -2(vi02) | madd.xyzw vf05, vf16, vf14
auto flag_vi07 = m_vec_data_2d[sprite_idx].flag();
Vector4f vf05_sincos(0, 0, std::sin(flags_vf05.z()), std::cos(flags_vf05.z()));
// lq.xyzw vf30, 904(vi08) | nop
// pipline
// lqi.xyzw vf23, vi02 | miniw.x vf01, vf01, vf00
// pipeline
scales_vf01.x() = std::min(scales_vf01.x(), 1.f);
// lqi.xyzw vf24, vi02 | mulx.w vf11, vf11, vf01
// pipeline
color_vf11.w() *= scales_vf01.x(); // is this right? doesn't this stall??
// fcand vi01, 0x3f | mulaw.xyzw ACC, vf28, vf00
// already computed pipeline
// lq.xyzw vf17, 1006(vi00) | maddax.xyzw ACC, vf25, vf23 (pipeline)
Vector4f basis_x_vf17 = m_frame_data.basis_x;
// lq.xyzw vf18, 1007(vi00) | madday.xyzw ACC, vf26, vf23 (pipeline)
Vector4f basis_y_vf18 = m_frame_data.basis_y;
assert(flag_vi07 == 0);
Vector4f* xy_array = m_frame_data.xy_array + flag_vi07;
// lq.xyzw vf19, 980(vi07) | ftoi0.xyzw vf11, vf11
Vector4f xy0_vf19 = xy_array[0];
math::Vector<s32, 4> color_integer_vf11 = color_vf11.cast<s32>();
// lq.xyzw vf20, 981(vi07) | maddz.xyzw vf02, vf27, vf23 (pipeline)
Vector4f xy1_vf20 = xy_array[1];
// lq.xyzw vf21, 982(vi07) | mulaw.xyzw ACC, vf17, vf05
Vector4f xy2_vf21 = xy_array[2];
Vector4f acc = basis_x_vf17 * vf05_sincos.w();
// lq.xyzw vf22, 983(vi07) | msubz.xyzw vf12, vf18, vf05
Vector4f xy3_vf22 = xy_array[3];
Vector4f vf12_rotated = acc - (basis_y_vf18 * vf05_sincos.z());
// sq.xyzw vf11, 3(vi05) | mulaz.xyzw ACC, vf17, vf05
// EIGHTH is color integer
packet.color = color_integer_vf11;
acc = basis_x_vf17 * vf05_sincos.z();
// lqi.xyzw vf11, vi02 | maddw.xyzw vf13, vf18, vf05
// (pipeline)
Vector4f vf13_rotated_trans = acc + basis_y_vf18 * vf05_sincos.w();
// move.w vf24, vf00 | addw.z vf23, vf00, vf24 (pipeline both)
// div Q, vf31.x, vf02.w | mulw.xyzw vf12, vf12, vf01
// (pipeline)
vf12_rotated *= scales_vf01.w();
// ibne vi00, vi01, L9 | muly.z vf24, vf24, vf31 (pipeline)
if (fcand_result) {
if (m_extra_debug) {
ImGui::TextColored(ImVec4(0.8, 0.2, 0.2, 1.0), "fcand reject");
ImGui::Separator();
}
continue; // reject (could move earlier)
}
// ilw.y vi08, 1(vi02) | mulz.xyzw vf13, vf13, vf01
// (pipeline)
vf13_rotated_trans *= scales_vf01.z();
// LEFT OFF HERE!
// sqi.xyzw vf06, vi05 | mul.xyzw vf03, vf02, vf29
// FIFTH is fourth user
// sqi.xyzw vf07, vi05 | mulaw.xyzw ACC, vf10, vf00
// SIXTH is fifth user
acc = offset_pos_vf10;
// sqi.xyzw vf08, vi05 | maddax.xyzw ACC, vf12, vf19
// SEVENTH is giftag2
packet.sprite_giftag = m_frame_data.sprite_2d_giftag2;
acc += vf12_rotated * xy0_vf19.x();
// lq.xyzw vf06, 988(vi00) | maddy.xyzw vf19, vf13, vf19
Vector4f st0_vf06 = m_frame_data.st_array[0];
xy0_vf19 = acc + vf13_rotated_trans * xy0_vf19.y();
// lq.xyzw vf07, 989(vi00) | mulaw.xyzw ACC, vf10, vf00
Vector4f st1_vf07 = m_frame_data.st_array[1];
acc = offset_pos_vf10;
// lq.xyzw vf08, 990(vi00) | maddax.xyzw ACC, vf12, vf20
Vector4f st2_vf08 = m_frame_data.st_array[2];
acc += vf12_rotated * xy1_vf20.x();
// lq.xyzw vf09, 991(vi00) | maddy.xyzw vf20, vf13, vf20
Vector4f st3_vf09 = m_frame_data.st_array[3];
xy1_vf20 = acc + vf13_rotated_trans * xy1_vf20.y();
// sq.xyzw vf06, 1(vi05) | mulaw.xyzw ACC, vf10, vf00
// NINTH is st0
packet.st0 = st0_vf06;
acc = offset_pos_vf10;
// sq.xyzw vf07, 3(vi05) | maddax.xyzw ACC, vf12, vf21
// ELEVEN is st1
packet.st1 = st1_vf07;
acc += vf12_rotated * xy2_vf21.x();
// sq.xyzw vf08, 5(vi05) | maddy.xyzw vf21, vf13, vf21
// THIRTEEN is st2
packet.st2 = st2_vf08;
xy2_vf21 = acc + vf13_rotated_trans * xy2_vf21.y();
// sq.xyzw vf09, 7(vi05) | mulaw.xyzw ACC, vf10, vf00
// FIFTEEN is st3
packet.st3 = st3_vf09;
acc = offset_pos_vf10;
// nop | maddax.xyzw ACC, vf12, vf22
acc += vf12_rotated * xy3_vf22.x();
// nop | maddy.xyzw vf22, vf13, vf22
xy3_vf22 = acc + vf13_rotated_trans * xy3_vf22.y();
// lq.xyzw vf12, 1020(vi00) | ftoi4.xyzw vf19, vf19
// (pipeline)
auto xy0_vf19_int = (xy0_vf19 * 16.f).cast<s32>();
// lq.xyzw vf14, 1001(vi00) | ftoi4.xyzw vf20, vf20
// (pipeline)
auto xy1_vf20_int = (xy1_vf20 * 16.f).cast<s32>();
// move.xyzw vf05, vf24 | ftoi4.xyzw vf21, vf21
// (pipeline)
auto xy2_vf21_int = (xy2_vf21 * 16.f).cast<s32>();
// move.xyzw vf01, vf23 | ftoi4.xyzw vf22, vf22
// (pipeline)
auto xy3_vf22_int = (xy3_vf22 * 16.f).cast<s32>();
if (m_extra_debug) {
u32 zi = xy3_vf22_int.z() >> 4;
ImGui::Text("z (int): 0x%08x %s", zi, zi >= (1 << 24) ? "bad" : "");
ImGui::Text("z (flt): %f", (double)(((u32)zi) << 8) / UINT32_MAX);
}
// sq.xyzw vf19, 2(vi05) | mulz.z vf04, vf24, vf24 (pipeline)
// TENTH is xy0int
packet.xy0 = xy0_vf19_int;
// sq.xyzw vf20, 4(vi05) | clipw.xyz vf03, vf03 (pipeline)
// TWELVE is xy1int
packet.xy1 = xy1_vf20_int;
// sq.xyzw vf21, 6(vi05) | nop
// FOURTEEN is xy2int
packet.xy2 = xy2_vf21_int;
// sq.xyzw vf22, 8(vi05) | nop
// SIXTEEN is xy3int
packet.xy3 = xy3_vf22_int;
m_sprite_renderer.render_gif((const u8*)&packet, sizeof(packet), render_state, prof);
if (m_extra_debug) {
imgui_vec(vf12_rotated, "vf12", 2);
imgui_vec(vf13_rotated_trans, "vf13", 2);
ImGui::Separator();
}
// xgkick vi15 | nop
// iaddi vi04, vi04, -0x1 | nop
// iaddiu vi01, vi00, 0x672 | nop
// ibne vi00, vi04, L8 | nop
// isub vi15, vi01, vi15 | adda.xyzw ACC, vf11, vf11
// nop | nop :e
// nop | nop
// L9:
// iaddi vi04, vi04, -0x1 | nop
// iaddi vi02, vi02, -0x3 | nop
// ibne vi00, vi04, L7 | nop
// nop | nop
// nop | nop :e
// nop | nop
// L10:
// iaddi vi04, vi04, -0x1 | nop
// iaddi vi03, vi03, 0x4 | nop
// ibne vi00, vi04, L7 | nop
// nop | nop
// nop | nop :e
// nop | nop
}
if (m_extra_debug) {
ImGui::End();
}
}
void SpriteRenderer::do_2d_group0_block_cpu(u32 count,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
if (m_extra_debug) {
ImGui::Begin("Sprite Extra Debug 2d_0");
}
Matrix4f camera_matrix = m_3d_matrix_data.camera; // vf25, vf26, vf27, vf28
for (u32 sprite_idx = 0; sprite_idx < count; sprite_idx++) {
if (m_extra_debug) {
ImGui::Text("Sprite: %d", sprite_idx);
}
SpriteHud2DPacket packet;
memset(&packet, 0, sizeof(packet));
// ilw.y vi08, 1(vi02) | nop vi08 = matrix
u32 offset_selector = m_vec_data_2d[sprite_idx].matrix();
// assert(offset_selector == 0 || offset_selector == 1);
// moved this out of the loop.
// lq.xyzw vf25, 900(vi00) | nop vf25 = cam_mat
// lq.xyzw vf26, 901(vi00) | nop
// lq.xyzw vf27, 902(vi00) | nop
// lq.xyzw vf28, 903(vi00) | nop
// lq.xyzw vf30, 904(vi00) | nop vf30 = hvdf_offset
// vf30
Vector4f hvdf_offset = m_3d_matrix_data.hvdf_offset;
// lqi.xyzw vf01, vi02 | nop
Vector4f pos_vf01 = m_vec_data_2d[sprite_idx].xyz_sx;
if (m_extra_debug) {
imgui_vec(pos_vf01, "POS", 2);
}
// lqi.xyzw vf05, vi02 | nop
Vector4f flags_vf05 = m_vec_data_2d[sprite_idx].flag_rot_sy;
// lqi.xyzw vf11, vi02 | nop
Vector4f color_vf11 = m_vec_data_2d[sprite_idx].rgba;
// multiplications from the right column
Vector4f transformed_pos_vf02 = matrix_transform(camera_matrix, pos_vf01);
Vector4f scales_vf01 = pos_vf01; // now used for something else.
// lq.xyzw vf12, 1020(vi00) | mulaw.xyzw ACC, vf28, vf00
// vf12 is fog consts
Vector4f fog_consts_vf12(m_frame_data.fog_min, m_frame_data.fog_max, m_frame_data.max_scale,
m_frame_data.bonus);
// ilw.y vi08, 1(vi02) | maddax.xyzw ACC, vf25, vf01
// load offset selector for the next round.
// nop | madday.xyzw ACC, vf26, vf01
// nop | maddz.xyzw vf02, vf27, vf01
// move.w vf05, vf00 | addw.z vf01, vf00, vf05
// scales_vf01.z = sy
scales_vf01.z() = flags_vf05.w(); // start building the scale vector
flags_vf05.w() = 1.f; // what are we building in flags right now??
// nop | nop
// div Q, vf31.x, vf02.w | muly.z vf05, vf05, vf31
float Q = m_frame_data.pfog0 / transformed_pos_vf02.w();
flags_vf05.z() *= m_frame_data.deg_to_rad;
// nop | mul.xyzw vf03, vf02, vf29
Vector4f scaled_pos_vf03 = transformed_pos_vf02.elementwise_multiply(m_frame_data.hmge_scale);
// nop | nop
// nop | nop
// nop | mulz.z vf04, vf05, vf05 (ts)
// fmt::print("rot is {} degrees\n", flags_vf05.z() * 360.0 / (2.0 * M_PI));
// the load is for rotation stuff,
// lq.xyzw vf14, 1001(vi00) | clipw.xyz vf03, vf03 (used for fcand)
// iaddi vi06, vi00, 0x1 | adda.xyzw ACC, vf11, vf11 (used for fmand)
// upcoming fcand with 0x3f, that checks all of them.
bool fcand_result = clip_xyz_plus_minus(scaled_pos_vf03);
bool fmand_result = color_vf11.w() == 0; // (really w+w, but I don't think it matters?)
// L8:
// xgkick double buffer setup
// ior vi05, vi15, vi00 | mul.zw vf01, vf01, Q
scales_vf01.z() *= Q; // sy
scales_vf01.w() *= Q; // sx
// lq.xyzw vf06, 998(vi00) | mulz.xyzw vf15, vf05, vf04 (ts)
auto adgif_vf06 = m_frame_data.adgif_giftag;
// lq.xyzw vf14, 1002(vi00) ts| mula.xyzw ACC, vf05, vf14 (ts)
// fmand vi01, vi06 | mul.xyz vf02, vf02, Q
transformed_pos_vf02.x() *= Q;
transformed_pos_vf02.y() *= Q;
transformed_pos_vf02.z() *= Q;
// if (m_extra_debug) {
// imgui_vec(transformed_pos_vf02, "scaled xf");
// }
// ibne vi00, vi01, L10 | addz.x vf01, vf00, vf01
scales_vf01.x() = scales_vf01.z(); // = sy
if (fmand_result) {
if (m_extra_debug) {
ImGui::TextColored(ImVec4(0.8, 0.2, 0.2, 1.0), "fmand (1) reject");
ImGui::Separator();
}
continue; // reject!
}
// lqi.xyzw vf07, vi03 | mulz.xyzw vf16, vf15, vf04 (ts)
// vf07 is first use adgif
// lq.xyzw vf14, 1003(vi00) | madda.xyzw ACC, vf15, vf14 (ts both)
// lqi.xyzw vf08, vi03 | add.xyzw vf10, vf02, vf30
// vf08 is second user adgif
Vector4f offset_pos_vf10 = transformed_pos_vf02 + hvdf_offset;
// if (m_extra_debug) {
// ImGui::Text("sel %d", offset_selector);
// //ImGui::Text("hvdf off z: %f tf/w z: %f", hvdf_offset.z(), transformed_pos_vf02.z());
// imgui_vec(hvdf_offset, "hvdf");
// imgui_vec(transformed_pos_vf02, "tf'd");
// }
// lqi.xyzw vf09, vi03 | mulw.x vf01, vf01, vf01
// vf09 is third user adgif
scales_vf01.x() *= scales_vf01.w(); // x = sx * sy
// sqi.xyzw vf06, vi05 | mulz.xyzw vf15, vf16, vf04 (ts)
// FIRST ADGIF IS adgif_vf06
packet.adgif_giftag = adgif_vf06;
// lq.xyzw vf14, 1004(vi00) | madda.xyzw ACC, vf16, vf14 (ts both)
// sqi.xyzw vf07, vi05 | maxx.w vf10, vf10, vf12
// SECOND ADGIF is first user
// just do all 5 now.
packet.user_adgif = m_adgif[sprite_idx];
offset_pos_vf10.w() = std::max(offset_pos_vf10.w(), m_frame_data.fog_max);
// sqi.xyzw vf08, vi05 | maxz.zw vf01, vf01, vf31
// THIRD ADGIF is second user
scales_vf01.z() = std::max(scales_vf01.z(), m_frame_data.min_scale);
scales_vf01.w() = std::max(scales_vf01.w(), m_frame_data.min_scale);
// sqi.xyzw vf09, vi05 | mulz.xyzw vf16, vf15, vf04 (ts)
// FOURTH ADGIF is third user
// lq.xyzw vf14, 1005(vi00) | madda.xyzw ACC, vf15, vf14 (ts both)
// lqi.xyzw vf06, vi03 | mulw.x vf01, vf01, vf31
// vf06 is fourth user adgif
scales_vf01.x() *= m_frame_data.inv_area; // x = sx * sy * inv_area (area ratio)
// lqi.xyzw vf07, vi03 | miniy.w vf10, vf10, vf12
// vf07 is fifth user adgif
offset_pos_vf10.w() = std::min(offset_pos_vf10.w(), m_frame_data.fog_min);
// lq.xyzw vf08, 999(vi00) | miniz.zw vf01, vf01, vf12
// vf08 is 2d giftag 1 (NOTE THIS IS DIFFERENT FROM 2d 1)!!!!!
scales_vf01.z() = std::min(scales_vf01.z(), fog_consts_vf12.z());
scales_vf01.w() = std::min(scales_vf01.w(), fog_consts_vf12.z());
bool use_first_giftag = offset_selector == 0;
// ilw.x vi07, -2(vi02) | madd.xyzw vf05, vf16, vf14
auto flag_vi07 = m_vec_data_2d[sprite_idx].flag();
Vector4f vf05_sincos(0, 0, std::sin(flags_vf05.z()), std::cos(flags_vf05.z()));
// lqi.xyzw vf23, vi02 | miniw.x vf01, vf01, vf00
// pipeline
scales_vf01.x() = std::min(scales_vf01.x(), 1.f);
// nop | suby.w vf02, vf10, vf12 (unique)
transformed_pos_vf02.w() = offset_pos_vf10.w() - fog_consts_vf12.y();
// lqi.xyzw vf24, vi02 | mulx.w vf11, vf11, vf01
// pipeline
color_vf11.w() *= scales_vf01.x(); // is this right? doesn't this stall??
// fcand vi01, 0x3f | mulaw.xyzw ACC, vf28, vf00
// already computed pipeline
// lq.xyzw vf17, 1006(vi00) | maddax.xyzw ACC, vf25, vf23 (pipeline)
Vector4f basis_x_vf17 = m_frame_data.basis_x;
// fmand vi09, vi06 | nop
// ibne vi00, vi09, L6 | nop
if (transformed_pos_vf02.w() != 0) {
if (m_extra_debug) {
ImGui::TextColored(ImVec4(0.8, 0.2, 0.2, 1.0), "fmand (2) trick");
}
use_first_giftag = false;
}
// lq.xyzw vf18, 1007(vi00) | madday.xyzw ACC, vf26, vf23 (pipeline)
Vector4f basis_y_vf18 = m_frame_data.basis_y;
// assert(flag_vi07 == 0);
Vector4f* xy_array = m_frame_data.xy_array + flag_vi07;
// lq.xyzw vf19, 980(vi07) | ftoi0.xyzw vf11, vf11
Vector4f xy0_vf19 = xy_array[0];
math::Vector<s32, 4> color_integer_vf11 = color_vf11.cast<s32>();
// lq.xyzw vf20, 981(vi07) | maddz.xyzw vf02, vf27, vf23 (pipeline)
Vector4f xy1_vf20 = xy_array[1];
// lq.xyzw vf21, 982(vi07) | mulaw.xyzw ACC, vf17, vf05
Vector4f xy2_vf21 = xy_array[2];
Vector4f acc = basis_x_vf17 * vf05_sincos.w();
// lq.xyzw vf22, 983(vi07) | msubz.xyzw vf12, vf18, vf05
Vector4f xy3_vf22 = xy_array[3];
Vector4f vf12_rotated = acc - (basis_y_vf18 * vf05_sincos.z());
// sq.xyzw vf11, 3(vi05) | mulaz.xyzw ACC, vf17, vf05
// EIGHTH is color integer
packet.color = color_integer_vf11;
acc = basis_x_vf17 * vf05_sincos.z();
// lqi.xyzw vf11, vi02 | maddw.xyzw vf13, vf18, vf05
// (pipeline)
Vector4f vf13_rotated_trans = acc + basis_y_vf18 * vf05_sincos.w();
// move.w vf24, vf00 | addw.z vf23, vf00, vf24 (pipeline both)
// div Q, vf31.x, vf02.w | mulw.xyzw vf12, vf12, vf01
// (pipeline)
vf12_rotated *= scales_vf01.w();
// ibne vi00, vi01, L9 | muly.z vf24, vf24, vf31 (pipeline)
if (fcand_result) {
if (m_extra_debug) {
ImGui::TextColored(ImVec4(0.8, 0.2, 0.2, 1.0), "fcand reject");
ImGui::Separator();
}
continue; // reject (could move earlier)
}
// ilw.y vi08, 1(vi02) | mulz.xyzw vf13, vf13, vf01
// (pipeline)
vf13_rotated_trans *= scales_vf01.z();
// LEFT OFF HERE!
// sqi.xyzw vf06, vi05 | mul.xyzw vf03, vf02, vf29
// FIFTH is fourth user
// sqi.xyzw vf07, vi05 | mulaw.xyzw ACC, vf10, vf00
// SIXTH is fifth user
acc = offset_pos_vf10;
// sqi.xyzw vf08, vi05 | maddax.xyzw ACC, vf12, vf19
// SEVENTH is giftag2
packet.sprite_giftag =
use_first_giftag ? m_frame_data.sprite_2d_giftag : m_frame_data.sprite_2d_giftag2;
acc += vf12_rotated * xy0_vf19.x();
// lq.xyzw vf06, 988(vi00) | maddy.xyzw vf19, vf13, vf19
Vector4f st0_vf06 = m_frame_data.st_array[0];
xy0_vf19 = acc + vf13_rotated_trans * xy0_vf19.y();
// lq.xyzw vf07, 989(vi00) | mulaw.xyzw ACC, vf10, vf00
Vector4f st1_vf07 = m_frame_data.st_array[1];
acc = offset_pos_vf10;
// lq.xyzw vf08, 990(vi00) | maddax.xyzw ACC, vf12, vf20
Vector4f st2_vf08 = m_frame_data.st_array[2];
acc += vf12_rotated * xy1_vf20.x();
// lq.xyzw vf09, 991(vi00) | maddy.xyzw vf20, vf13, vf20
Vector4f st3_vf09 = m_frame_data.st_array[3];
xy1_vf20 = acc + vf13_rotated_trans * xy1_vf20.y();
// sq.xyzw vf06, 1(vi05) | mulaw.xyzw ACC, vf10, vf00
// NINTH is st0
packet.st0 = st0_vf06;
acc = offset_pos_vf10;
// sq.xyzw vf07, 3(vi05) | maddax.xyzw ACC, vf12, vf21
// ELEVEN is st1
packet.st1 = st1_vf07;
acc += vf12_rotated * xy2_vf21.x();
// sq.xyzw vf08, 5(vi05) | maddy.xyzw vf21, vf13, vf21
// THIRTEEN is st2
packet.st2 = st2_vf08;
xy2_vf21 = acc + vf13_rotated_trans * xy2_vf21.y();
// sq.xyzw vf09, 7(vi05) | mulaw.xyzw ACC, vf10, vf00
// FIFTEEN is st3
packet.st3 = st3_vf09;
acc = offset_pos_vf10;
// nop | maddax.xyzw ACC, vf12, vf22
acc += vf12_rotated * xy3_vf22.x();
// nop | maddy.xyzw vf22, vf13, vf22
xy3_vf22 = acc + vf13_rotated_trans * xy3_vf22.y();
// lq.xyzw vf12, 1020(vi00) | ftoi4.xyzw vf19, vf19
// (pipeline)
auto xy0_vf19_int = (xy0_vf19 * 16.f).cast<s32>();
// lq.xyzw vf14, 1001(vi00) | ftoi4.xyzw vf20, vf20
// (pipeline)
auto xy1_vf20_int = (xy1_vf20 * 16.f).cast<s32>();
// move.xyzw vf05, vf24 | ftoi4.xyzw vf21, vf21
// (pipeline)
auto xy2_vf21_int = (xy2_vf21 * 16.f).cast<s32>();
// move.xyzw vf01, vf23 | ftoi4.xyzw vf22, vf22
// (pipeline)
auto xy3_vf22_int = (xy3_vf22 * 16.f).cast<s32>();
if (m_extra_debug) {
u32 zi = xy3_vf22_int.z() >> 4;
ImGui::Text("z (int): 0x%08x %s", zi, zi >= (1 << 24) ? "bad" : "");
ImGui::Text("z (flt): %f", (double)(((u32)zi) << 8) / UINT32_MAX);
}
// sq.xyzw vf19, 2(vi05) | mulz.z vf04, vf24, vf24 (pipeline)
// TENTH is xy0int
packet.xy0 = xy0_vf19_int;
// sq.xyzw vf20, 4(vi05) | clipw.xyz vf03, vf03 (pipeline)
// TWELVE is xy1int
packet.xy1 = xy1_vf20_int;
// sq.xyzw vf21, 6(vi05) | nop
// FOURTEEN is xy2int
packet.xy2 = xy2_vf21_int;
// sq.xyzw vf22, 8(vi05) | nop
// SIXTEEN is xy3int
packet.xy3 = xy3_vf22_int;
m_sprite_renderer.render_gif((const u8*)&packet, sizeof(packet), render_state, prof);
if (m_extra_debug) {
imgui_vec(vf12_rotated, "vf12", 2);
imgui_vec(vf13_rotated_trans, "vf13", 2);
ImGui::Separator();
}
// xgkick vi15 | nop
// iaddi vi04, vi04, -0x1 | nop
// iaddiu vi01, vi00, 0x672 | nop
// ibne vi00, vi04, L8 | nop
// isub vi15, vi01, vi15 | adda.xyzw ACC, vf11, vf11
// nop | nop :e
// nop | nop
// L9:
// iaddi vi04, vi04, -0x1 | nop
// iaddi vi02, vi02, -0x3 | nop
// ibne vi00, vi04, L7 | nop
// nop | nop
// nop | nop :e
// nop | nop
// L10:
// iaddi vi04, vi04, -0x1 | nop
// iaddi vi03, vi03, 0x4 | nop
// ibne vi00, vi04, L7 | nop
// nop | nop
// nop | nop :e
// nop | nop
}
if (m_extra_debug) {
ImGui::End();
}
}