chore: reimplement alpha blending
This commit is contained in:
		
							
								
								
									
										344
									
								
								src/core/ppu.zig
									
									
									
									
									
								
							
							
						
						
									
										344
									
								
								src/core/ppu.zig
									
									
									
									
									
								
							@@ -398,7 +398,7 @@ pub const Ppu = struct {
 | 
			
		||||
            // Sprite Palette starts at 0x0500_0200
 | 
			
		||||
            if (pal_id != 0) {
 | 
			
		||||
                const bgr555 = self.palette.read(u16, 0x200 + pal_id * 2);
 | 
			
		||||
                copyToSpriteBuffer(self.bld.cnt, &self.scanline, x, bgr555);
 | 
			
		||||
                drawSpritePixel(self.bld.cnt, &self.scanline, x, bgr555);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -448,7 +448,7 @@ pub const Ppu = struct {
 | 
			
		||||
            // Sprite Palette starts at 0x0500_0200
 | 
			
		||||
            if (pal_id != 0) {
 | 
			
		||||
                const bgr555 = self.palette.read(u16, 0x200 + pal_id * 2);
 | 
			
		||||
                copyToSpriteBuffer(self.bld.cnt, &self.scanline, x, bgr555);
 | 
			
		||||
                drawSpritePixel(self.bld.cnt, &self.scanline, x, bgr555);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -493,10 +493,7 @@ pub const Ppu = struct {
 | 
			
		||||
            const tile_addr = char_base + (tile_id * 0x40) + (row * 0x8) + col;
 | 
			
		||||
            const pal_id: u16 = self.vram.buf[tile_addr];
 | 
			
		||||
 | 
			
		||||
            if (pal_id != 0) {
 | 
			
		||||
                const bgr555 = self.palette.read(u16, pal_id * 2);
 | 
			
		||||
                self.copyToBackgroundBuffer(n, win_bounds, i, bgr555);
 | 
			
		||||
            }
 | 
			
		||||
            if (pal_id != 0) self.drawBackgroundPixel(n, i, self.palette.read(u16, pal_id * 2));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Update BGxX and BGxY
 | 
			
		||||
@@ -551,10 +548,7 @@ pub const Ppu = struct {
 | 
			
		||||
            // and then we can index the palette
 | 
			
		||||
            const pal_id: u16 = if (!is_8bpp) get4bppTilePalette(entry.pal_bank.read(), col, tile) else tile;
 | 
			
		||||
 | 
			
		||||
            if (pal_id != 0) {
 | 
			
		||||
                const bgr555 = self.palette.read(u16, pal_id * 2);
 | 
			
		||||
                self.copyToBackgroundBuffer(n, win_bounds, i, bgr555);
 | 
			
		||||
            }
 | 
			
		||||
            if (pal_id != 0) self.drawBackgroundPixel(n, i, self.palette.read(u16, pal_id * 2));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -669,8 +663,7 @@ pub const Ppu = struct {
 | 
			
		||||
        // FIXME: @ptrCast between slices changing the length isn't implemented yet
 | 
			
		||||
        const framebuf = @ptrCast([*]u32, @alignCast(@alignOf(u32), self.framebuf.get(.Emulator)));
 | 
			
		||||
 | 
			
		||||
        for (self.scanline.top()) |maybe_px, i| {
 | 
			
		||||
            const maybe_top = maybe_px;
 | 
			
		||||
        for (self.scanline.top()) |maybe_top, i| {
 | 
			
		||||
            const maybe_btm = self.scanline.btm()[i];
 | 
			
		||||
 | 
			
		||||
            const bgr555 = self.getBgr555(maybe_top, maybe_btm);
 | 
			
		||||
@@ -683,12 +676,25 @@ pub const Ppu = struct {
 | 
			
		||||
        std.mem.set(?Sprite, self.scanline_sprites, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn getBgr555(self: *Self, maybe_top: ?u16, maybe_btm: ?u16) u16 {
 | 
			
		||||
        if (maybe_btm) |btm| {
 | 
			
		||||
            return switch (self.bld.cnt.mode.read()) {
 | 
			
		||||
                0b00 => if (maybe_top) |top| top else btm,
 | 
			
		||||
                0b01 => if (maybe_top) |top| alphaBlend(btm, top, self.bld.alpha) else btm,
 | 
			
		||||
                0b10 => blk: {
 | 
			
		||||
    fn getBgr555(self: *Self, maybe_top: Scanline.Pixel, maybe_btm: Scanline.Pixel) u16 {
 | 
			
		||||
        return switch (self.bld.cnt.mode.read()) {
 | 
			
		||||
            0b00 => switch (maybe_top) {
 | 
			
		||||
                .set => |top| top,
 | 
			
		||||
                else => self.palette.backdrop(),
 | 
			
		||||
            },
 | 
			
		||||
            0b01 => switch (maybe_top) {
 | 
			
		||||
                .set => |top| switch (maybe_btm) {
 | 
			
		||||
                    .set => |btm| alphaBlend(top, btm, self.bld.alpha), // ALPHA_BLEND
 | 
			
		||||
                    else => top,
 | 
			
		||||
                },
 | 
			
		||||
                else => switch (maybe_btm) {
 | 
			
		||||
                    .set => |btm| btm,
 | 
			
		||||
                    else => self.palette.backdrop(),
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
            0b10 => switch (maybe_btm) {
 | 
			
		||||
                .set => |btm| blk: {
 | 
			
		||||
                    // BLD_WHITE
 | 
			
		||||
                    const evy: u16 = self.bld.y.evy.read();
 | 
			
		||||
 | 
			
		||||
                    const r = btm & 0x1F;
 | 
			
		||||
@@ -701,51 +707,87 @@ pub const Ppu = struct {
 | 
			
		||||
 | 
			
		||||
                    break :blk (bld_b << 10) | (bld_g << 5) | bld_r;
 | 
			
		||||
                },
 | 
			
		||||
                0b11 => blk: {
 | 
			
		||||
                else => switch (maybe_top) {
 | 
			
		||||
                    .set => |top| top,
 | 
			
		||||
                    else => self.palette.backdrop(),
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
            0b11 => switch (maybe_btm) {
 | 
			
		||||
                .set => |btm| blk: {
 | 
			
		||||
                    // BLD_BLACK
 | 
			
		||||
                    const evy: u16 = self.bld.y.evy.read();
 | 
			
		||||
 | 
			
		||||
                    const btm_r = btm & 0x1F;
 | 
			
		||||
                    const btm_g = (btm >> 5) & 0x1F;
 | 
			
		||||
                    const btm_b = (btm >> 10) & 0x1F;
 | 
			
		||||
                    const r = btm & 0x1F;
 | 
			
		||||
                    const g = (btm >> 5) & 0x1F;
 | 
			
		||||
                    const b = (btm >> 10) & 0x1F;
 | 
			
		||||
 | 
			
		||||
                    const bld_r = btm_r - ((btm_r * evy) >> 4);
 | 
			
		||||
                    const bld_g = btm_g - ((btm_g * evy) >> 4);
 | 
			
		||||
                    const bld_b = btm_b - ((btm_b * evy) >> 4);
 | 
			
		||||
                    const bld_r = r - ((r * evy) >> 4);
 | 
			
		||||
                    const bld_g = g - ((g * evy) >> 4);
 | 
			
		||||
                    const bld_b = b - ((b * evy) >> 4);
 | 
			
		||||
 | 
			
		||||
                    break :blk (bld_b << 10) | (bld_g << 5) | bld_r;
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (maybe_top) |top| return top;
 | 
			
		||||
        return self.palette.backdrop();
 | 
			
		||||
                else => switch (maybe_top) {
 | 
			
		||||
                    .set => |top| top,
 | 
			
		||||
                    else => self.palette.backdrop(),
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn copyToBackgroundBuffer(self: *Self, comptime n: u2, bounds: ?WindowBounds, i: usize, bgr555: u16) void {
 | 
			
		||||
        if (self.bld.cnt.mode.read() != 0b00) {
 | 
			
		||||
            // Standard Alpha Blending
 | 
			
		||||
            const a_layers = self.bld.cnt.layer_a.read();
 | 
			
		||||
            const is_blend_enabled = (a_layers >> n) & 1 == 1;
 | 
			
		||||
    fn drawBackgroundPixel(self: *Self, comptime layer: u2, i: usize, bgr555: u16) void {
 | 
			
		||||
        // When writing to the scanline buffer, we want to be aware of a top and bottom layer. Some preconditions were
 | 
			
		||||
        // already determined by shouldDrawBackground, so we should be aware of what we can assume to be true or false
 | 
			
		||||
 | 
			
		||||
            // If Alpha Blending is enabled and we've found an eligible layer for
 | 
			
		||||
            // Pixel A, store the pixel in the bottom pixel buffer
 | 
			
		||||
        switch (self.bld.cnt.mode.read()) {
 | 
			
		||||
            0b00 => {}, // pass through
 | 
			
		||||
            0b01 => {
 | 
			
		||||
                // We are to alpha blend here so we should pay attention to which layer ths pixel should be written to
 | 
			
		||||
                // FIXME: We redo work here that we've already figured out. Is this worth refactorning?
 | 
			
		||||
 | 
			
		||||
            const win_part = if (bounds) |win| blk: {
 | 
			
		||||
                // Window Enabled
 | 
			
		||||
                break :blk switch (win) {
 | 
			
		||||
                    .win0 => self.win.in.w0_bld.read(),
 | 
			
		||||
                    .win1 => self.win.in.w1_bld.read(),
 | 
			
		||||
                    .out => self.win.out.out_bld.read(),
 | 
			
		||||
                };
 | 
			
		||||
            } else true;
 | 
			
		||||
                // If the current layer is makred as Layer A, write to top buffer
 | 
			
		||||
                const top_layer = self.bld.cnt.layer_a.read();
 | 
			
		||||
                const is_top_layer = (top_layer >> layer) & 1 == 1;
 | 
			
		||||
 | 
			
		||||
            if (win_part and is_blend_enabled) {
 | 
			
		||||
                self.scanline.btm()[i] = bgr555;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
                if (is_top_layer) {
 | 
			
		||||
                    self.scanline.top()[i] = Scanline.Pixel.from(bgr555);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // If the current layer is marked as Layer B, we want to continue if there's an available space on that buffer
 | 
			
		||||
                const btm_layer = self.bld.cnt.layer_b.read();
 | 
			
		||||
                const is_btm_layer = (btm_layer >> layer) & 1 == 1;
 | 
			
		||||
 | 
			
		||||
                if (is_btm_layer) {
 | 
			
		||||
                    self.scanline.btm()[i] = Scanline.Pixel.from(bgr555);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // The code we're about to fall-through to assumes that alpha blending takes place. In order to withold all invariants
 | 
			
		||||
                // we need to discard anything that might be in the bottom buffer.
 | 
			
		||||
                self.scanline.btm()[i] = .hidden;
 | 
			
		||||
            },
 | 
			
		||||
            0b10, 0b11 => {
 | 
			
		||||
                // BLD_WHITE, BLD_BLACK
 | 
			
		||||
                // Weare to blend with White or black here. By convention we store regular ol' pixels in the top layer, which means that if we want to
 | 
			
		||||
                // treat some pixels (in this case the ones relegated to blending) we need to keep them separate as we can't apply the blending to the top layer.
 | 
			
		||||
 | 
			
		||||
                // While in these modes, (and since this is a scanline renderer), the bottom layer will be completely unused. While it's a bit unintuitive, since we'll
 | 
			
		||||
                // be moving layer A pixels there, we will repurpose the bottom layer as the "to blend", layer
 | 
			
		||||
 | 
			
		||||
                // If the current layer is makred as Layer A, write to top buffer
 | 
			
		||||
                const top_layer = self.bld.cnt.layer_a.read();
 | 
			
		||||
                const is_top_layer = (top_layer >> layer) & 1 == 1;
 | 
			
		||||
 | 
			
		||||
                if (is_top_layer) {
 | 
			
		||||
                    self.scanline.btm()[i] = Scanline.Pixel.from(bgr555); // this is intentional
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.scanline.top()[i] = bgr555;
 | 
			
		||||
        // If we aren't blending here at all, just add the pixel to the top layer
 | 
			
		||||
        self.scanline.top()[i] = Scanline.Pixel.from(bgr555);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const WindowBounds = enum { win0, win1, out };
 | 
			
		||||
@@ -763,48 +805,76 @@ pub const Ppu = struct {
 | 
			
		||||
        return .out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn shouldDrawBackground(self: *Self, comptime n: u2, bounds: ?WindowBounds, i: usize) bool {
 | 
			
		||||
        // If a pixel has been drawn on the top layer, it's because:
 | 
			
		||||
        // 1. The pixel is to be blended with a pixel on the bottom layer
 | 
			
		||||
        // 2. The pixel is not to be blended at all
 | 
			
		||||
        // Also, if we find a pixel on the top layer we don't need to bother with this I think?
 | 
			
		||||
        if (self.scanline.top()[i] != null) return false;
 | 
			
		||||
    fn shouldDrawBackground(self: *Self, comptime layer: u2, bounds: ?WindowBounds, i: usize) bool {
 | 
			
		||||
        switch (self.bld.cnt.mode.read()) {
 | 
			
		||||
            0b00 => if (self.scanline.top()[i] == .set) return false, // pass through
 | 
			
		||||
            0b01 => blk: {
 | 
			
		||||
                // BLD_ALPHA
 | 
			
		||||
 | 
			
		||||
        if (bounds) |win| {
 | 
			
		||||
            switch (win) {
 | 
			
		||||
                .win0 => if ((self.win.in.w0_bg.read() >> n) & 1 == 0) return false,
 | 
			
		||||
                .win1 => if ((self.win.in.w1_bg.read() >> n) & 1 == 0) return false,
 | 
			
		||||
                .out => if ((self.win.out.out_bg.read() >> n) & 1 == 0) return false,
 | 
			
		||||
                // If the current layer is marked as Layer B, we want to continue if there's an available space on that buffer
 | 
			
		||||
                const btm_layer = self.bld.cnt.layer_b.read();
 | 
			
		||||
                const is_btm_layer = (btm_layer >> layer) & 1 == 1;
 | 
			
		||||
 | 
			
		||||
                if (is_btm_layer) {
 | 
			
		||||
                    if (self.scanline.btm()[i] == .set) return false;
 | 
			
		||||
 | 
			
		||||
                    // In some previous iteration we have determined that an opaque pixel was drawn at this position
 | 
			
		||||
                    // therefore there's no reason to draw anything here
 | 
			
		||||
                    if (self.scanline.btm()[i] == .hidden) return false;
 | 
			
		||||
 | 
			
		||||
                    // We have a pixel and we know it to be a part of hte bottom layer.
 | 
			
		||||
                    // when getBgr555 sees that thre's a pixel in the top and bottom layer it chooses to blend the two
 | 
			
		||||
                    // Meaning that if we want to prevent Alpha Blending from happening (like for example if a window is preventing it)
 | 
			
		||||
                    // we need to make that happen now.
 | 
			
		||||
 | 
			
		||||
                    // We can do this by not drawing the bottom pixel, since with alpha blending disabled it wouldn't be visible anyways
 | 
			
		||||
 | 
			
		||||
                    // if (bounds) |win| {
 | 
			
		||||
                    //     switch (win) {
 | 
			
		||||
                    //         .win0 => if (!self.win.in.w0_bld.read()) return false,
 | 
			
		||||
                    //         .win1 => if (!self.win.in.w1_bld.read()) return false,
 | 
			
		||||
                    //         .out => if (!self.win.out.out_bld.read()) return false,
 | 
			
		||||
                    //     }
 | 
			
		||||
                    // }
 | 
			
		||||
 | 
			
		||||
                    break :blk;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (self.scanline.top()[i] == .set) return false;
 | 
			
		||||
            },
 | 
			
		||||
            0b10, 0b11 => {
 | 
			
		||||
                // BLD_WHITE and BLD_BLACK
 | 
			
		||||
 | 
			
		||||
                // we want to treat the bottom layer the same as the top (despite it being repurposed)
 | 
			
		||||
                // so we should apply the same logic to the bottom layer
 | 
			
		||||
 | 
			
		||||
                if (self.scanline.top()[i] == .set) return false;
 | 
			
		||||
                if (self.scanline.btm()[i] == .set) return false;
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // At this point we will have exited early if we determined that we'd be overwriting a pixel
 | 
			
		||||
        // with a higher priority. We can now move own to determining whether the pixel is visible or not
 | 
			
		||||
 | 
			
		||||
        // The first thing that may or may not affect visibility is windowing. We should check to see if ths pixel is in bounds
 | 
			
		||||
        // of of the background Window if it is enabled
 | 
			
		||||
        // TODO: Do Window Bounds checking here instead of outside this function?
 | 
			
		||||
 | 
			
		||||
        if (bounds) |window| {
 | 
			
		||||
            // If this parameter is non-null, we know that:
 | 
			
		||||
            // 1. Win0, Win1 or WinObj are enabled
 | 
			
		||||
            // 2. This specific pixel exists within the range of a window
 | 
			
		||||
 | 
			
		||||
            // Here, we check to see if the Window for this background is enabled. If not, we won't render the pixel
 | 
			
		||||
            // FIXME: We perform needless computations on Window Bounds by checking for enable here after we've already computed this information
 | 
			
		||||
            switch (window) {
 | 
			
		||||
                .win0 => if ((self.win.in.w0_bg.read() >> layer) & 1 == 0) return false,
 | 
			
		||||
                .win1 => if ((self.win.in.w1_bg.read() >> layer) & 1 == 0) return false,
 | 
			
		||||
                .out => if ((self.win.out.out_bg.read() >> layer) & 1 == 0) return false,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (self.scanline.btm()[i] != null) {
 | 
			
		||||
            // The pixel found in the bottom layer is:
 | 
			
		||||
            // 1. From a higher priority background
 | 
			
		||||
            // 2. From a background that is marked for blending (Pixel A)
 | 
			
		||||
 | 
			
		||||
            // If Alpha Blending isn't enabled, then we've already found a higher prio
 | 
			
		||||
            // pixel, we can return early
 | 
			
		||||
            if (self.bld.cnt.mode.read() != 0b01) return false;
 | 
			
		||||
 | 
			
		||||
            const b_layers = self.bld.cnt.layer_b.read();
 | 
			
		||||
 | 
			
		||||
            const win_part = if (bounds) |win| blk: {
 | 
			
		||||
                // Window Enabled
 | 
			
		||||
                break :blk switch (win) {
 | 
			
		||||
                    .win0 => self.win.in.w0_bld.read(),
 | 
			
		||||
                    .win1 => self.win.in.w1_bld.read(),
 | 
			
		||||
                    .out => self.win.out.out_bld.read(),
 | 
			
		||||
                };
 | 
			
		||||
            } else true;
 | 
			
		||||
 | 
			
		||||
            // If the Background is not marked for blending, we've already found
 | 
			
		||||
            // a higher priority pixel, move on.
 | 
			
		||||
 | 
			
		||||
            const is_blend_enabled = win_part and ((b_layers >> n) & 1 == 1);
 | 
			
		||||
            if (!is_blend_enabled) return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Otherwise, return true
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1287,56 +1357,106 @@ fn alphaBlend(top: u16, btm: u16, bldalpha: io.BldAlpha) u16 {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn shouldDrawSprite(bldcnt: io.BldCnt, scanline: *Scanline, x: u9) bool {
 | 
			
		||||
    if (scanline.top()[x] != null) return false;
 | 
			
		||||
    if (scanline.top()[x] == .set) return false;
 | 
			
		||||
 | 
			
		||||
    if (scanline.btm()[x] != null) {
 | 
			
		||||
        if (bldcnt.mode.read() != 0b01) return false;
 | 
			
		||||
    switch (bldcnt.mode.read()) {
 | 
			
		||||
        0b00 => if (scanline.top()[x] == .set) return false, // pass through
 | 
			
		||||
        0b01 => {
 | 
			
		||||
            // BLD_ALPHA
 | 
			
		||||
 | 
			
		||||
        const b_layers = bldcnt.layer_b.read();
 | 
			
		||||
        const is_blend_enabled = (b_layers >> 4) & 1 == 1;
 | 
			
		||||
        if (!is_blend_enabled) return false;
 | 
			
		||||
            // We want to check if we're concerned aout the bottom layer first 
 | 
			
		||||
            // because if so, the top layer already having a pixel is OK
 | 
			
		||||
            const btm_layers = bldcnt.layer_b.read();
 | 
			
		||||
            const is_btm_layer = (btm_layers >> 4) & 1 == 1;
 | 
			
		||||
 | 
			
		||||
            if (is_btm_layer and scanline.btm()[x] == .set) return false;
 | 
			
		||||
 | 
			
		||||
            if (scanline.top()[x] == .set) return false;
 | 
			
		||||
        },
 | 
			
		||||
        0b10, 0b11 => {
 | 
			
		||||
            if (scanline.top()[x] == .set) return false;
 | 
			
		||||
            if (scanline.btm()[x] == .set) return false;
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn copyToSpriteBuffer(bldcnt: io.BldCnt, scanline: *Scanline, x: u9, bgr555: u16) void {
 | 
			
		||||
    if (bldcnt.mode.read() != 0b00) {
 | 
			
		||||
        // Alpha Blending
 | 
			
		||||
        const a_layers = bldcnt.layer_a.read();
 | 
			
		||||
        const is_blend_enabled = (a_layers >> 4) & 1 == 1;
 | 
			
		||||
fn drawSpritePixel(bldcnt: io.BldCnt, scanline: *Scanline, x: u9, bgr555: u16) void {
 | 
			
		||||
    switch (bldcnt.mode.read()) {
 | 
			
		||||
        0b00 => {}, // pass through
 | 
			
		||||
        0b01 => {
 | 
			
		||||
            // BLD_ALPHA
 | 
			
		||||
            const top_layers = bldcnt.layer_a.read();
 | 
			
		||||
            const is_top_layer = (top_layers >> 4) & 1 == 1;
 | 
			
		||||
 | 
			
		||||
        if (is_blend_enabled) {
 | 
			
		||||
            scanline.btm()[x] = bgr555;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
            if (is_top_layer) {
 | 
			
		||||
                scanline.top()[x] = Scanline.Pixel.from(bgr555);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const btm_layers = bldcnt.layer_b.read();
 | 
			
		||||
            const is_btm_layer = (btm_layers >> 4) & 1 == 1;
 | 
			
		||||
 | 
			
		||||
            if (is_btm_layer) {
 | 
			
		||||
                scanline.btm()[x] = Scanline.Pixel.from(bgr555);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // We're rendering a normal pixel that isn't alpha blended
 | 
			
		||||
            // we can mark the pixel on the bottom layer as hidden
 | 
			
		||||
            scanline.btm()[x] = .hidden;
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        0b10, 0b11 => {
 | 
			
		||||
            // This is explained in drawBackgroundPixel, we're reusing the bottom layer to draw layer A pixels we will want to
 | 
			
		||||
            // later blend with WHITE or BLACK
 | 
			
		||||
 | 
			
		||||
            const top_layers = bldcnt.layer_a.read();
 | 
			
		||||
            const is_top_layer = (top_layers >> 4) & 1 == 1;
 | 
			
		||||
 | 
			
		||||
            if (is_top_layer) {
 | 
			
		||||
                scanline.btm()[x] = Scanline.Pixel.from(bgr555); // This is intentional
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    scanline.top()[x] = bgr555;
 | 
			
		||||
    scanline.top()[x] = Scanline.Pixel.from(bgr555);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Scanline = struct {
 | 
			
		||||
    const Self = @This();
 | 
			
		||||
 | 
			
		||||
    layers: [2][]?u16,
 | 
			
		||||
    buf: []?u16,
 | 
			
		||||
    const Pixel = union(enum) {
 | 
			
		||||
        set: u16,
 | 
			
		||||
        unset: void,
 | 
			
		||||
        hidden: void,
 | 
			
		||||
 | 
			
		||||
        fn from(bgr555: u16) Pixel {
 | 
			
		||||
            return .{ .set = bgr555 };
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    layers: [2][]Pixel,
 | 
			
		||||
    buf: []Pixel,
 | 
			
		||||
 | 
			
		||||
    allocator: Allocator,
 | 
			
		||||
 | 
			
		||||
    fn init(allocator: Allocator) !Self {
 | 
			
		||||
        const buf = try allocator.alloc(?u16, width * 2); // Top & Bottom Scanline
 | 
			
		||||
        std.mem.set(?u16, buf, null);
 | 
			
		||||
        const buf = try allocator.alloc(Pixel, width * 2); // Top & Bottom Scanline
 | 
			
		||||
        std.mem.set(Pixel, buf, .unset);
 | 
			
		||||
 | 
			
		||||
        return .{
 | 
			
		||||
            // Top & Bototm Layers
 | 
			
		||||
            .layers = [_][]?u16{ buf[0..][0..width], buf[width..][0..width] },
 | 
			
		||||
            .layers = [_][]Pixel{ buf[0..][0..width], buf[width..][0..width] },
 | 
			
		||||
            .buf = buf,
 | 
			
		||||
            .allocator = allocator,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn reset(self: *Self) void {
 | 
			
		||||
        std.mem.set(?u16, self.buf, null);
 | 
			
		||||
        std.mem.set(Pixel, self.buf, .unset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn deinit(self: *Self) void {
 | 
			
		||||
@@ -1344,11 +1464,11 @@ const Scanline = struct {
 | 
			
		||||
        self.* = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn top(self: *Self) []?u16 {
 | 
			
		||||
    fn top(self: *Self) []Pixel {
 | 
			
		||||
        return self.layers[0];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn btm(self: *Self) []?u16 {
 | 
			
		||||
    fn btm(self: *Self) []Pixel {
 | 
			
		||||
        return self.layers[1];
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user