[Maintenance] Remove the `operator =` overloads for Subpixels

At least we've now documented their negative effects.

Part of P0110, funded by [Anonymous] and Blue Bolt.
This commit is contained in:
nmlgc 2020-08-19 11:11:59 +02:00
parent 7c4d31c02b
commit 2ef3db3052
8 changed files with 79 additions and 42 deletions

View File

@ -130,6 +130,47 @@ case it's part of an arithmetic expression that was promoted to `int`.
// Note the opposite order of variables!
```
* For trivially copyable structures, copy assignments are optimized to an
equivalent of `memcpy()`:
| Structure size | (no flags) | -G |
|----------------|-------------|----------------------|
| 1 | via `AL` | via `AL` |
| 2 | via `AX` | via `AX` |
| 3 | `SCOPY@` | via `AX` and `AL` |
| 4 | via `DX:AX` | via `DX:AX` |
| 5, 7, 9 | `SCOPY@` | via `AX` and `AL` |
| 6, 8 | `SCOPY@` | via `AX` |
| 10, 12, 14, … | `SCOPY@` | `REP MOVSW` |
| 11, 13, 15, … | `SCOPY@` | `REP MOVSW`, `MOVSB` |
(With the `-3` flag, `EAX` is used instead of `DX:AX` in the 4-byte case,
but everything else stays the same.)
Breaking triviality by overloading `operator =` in any of the structure
members also breaks this optimization. In some cases, it might be possible
to recreate it, by simulating triviality in an overloaded copy assignment
operator inside the class in question:
```c++
struct Nontrivial {
nontrivial_char_t e[100];
// Functions containing local classes aren't expanded inline, so...
struct Trivial {
char e[100];
};
void operator =(const Nontrivial &other) {
reinterpret_cast<Trivial &>(*this) = (
reinterpret_cast<const Trivial &>(other)
);
}
};
```
However, this only generates identical code to the original optimization if
passing the `other` parameter can be inlined, which isn't always the case.
## `switch` statements
* Sequence of the individual cases is identical in both C and ASM

View File

@ -82,7 +82,7 @@ bool16 pattern_velocity_set(
switch(pattern) {
case PP_1:
ret_y.v = speed;
ret_x = 0.0f;
ret_x.set(0.0f);
done = true;
break;
case PP_1_AIMED:
@ -179,7 +179,7 @@ inline subpixel_t base_speed_for_rank(void)
#define pellet_init(pellet, left, top, pattern) \
pellet->decay_frame = 0; \
pellet->cur_left.v = TO_SP(left); \
pellet->cur_top = top; \
pellet->cur_top.v = TO_SP(top); \
pellet->cloud_left = left; \
pellet->cloud_top = top; \
if(spawn_with_cloud) { \
@ -271,7 +271,7 @@ void CPellets::add_single(
p->age = 0;
alive_count++;
p->spin_center.x.v = TO_SP(spin_center_x);
p->spin_center.y = spin_center_y;
p->spin_center.y.v = TO_SP(spin_center_y);
if(motion_type == PM_SPIN) {
vector2(vel_x.v, vel_y.v, speed_for_motion_fixed, angle);
p->spin_velocity.x.v = vel_x.v;
@ -339,27 +339,27 @@ void CPellets::motion_type_apply_for_cur(void)
|| (p->cur_top.v <= to_sp(PELLET_BOUNCE_TOP_MIN))
) {
p->velocity.x.v = -p->velocity.x.v;
p->velocity.y = 0.0f;
p->velocity.y.set(0.0f);
p->motion_type = PM_GRAVITY;
// Nope, this doesn't help.
if(p->cur_left.v <= to_sp(PELLET_LEFT_MIN)) {
p->cur_left = (PELLET_LEFT_MIN + 1.0f);
p->cur_left.set(PELLET_LEFT_MIN + 1.0f);
}
if(p->cur_left.v >= to_sp(PELLET_LEFT_MAX)) {
p->cur_left = (PELLET_LEFT_MAX - 1.0f);
p->cur_left.set(PELLET_LEFT_MAX - 1.0f);
}
if(p->cur_top.v <= to_sp(PELLET_TOP_MIN)) {
p->cur_top = (PELLET_TOP_MIN + 1.0f);
p->cur_top.set(PELLET_TOP_MIN + 1.0f);
}
}
break;
case PM_FALL_STRAIGHT_FROM_TOP_THEN_NORMAL:
if(p->cur_top.to_screen() <= PELLET_BOUNCE_TOP_MIN) {
p->velocity.x = 0.0f;
p->velocity.x.set(0.0f);
p->velocity.y.v = p->speed.v;
p->motion_type = PM_NORMAL;
if(p->cur_top.to_screen() <= PLAYFIELD_TOP) {
p->cur_top = (PLAYFIELD_TOP + 1.0f);
p->cur_top.set(PLAYFIELD_TOP + 1.0f);
}
}
break;
@ -370,8 +370,7 @@ void CPellets::motion_type_apply_for_cur(void)
p->cur_top.v = (to_spin_circle(Sin8(p->angle)) + p->spin_center.y.v);
p->spin_center.x.v += p->spin_velocity.x.v;
p->spin_center.y.v += p->spin_velocity.y.v;
p->velocity.x = 0.0f;
p->velocity.y = 0.0f;
p->velocity.set(0.0f, 0.0f);
p->angle += PELLET_SPIN_DELTA_ANGLE;
#undef to_spin_circle
break;

View File

@ -30,11 +30,13 @@ public:
this->v -= static_cast<T>(to_sp(screen_v));
}
void operator =(float screen_v) {
// No overloads of `operator =()`, since the class needs to be trivially
// copyable.
void set(float screen_v) {
v = static_cast<T>(to_sp(screen_v));
}
void operator =(const T &screen_v) {
void set(const T &screen_v) {
v = TO_SP(screen_v);
}
@ -55,13 +57,8 @@ template <class T> struct SPPointBase {
T x, y;
void set(float screen_x, float screen_y) {
x = screen_x;
y = screen_y;
}
void set(const T &screen_x, const T &screen_y) {
x = screen_x;
y = screen_y;
x.set(screen_x);
y.set(screen_y);
}
};

View File

@ -4,9 +4,9 @@ typedef struct {
SPPoint velocity;
void init(float screen_x, float screen_y) {
cur.x = screen_x;
prev.x = screen_x;
cur.y = screen_y;
prev.y = screen_y;
cur.x.set(screen_x);
prev.x.set(screen_x);
cur.y.set(screen_y);
prev.y.set(screen_y);
}
} motion_t;

View File

@ -66,8 +66,8 @@ struct ShotAddIterator {
#define MISSILE_R shot->type = ST_MISSILE_RIGHT
#define MISSILE_S shot->type = ST_MISSILE_STRAIGHT
#define VELOCITY_X(screen_x) shot->pos.velocity.x = screen_x;
#define VELOCITY_Y(screen_y) shot->pos.velocity.y = screen_y;
#define VELOCITY_X(screen_x) shot->pos.velocity.x.set(screen_x);
#define VELOCITY_Y(screen_y) shot->pos.velocity.y.set(screen_y);
#define VELOCITY_XY(screen_x, screen_y) \
VELOCITY_X(screen_x); \

View File

@ -40,7 +40,7 @@ void pascal near stage2_setup(void)
midboss_render_func = midboss2_render;
midboss.frames_until = 2750;
midboss.pos.init(192, -32);
midboss.pos.velocity.set(0, 0.5f);
midboss.pos.velocity.set(0.0f, 0.5f);
midboss.hp = 1400;
midboss.sprite = 202;
@ -67,7 +67,7 @@ void pascal near stage3_setup(void)
midboss_render_func = midboss3_render;
midboss.frames_until = 5750;
midboss.pos.init(192, -32);
midboss.pos.velocity.set(0, 0.5f);
midboss.pos.velocity.set(0.0f, 0.5f);
midboss.hp = 1400;
midboss.sprite = 208;
@ -97,7 +97,7 @@ void pascal near stage4_setup(void)
midboss_render_func = midboss4_render;
midboss.frames_until = 3900;
midboss.pos.init(192, -32);
midboss.pos.velocity.set(0, 0.5f);
midboss.pos.velocity.set(0.0f, 0.5f);
midboss.hp = 1100;
midboss.sprite = 208;
@ -126,7 +126,7 @@ void pascal near stage5_setup(void)
midboss_render_func = midboss5_render;
midboss.frames_until = 4800;
midboss.pos.init(192, -32);
midboss.pos.velocity.set(0, 0.5f);
midboss.pos.velocity.set(0.0f, 0.5f);
midboss.hp = 1550;
midboss.sprite = 212;

View File

@ -18,7 +18,7 @@ void pascal near shot_mima_l2(void)
if(sai.i <= 2) {
if(sai.i == 2) { shot->from_option_l(); }
else/*i == 1*/ { shot->from_option_r(); }
shot->pos.velocity.y = -20.0f;
shot->pos.velocity.y.set(-20.0f);
shot->set_option_sprite_and_damage(5);
} else {
shot->set_random_angle_forwards();
@ -37,12 +37,12 @@ void pascal near shot_mima_l3(void)
if(sai.i <= 2) {
if(sai.i == 2) { shot->from_option_l(); }
else/*i == 1*/ { shot->from_option_r(); }
shot->pos.velocity.y = -20.0f;
shot->pos.velocity.y.set(-20.0f);
shot->set_option_sprite_and_damage(5);
} else {
if(sai.i == 4) { shot->pos.cur.x -= 8.0f; }
else/*i == 3*/ { shot->pos.cur.x += 8.0f; }
shot->pos.velocity.y = -12.0f;
shot->pos.velocity.y.set(-12.0f);
shot->damage = 7;
}
if(sai.next() <= 0) {
@ -63,12 +63,12 @@ void pascal near shot_mima_l4(void)
case 3: shot->from_option_l(); break;
case 2: shot->from_option_r(); break;
}
shot->pos.velocity.y = -20.0f;
shot->pos.velocity.y.set(-20.0f);
shot->set_option_sprite_and_damage(4);
} else {
if(sai.i == 6) { shot->pos.cur.x -= 8.0f; }
else/* i== 5*/ { shot->pos.cur.x += 8.0f; }
shot->pos.velocity.y = -12.0f;
shot->pos.velocity.y.set(-12.0f);
shot->damage = 7;
}
if(sai.next() <= 0) {
@ -90,7 +90,7 @@ void pascal near shot_mima_l5(void)
case 1: VELOCITY_X(-1.0f); shot->from_option_r(); break;
case 2: shot->from_option_r(); break;
}
shot->pos.velocity.y = -20.0f;
shot->pos.velocity.y.set(-20.0f);
shot->set_option_sprite_and_damage(4);
} else {
shot_velocity_set(&shot->pos.velocity, sai.angle);
@ -115,7 +115,7 @@ void pascal near shot_mima_l6(void)
case 2: VELOCITY_X(-0.5f); shot->from_option_r(); break;
case 3: VELOCITY_X( 0.5f); shot->from_option_l(); break;
}
shot->pos.velocity.y = -20.0f;
shot->pos.velocity.y.set(-20.0f);
shot->set_option_sprite_and_damage(4);
} else {
shot_velocity_set(&shot->pos.velocity, sai.angle);
@ -143,7 +143,7 @@ void pascal near shot_mima_l7(void)
case 3: VELOCITY_X( 0.5f); shot->from_option_l(); break;
case 5: shot->from_option_l(); break;
}
shot->pos.velocity.y = -20.0f;
shot->pos.velocity.y.set(-20.0f);
shot->set_option_sprite_and_damage(4);
} else {
shot_velocity_set(&shot->pos.velocity, sai.angle);
@ -162,7 +162,7 @@ void pascal near shot_mima_l8(void)
sai.angle = 186;
while(( shot = shots_add() ) != NULL) {
if(sai.i <= 6) {
shot->pos.velocity.y = -20.0f;
shot->pos.velocity.y.set(-20.0f);
switch(sai.i - 1) {
// Beware, non-sequential case order!
case 0: VELOCITY_XY( 1.75f, -18.0f); shot->from_option_r(8.0f); break;
@ -172,7 +172,7 @@ void pascal near shot_mima_l8(void)
case 3: VELOCITY_X ( 0.5f); shot->from_option_l(); break;
case 5: shot->from_option_l(); break;
}
shot->pos.velocity.y = -20.0f;
shot->pos.velocity.y.set(-20.0f);
shot->set_option_sprite_and_damage(4);
} else {
shot_velocity_set(&shot->pos.velocity, sai.angle);
@ -190,7 +190,7 @@ void pascal near shot_mima_l9(void)
SHOT_FUNC_INIT(6, SC_6X, SC_3X, i += 4);
while(( shot = shots_add() ) != NULL) {
if(sai.i <= 6) {
shot->pos.velocity.y = -20.0f;
shot->pos.velocity.y.set(-20.0f);
switch(sai.i - 1) {
case 0: VELOCITY_XY( 2.625f, -18.0f); shot->from_option_r(8.0f); break;
case 1: VELOCITY_XY(-2.625f, -18.0f); shot->from_option_l(8.0f); break;

View File

@ -102,7 +102,7 @@ void pascal near shot_yuuka_l4(void)
sai.i = 1;
}
}
shot->pos.velocity.y = 2.0f;
shot->pos.velocity.y.set(2.0f);
shot->set_option_sprite_and_damage(6);
}
shot_velocity_set(&shot->pos.velocity, sai.angle);