up to bsp

This commit is contained in:
water111
2026-08-18 22:41:41 -07:00
parent aa2cad59ff
commit ac70997e9e
18 changed files with 375 additions and 328 deletions
+9
View File
@@ -272,6 +272,7 @@ void try_reverse_lookup_inline_array(const FieldReverseLookupInput& input,
next_input.stride = 0;
next_input.offset = input.offset; // includes the offset.
next_input.base_type = di.result_type;
next_input.field_score_overrides = input.field_score_overrides;
try_reverse_lookup(next_input, ts, &var_idx_node, output, max_count);
return;
}
@@ -318,6 +319,7 @@ void try_reverse_lookup_inline_array(const FieldReverseLookupInput& input,
next_input.stride = input.stride;
next_input.offset = offset_into_elt;
next_input.base_type = di.result_type;
next_input.field_score_overrides = input.field_score_overrides;
try_reverse_lookup(next_input, ts, &const_idx_node, output, max_count);
}
@@ -368,6 +370,12 @@ void try_reverse_lookup_other(const FieldReverseLookupInput& input,
token.kind = FieldReverseLookupOutput::Token::Kind::FIELD;
token.name = field.name();
token.field_score = field.field_score();
for (const auto& score_override : input.field_score_overrides) {
if (score_override.type_name == input.base_type.base_type() &&
score_override.field_name == field.name()) {
token.field_score += score_override.score;
}
}
if (field_deref.needs_deref) {
if (offset_into_field == 0) {
@@ -440,6 +448,7 @@ void try_reverse_lookup_other(const FieldReverseLookupInput& input,
next_input.offset = offset_into_field - expected_offset_into_field;
next_input.stride = input.stride;
next_input.base_type = field_deref.type;
next_input.field_score_overrides = input.field_score_overrides;
ReverseLookupNode node;
node.prev = parent;
node.token = token;
+9
View File
@@ -64,11 +64,20 @@ struct DerefKind {
RegClass reg_kind = RegClass::INVALID;
};
struct FieldReverseLookupScoreOverride {
std::string type_name;
std::string field_name;
double score = 0;
};
struct FieldReverseLookupInput {
std::optional<DerefKind> deref = std::nullopt; // if we actually access memory
int offset = 0; // if we apply a constant offset
int stride = 0; // if we are doing a + (idx * stride)
TypeSpec base_type; // the type of the thing we're accessing
// Per-call scoring adjustments for ambiguous overlay fields. Lookup still discovers and
// validates every candidate normally; these only affect which valid candidate is preferred.
std::vector<FieldReverseLookupScoreOverride> field_score_overrides;
};
constexpr double CONSTANT_INDEX_SCORE = -10.0;
+7 -7
View File
@@ -76,7 +76,7 @@ FormElement* SetVarOp::get_as_form(FormPool& pool, const Env& env) const {
rd_in.stride = 0;
rd_in.offset = m_src.get_arg(1).get_int();
rd_in.base_type = arg0_type.typespec();
auto rd = env.dts->ts.reverse_field_lookup(rd_in);
auto rd = env.reverse_field_lookup(rd_in);
if (rd.success) {
auto source = pool.alloc_single_element_form<SimpleExpressionElement>(
@@ -235,7 +235,7 @@ FormElement* StoreOp::get_vf_store_as_form(FormPool& pool, const Env& env) const
rd_in.base_type = input_type.typespec();
rd_in.stride = 0;
rd_in.offset = ro.offset;
auto rd = env.dts->ts.reverse_field_lookup(rd_in);
auto rd = env.reverse_field_lookup(rd_in);
if (rd.success) {
auto source = pool.alloc_single_element_form<SimpleExpressionElement>(
@@ -251,7 +251,7 @@ FormElement* StoreOp::get_vf_store_as_form(FormPool& pool, const Env& env) const
} else {
// try again with no deref.
rd_in.deref = {};
auto rd_no_deref = env.dts->ts.reverse_field_lookup(rd_in);
auto rd_no_deref = env.reverse_field_lookup(rd_in);
if (rd_no_deref.success) {
auto source = pool.alloc_single_element_form<SimpleExpressionElement>(
nullptr, SimpleAtom::make_var(ro.var).as_expr(), m_my_idx);
@@ -378,7 +378,7 @@ FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
rd_in.base_type = input_type.get_obj_plus_const_mult_typespec();
rd_in.stride = input_type.get_multiplier();
rd_in.offset = ro.offset;
auto rd = env.dts->ts.reverse_field_lookup(rd_in);
auto rd = env.reverse_field_lookup(rd_in);
if (rd.success) {
std::vector<DerefToken> tokens;
@@ -412,7 +412,7 @@ FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
rd_in.base_type = input_type.typespec();
rd_in.stride = 0;
rd_in.offset = ro.offset;
auto rd = env.dts->ts.reverse_field_lookup(rd_in);
auto rd = env.reverse_field_lookup(rd_in);
if (rd.success) {
auto source = pool.alloc_single_element_form<SimpleExpressionElement>(
@@ -626,7 +626,7 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
rd_in.stride = 1;
}
rd_in.offset = ro.offset;
auto rd = env.dts->ts.reverse_field_lookup(rd_in);
auto rd = env.reverse_field_lookup(rd_in);
if (rd.success) {
std::vector<DerefToken> tokens;
@@ -673,7 +673,7 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
rd_in.base_type = input_type.typespec();
rd_in.stride = 0;
rd_in.offset = ro.offset;
auto rd = env.dts->ts.reverse_field_lookup(rd_in);
auto rd = env.reverse_field_lookup(rd_in);
// todo, error here?
+13 -13
View File
@@ -318,7 +318,7 @@ TP_Type get_stack_type_at_constant_offset(int offset,
rd_in.stride = 0; // not a strided access
rd_in.offset = offset - var.hint.stack_offset; // offset into this var
rd_in.base_type = var.ref_type; // use ref type for ptr.
auto rd = dts.ts.reverse_field_lookup(rd_in);
auto rd = env.reverse_field_lookup(rd_in);
if (rd.success) {
auto result = TP_Type::make_from_ts(coerce_to_reg_type(rd.result_type));
lg::print("Matched a stack variable! {}\n", result.print());
@@ -476,7 +476,7 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
rd_in.offset = arg1_type.get_integer_constant();
rd_in.stride = 0;
rd_in.base_type = arg0_type.typespec();
auto out = env.dts->ts.reverse_field_lookup(rd_in);
auto out = env.reverse_field_lookup(rd_in);
if (out.success) {
sum_type = coerce_to_reg_type(out.result_type);
}
@@ -514,7 +514,7 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
rd_in.offset = arg0_type.get_add_int_constant();
rd_in.stride = arg0_type.get_mult_int_constant();
rd_in.base_type = arg1_type.typespec();
auto out = env.dts->ts.reverse_field_lookup(rd_in);
auto out = env.reverse_field_lookup(rd_in);
if (out.success) {
return TP_Type::make_from_ts(coerce_to_reg_type(out.result_type));
}
@@ -524,7 +524,7 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
rd_in.offset = arg1_type.get_add_int_constant();
rd_in.stride = arg1_type.get_mult_int_constant();
rd_in.base_type = arg0_type.typespec();
auto out = env.dts->ts.reverse_field_lookup(rd_in);
auto out = env.reverse_field_lookup(rd_in);
if (out.success) {
return TP_Type::make_from_ts(coerce_to_reg_type(out.result_type));
}
@@ -535,7 +535,7 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
rd_in.offset = arg0_type.get_integer_constant();
rd_in.stride = 1;
rd_in.base_type = arg1_type.typespec();
auto out = env.dts->ts.reverse_field_lookup(rd_in);
auto out = env.reverse_field_lookup(rd_in);
if (out.success) {
return TP_Type::make_from_ts(coerce_to_reg_type(out.result_type));
}
@@ -577,7 +577,7 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
rd_in.stride = 0;
rd_in.offset = m_args[1].get_int();
rd_in.base_type = arg0_type.typespec();
auto rd = dts.ts.reverse_field_lookup(rd_in);
auto rd = env.reverse_field_lookup(rd_in);
if (rd.success) {
return TP_Type::make_from_ts(coerce_to_reg_type(rd.result_type));
@@ -593,7 +593,7 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
rd_in.stride = arg1_type.get_multiplier();
rd_in.offset = 0;
rd_in.base_type = arg0_type.typespec();
auto rd = dts.ts.reverse_field_multi_lookup(rd_in);
auto rd = env.reverse_field_multi_lookup(rd_in);
for (int i = 0; i < (int)rd.results.size(); i++) {
if (rd.results.at(i).has_variable_token()) {
@@ -611,7 +611,7 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
rd_in.stride = arg0_type.get_multiplier();
rd_in.offset = 0;
rd_in.base_type = arg1_type.typespec();
auto rd = dts.ts.reverse_field_multi_lookup(rd_in);
auto rd = env.reverse_field_multi_lookup(rd_in);
for (int i = 0; i < (int)rd.results.size(); i++) {
if (rd.results.at(i).has_variable_token()) {
@@ -698,7 +698,7 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
rd_in.offset = arg0_type.get_integer_constant();
rd_in.stride = 0;
rd_in.base_type = arg1_type.typespec();
auto out = env.dts->ts.reverse_field_lookup(rd_in);
auto out = env.reverse_field_lookup(rd_in);
if (out.success) {
sum_type = coerce_to_reg_type(out.result_type);
}
@@ -1100,7 +1100,7 @@ TP_Type LoadVarOp::get_src_type(const TypeState& input,
rd_in.base_type = input_type.get_obj_plus_const_mult_typespec();
rd_in.stride = input_type.get_multiplier();
rd_in.offset = ro.offset;
auto rd = dts.ts.reverse_field_lookup(rd_in);
auto rd = env.reverse_field_lookup(rd_in);
if (rd.success) {
// load_path_set = true;
@@ -1145,7 +1145,7 @@ TP_Type LoadVarOp::get_src_type(const TypeState& input,
rd_in.base_type = input_type.typespec();
rd_in.stride = 0;
rd_in.offset = ro.offset;
auto rd = dts.ts.reverse_field_lookup(rd_in);
auto rd = env.reverse_field_lookup(rd_in);
if (rd.success) {
if (rd_in.base_type.base_type() == "state" && rd.tokens.size() == 1 &&
@@ -1210,7 +1210,7 @@ TP_Type LoadVarOp::get_src_type(const TypeState& input,
rd_in.deref = dk;
rd_in.base_type = input_type.get_objects_typespec();
rd_in.offset = ro.offset;
auto rd = dts.ts.reverse_field_lookup(rd_in);
auto rd = env.reverse_field_lookup(rd_in);
if (rd.success) {
return TP_Type::make_from_ts(coerce_to_reg_type(rd.result_type));
}
@@ -1227,7 +1227,7 @@ TP_Type LoadVarOp::get_src_type(const TypeState& input,
rd_in.base_type = input_type.get_objects_typespec();
rd_in.stride = 0;
rd_in.offset = input_type.get_integer_constant();
auto rd = dts.ts.reverse_field_lookup(rd_in);
auto rd = env.reverse_field_lookup(rd_in);
if (rd.success) {
return TP_Type::make_from_ts(coerce_to_reg_type(rd.result_type));
}
+11
View File
@@ -17,6 +17,17 @@
namespace decompiler {
FieldReverseLookupOutput Env::reverse_field_lookup(FieldReverseLookupInput input) const {
input.field_score_overrides = m_reverse_lookup_field_score_overrides;
return dts->ts.reverse_field_lookup(input);
}
FieldReverseMultiLookupOutput Env::reverse_field_multi_lookup(FieldReverseLookupInput input,
int max_count) const {
input.field_score_overrides = m_reverse_lookup_field_score_overrides;
return dts->ts.reverse_field_multi_lookup(input, max_count);
}
constexpr const char* reg_names[] = {"a0-0", "a1-0", "a2-0", "a3-0",
"t0-0", "t1-0", "t2-0", "t3-0"};
+9
View File
@@ -4,6 +4,7 @@
#include <vector>
#include "common/goos/Object.h"
#include "common/type_system/TypeSystem.h"
#include "common/util/Assert.h"
#include "decompiler/Disasm/Register.h"
@@ -187,6 +188,13 @@ class Env {
void set_scratchpad_type(const TypeSpec& type) { m_scratchpad_type = type; }
const std::optional<TypeSpec>& scratchpad_type() const { return m_scratchpad_type; }
void set_reverse_lookup_field_score_overrides(
std::vector<FieldReverseLookupScoreOverride> overrides) {
m_reverse_lookup_field_score_overrides = std::move(overrides);
}
FieldReverseLookupOutput reverse_field_lookup(FieldReverseLookupInput input) const;
FieldReverseMultiLookupOutput reverse_field_multi_lookup(FieldReverseLookupInput input,
int max_count = 100) const;
void note_scratchpad_access() const { m_uses_scratchpad = true; }
bool uses_scratchpad() const { return m_uses_scratchpad; }
@@ -288,6 +296,7 @@ class Env {
std::unordered_map<int, std::vector<RegisterTypeCast>> m_typecasts;
std::unordered_map<int, StackTypeCast> m_stack_typecasts;
std::optional<TypeSpec> m_scratchpad_type;
std::vector<FieldReverseLookupScoreOverride> m_reverse_lookup_field_score_overrides;
mutable bool m_uses_scratchpad = false;
std::vector<StackStructureEntry> m_stack_structures;
std::unordered_map<std::string, std::string> m_var_remap;
+9 -11
View File
@@ -671,8 +671,6 @@ Form* cast_form_from(Form* in,
FormPool& pool,
const Env& env,
bool tc_pass = false) {
auto& ts = env.dts->ts;
auto form_type = try_get_deref_form_type(in, env);
if (form_type == new_type) {
return in;
@@ -694,7 +692,7 @@ Form* cast_form_from(Form* in,
suffix_lookup.offset = 0;
suffix_lookup.stride = 0;
suffix_lookup.base_type = new_type;
auto suffix_results = ts.reverse_field_multi_lookup(suffix_lookup);
auto suffix_results = env.reverse_field_multi_lookup(suffix_lookup);
for (const auto& result : suffix_results.results) {
if (!result.addr_of && result.result_type == *form_type &&
deref_suffix_matches_lookup(deref->tokens(), keep, result)) {
@@ -717,7 +715,7 @@ Form* cast_form_from(Form* in,
lookup_input.offset = 0;
lookup_input.stride = 0;
lookup_input.base_type = form_type.value_or(old_type);
auto lookup_result = ts.reverse_field_multi_lookup(lookup_input);
auto lookup_result = env.reverse_field_multi_lookup(lookup_input);
if (lookup_result.success) {
for (auto& result : lookup_result.results) {
if (result.result_type == new_type) {
@@ -1337,7 +1335,7 @@ DerefElement* try_reassociate_inline_array_field_access(Form* field_access,
lookup.stride = stride;
lookup.offset = 0;
lookup.base_type = *base_type;
auto reverse = env.dts->ts.reverse_field_multi_lookup(lookup);
auto reverse = env.reverse_field_multi_lookup(lookup);
for (const auto& candidate : reverse.results) {
if (candidate.has_variable_token()) {
auto tokens = deref->tokens();
@@ -1360,7 +1358,7 @@ DerefElement* try_reassociate_inline_array_field_access(Form* field_access,
lookup.stride = stride;
lookup.offset = 0;
lookup.base_type = *field_type;
auto reverse = env.dts->ts.reverse_field_multi_lookup(lookup);
auto reverse = env.reverse_field_multi_lookup(lookup);
for (const auto& candidate : reverse.results) {
if (!candidate.has_variable_token()) {
continue;
@@ -1472,7 +1470,7 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
input.offset = arg0_type.get_integer_constant();
input.stride = 1;
input.base_type = arg1_type.typespec();
auto out = env.dts->ts.reverse_field_lookup(input);
auto out = env.reverse_field_lookup(input);
if (out.success && out.has_variable_token()) {
// it is. now we have to modify things
// first, look for the index
@@ -1509,7 +1507,7 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
input.offset = arg0_type.get_add_int_constant();
input.stride = arg0_type.get_mult_int_constant();
input.base_type = arg1_type.typespec();
auto out = env.dts->ts.reverse_field_lookup(input);
auto out = env.reverse_field_lookup(input);
if (out.success && out.has_variable_token()) {
// it is. now we have to modify things
// first, look for the index
@@ -1585,7 +1583,7 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
rd_in.stride = arg1_type.get_multiplier();
rd_in.offset = 0;
rd_in.base_type = arg0_type.typespec();
auto rd = env.dts->ts.reverse_field_multi_lookup(rd_in);
auto rd = env.reverse_field_multi_lookup(rd_in);
int idx_of_success = -1;
if (rd.success) {
for (int i = 0; i < (int)rd.results.size(); i++) {
@@ -1637,7 +1635,7 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
rd_in.stride = arg0_type.get_multiplier();
rd_in.offset = 0;
rd_in.base_type = arg1_type.typespec();
auto rd = env.dts->ts.reverse_field_multi_lookup(rd_in);
auto rd = env.reverse_field_multi_lookup(rd_in);
int idx_of_success = -1;
if (rd.success) {
for (int i = 0; i < (int)rd.results.size(); i++) {
@@ -1689,7 +1687,7 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
input.offset = arg0_type.get_integer_constant();
input.stride = 0;
input.base_type = arg1_type.typespec();
auto out = env.dts->ts.reverse_field_lookup(input);
auto out = env.reverse_field_lookup(input);
if (out.success && !out.has_variable_token()) {
// it is. now we have to modify things
// first, look for the index
+7 -1
View File
@@ -614,7 +614,13 @@ void ObjectFileDB::ir2_type_analysis_pass(int seg, const Config& config, ObjectF
auto scratchpad_type = config.scratchpad_types_by_object.find(obj_name);
if (scratchpad_type != config.scratchpad_types_by_object.end()) {
func.ir2.env.set_scratchpad_type(TypeSpec(scratchpad_type->second));
func.ir2.env.set_scratchpad_type(TypeSpec(scratchpad_type->second.type_name));
std::vector<FieldReverseLookupScoreOverride> score_overrides;
for (const auto& override_config : scratchpad_type->second.field_score_overrides) {
score_overrides.push_back(
{override_config.type_name, override_config.field_name, override_config.score});
}
func.ir2.env.set_reverse_lookup_field_score_overrides(std::move(score_overrides));
}
if (config.hacks.pair_functions_by_name.find(func_name) !=
+16 -1
View File
@@ -196,7 +196,22 @@ Config make_config_via_json(nlohmann::json& json) {
if (json.contains("scratchpad_types_file")) {
auto scratchpad_types_json = read_json_file_from_config(json, "scratchpad_types_file");
for (auto& kv : scratchpad_types_json.items()) {
config.scratchpad_types_by_object[kv.key()] = kv.value().get<std::string>();
ScratchpadTypeConfig scratchpad_config;
if (kv.value().is_string()) {
scratchpad_config.type_name = kv.value().get<std::string>();
} else {
scratchpad_config.type_name = kv.value().at("type").get<std::string>();
if (kv.value().contains("field_score_overrides")) {
for (const auto& score_json : kv.value().at("field_score_overrides")) {
ScratchpadTypeConfig::FieldScoreOverride score_override;
score_override.type_name = score_json.at("type").get<std::string>();
score_override.field_name = score_json.at("field").get<std::string>();
score_override.score = score_json.at("score").get<double>();
scratchpad_config.field_score_overrides.push_back(std::move(score_override));
}
}
}
config.scratchpad_types_by_object[kv.key()] = std::move(scratchpad_config);
}
}
+11 -1
View File
@@ -28,6 +28,16 @@ struct StackTypeCast {
std::string type_name;
};
struct ScratchpadTypeConfig {
std::string type_name;
struct FieldScoreOverride {
std::string type_name;
std::string field_name;
double score = 0;
};
std::vector<FieldScoreOverride> field_score_overrides;
};
struct LabelConfigInfo {
// if the label is a "value" type, it will be loaded directly into a register.
// in all cases, this is a constant, either a 64-bit integer or a float.
@@ -167,7 +177,7 @@ struct Config {
std::unordered_map<std::string, std::unordered_map<std::string, LabelConfigInfo>> label_types;
std::unordered_map<std::string, std::vector<StackStructureHint>>
stack_structure_hints_by_function;
std::unordered_map<std::string, std::string> scratchpad_types_by_object;
std::unordered_map<std::string, ScratchpadTypeConfig> scratchpad_types_by_object;
std::unordered_map<std::string, ObjectPatchInfo> object_patches;
std::unordered_map<std::string, int> bad_format_strings;
+1 -1
View File
@@ -4319,7 +4319,7 @@ sound." (_type_ int) int) ;; 12
(define-extern dma-send-chain-no-tte "Wait for a channel, flush the cache, and start a source-chain DMA without transferring its tags. This function is unused." (function dma-bank-source uint none))
(define-extern dma-send-chain-no-flush "Wait for a channel and start a source-chain DMA with tag transfer enabled without first flushing the data cache." (function dma-bank-source uint none))
(define-extern dma-send-to-spr "Transfer quadwords from main memory to scratchpad, optionally waiting for completion." (function uint uint uint symbol none))
(define-extern dma-send-to-spr-no-flush "Transfer quadwords from main memory to scratchpad without flushing the data cache, optionally waiting for completion." (function uint uint uint symbol none))
(define-extern dma-send-to-spr-no-flush "Transfer quadwords from main memory to scratchpad without flushing the data cache, optionally waiting for completion." (function pointer pointer uint symbol none))
(define-extern dma-send-from-spr "Transfer quadwords from scratchpad to main memory, optionally waiting for completion." (function uint uint uint symbol none))
(define-extern dma-send-from-spr-no-flush "Transfer quadwords from scratchpad to main memory without flushing the data cache, optionally waiting for completion." (function uint uint uint symbol none))
(define-extern dma-initialize "Mask the VIF0 and VIF1 DMAtag mismatch error required by the EE hardware workaround." (function none))
+31
View File
@@ -155,6 +155,26 @@
{"count": "14", "fields": ":regs0 (gif-reg-id $reg0) :regs1 (gif-reg-id $reg1) :regs2 (gif-reg-id $reg2) :regs3 (gif-reg-id $reg3) :regs4 (gif-reg-id $reg4) :regs5 (gif-reg-id $reg5) :regs6 (gif-reg-id $reg6) :regs7 (gif-reg-id $reg7) :regs8 (gif-reg-id $reg8) :regs9 (gif-reg-id $reg9) :regs10 (gif-reg-id $reg10) :regs11 (gif-reg-id $reg11) :regs12 (gif-reg-id $reg12) :regs13 (gif-reg-id $reg13)", "regs": "$reg0 $reg1 $reg2 $reg3 $reg4 $reg5 $reg6 $reg7 $reg8 $reg9 $reg10 $reg11 $reg12 $reg13"},
{"count": "15", "fields": ":regs0 (gif-reg-id $reg0) :regs1 (gif-reg-id $reg1) :regs2 (gif-reg-id $reg2) :regs3 (gif-reg-id $reg3) :regs4 (gif-reg-id $reg4) :regs5 (gif-reg-id $reg5) :regs6 (gif-reg-id $reg6) :regs7 (gif-reg-id $reg7) :regs8 (gif-reg-id $reg8) :regs9 (gif-reg-id $reg9) :regs10 (gif-reg-id $reg10) :regs11 (gif-reg-id $reg11) :regs12 (gif-reg-id $reg12) :regs13 (gif-reg-id $reg13) :regs14 (gif-reg-id $reg14)", "regs": "$reg0 $reg1 $reg2 $reg3 $reg4 $reg5 $reg6 $reg7 $reg8 $reg9 $reg10 $reg11 $reg12 $reg13 $reg14"},
{"count": "16", "fields": ":regs0 (gif-reg-id $reg0) :regs1 (gif-reg-id $reg1) :regs2 (gif-reg-id $reg2) :regs3 (gif-reg-id $reg3) :regs4 (gif-reg-id $reg4) :regs5 (gif-reg-id $reg5) :regs6 (gif-reg-id $reg6) :regs7 (gif-reg-id $reg7) :regs8 (gif-reg-id $reg8) :regs9 (gif-reg-id $reg9) :regs10 (gif-reg-id $reg10) :regs11 (gif-reg-id $reg11) :regs12 (gif-reg-id $reg12) :regs13 (gif-reg-id $reg13) :regs14 (gif-reg-id $reg14) :regs15 (gif-reg-id $reg15)", "regs": "$reg0 $reg1 $reg2 $reg3 $reg4 $reg5 $reg6 $reg7 $reg8 $reg9 $reg10 $reg11 $reg12 $reg13 $reg14 $reg15"}
],
// A static rgba constructor omits fields whose component is zero. Recover the source macro
// for every possible subset while supplying the omitted zeroes explicitly.
"static-rgba": [
{"mask": "0000", "fields": "", "r": "0", "g": "0", "b": "0", "a": "0"},
{"mask": "1000", "fields": ":r $r", "r": "$r", "g": "0", "b": "0", "a": "0"},
{"mask": "0100", "fields": ":g $g", "r": "0", "g": "$g", "b": "0", "a": "0"},
{"mask": "0010", "fields": ":b $b", "r": "0", "g": "0", "b": "$b", "a": "0"},
{"mask": "0001", "fields": ":a $a", "r": "0", "g": "0", "b": "0", "a": "$a"},
{"mask": "1100", "fields": ":r $r :g $g", "r": "$r", "g": "$g", "b": "0", "a": "0"},
{"mask": "1010", "fields": ":r $r :b $b", "r": "$r", "g": "0", "b": "$b", "a": "0"},
{"mask": "1001", "fields": ":r $r :a $a", "r": "$r", "g": "0", "b": "0", "a": "$a"},
{"mask": "0110", "fields": ":g $g :b $b", "r": "0", "g": "$g", "b": "$b", "a": "0"},
{"mask": "0101", "fields": ":g $g :a $a", "r": "0", "g": "$g", "b": "0", "a": "$a"},
{"mask": "0011", "fields": ":b $b :a $a", "r": "0", "g": "0", "b": "$b", "a": "$a"},
{"mask": "1110", "fields": ":r $r :g $g :b $b", "r": "$r", "g": "$g", "b": "$b", "a": "0"},
{"mask": "1101", "fields": ":r $r :g $g :a $a", "r": "$r", "g": "$g", "b": "0", "a": "$a"},
{"mask": "1011", "fields": ":r $r :b $b :a $a", "r": "$r", "g": "0", "b": "$b", "a": "$a"},
{"mask": "0111", "fields": ":g $g :b $b :a $a", "r": "0", "g": "$g", "b": "$b", "a": "$a"},
{"mask": "1111", "fields": ":r $r :g $g :b $b :a $a", "r": "$r", "g": "$g", "b": "$b", "a": "$a"}
]
},
"rules": [
@@ -496,6 +516,17 @@
"match": "(new 'static 'gif-tag-regs {{fields}})",
"rewrite": "(gs-reg-list {{regs}})"
},
{
"name": "static-rgba-{{mask}}",
"for_each": "static-rgba",
"match": "(new 'static 'rgba {{fields}})",
"rewrite": "(static-rgba {{r}} {{g}} {{b}} {{a}})"
},
{
"name": "add-profile-frame",
"match": "(if *debug-segment* (add-frame (-> (current-frame) profile-bar 0) $name (static-rgba $r $g $b $a)))",
"rewrite": "(add-profile-frame! $r $g $b $a $name)"
},
{
"name": "scratchpad-object-direct-{{type}}",
"for_each": "scratchpad-object-type",
@@ -1,3 +1,9 @@
{
"cam-debug": "cam-dbg-scratch"
"cam-debug": "cam-dbg-scratch",
"bsp": {
"type": "terrain-context",
"field_score_overrides": [
{"type": "work-area", "field": "background", "score": 1000}
]
}
}
@@ -1208,16 +1208,7 @@
],
"(method 10 bsp-header)": [
[[51, 61], "a0", "(pointer uint128)"],
[[51, 61], "a1", "(pointer uint128)"],
[133, "v1", "terrain-bsp"],
[141, "v1", "terrain-bsp"],
[148, "v1", "terrain-bsp"],
[5, "a0", "terrain-bsp"],
[8, "a0", "terrain-bsp"]
],
"(method 15 bsp-header)": [
[5, "a0", "terrain-bsp"],
[8, "a0", "terrain-bsp"]
[[51, 61], "a1", "(pointer uint128)"]
],
"upload-vis-bits": [
[[4, 16], "a1", "(pointer uint128)"],
+1 -1
View File
@@ -150,7 +150,7 @@
(if wait-for-completion? (dma-sync (the-as pointer bank) 0 0))
(none))
(defun dma-send-to-spr-no-flush ((scratchpad-address uint) (memory-address uint) (quadword-count uint) (wait-for-completion? symbol))
(defun dma-send-to-spr-no-flush ((scratchpad-address pointer) (memory-address pointer) (quadword-count uint) (wait-for-completion? symbol))
"Transfer quadwords from main memory to scratchpad without flushing the data cache, optionally
waiting for completion."
(local-vars (bank dma-bank-spr))
+20 -29
View File
@@ -9,34 +9,23 @@
;; Both shapes are stored the same way: origin is one end of the centerline, axis is a unit
;; direction, length is the centerline length, and radius is the distance from the centerline. So the
;; far end is origin + axis * length, and a capsule extends radius beyond each end while a flat
;; cylinder does not.
;; far end is origin + axis * length.
;;
;; The intersect methods take a finite probe: the segment from probe-origin to
;; probe-origin + probe-displacement. They return the fraction of that displacement at which the
;; earliest contact happens, always between zero and one, or COLLISION_MISS. Compare a returned
;; fraction against zero before using it, since COLLISION_MISS is a large negative number rather
;; than a distinguished type.
;;
;; The debug-draw methods build one longitudinal outline, rotate it a sixteenth of a revolution at a
;; time, and draw both the rotated copy and the segment joining it to the previous one. The rotation
;; matrix has its translation row patched so it turns about the shape's own axis rather than the
;; world origin, which is what lets the same matrix be reapplied sixteen times.
;; earliest contact happens, always between zero and one, or COLLISION_MISS.
;; DECOMP BEGINS
(defmethod ray-capsule-intersect ((this cylinder) (probe-origin vector) (probe-displacement vector))
"Test the finite probe origin + fraction * displacement against the cylindrical wall and both
hemispherical ends. Return the earliest zero-to-one fraction, or COLLISION_MISS.
The wall test's axis-point output is computed into a stack vector and discarded, so this method
reports when the capsule is hit but not where."
hemispherical ends. Return the earliest zero-to-one fraction, or COLLISION_MISS."
(let ((axis-point (new 'stack-no-clear 'vector))
(end-center (new 'stack-no-clear 'vector)))
0.0
0.0
;; Test the wall first, then keep either spherical-cap contact when it is earlier.
;; A negative wall fraction means the wall was missed, so the first cap contact replaces it
;; outright; after that the comparison is a genuine "is this one earlier".
;; test wall
(let ((contact-fraction (ray-cylinder-intersect probe-origin
probe-displacement
(-> this origin)
@@ -44,13 +33,18 @@
(-> this radius)
(-> this length)
axis-point)))
;; test near cap
(let ((origin-cap-fraction (ray-sphere-intersect probe-origin probe-displacement (-> this origin) (-> this radius))))
(if (and (>= origin-cap-fraction 0.0) (or (< contact-fraction 0.0) (< origin-cap-fraction contact-fraction)))
(set! contact-fraction origin-cap-fraction)))
(set! contact-fraction origin-cap-fraction)))
;; test far cap
(vector+float*! end-center (-> this origin) (-> this axis) (-> this length))
(let ((end-cap-fraction (ray-sphere-intersect probe-origin probe-displacement end-center (-> this radius))))
(if (and (>= end-cap-fraction 0.0) (or (< contact-fraction 0.0) (< end-cap-fraction contact-fraction)))
(set! contact-fraction end-cap-fraction)))
(set! contact-fraction end-cap-fraction)))
;; return closest hit.
contact-fraction)))
;; This is used to hold vertices for debug-drawing cylinders.
@@ -124,14 +118,11 @@
(defun ray-arbitrary-circle-intersect ((probe-origin vector) (probe-displacement vector) (circle-origin vector) (circle-normal vector) (radius float))
"Intersect the finite probe origin + fraction * displacement with the filled disk centered at
circle-origin in circle-normal's plane. Return a zero-to-one fraction only when the plane contact
lies strictly inside radius; return COLLISION_MISS otherwise.
circle-normal need not be unit, since it appears in both halves of the ratio, but it must not be
perpendicular to probe-displacement: a displacement parallel to the plane divides by zero and the
resulting fraction is compared without any special case. A point exactly on the rim counts as a
miss, and the disk is two-sided."
lies strictly inside radius; return COLLISION_MISS otherwise."
;; The membership test squares both sides rather than taking a square root of the radial offset.
(let* ((center-offset (vector-! (new 'stack-no-clear 'vector) circle-origin probe-origin))
(contact-fraction (/ (vector-dot center-offset circle-normal) (vector-dot probe-displacement circle-normal))))
(contact-fraction (/ (vector-dot center-offset circle-normal)
(vector-dot probe-displacement circle-normal))))
(cond
((or (< 1.0 contact-fraction) (< contact-fraction 0.0)) COLLISION_MISS)
((let ((radial-offset (new 'stack-no-clear 'vector)))
@@ -143,9 +134,7 @@
(defmethod ray-flat-cyl-intersect ((this cylinder-flat) (probe-origin vector) (probe-displacement vector))
"Test the finite probe origin + fraction * displacement against the cylindrical wall and both
filled end caps. Return the earliest zero-to-one fraction, or COLLISION_MISS.
Unlike ray-capsule-intersect this keeps a contact position in a stack vector, overwriting it with
the relevant cap center when a cap wins, but that vector is local and is discarded on return."
filled end caps. Return the earliest zero-to-one fraction, or COLLISION_MISS."
(let ((axis-point (new 'stack-no-clear 'vector))
(end-center (new 'stack-no-clear 'vector)))
0.0
@@ -217,7 +206,9 @@
(dotimes (vertex-index 10)
(vector-matrix*! (-> rotated-vertices vert vertex-index) (-> vertices vert vertex-index) rotation-matrix)
(camera-line (-> vertices vert vertex-index) (-> rotated-vertices vert vertex-index) color)
(if (nonzero? vertex-index) (camera-line (-> vertices vert vertex-index) (-> vertices vert (+ vertex-index -1)) color)))
(let ((swap vertices)) (set! vertices rotated-vertices) (set! rotated-vertices swap)))))
(if (nonzero? vertex-index)
(camera-line (-> vertices vert vertex-index) (-> vertices vert (+ vertex-index -1)) color)))
(let ((swap vertices))
(set! vertices rotated-vertices) (set! rotated-vertices swap)))))
0
(none)))
+5
View File
@@ -202,6 +202,11 @@
(defmacro current-frame ()
`(-> *display* frames (-> *display* on-screen) frame))
(defmacro add-profile-frame! (r g b a name)
"Add a colored marker to the current profile bar in debug builds."
`(if *debug-segment*
(add-frame (-> (current-frame) profile-bar 0) ,name (static-rgba ,r ,g ,b ,a))))
(defmacro current-time ()
`(-> *display* base-frame-counter))
+208 -252
View File
@@ -15,7 +15,7 @@
(defun-recursive mem-usage-bsp-tree none ((header bsp-header) (node bsp-node) (mem-use memory-usage-block) (flags mem-usage-flags))
"Recursively count the 32-byte internal nodes reachable from node in the BSP-node memory
category. Nonpositive terminal children are not followed; header and flags are unused."
category."
(cond
((zero? node))
(else
@@ -30,238 +30,173 @@
(defmethod mem-usage ((this bsp-header) (mem-use memory-usage-block) (flags mem-usage-flags))
"Account for this header's owned level data and delegate memory accounting to its drawable
trees and cameras."
;; start off by setting the current bsp
;; seems unused?
(set! (-> mem-use work-bsp) this)
;; this seems slightly wrong, we count the file-info toward array.
(when (nonzero? (-> this info))
(mem-usage-add! mem-use array 1 (asize-of (-> this info))))
;; measure the drawable trees.
(if (nonzero? (-> this drawable-trees)) (mem-usage (-> this drawable-trees) mem-use flags))
;; add stuff
(if (nonzero? (-> this drawable-trees))
(mem-usage (-> this drawable-trees) mem-use flags))
;; set some names for categories that don't use the normal macros
(set! (-> mem-use length) (max 63 (-> mem-use length)))
(set! (-> mem-use data 43 name) "entity")
(set! (-> mem-use data 44 name) "camera")
(set! (-> mem-use data 62 name) "pat")
(set! (-> mem-use data 58 name) "bsp-node")
;; add the bsp-header itself
(mem-usage-add! mem-use bsp-main 1 400)
;; add the visible list
(mem-usage-add! mem-use bsp-leaf-vis-self 1 (-> this visible-list-length))
;; add the unk-data-0
(mem-usage-add! mem-use bsp-misc 1 (* (-> this texture-remap-table-len) 8))
;; add the unk-data-1
(mem-usage-add! mem-use bsp-misc 1 (* (-> this texture-page-count) 4))
;; add unk-zero-0
(when (nonzero? (-> this unk-zero-0))
(mem-usage-add! mem-use bsp-misc 1 (asize-of (-> this unk-zero-0))))
;; add adgifs
(when (nonzero? (-> this adgifs))
(mem-usage-add! mem-use bsp-misc 1 (asize-of (-> this adgifs))))
;; add boxes
(when (nonzero? (-> this boxes))
(mem-usage-add! mem-use bsp-misc 1 (asize-of (-> this boxes)))
;; add box indices
(when (nonzero? (-> this split-box-indices))
(mem-usage-add! mem-use bsp-misc 1 (* (-> this boxes length) 2))))
;; add actor-birth-order
(when (nonzero? (-> this actor-birth-order))
;; add actors
(mem-usage-add! mem-use bsp-misc 1 (* (-> this actors length) 4)))
;; add pat
(+! (-> mem-use data 62 count) (-> this pat-length))
(let ((pat-bytes (* (-> this pat-length) 4)))
(+! (-> mem-use data 62 used) pat-bytes)
(+! (-> mem-use data 62 total) (logand -16 (+ pat-bytes 15))))
;; add cameras
(let ((cameras (-> this cameras)))
(when (nonzero? cameras)
(dotimes (i (-> cameras length))
(mem-usage (-> cameras i) mem-use (logior flags (mem-usage-flags resource-camera))))))
;; add the tree itself
(mem-usage-bsp-tree this (the-as bsp-node (-> this nodes)) mem-use flags)
;; recursively add bsp tree nodes
(mem-usage-bsp-tree this (-> this nodes 0) mem-use flags)
this)
(defmethod login ((this bsp-header))
"Log in the level's drawable trees and every ADGIF shader."
"Log in the level's drawable trees and every ADGIF shader.
This method takes too long to run and level.gc's login state
machine splits the work over multiple frames."
;; login our drawables
(if (nonzero? (-> this drawable-trees)) (login (-> this drawable-trees)))
(if (nonzero? (-> this drawable-trees))
(login (-> this drawable-trees)))
;; login our shaders
(when (nonzero? (-> this adgifs))
(let ((shaders (-> this adgifs))) (dotimes (i (-> shaders length)) (adgif-shader-login-no-remap (-> shaders data i)))))
(let ((shaders (-> this adgifs)))
(dotimes (i (-> shaders length))
(adgif-shader-login-no-remap (-> shaders data i)))))
this)
(define *test-shrub* 0) ;; unused.
(defmacro setup-drawing-registers ()
"Set VU0 registers to hold camera constants used in background rendering code."
`(let ((math-cam *math-camera*))
(with-vf (vf16 vf17 vf18 vf19 vf20 vf21 vf22 vf23 vf24 vf25 vf26 vf27 vf28 vf29 vf30 vf31)
:rw 'write
(.lvf vf16 (&-> math-cam plane 0 quad))
(.lvf vf17 (&-> math-cam plane 1 quad))
(.lvf vf18 (&-> math-cam plane 2 quad))
(.lvf vf19 (&-> math-cam plane 3 quad))
(.lvf vf20 (&-> math-cam shrub-mat vector 0 quad))
(.lvf vf21 (&-> math-cam shrub-mat vector 1 quad))
(.lvf vf22 (&-> math-cam shrub-mat vector 2 quad))
(.lvf vf23 (&-> math-cam shrub-mat vector 3 quad))
(.lvf vf24 (&-> math-cam camera-rot vector 0 quad))
(.lvf vf25 (&-> math-cam camera-rot vector 1 quad))
(.lvf vf26 (&-> math-cam camera-rot vector 2 quad))
(.lvf vf27 (&-> math-cam camera-rot vector 3 quad))
(.lvf vf28 (&-> math-cam camera-temp vector 0 quad))
(.lvf vf29 (&-> math-cam camera-temp vector 1 quad))
(.lvf vf30 (&-> math-cam camera-temp vector 2 quad))
(.lvf vf31 (&-> math-cam camera-temp vector 3 quad)))))
(defmethod draw ((this bsp-header) (other-draw bsp-header) (disp-frame display-frame))
"Prepare scratchpad visibility, subdivision state, and math-camera VU registers; draw the
level's drawable trees and three foreground engines for the current display frame."
(local-vars (inverted-quad uint128) (flipped-quad uint128) (zero-quad uint128))
(set! zero-quad (the-as uint128 0))
(let ((lev (-> this level)))
;; set up some stuff in the scratchpad
(set! (-> (scratchpad-object terrain-bsp :offset TERRAIN_BSP_SCRATCHPAD) lev-index) (-> lev index))
(set! (-> (scratchpad-object terrain-bsp :offset TERRAIN_BSP_SCRATCHPAD) mood) (-> lev mood))
;; update the subdivision settings
(if *artist-use-menu-subdiv*
(update-subdivide-settings! *subdivide-settings* *math-camera* 3)
(update-subdivide-settings! *subdivide-settings* *math-camera* (-> lev index)))
;; at this point we are no longer adding textures, so we can mark the end of the
;; textures with interrupts to get rendering VIF interrupts for profiling.
(add-irq-to-tex-buckets! lev)
;; upload the visible list to the scratchpad.
;; the final result of all visibility calculations is stored in vis-bits.
;; this goes at the end of the scratchpad.
(let ((vis-list-qwc (/ (+ (-> this visible-list-length) 15) 16)))
(dma-send-to-spr-no-flush (scratchpad-object uint :offset VISIBLE_LIST_SCRATCHPAD)
(the-as uint (-> lev vis-bits))
(the-as uint vis-list-qwc)
#f)))
;; this is a race condition with the previous DMA transfer.
;; probably the DMA wins though.
;; this messes with the visibility bits to invert what was just uploaded
(when *artist-flip-visible*
(let ((vis-list-qwc2 (/ (+ (-> this visible-list-length) 15) 16))
(vis-list-spad (scratchpad-object (pointer uint128) :offset VISIBLE_LIST_SCRATCHPAD))
(vis-list-lev (the-as (pointer uint128) (-> this all-visible-list))))
;; iterate through all qw's in the visible list.
(dotimes (current-qw vis-list-qwc2)
;; invert the stuff in the spad list
(let ((scratch-quad (-> vis-list-spad current-qw)))
;; note: this isn't great x86 code, but it's probably fine.
(.pnor inverted-quad scratch-quad zero-quad))
;; and with the all visible list so we don't
;; accidentally turn on the vis bit for something that
;; doesn't exist.
(let ((all-visible-quad (-> vis-list-lev current-qw))) (.pand flipped-quad inverted-quad all-visible-quad))
(set! (-> vis-list-spad current-qw) flipped-quad))))
;; set up the math camera registers
(let ((math-cam *math-camera*))
(with-vf (vf16 vf17 vf18 vf19 vf20 vf21 vf22 vf23 vf24 vf25 vf26 vf27 vf28 vf29 vf30 vf31)
:rw 'write
(.lvf vf16 (&-> math-cam plane 0 quad))
(.lvf vf17 (&-> math-cam plane 1 quad))
(.lvf vf18 (&-> math-cam plane 2 quad))
(.lvf vf19 (&-> math-cam plane 3 quad))
(.lvf vf20 (&-> math-cam shrub-mat vector 0 quad))
(.lvf vf21 (&-> math-cam shrub-mat vector 1 quad))
(.lvf vf22 (&-> math-cam shrub-mat vector 2 quad))
(.lvf vf23 (&-> math-cam shrub-mat vector 3 quad))
(.lvf vf24 (&-> math-cam camera-rot vector 0 quad))
(.lvf vf25 (&-> math-cam camera-rot vector 1 quad))
(.lvf vf26 (&-> math-cam camera-rot vector 2 quad))
(.lvf vf27 (&-> math-cam camera-rot vector 3 quad))
(.lvf vf28 (&-> math-cam camera-temp vector 0 quad))
(.lvf vf29 (&-> math-cam camera-temp vector 1 quad))
(.lvf vf30 (&-> math-cam camera-temp vector 2 quad))
(.lvf vf31 (&-> math-cam camera-temp vector 3 quad))))
;; draw the drawables!
;; start a profile bar.
(when (nonzero? (-> this drawable-trees))
(if *debug-segment*
(add-frame (-> *display* frames (-> *display* on-screen) frame profile-bar 0)
'draw
(new 'static 'rgba :r #x40 :b #x40 :a #x80)))
;; draw!
(let ((trees (-> this drawable-trees))) (draw trees trees disp-frame))
;; end a profile bar
(if *debug-segment*
(add-frame (-> *display* frames (-> *display* on-screen) frame profile-bar 0)
'draw
(new 'static 'rgba :r #x80 :g #xc0 :a #x80))))
;; run the foreground system (0 check added)
(when (nonzero? foreground-engine-execute)
(local-vars (inverted-quad uint128) (flipped-quad uint128))
(slet (spad terrain-context)
(let ((lev (-> this level)))
;; cache level info in scratchpad
(set! (-> spad bsp lev-index) (-> lev index))
(set! (-> spad bsp mood) (-> lev mood))
;; update background rendering subdivision (level of detail) thresholds consumed by
;; renderers. level "3" is set from the menu
(if *artist-use-menu-subdiv*
(update-subdivide-settings! *subdivide-settings* *math-camera* 3)
(update-subdivide-settings! *subdivide-settings* *math-camera* (-> lev index)))
;; set up interrupts on texture bucket completion
;; texture upload DMA is already built, using info from the last frame.
(add-irq-to-tex-buckets! lev)
;; upload visibility list to scratchpad (no sync happens, this is a race)
(let ((vis-list-qwc (/ (+ (-> this visible-list-length) 15) 16)))
(dma-send-to-spr-no-flush (-> spad work background vis-list) (-> lev vis-bits) (the-as uint vis-list-qwc) #f)))
;; flip visibility bits. Note that not every bit in the string is used and we only toggle used bits.
(when *artist-flip-visible*
(let ((vis-list-qwc2 (/ (+ (-> this visible-list-length) 15) 16))
(vis-list-spad (the-as (pointer uint128) (-> spad work background vis-list)))
(vis-list-lev (the-as (pointer uint128) (-> this all-visible-list))))
(dotimes (current-qw vis-list-qwc2)
;; invert all bits
(let ((scratch-quad (-> vis-list-spad current-qw)))
(.pnor inverted-quad scratch-quad 0))
;; mask with all-visible list
(let ((all-visible-quad (-> vis-list-lev current-qw)))
(.pand flipped-quad inverted-quad all-visible-quad))
(set! (-> vis-list-spad current-qw) flipped-quad))))
;; set up VU0 registers prior to traversing drawable trees
(setup-drawing-registers)
(when (nonzero? (-> this drawable-trees))
(add-profile-frame! #x40 0 #x40 #x80 'draw)
(let ((trees (-> this drawable-trees)))
;; draw! This ends up doing relatively little, most trees defer their drawing,
;; and this just does book-keeping on what needs drawing later.
(draw trees trees disp-frame))
(add-profile-frame! #x80 #xc0 0 #x80 'draw))
;; draw foreground geometry. The foreground engines store the process-drawables associated with
;; this level.
(let ((frame (current-frame)))
;; 0
(foreground-engine-execute (-> this level foreground-draw-engine 0)
frame
(-> (scratchpad-object terrain-bsp :offset TERRAIN_BSP_SCRATCHPAD) lev-index)
0)
;; 1
(foreground-engine-execute (-> this level foreground-draw-engine 1)
frame
(-> (scratchpad-object terrain-bsp :offset TERRAIN_BSP_SCRATCHPAD) lev-index)
1)
;; 2
(foreground-engine-execute (-> this level foreground-draw-engine 2)
frame
(-> (scratchpad-object terrain-bsp :offset TERRAIN_BSP_SCRATCHPAD) lev-index)
2)))
(none))
(foreground-engine-execute (-> this level foreground-draw-engine 0) frame (-> spad bsp lev-index) 0)
(foreground-engine-execute (-> this level foreground-draw-engine 1) frame (-> spad bsp lev-index) 1)
(foreground-engine-execute (-> this level foreground-draw-engine 2) frame (-> spad bsp lev-index) 2))
(none)))
(defmethod debug-draw ((this bsp-header) (other-draw drawable) (disp-frame display-frame))
"Prepare the level's scratchpad visibility and math-camera VU registers, then submit debug
geometry from its drawable trees."
(let ((lev (-> this level)))
;; set up some stuff in the scratchpad
(set! (-> (scratchpad-object terrain-bsp :offset TERRAIN_BSP_SCRATCHPAD) lev-index) (-> lev index))
(set! (-> (scratchpad-object terrain-bsp :offset TERRAIN_BSP_SCRATCHPAD) mood) (-> lev mood))
(add-irq-to-tex-buckets! lev)
(let ((vis-list-qwc (/ (+ (-> this visible-list-length) 15) 16)))
(dma-send-to-spr-no-flush (scratchpad-object uint :offset VISIBLE_LIST_SCRATCHPAD)
(the-as uint (-> lev vis-bits))
(the-as uint vis-list-qwc)
#f)))
(let ((math-cam *math-camera*))
(with-vf (vf16 vf17 vf18 vf19 vf20 vf21 vf22 vf23 vf24 vf25 vf26 vf27 vf28 vf29 vf30 vf31)
:rw 'write
(.lvf vf16 (&-> math-cam plane 0 quad))
(.lvf vf17 (&-> math-cam plane 1 quad))
(.lvf vf18 (&-> math-cam plane 2 quad))
(.lvf vf19 (&-> math-cam plane 3 quad))
(.lvf vf20 (&-> math-cam shrub-mat vector 0 quad))
(.lvf vf21 (&-> math-cam shrub-mat vector 1 quad))
(.lvf vf22 (&-> math-cam shrub-mat vector 2 quad))
(.lvf vf23 (&-> math-cam shrub-mat vector 3 quad))
(.lvf vf24 (&-> math-cam camera-rot vector 0 quad))
(.lvf vf25 (&-> math-cam camera-rot vector 1 quad))
(.lvf vf26 (&-> math-cam camera-rot vector 2 quad))
(.lvf vf27 (&-> math-cam camera-rot vector 3 quad))
(.lvf vf28 (&-> math-cam camera-temp vector 0 quad))
(.lvf vf29 (&-> math-cam camera-temp vector 1 quad))
(.lvf vf30 (&-> math-cam camera-temp vector 2 quad))
(.lvf vf31 (&-> math-cam camera-temp vector 3 quad))))
(when (nonzero? (-> this drawable-trees))
(if *debug-segment*
(add-frame (-> *display* frames (-> *display* on-screen) frame profile-bar 0)
'draw
(new 'static 'rgba :r #x40 :b #x40 :a #x80)))
(let ((trees (-> this drawable-trees))) (debug-draw trees trees disp-frame))
(if *debug-segment*
(add-frame (-> *display* frames (-> *display* on-screen) frame profile-bar 0)
'draw
(new 'static 'rgba :r #x80 :g #xc0 :a #x80))))
(none))
(slet (spad terrain-context)
(let ((lev (-> this level)))
(set! (-> spad bsp lev-index) (-> lev index))
(set! (-> spad bsp mood) (-> lev mood))
(add-irq-to-tex-buckets! lev)
(let ((vis-list-qwc (/ (+ (-> this visible-list-length) 15) 16)))
(dma-send-to-spr-no-flush (-> spad work background vis-list) (-> lev vis-bits) (the-as uint vis-list-qwc) #f)))
(setup-drawing-registers)
(when (nonzero? (-> this drawable-trees))
(add-profile-frame! #x40 0 #x40 #x80 'draw)
(let ((trees (-> this drawable-trees)))
(debug-draw trees trees disp-frame))
(add-profile-frame! #x80 #xc0 0 #x80 'draw))
(none)))
(defmethod collect-stats ((this bsp-header))
"Upload the current visibility bits and math-camera VU registers, then collect renderer
statistics from the level's drawable trees."
(let ((lev (-> this level))
(vis-list-qwc (/ (+ (-> this visible-list-length) 15) 16)))
(dma-send-to-spr-no-flush (scratchpad-object uint :offset VISIBLE_LIST_SCRATCHPAD)
(the-as uint (-> lev vis-bits))
(the-as uint vis-list-qwc)
#f))
(let ((math-cam *math-camera*))
(with-vf (vf16 vf17 vf18 vf19 vf20 vf21 vf22 vf23 vf24 vf25 vf26 vf27 vf28 vf29 vf30 vf31)
:rw 'write
(.lvf vf16 (&-> math-cam plane 0 quad))
(.lvf vf17 (&-> math-cam plane 1 quad))
(.lvf vf18 (&-> math-cam plane 2 quad))
(.lvf vf19 (&-> math-cam plane 3 quad))
(.lvf vf20 (&-> math-cam shrub-mat vector 0 quad))
(.lvf vf21 (&-> math-cam shrub-mat vector 1 quad))
(.lvf vf22 (&-> math-cam shrub-mat vector 2 quad))
(.lvf vf23 (&-> math-cam shrub-mat vector 3 quad))
(.lvf vf24 (&-> math-cam camera-rot vector 0 quad))
(.lvf vf25 (&-> math-cam camera-rot vector 1 quad))
(.lvf vf26 (&-> math-cam camera-rot vector 2 quad))
(.lvf vf27 (&-> math-cam camera-rot vector 3 quad))
(.lvf vf28 (&-> math-cam camera-temp vector 0 quad))
(.lvf vf29 (&-> math-cam camera-temp vector 1 quad))
(.lvf vf30 (&-> math-cam camera-temp vector 2 quad))
(.lvf vf31 (&-> math-cam camera-temp vector 3 quad))))
(if (nonzero? (-> this drawable-trees)) (collect-stats (-> this drawable-trees)))
(none))
(slet (spad terrain-context)
(let ((lev (-> this level))
(vis-list-qwc (/ (+ (-> this visible-list-length) 15) 16)))
(dma-send-to-spr-no-flush (-> spad work background vis-list) (-> lev vis-bits) (the-as uint vis-list-qwc) #f))
(setup-drawing-registers)
(if (nonzero? (-> this drawable-trees))
(collect-stats (-> this drawable-trees)))
(none)))
(#unless PC_PORT
(defun bsp-camera-asm ((header bsp-header) (camera-position vector))
@@ -302,36 +237,24 @@
(m result r0)
(jr ra :delay (add sp sp r0)))))
(#when PC_PORT
(defun bsp-camera-asm ((header bsp-header) (camera-position vector))
"Traverse the BSP for camera-position. At each split, select front when
dot(camera-position, plane.xyz) - plane.w is nonnegative. Store the terminal leaf index and
that side's packed neighboring-level flags in header."
(local-vars (plane-side int) (node bsp-node))
(rlet ((vf1 :class vf)
(vf2 :class vf))
(nop!)
(let ((next-node (the-as bsp-node (-> header nodes))))
(.lvf vf1 (&-> camera-position quad))
(label cfg-1)
(b! (< (the-as int next-node) 0) cfg-4 :delay (set! node next-node))
(.lvf vf2 (&-> node plane quad))
(.mul.vf.xyz vf2 vf2 vf1)
(.add.x.vf.y vf2 vf2 vf2)
(.add.z.vf.y vf2 vf2 vf2)
(.sub.w.vf.y vf2 vf2 vf2)
;; The integer sign test reads the y lane's sign after the VU-to-EE transfer.
(.mov plane-side vf2)
(let ((side-flags (-> node front-flags)))
(b! (>= plane-side 0) cfg-1 :delay (set! next-node (the-as bsp-node (-> node front))))
(set! side-flags (-> node back-flags))
(b! #t cfg-1 :delay (set! next-node (the-as bsp-node (-> node back))))
(label cfg-4)
(set! (-> header current-leaf-idx) (the-as uint next-node))
(set! (-> header current-bsp-flags) side-flags)
))
0
(none))))
(defun bsp-camera-asm ((bsp bsp-header) (pos vector))
"Look up the leaf node containing pos and store its index in flags in the bsp-header"
(let ((iter (-> bsp nodes 0))
(flags (the uint 0)))
(while (>= (the int iter) 0)
;; do front/back test
(let ((plane-test (- (vector-dot pos (-> iter plane)) (-> iter plane w))))
(cond
((>= plane-test 0.0)
(set! flags (-> iter front-flags))
(set! iter (the bsp-node (-> iter front))))
(else
(set! flags (-> iter back-flags))
(set! iter (the bsp-node (-> iter back)))))))
;; update header - lower 16-bits of the final front/back value
(set! (-> bsp current-leaf-idx) (the uint iter))
(set! (-> bsp current-bsp-flags) flags))
(none))
;;;;;;;;;;;;;;;
;; Collision
@@ -356,10 +279,12 @@
(none))
(defmethod collide-ray ((this bsp-header) (length int) (result collide-list))
"Forward the active swept-sphere ray query to every drawable tree and append their geometry to
result. length is passed through for child implementations that use it."
"Forward the active swept-sphere ray query to every drawable tree and append their geometry
to result. length is passed through for child implementations that use it."
(+! (-> *collide-stats* calls) 1)
(let ((trees (-> this drawable-trees))) (dotimes (i (-> trees length)) (collide-ray (-> trees trees i) length result)))
(let ((trees (-> this drawable-trees)))
(dotimes (i (-> trees length))
(collide-ray (-> trees trees i) length result)))
(none))
(defmethod collect-ambients ((this bsp-header) (query-sphere sphere) (length int) (result ambient-list))
@@ -400,10 +325,7 @@
"Print the frame's collision counts, per-call averages, and target timing breakdown, then clear
the counters and restart all three collision stopwatches."
;; for some unknown reason, we profile this.
(if *debug-segment*
(add-frame (-> *display* frames (-> *display* on-screen) frame profile-bar 0)
'draw
(new 'static 'rgba :r #x40 :b #x40 :a #x80)))
(add-profile-frame! #x40 0 #x40 #x80 'draw)
(format *stdcon* "~0k frags tris output~%")
(print-cl-stat (-> *collide-stats* other) "other")
(format *stdcon* "~0k---------------------------------------------------------------~%")
@@ -431,43 +353,77 @@
(stopwatch-init (-> *collide-stats* total-target))
(stopwatch-init (-> *collide-stats* target-cache-fill))
(stopwatch-init (-> *collide-stats* target-ray-poly))
(if *debug-segment*
(add-frame (-> *display* frames (-> *display* on-screen) frame profile-bar 0) 'draw (new 'static 'rgba :b #xff :a #x80)))
(add-profile-frame! 0 0 #xff #x80 'draw)
0
(none))
(defun level-remap-texture ((tex-id texture-id))
"Binary-search the active level's sorted texture-remap table using the texture ID's upper 24
bits. Return the original ID when no level BSP or matching entry exists; otherwise return the
entry's remapped ID with the canonical #x14 low-byte tag."
(let ((bsp-hdr (-> *level* log-in-level-bsp)))
(when bsp-hdr
(let* ((table-size (-> bsp-hdr texture-remap-table-len)) ;; in 64-bit entries
(search-start (-> bsp-hdr texture-remap-table))
(table-start search-start)
(entry-align-mask (the-as uint #xfffffff8)) ;; mask for table entry addresses
(lookup-id (logand (the-as uint #xffffff00) tex-id)) ;; bits of tex-id we care about
(search-end (&+ table-start (* table-size 8))))
;; top of binary search
(label cfg-2)
;; if we didn't find anything, quit
(b! (= search-start search-end) cfg-8)
;; find the middle entry.
(let ((midpoint (logand (/ (+ (the-as int search-start) (the-as int search-end)) 2) entry-align-mask)))
;; how did we do?
(let ((diff (- (-> (the-as (pointer int32) midpoint) 0) (the-as int lookup-id))))
(b! (zero? diff) cfg-7)
(b! (< diff 0) cfg-6 :delay (nop!)))
;; in the lower section
(b! #t cfg-2 :delay (set! search-end (the-as (pointer uint64) midpoint)))
;; in the upper section (not including the midpoint)
(label cfg-6)
(b! #t cfg-2 :delay (set! search-start (the-as (pointer uint64) (+ (the-as int midpoint) 8))))
;; exact match
(label cfg-7)
;; The key ignores the low byte. The matched value has that byte clear, so restore the
;; canonical #x14 tag used by in-game texture IDs.
(set! tex-id (the-as texture-id (logior (-> (the-as (pointer int32) midpoint) 1) 20)))))
(label cfg-8)
0))
(the-as texture-id tex-id))
"Remap an actor texture ID to its combined level texture-page ID."
;; actor art data references textures in a per-actor texture page.
;; levels contain a larger texture page merging all the per-actor pages and this function
;; remaps from the per-actor texture page to the big level page.
;; simplified GOAL version
(let ((bsp (-> *level* log-in-level-bsp)))
(when bsp
(let ((table (the-as (pointer texture-id) (-> bsp texture-remap-table)))
(lookup-id (the texture-id (logand tex-id #xffffff00)))
(begin 0)
(end (-> bsp texture-remap-table-len)))
(while (< begin end)
(let* ((midpoint (/ (+ begin end) 2))
(entry-index (* midpoint 2))
(source-id (-> table entry-index)))
(cond
((< source-id lookup-id)
(set! begin (+ midpoint 1)))
((> source-id lookup-id)
(set! end midpoint))
(else
(let ((remapped-id (-> table (+ entry-index 1))))
;; restore 0x14 here: low bytes of texture-id are part of A+D
;; format data, this 0x14 specifies TEX1_1 register.
(return (the texture-id (logior remapped-id #x14)))))))))))
tex-id)
(#unless PC_PORT
(defun level-remap-texture ((tex-id texture-id))
"Binary-search the active level's sorted texture-remap table using the texture ID's upper 24
bits. Return the original ID when no level BSP or matching entry exists; otherwise return the
entry's remapped ID with the canonical #x14 low-byte tag."
;; actor art data references textures in a per-actor texture page.
;; levels contain a larger texture page merging all the per-actor pages and this function
;; remaps from the per-actor texture page to the big level page.
(let ((bsp-hdr (-> *level* log-in-level-bsp)))
(when bsp-hdr
(let* ((table-size (-> bsp-hdr texture-remap-table-len)) ;; in 64-bit entries
(search-start (-> bsp-hdr texture-remap-table))
(table-start search-start)
(entry-align-mask (the-as uint #xfffffff8)) ;; mask for table entry addresses
(lookup-id (logand (the-as uint #xffffff00) tex-id)) ;; bits of tex-id we care about
(search-end (&+ table-start (* table-size 8))))
;; top of binary search
(label cfg-2)
;; if we didn't find anything, quit
(b! (= search-start search-end) cfg-8)
;; find the middle entry.
(let ((midpoint (logand (/ (+ (the-as int search-start) (the-as int search-end)) 2) entry-align-mask)))
;; how did we do?
(let ((diff (- (-> (the-as (pointer int32) midpoint) 0) (the-as int lookup-id))))
(b! (zero? diff) cfg-7)
(b! (< diff 0) cfg-6 :delay (nop!)))
;; in the lower section
(b! #t cfg-2 :delay (set! search-end (the-as (pointer uint64) midpoint)))
;; in the upper section (not including the midpoint)
(label cfg-6)
(b! #t cfg-2 :delay (set! search-start (the-as (pointer uint64) (+ (the-as int midpoint) 8))))
;; exact match
(label cfg-7)
;; The key ignores the low byte. The matched value has that byte clear, so restore the
;; canonical #x14 tag used by in-game texture IDs.
(set! tex-id (the-as texture-id (logior (-> (the-as (pointer int32) midpoint) 1) 20)))))
(label cfg-8)
0))
(the-as texture-id tex-id)))