mirror of
https://github.com/open-goal/jak-project
synced 2026-08-20 06:16:35 -04:00
clean up cam-combiner
This commit is contained in:
@@ -3149,6 +3149,110 @@ bool try_to_rewrite_vector_inline_ctor(const Env& env,
|
||||
return false;
|
||||
}
|
||||
|
||||
struct Jak1MatrixRowDeref {
|
||||
RegisterAccess base;
|
||||
std::vector<DerefToken> matrix_tokens;
|
||||
};
|
||||
|
||||
std::optional<RegisterAccess> identity_var(Form* form) {
|
||||
auto* elt = form ? form->try_as_element<SimpleExpressionElement>() : nullptr;
|
||||
if (!elt || elt->expr().kind() != SimpleExpression::Kind::IDENTITY ||
|
||||
!elt->expr().get_arg(0).is_var()) {
|
||||
return {};
|
||||
}
|
||||
return elt->expr().get_arg(0).var();
|
||||
}
|
||||
|
||||
std::optional<TypeSpec> deref_result_type(RegisterAccess base,
|
||||
const std::vector<DerefToken>& tokens,
|
||||
const Env& env) {
|
||||
TypeSpec current = env.get_variable_type(base, true);
|
||||
for (const auto& token : tokens) {
|
||||
if (token.kind() == DerefToken::Kind::FIELD_NAME) {
|
||||
try {
|
||||
current = env.dts->ts.lookup_field_info(current.base_type(), token.field_name()).type;
|
||||
} catch (const std::exception&) {
|
||||
return {};
|
||||
}
|
||||
} else if (token.kind() == DerefToken::Kind::INTEGER_CONSTANT ||
|
||||
token.kind() == DerefToken::Kind::INTEGER_EXPRESSION) {
|
||||
if (!current.has_single_arg()) {
|
||||
return {};
|
||||
}
|
||||
// get_single_arg() refers into current, so copy it before assignment destroys the argument
|
||||
// storage.
|
||||
const auto element_type = current.get_single_arg();
|
||||
current = element_type;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
std::optional<Jak1MatrixRowDeref> match_jak1_matrix_row(Form* form, int row, const Env& env) {
|
||||
auto* deref = form ? form->try_as_element<DerefElement>() : nullptr;
|
||||
if (!deref || deref->is_addr_of() || deref->tokens().size() < 3) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto suffix = deref->tokens().size() - 3;
|
||||
if (!deref->tokens().at(suffix).is_field_name("vector") ||
|
||||
!deref->tokens().at(suffix + 1).is_int(row) ||
|
||||
!deref->tokens().at(suffix + 2).is_field_name("quad")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto base = identity_var(deref->base());
|
||||
if (!base) {
|
||||
return {};
|
||||
}
|
||||
std::vector<DerefToken> matrix_tokens(deref->tokens().begin(), deref->tokens().begin() + suffix);
|
||||
auto matrix_type = deref_result_type(*base, matrix_tokens, env);
|
||||
if (!matrix_type || *matrix_type != TypeSpec("matrix")) {
|
||||
return {};
|
||||
}
|
||||
return Jak1MatrixRowDeref{*base, std::move(matrix_tokens)};
|
||||
}
|
||||
|
||||
bool same_deref_tokens(std::vector<DerefToken> lhs, std::vector<DerefToken> rhs, const Env& env) {
|
||||
if (lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < lhs.size(); ++i) {
|
||||
if (lhs.at(i).kind() != rhs.at(i).kind()) {
|
||||
return false;
|
||||
}
|
||||
switch (lhs.at(i).kind()) {
|
||||
case DerefToken::Kind::FIELD_NAME:
|
||||
if (lhs.at(i).field_name() != rhs.at(i).field_name()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DerefToken::Kind::INTEGER_CONSTANT:
|
||||
if (lhs.at(i).int_constant() != rhs.at(i).int_constant()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DerefToken::Kind::INTEGER_EXPRESSION:
|
||||
if (lhs.at(i).expr()->to_string(env) != rhs.at(i).expr()->to_string(env)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Form* append_deref_tokens(Form* base, const std::vector<DerefToken>& tokens, FormPool& pool) {
|
||||
if (tokens.empty()) {
|
||||
return base;
|
||||
}
|
||||
return pool.form<DerefElement>(base, false, tokens);
|
||||
}
|
||||
|
||||
bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack& stack) {
|
||||
if (env.func->name() == "matrix-copy!" ||
|
||||
env.func->name() == "(method 63 collide-shape-moving)") {
|
||||
@@ -3160,49 +3264,58 @@ bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack
|
||||
// first, check the loads. they should be something like source = (-> MAT vec quad)
|
||||
// MAT should always be a variable
|
||||
std::vector<RegisterAccess> load_src_ras, store_dest_ras, store_src_ras;
|
||||
std::vector<DerefToken> src_matrix_tokens, dst_matrix_tokens;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Matcher deref_matcher;
|
||||
if (env.version == GameVersion::Jak1) {
|
||||
deref_matcher = Matcher::deref(
|
||||
Matcher::any_reg(0), false,
|
||||
{DerefTokenMatcher::string("vector"), DerefTokenMatcher::integer(i),
|
||||
DerefTokenMatcher::string("quad")});
|
||||
auto row = match_jak1_matrix_row(matrix_entries->at(i).source, i, env);
|
||||
if (!row || (i && !same_deref_tokens(src_matrix_tokens, row->matrix_tokens, env))) {
|
||||
return false;
|
||||
}
|
||||
load_src_ras.push_back(row->base);
|
||||
if (!i) {
|
||||
src_matrix_tokens = std::move(row->matrix_tokens);
|
||||
}
|
||||
} else {
|
||||
const char* names[] = {"rvec", "uvec", "fvec", "trans"};
|
||||
deref_matcher =
|
||||
Matcher::deref(Matcher::any_reg(0), false,
|
||||
{DerefTokenMatcher::string(names[i]),
|
||||
DerefTokenMatcher::string("quad")});
|
||||
auto deref_matcher = Matcher::deref(
|
||||
Matcher::any_reg(0), false,
|
||||
{DerefTokenMatcher::string(names[i]), DerefTokenMatcher::string("quad")});
|
||||
auto mr = match(deref_matcher, matrix_entries->at(i).source, &env);
|
||||
if (!mr.matched) {
|
||||
return false;
|
||||
}
|
||||
load_src_ras.push_back(mr.maps.regs.at(0).value());
|
||||
}
|
||||
auto mr = match(deref_matcher, matrix_entries->at(i).source, &env);
|
||||
if (!mr.matched) {
|
||||
return false;
|
||||
}
|
||||
load_src_ras.push_back(mr.maps.regs.at(0).value());
|
||||
}
|
||||
|
||||
// check the stores
|
||||
for (int i = 4; i < 8; i++) {
|
||||
Matcher dst_matcher;
|
||||
if (env.version == GameVersion::Jak1) {
|
||||
dst_matcher = Matcher::deref(
|
||||
Matcher::any_reg(0), false,
|
||||
{DerefTokenMatcher::string("vector"), DerefTokenMatcher::integer(i - 4),
|
||||
DerefTokenMatcher::string("quad")});
|
||||
auto* set = dynamic_cast<SetFormFormElement*>(matrix_entries->at(i).elt);
|
||||
auto row = set ? match_jak1_matrix_row(set->dst(), i - 4, env) : std::nullopt;
|
||||
auto src = set ? identity_var(set->src()) : std::nullopt;
|
||||
if (!row || !src ||
|
||||
(i != 4 && !same_deref_tokens(dst_matrix_tokens, row->matrix_tokens, env))) {
|
||||
return false;
|
||||
}
|
||||
store_dest_ras.push_back(row->base);
|
||||
store_src_ras.push_back(*src);
|
||||
if (i == 4) {
|
||||
dst_matrix_tokens = std::move(row->matrix_tokens);
|
||||
}
|
||||
} else {
|
||||
const char* names[] = {"rvec", "uvec", "fvec", "trans"};
|
||||
dst_matcher =
|
||||
Matcher::deref(Matcher::any_reg(0), false,
|
||||
{DerefTokenMatcher::string(names[i - 4]),
|
||||
DerefTokenMatcher::string("quad")});
|
||||
auto dst_matcher = Matcher::deref(
|
||||
Matcher::any_reg(0), false,
|
||||
{DerefTokenMatcher::string(names[i - 4]), DerefTokenMatcher::string("quad")});
|
||||
Matcher matcher = Matcher::set(dst_matcher, Matcher::any_reg(1));
|
||||
auto mr = match(matcher, matrix_entries->at(i).elt, &env);
|
||||
if (!mr.matched) {
|
||||
return false;
|
||||
}
|
||||
store_dest_ras.push_back(mr.maps.regs.at(0).value());
|
||||
store_src_ras.push_back(mr.maps.regs.at(1).value());
|
||||
}
|
||||
Matcher matcher = Matcher::set(dst_matcher, Matcher::any_reg(1));
|
||||
auto mr = match(matcher, matrix_entries->at(i).elt, &env);
|
||||
if (!mr.matched) {
|
||||
return false;
|
||||
}
|
||||
store_dest_ras.push_back(mr.maps.regs.at(0).value());
|
||||
store_src_ras.push_back(mr.maps.regs.at(1).value());
|
||||
}
|
||||
|
||||
// check loads are all loading from the same matrix
|
||||
@@ -3226,13 +3339,15 @@ bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack
|
||||
}
|
||||
}
|
||||
|
||||
// check types
|
||||
if (env.get_variable_type(load_src_ras.at(0), true) != TypeSpec("matrix")) {
|
||||
return false;
|
||||
}
|
||||
// For Jak 1, match_jak1_matrix_row checked the types after following the dereference prefix.
|
||||
if (env.version != GameVersion::Jak1) {
|
||||
if (env.get_variable_type(load_src_ras.at(0), true) != TypeSpec("matrix")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (env.get_variable_type(store_dest_ras.at(0), true) != TypeSpec("matrix")) {
|
||||
return false;
|
||||
if (env.get_variable_type(store_dest_ras.at(0), true) != TypeSpec("matrix")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
stack.pop(8);
|
||||
@@ -3248,6 +3363,7 @@ bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack
|
||||
} else {
|
||||
// lg::info(" popped src: {}", src_repopped->to_string(env));
|
||||
}
|
||||
src_repopped = append_deref_tokens(src_repopped, src_matrix_tokens, pool);
|
||||
|
||||
// src_repopped = matrix_entries->at(0).source;
|
||||
bool found = false;
|
||||
@@ -3261,8 +3377,11 @@ bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack
|
||||
// lg::info(" popped dst: {} {} {}", dst_repopped->to_string(env), ra.reg().to_string(),
|
||||
// ra.mode() == AccessMode::WRITE);
|
||||
}
|
||||
dst_repopped = append_deref_tokens(dst_repopped, dst_matrix_tokens, pool);
|
||||
|
||||
if (found) {
|
||||
// If the matrix is a field of the popped value, the copy result is not the value of that
|
||||
// containing object. Emit it as a standalone side-effecting form instead.
|
||||
if (found && dst_matrix_tokens.empty()) {
|
||||
stack.push_value_to_reg(
|
||||
ra,
|
||||
pool.form<GenericElement>(
|
||||
@@ -7094,7 +7213,19 @@ void TypeOfElement::update_from_stack(const Env& env,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) {
|
||||
mark_popped();
|
||||
value->update_children_from_stack(env, pool, stack, allow_side_effects);
|
||||
// The CFG rewrite replaces several uses in the expanded runtime-type check with this single
|
||||
// expression. The original register-use analysis therefore does not mark the input as consumed,
|
||||
// even though rtype-of is now its last use. Supply the post-rewrite consumption explicitly so a
|
||||
// one-use input can still be propagated into rtype-of.
|
||||
auto value_var = identity_var(value);
|
||||
if (value_var) {
|
||||
value =
|
||||
pop_to_forms({*value_var}, env, pool, stack, allow_side_effects, RegSet{value_var->reg()})
|
||||
.at(0);
|
||||
value->parent_element = this;
|
||||
} else {
|
||||
value->update_children_from_stack(env, pool, stack, allow_side_effects);
|
||||
}
|
||||
result->push_back(this);
|
||||
}
|
||||
////////////////////////
|
||||
|
||||
@@ -17405,6 +17405,30 @@ its matching records and each allocation bitmap begins with every slot free."
|
||||
|
||||
(defenum cam-slave-options
|
||||
:bitfield #t
|
||||
:type uint32
|
||||
(BUTT_CAM)
|
||||
(SAME_SIDE)
|
||||
(MOVE_SPHERICAL)
|
||||
(ALLOW_Z_ROT)
|
||||
(JUMP_PITCHES)
|
||||
(COLLIDE)
|
||||
(FIND_HIDDEN_TARGET)
|
||||
(DRAG)
|
||||
(PLAYER_MOVING_CAMERA)
|
||||
(LINE_OF_SIGHT)
|
||||
(MOVEMENT_BLOCKED)
|
||||
(SHRINK_MAX_ANGLE)
|
||||
(GOTO_GOOD_POINT)
|
||||
(BLOCK_SHIFT_BUTTONS)
|
||||
(BIKE_MODE)
|
||||
(NO_ROTATE)
|
||||
(STICKY_ANGLE)
|
||||
(AIR_EXIT)
|
||||
)
|
||||
|
||||
(defenum cam-slave-options-i
|
||||
:bitfield #t
|
||||
:type int32
|
||||
(BUTT_CAM)
|
||||
(SAME_SIDE)
|
||||
(MOVE_SPHERICAL)
|
||||
@@ -17440,6 +17464,25 @@ its matching records and each allocation bitmap begins with every slot free."
|
||||
(between 3)
|
||||
)
|
||||
|
||||
(defenum cam-track-status
|
||||
:type uint64
|
||||
(use-slave-tracking 0)
|
||||
(track-at-combiner 1)
|
||||
(track-at-dst 2)
|
||||
(track-at-src 3))
|
||||
|
||||
(defenum cam-master-options
|
||||
:type uint32
|
||||
:bitfield #t
|
||||
(ignore-regions 0)
|
||||
(have-target 1)
|
||||
(switch-only-on-ground 2)
|
||||
(set-combiner-axis 3)
|
||||
(flip-combiner 4)
|
||||
(have-ease-to-pos 5)
|
||||
(in-base-region 6)
|
||||
)
|
||||
|
||||
;; - Types
|
||||
|
||||
;; Shared gameplay-camera tuning used for collision movement, input response,
|
||||
@@ -17641,8 +17684,8 @@ its matching records and each allocation bitmap begins with every slot free."
|
||||
(dist-from-dest float :offset-assert 208)
|
||||
(flip-control-axis vector :inline :offset-assert 224)
|
||||
(velocity vector :inline :offset-assert 240)
|
||||
(tracking-status uint64 :offset-assert 256)
|
||||
(tracking-options int32 :offset-assert 264)
|
||||
(tracking-status cam-track-status :offset-assert 256)
|
||||
(tracking-options cam-slave-options-i :offset-assert 264)
|
||||
(tracking cam-rotation-tracker :inline :offset-assert 272)
|
||||
)
|
||||
:heap-base #x170
|
||||
@@ -17675,7 +17718,7 @@ its matching records and each allocation bitmap begins with every slot free."
|
||||
(circular-follow vector :inline :offset-assert 2176)
|
||||
(max-angle-offset float :offset-assert 2192)
|
||||
(max-angle-curr float :offset-assert 2196)
|
||||
(options uint32 :offset-assert 2200)
|
||||
(options cam-slave-options :offset-assert 2200)
|
||||
(cam-entity entity :offset-assert 2204) ; not totally confirmed yet, could be entity-actor
|
||||
(velocity vector :inline :offset-assert 2208)
|
||||
(desired-pos vector :inline :offset-assert 2224)
|
||||
@@ -17745,7 +17788,7 @@ its matching records and each allocation bitmap begins with every slot free."
|
||||
;; active slaves, target transforms and tracking trail, and transition state;
|
||||
;; the combiner produces the final rendered camera.
|
||||
(deftype camera-master (process)
|
||||
((master-options uint32 :offset-assert 112)
|
||||
((master-options cam-master-options :offset-assert 112)
|
||||
(num-slaves int32 :offset-assert 116)
|
||||
(slave (pointer camera-slave) 2 :offset-assert 120)
|
||||
(slave-options uint32 :offset-assert 128)
|
||||
@@ -24540,8 +24583,8 @@ optional bottom depth. Enable ordinary water particles after loading the data."
|
||||
lead with a quartic transition." (function cam-rotation-tracker vector symbol vector))
|
||||
(define-extern slave-set-rotation! "Build tracker's inverse camera rotation from its follow point,
|
||||
optional point of interest and tilt; keep the target in frame, optionally blend toward the new
|
||||
orientation, and remove roll. options-bits is a raw cam-slave-options word carried in a float."
|
||||
(function cam-rotation-tracker vector float float symbol none))
|
||||
orientation, and remove roll."
|
||||
(function cam-rotation-tracker vector cam-slave-options float symbol none))
|
||||
(define-extern camera-slave-debug (function camera-slave none))
|
||||
(define-extern camera-line-rel-len (function vector vector float vector4w none))
|
||||
(define-extern cam-slave-get-flags "Read property's base flag word, set bits from property-on,
|
||||
@@ -30392,14 +30435,6 @@ from the entity name, then load the art group named by the entity type."))
|
||||
(gouraud 2)
|
||||
)
|
||||
|
||||
;; Known camera-master settings exposed on the debug menu.
|
||||
(defenum cam-master-options
|
||||
:type uint64
|
||||
:bitfield #t
|
||||
(ignore-regions 0)
|
||||
(switch-only-on-ground 2)
|
||||
)
|
||||
|
||||
;; - Functions
|
||||
|
||||
(define-extern build-continue-menu
|
||||
|
||||
@@ -309,6 +309,8 @@
|
||||
// that they used the correct number. This will override the decompiler's
|
||||
// automatic detection.
|
||||
"bad_format_strings": {
|
||||
"ERROR <GMJ>: missing camera-slave parameter to *camera-combiner* start-tracking~%": 1,
|
||||
"ERROR <GMJ>: missing camera-slave parameter to *camera-combiner* copy-tracking~%": 1,
|
||||
"ERROR: dma tag has data in reserved bits ~X~%": 0,
|
||||
"#<surface f0:~m f1:~f tf+:~f tf-:~f sf:~f tvv:~m": 5,
|
||||
"ERROR<GMJ>: value of symbol ~A in task-controls is not a task-control~%": 0,
|
||||
|
||||
@@ -1353,7 +1353,7 @@
|
||||
],
|
||||
"(event cam-combiner-active)": [
|
||||
[10, "a0", "vector"],
|
||||
[[99, 127], "gp", "camera-slave"],
|
||||
[[99, 128], "gp", "camera-slave"],
|
||||
[[187, 231], "gp", "camera-slave"]
|
||||
],
|
||||
"(method 15 tracking-spline)": [
|
||||
|
||||
@@ -6826,28 +6826,8 @@
|
||||
},
|
||||
"(event cam-combiner-active)": {
|
||||
"vars": {
|
||||
"t9-2": "print-error-fn",
|
||||
"a0-15": "output-stream",
|
||||
"a1-3": "error-message",
|
||||
"v1-7": "argument",
|
||||
"t9-3": "type-check-fn",
|
||||
"v1-8": "argument",
|
||||
"t9-4": "print-error-fn",
|
||||
"a0-18": "output-stream",
|
||||
"a1-5": "error-message",
|
||||
"v1-10": "argument",
|
||||
"gp-1": ["source-slave", "camera-slave"],
|
||||
"gp-2": ["source-position", "vector"],
|
||||
"t9-10": "print-error-fn",
|
||||
"a0-27": "output-stream",
|
||||
"a1-11": "error-message",
|
||||
"v1-23": "argument",
|
||||
"t9-11": "type-check-fn",
|
||||
"v1-24": "argument",
|
||||
"t9-12": "print-error-fn",
|
||||
"a0-30": "output-stream",
|
||||
"a1-13": "error-message",
|
||||
"v1-25": "argument",
|
||||
"gp-3": "source-slave",
|
||||
"a2-17": "destination-tracker",
|
||||
"a3-3": "source-tracker",
|
||||
|
||||
Reference in New Issue
Block a user