1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00

Merge pull request #1075 from TomHarte/PlayfieldMasking

Add comments, fix playfield sprite masking.
This commit is contained in:
Thomas Harte 2022-07-22 21:20:50 -04:00 committed by GitHub
commit eb0b6e9df9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 110 additions and 72 deletions

View File

@ -64,7 +64,7 @@ class BitplaneShifter {
int odd_delay,
int even_delay);
/// Shifts either two pixels (in low-res mode) and four pixels (in high-res).
/// Shifts either two pixels (in low-res mode) or four pixels (in high-res).
void shift(bool high_res) {
constexpr int shifts[] = {16, 32};
@ -73,8 +73,14 @@ class BitplaneShifter {
}
/// @returns The next four pixels to output; in low-resolution mode only two
/// of them will be unique. The value is arranges so that MSB = first pixel to output,
/// LSB = last. Each byte is formed as 00[bitplane 5][bitplane 4]...[bitplane 0].
/// of them will be unique.
///
/// The value is arranges so that MSB = first pixel to output, LSB = last.
///
/// Each byte is swizzled to provide easier playfield separation, being in the form:
/// b6, b7 = 0;
/// b3b5: planes 1, 3 and 5;
/// b0b2: planes 0, 2 and 4.
uint32_t get(bool high_res) {
if(high_res) {
return uint32_t(data_[1] >> 32);

View File

@ -199,82 +199,114 @@ void Chipset::output_pixels(int cycles_until_sync) {
}
}
// Compute masks potentially to obscure sprites.
int playfield_odd_pixel_mask =
(((playfield >> 22) | (playfield >> 24) | (playfield >> 26)) & 8) |
(((playfield >> 15) | (playfield >> 17) | (playfield >> 19)) & 4) |
(((playfield >> 8) | (playfield >> 10) | (playfield >> 12)) & 2) |
(((playfield >> 1) | (playfield >> 3) | (playfield >> 5)) & 1);
int playfield_even_pixel_mask =
(((playfield >> 21) | (playfield >> 23) | (playfield >> 25)) & 8) |
(((playfield >> 14) | (playfield >> 16) | (playfield >> 18)) & 4) |
(((playfield >> 7) | (playfield >> 9) | (playfield >> 11)) & 2) |
(((playfield >> 0) | (playfield >> 2) | (playfield >> 4)) & 1);
// If only a single playfield is in use, treat the mask as playing
// into the priority selected for the even bitfields.
if(!dual_playfields_) {
playfield_even_pixel_mask |= playfield_odd_pixel_mask;
playfield_odd_pixel_mask = 0;
}
// Process sprites.
// This will store flags to indicate presence or absence of sprite pixels for the four shifters.
int collision_masks[4] = {0, 0, 0, 0};
int index = int(sprite_shifters_.size());
for(auto shifter = sprite_shifters_.rbegin(); shifter != sprite_shifters_.rend(); ++shifter) {
// Update the index, and skip this shifter entirely if it's empty.
--index;
const uint8_t data = shifter->get();
if(!data) continue;
// Determine the collision mask.
collision_masks[index] = data | (data >> 1);
if(collisions_flags_ & (0x1000 << index)) {
collision_masks[index] |= (data >> 2) | (data >> 3);
// If there are sprites visible, bother to figure out the playfield masks here.
if(sprite_shifters_[0].get() | sprite_shifters_[1].get() | sprite_shifters_[2].get() | sprite_shifters_[3].get()) {
// The playfield value is arranged as:
//
// pixel = [0 0 b5 b3 b1 b4 b2 b0]
// full value = [pixel] [pixel] [pixel] [pixel]
//
// i.e. the odd pixel mask is:
// b0 = bits 3, 4, 5;
// b1 = bits 11, 12, 13;
// b2 = bits 19, 20, 21;
// b3 = bits 27, 28, 29.
//
// ... and the even pixel mask is the other set.
// Ensure that b0, b8, b16, b24 are the complete mask state of the even playfields,
// and b3, b11, b19, b27 are the complete mask state of the odd playfields.
const uint32_t merged_playfield = playfield | (playfield >> 1) | (playfield >> 2);
// Collect b0, b8, b16 and b24 as b0, b1, b2, b3 (and give no regard to the other bits).
uint32_t playfield_even_pixel_mask = merged_playfield & 0x01010101;
playfield_even_pixel_mask |= playfield_even_pixel_mask >> 7;
playfield_even_pixel_mask |= playfield_even_pixel_mask >> 14;
// Collect b3, b11, b19 and b27 as b0, b1, b2, b3 (and give no regard to the other bits).
uint32_t playfield_odd_pixel_mask = (merged_playfield >> 3) & 0x01010101;
playfield_odd_pixel_mask |= playfield_odd_pixel_mask >> 7;
playfield_odd_pixel_mask |= playfield_odd_pixel_mask >> 14;
// If only a single playfield is in use, treat the mask as playing
// into the priority selected for the even bitfields.
if(!dual_playfields_) {
playfield_even_pixel_mask |= playfield_odd_pixel_mask;
playfield_odd_pixel_mask = 0;
}
collision_masks[index] = (collision_masks[index] & 0x01) | ((collision_masks[index] & 0x10) >> 3);
// Get the specific pixel mask.
const int pixel_mask =
(
((odd_priority_ <= index) ? playfield_odd_pixel_mask : 0) |
((even_priority_ <= index) ? playfield_even_pixel_mask : 0)
);
// Draw sprites.
int index = int(sprite_shifters_.size());
for(auto shifter = sprite_shifters_.rbegin(); shifter != sprite_shifters_.rend(); ++shifter) {
// Update the index, and skip this shifter entirely if it's empty.
--index;
const uint8_t data = shifter->get();
if(!data) continue;
// Output pixels, if a buffer exists.
const auto base = (index << 2) + 16;
if(pixels_) {
if(sprites_[size_t((index << 1) + 1)].attached) {
// Left pixel.
if(data >> 4) {
if(!(pixel_mask & 0x8)) pixels_[0] = palette_[16 + (data >> 4)];
if(!(pixel_mask & 0x4)) pixels_[1] = palette_[16 + (data >> 4)];
}
// Determine the collision mask.
collision_masks[index] = data | (data >> 1);
if(collisions_flags_ & (0x1000 << index)) {
collision_masks[index] |= (data >> 2) | (data >> 3);
}
collision_masks[index] = (collision_masks[index] & 0x01) | ((collision_masks[index] & 0x10) >> 3);
// Right pixel.
if(data & 15) {
if(!(pixel_mask & 0x2)) pixels_[2] = palette_[16 + (data & 15)];
if(!(pixel_mask & 0x1)) pixels_[3] = palette_[16 + (data & 15)];
}
} else {
// Left pixel.
if((data >> 4) & 3) {
if(!(pixel_mask & 0x8)) pixels_[0] = palette_[base + ((data >> 4)&3)];
if(!(pixel_mask & 0x4)) pixels_[1] = palette_[base + ((data >> 4)&3)];
}
if(data >> 6) {
if(!(pixel_mask & 0x8)) pixels_[0] = palette_[base + (data >> 6)];
if(!(pixel_mask & 0x4)) pixels_[1] = palette_[base + (data >> 6)];
}
// Get the specific pixel mask;
//
// Playfield priority meanings:
//
// 4: behind all sprites;
// 3: in front of sprites 6 & 7, behind all others;
// 2: in front of 4, 5, 6 & 7; behind all others;
// 1: in front of 2, 3, 4, 5, 6, & 7; behind 0 & 1;
// 0: in front of all sprites.
//
// i.e. the playfield is in front of the two sprites in shifter n
// if and only if it has a priority of n or less.
const auto pixel_mask =
(
((odd_priority_ <= index) ? playfield_odd_pixel_mask : 0) |
((even_priority_ <= index) ? playfield_even_pixel_mask : 0)
);
// Right pixel.
if(data & 3) {
if(!(pixel_mask & 0x2)) pixels_[2] = palette_[base + (data & 3)];
if(!(pixel_mask & 0x1)) pixels_[3] = palette_[base + (data & 3)];
}
if((data >> 2) & 3) {
if(!(pixel_mask & 0x2)) pixels_[2] = palette_[base + ((data >> 2)&3)];
if(!(pixel_mask & 0x1)) pixels_[3] = palette_[base + ((data >> 2)&3)];
// Output pixels, if a buffer exists and only where the pixel
// mask allows. TODO: try to find a less branchy version of the below.
const auto base = (index << 2) + 16;
if(pixels_) {
if(sprites_[size_t((index << 1) + 1)].attached) {
// Left pixel.
if(data >> 4) {
if(!(pixel_mask & 0x8)) pixels_[0] = palette_[16 + (data >> 4)];
if(!(pixel_mask & 0x4)) pixels_[1] = palette_[16 + (data >> 4)];
}
// Right pixel.
if(data & 15) {
if(!(pixel_mask & 0x2)) pixels_[2] = palette_[16 + (data & 15)];
if(!(pixel_mask & 0x1)) pixels_[3] = palette_[16 + (data & 15)];
}
} else {
// Left pixel.
if((data >> 4) & 3) {
if(!(pixel_mask & 0x8)) pixels_[0] = palette_[base + ((data >> 4)&3)];
if(!(pixel_mask & 0x4)) pixels_[1] = palette_[base + ((data >> 4)&3)];
}
if(data >> 6) {
if(!(pixel_mask & 0x8)) pixels_[0] = palette_[base + (data >> 6)];
if(!(pixel_mask & 0x4)) pixels_[1] = palette_[base + (data >> 6)];
}
// Right pixel.
if(data & 3) {
if(!(pixel_mask & 0x2)) pixels_[2] = palette_[base + (data & 3)];
if(!(pixel_mask & 0x1)) pixels_[3] = palette_[base + (data & 3)];
}
if((data >> 2) & 3) {
if(!(pixel_mask & 0x2)) pixels_[2] = palette_[base + ((data >> 2)&3)];
if(!(pixel_mask & 0x1)) pixels_[3] = palette_[base + ((data >> 2)&3)];
}
}
}
}