Files
2026-09-24 19:34:17 -03:00

178 lines
6.0 KiB
C++

#include "lcs_ge_exec.hpp"
#include "ge_renderer.hpp"
#include "psprecomp/common.hpp"
#include <array>
#include <cstdlib>
#include <iostream>
#include <string>
namespace lcs {
namespace {
std::array<std::uint32_t, 256> ge_commands{};
GeTransformState ge_transform{};
std::uint32_t ge_base_register = 0u;
std::uint32_t ge_offset_address = 0u;
std::uint32_t ge_vertex_address = 0u;
std::uint32_t ge_index_address = 0u;
bool ge_finish_seen = false;
std::uint32_t ge_finish_arg = 0u;
constexpr std::uint32_t kMaxCommands = 2'000'000u;
std::uint32_t relative_address(std::uint32_t data24) {
const std::uint32_t extended = ((ge_base_register & 0x000F0000u) << 8u) | (data24 & 0x00FFFFFFu);
return (ge_offset_address + extended) & 0x0FFFFFFFu;
}
} // namespace
void execute_ge_list_rendered(psprecomp::GuestMemory &memory, std::uint32_t list_address) {
ge_finish_seen = false;
ge_finish_arg = 0u;
if (list_address == 0u) return;
static const bool diag = std::getenv("LCS_GE_RENDER_DIAG") != nullptr;
std::uint64_t total_pixels_tested = 0u;
std::uint64_t total_pixels_written = 0u;
std::uint64_t total_triangles = 0u;
std::array<std::uint32_t, 16> call_stack{};
std::uint32_t call_depth = 0u;
std::uint32_t pc = list_address;
for (std::uint32_t executed = 0u; executed < kMaxCommands; ++executed) {
if (!memory.contains(pc, 4u)) break;
const std::uint32_t word = memory.load32(pc);
const std::uint32_t op_pc = pc;
pc = (pc + 4u) & 0x0FFFFFFFu;
const std::uint32_t command = word >> 24u;
const std::uint32_t data = word & 0x00FFFFFFu;
ge_commands[command] = data;
if (command >= 0x2Au && command <= 0x3Fu)
update_ge_transform_state(ge_transform, command, data);
switch (command) {
case 0x00u:
break;
case 0x01u:
ge_vertex_address = relative_address(data);
break;
case 0x02u:
ge_index_address = relative_address(data);
break;
case 0x10u:
ge_base_register = data;
break;
case 0x13u:
ge_offset_address = data << 8u;
break;
case 0x08u:
pc = relative_address(data);
break;
case 0x09u:
break;
case 0x0Au:
if (call_depth < call_stack.size()) call_stack[call_depth++] = pc;
pc = relative_address(data);
break;
case 0x0Bu:
if (call_depth != 0u) pc = call_stack[--call_depth];
break;
case 0x0Fu:
ge_finish_seen = true;
ge_finish_arg = data;
break;
case 0x0Cu:
flush_ge_deferred_rasterization(memory);
if (diag) {
static std::uint64_t lists = 0u;
if ((++lists % 30u) == 0u)
std::cerr << "[ge-render] list=" << lists << " tris=" << total_triangles
<< " tested=" << total_pixels_tested
<< " written=" << total_pixels_written << "\n";
}
return;
case 0x04u: {
GeRenderStats stats{};
std::string error;
if (!render_ge_primitive(memory, ge_commands, ge_transform, ge_vertex_address,
ge_index_address, data, stats, error, 1u, 0u, 0u, 0u,
diag)) {
if (diag)
std::cerr << "[ge-render] primitive failed at " << psprecomp::hex32(op_pc)
<< ": " << error << "\n";
flush_ge_deferred_rasterization(memory);
return;
}
ge_vertex_address = stats.next_vertex_address;
ge_index_address = stats.next_index_address;
if (diag) {
total_pixels_tested += stats.pixels_tested;
total_pixels_written += stats.pixels_written;
total_triangles += stats.triangles;
}
break;
}
default:
break;
}
}
flush_ge_deferred_rasterization(memory);
}
GeListPrescan prescan_ge_list(psprecomp::GuestMemory &memory, std::uint32_t list_address) {
GeListPrescan result{};
if (list_address == 0u) return result;
std::uint32_t base_register = ge_base_register;
std::uint32_t offset_address = ge_offset_address;
const auto relative = [&](std::uint32_t data24) {
const std::uint32_t extended = ((base_register & 0x000F0000u) << 8u) | (data24 & 0x00FFFFFFu);
return (offset_address + extended) & 0x0FFFFFFFu;
};
std::array<std::uint32_t, 16> call_stack{};
std::uint32_t call_depth = 0u;
std::uint32_t pc = list_address;
for (std::uint32_t executed = 0u; executed < kMaxCommands; ++executed) {
if (!memory.contains(pc, 4u)) break;
const std::uint32_t word = memory.load32(pc);
pc = (pc + 4u) & 0x0FFFFFFFu;
const std::uint32_t command = word >> 24u;
const std::uint32_t data = word & 0x00FFFFFFu;
switch (command) {
case 0x10u: base_register = data; break;
case 0x13u: offset_address = data << 8u; break;
case 0x08u: pc = relative(data); break;
case 0x0Au:
if (call_depth < call_stack.size()) call_stack[call_depth++] = pc;
pc = relative(data);
break;
case 0x0Bu:
if (call_depth != 0u) pc = call_stack[--call_depth];
break;
case 0x0Fu:
result.finished = true;
result.finish_argument = data;
break;
case 0x0Cu:
return result;
default:
break;
}
}
return result;
}
bool rendered_list_finished() { return ge_finish_seen; }
std::uint32_t rendered_finish_argument() { return ge_finish_arg; }
std::uint32_t rendered_render_target() {
return 0x04000000u | (ge_commands[0x9Cu] & 0x001FFFF0u);
}
std::uint32_t rendered_render_stride() { return ge_commands[0x9Du] & 0x7FCu; }
}