Web: plain RGBA blit on wasm (the multi-texture CRT material fights the GL shim); CRT stays native

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-29 14:26:51 +10:00
parent f706a9ce55
commit 5286d15892
2 changed files with 64 additions and 36 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ games/*/saves/
.DS_Store .DS_Store
web/dist/ web/dist/
web/game.bundle.json web/game.bundle.json
.claude/

View File

@ -229,21 +229,25 @@ async fn amain(game_dir: String) {
let mut index_img = Image::gen_image_color(PIC_W as u16, PIC_H as u16, BLACK); let mut index_img = Image::gen_image_color(PIC_W as u16, PIC_H as u16, BLACK);
let index_tex = Texture2D::from_image(&index_img); let index_tex = Texture2D::from_image(&index_img);
index_tex.set_filter(FilterMode::Nearest); index_tex.set_filter(FilterMode::Nearest);
let mut pal_img = Image::gen_image_color(256, 1, BLACK); #[cfg(not(target_arch = "wasm32"))]
let pal_tex = Texture2D::from_image(&pal_img); let (mut pal_img, pal_tex, material) = {
pal_tex.set_filter(FilterMode::Nearest); let pal_img = Image::gen_image_color(256, 1, BLACK);
let material = load_material( let pal_tex = Texture2D::from_image(&pal_img);
ShaderSource::Glsl { vertex: CRT_VERTEX, fragment: CRT_FRAGMENT }, pal_tex.set_filter(FilterMode::Nearest);
MaterialParams { let material = load_material(
textures: vec!["palette_tex".to_string()], ShaderSource::Glsl { vertex: CRT_VERTEX, fragment: CRT_FRAGMENT },
uniforms: vec![ MaterialParams {
UniformDesc::new("crt", UniformType::Float1), textures: vec!["palette_tex".to_string()],
UniformDesc::new("fade", UniformType::Float1), uniforms: vec![
], UniformDesc::new("crt", UniformType::Float1),
..Default::default() UniformDesc::new("fade", UniformType::Float1),
}, ],
) ..Default::default()
.expect("CRT shader compiles"); },
)
.expect("CRT shader compiles");
(pal_img, pal_tex, material)
};
let mut last_dir_sent: u8 = 0; let mut last_dir_sent: u8 = 0;
let mut ed = editor::Editor::new(); let mut ed = editor::Editor::new();
@ -496,28 +500,51 @@ async fn amain(game_dir: String) {
// --- draw ------------------------------------------------------------ // --- draw ------------------------------------------------------------
clear_background(Color::from_rgba(12, 12, 16, 255)); clear_background(Color::from_rgba(12, 12, 16, 255));
// Indices into the R channel; the shader does the palette lookup. // Native: indices into the R channel; the CRT shader does the
let indices = gs.render_indices(true); // palette lookup, scanlines, curvature and fades on the GPU.
for (i, &idx) in indices.iter().enumerate() { #[cfg(not(target_arch = "wasm32"))]
index_img.bytes[i * 4] = idx; {
index_img.bytes[i * 4 + 3] = 255; let indices = gs.render_indices(true);
} for (i, &idx) in indices.iter().enumerate() {
index_tex.update(&index_img); index_img.bytes[i * 4] = idx;
pal_img.bytes.copy_from_slice(&gs.effective_palette()); index_img.bytes[i * 4 + 3] = 255;
pal_tex.update(&pal_img); }
index_tex.update(&index_img);
pal_img.bytes.copy_from_slice(&gs.effective_palette());
pal_tex.update(&pal_img);
gl_use_material(&material); gl_use_material(&material);
material.set_texture("palette_tex", pal_tex.clone()); material.set_texture("palette_tex", pal_tex.clone());
material.set_uniform("crt", if ui.scanlines { 1.0f32 } else { 0.0f32 }); material.set_uniform("crt", if ui.scanlines { 1.0f32 } else { 0.0f32 });
material.set_uniform("fade", ui.fade); material.set_uniform("fade", ui.fade);
draw_texture_ex( draw_texture_ex(
&index_tex, &index_tex,
0.0, 0.0,
BAR_H, BAR_H,
WHITE, WHITE,
DrawTextureParams { dest_size: Some(vec2(PIC_W as f32 * SCALE, PIC_H as f32 * SCALE)), ..Default::default() }, DrawTextureParams { dest_size: Some(vec2(PIC_W as f32 * SCALE, PIC_H as f32 * SCALE)), ..Default::default() },
); );
gl_use_default_material(); gl_use_default_material();
}
// Web: miniquad's GL shim and the multi-texture material don't get
// along (stale-texture binds, frozen frame) — take the MRPGI-proven
// path instead: CPU palette blit, plain textured quad. The CRT stays
// a desktop luxury.
#[cfg(target_arch = "wasm32")]
{
let rgba = gs.render_visual(true);
index_img.bytes.copy_from_slice(&rgba);
index_tex.update(&index_img);
let tint = (ui.fade * 255.0) as u8;
draw_texture_ex(
&index_tex,
0.0,
BAR_H,
Color::from_rgba(tint, tint, tint, 255),
DrawTextureParams { dest_size: Some(vec2(PIC_W as f32 * SCALE, PIC_H as f32 * SCALE)), ..Default::default() },
);
}
if ui.flash > 0.0 { if ui.flash > 0.0 {
draw_rectangle( draw_rectangle(