From 5cd65a0cb0c3df69079e6c9af0bbd31d0df3dab9 Mon Sep 17 00:00:00 2001 From: MrBruh <4706866+cybersecurity@users.noreply.github.com> Date: Sun, 11 Oct 2026 22:38:11 +1300 Subject: [PATCH] Web: meter blend in the shader WebGL refuses blendFunc(CONSTANT_ALPHA, CONSTANT_COLOR), so the meters (ammo counters) drew with the previous blend and a GL error each frame. --- port/linux/src/d3d8_gl.c | 44 +++++++++++++++++++++++++++++++++++++-- port/linux/src/nv2a_psh.c | 13 ++++++++++++ port/linux/src/xgpu.h | 5 +++++ port/web/README.md | 4 ++++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/port/linux/src/d3d8_gl.c b/port/linux/src/d3d8_gl.c index 81a9f852..054aec19 100644 --- a/port/linux/src/d3d8_gl.c +++ b/port/linux/src/d3d8_gl.c @@ -394,6 +394,9 @@ struct program_entry GLint bump_matrix, bump_luminance, texture_scale; GLint texture_lod_bias; GLint screen_offset; + /* nv2a_pixel_shader_key's source_blend's color, and what it last had */ + GLint blend_constant; + float blend_constant_value[4]; /* the lights of a draw lit for each pixel (XGPU_MODEL_LIGHT_COUNT), and constants_serial at their last upload */ GLint model_lights; @@ -793,6 +796,27 @@ void xgpu_gl_texture_deleted(unsigned int texture) } } +/* the source blend factor the pixel shader applies (nv2a_pixel_shader_key's +source_blend), when the draw's blend mixes a constant color factor with a +constant alpha one, which WebGL refuses; 0 otherwise */ +static unsigned char source_blend_in_shader(void) +{ +#ifdef HALO_WEB + DWORD source = D3D__RenderState[D3DRS_SRCBLEND], destination = D3D__RenderState[D3DRS_DESTBLEND]; + int source_kind = source == D3DBLEND_CONSTANTCOLOR || source == D3DBLEND_INVCONSTANTCOLOR ? 1 : + source == D3DBLEND_CONSTANTALPHA || source == D3DBLEND_INVCONSTANTALPHA ? 2 : 0; + int destination_kind = destination == D3DBLEND_CONSTANTCOLOR || destination == D3DBLEND_INVCONSTANTCOLOR ? 1 : + destination == D3DBLEND_CONSTANTALPHA || destination == D3DBLEND_INVCONSTANTALPHA ? 2 : 0; + + if (D3D__RenderState[D3DRS_ALPHABLENDENABLE] && source_kind && destination_kind && source_kind != destination_kind) + { + return source == D3DBLEND_CONSTANTCOLOR ? 1 : source == D3DBLEND_INVCONSTANTCOLOR ? 2 : + source == D3DBLEND_CONSTANTALPHA ? 3 : 4; + } +#endif + return 0; +} + static void state_enable(unsigned char *shadow, GLenum capability, BOOL enabled) { unsigned char value = enabled ? 1 : 0; @@ -2827,6 +2851,7 @@ static struct program_entry *program_get(GLuint vertex_shader, GLuint fragment_s entry->texture_scale = glGetUniformLocation(entry->program, "texture_scale"); entry->texture_lod_bias = glGetUniformLocation(entry->program, "texture_lod_bias"); entry->screen_offset = glGetUniformLocation(entry->program, "screen_offset"); + entry->blend_constant = glGetUniformLocation(entry->program, "blend_constant"); entry->model_lights = glGetUniformLocation(entry->program, "model_lights"); for (stage = 0; stage < D3DTSS_MAXSTAGES; stage++) { @@ -3485,10 +3510,13 @@ static void apply_raster_state(BOOL has_depth) GLenum equation = blend_equation(rs[D3DRS_BLENDOP]); float blend_color[4]; - if (gl_state.blend_source != (GLenum)rs[D3DRS_SRCBLEND] || + /* (a constant factor the pixel shader applies: source_blend_in_shader) */ + GLenum source = source_blend_in_shader() ? GL_ONE : (GLenum)rs[D3DRS_SRCBLEND]; + + if (gl_state.blend_source != source || gl_state.blend_destination != (GLenum)rs[D3DRS_DESTBLEND]) { - gl_state.blend_source = (GLenum)rs[D3DRS_SRCBLEND]; + gl_state.blend_source = source; gl_state.blend_destination = (GLenum)rs[D3DRS_DESTBLEND]; glBlendFunc(gl_state.blend_source, gl_state.blend_destination); } @@ -3721,6 +3749,7 @@ static struct program_entry *prepare_draw(BOOL immediate) key.alpha_test_samples = (unsigned char)target_samples; #endif key.fog_enable = D3D__RenderState[D3DRS_FOGENABLE] != 0; + key.source_blend = source_blend_in_shader(); key.fog_table_mode = (unsigned char)D3D__RenderState[D3DRS_FOGTABLEMODE]; #ifdef HALO_GLES key.count_samples = device.visibility_test_active && xgpu_capabilities.atomic_counters; @@ -3756,6 +3785,17 @@ static struct program_entry *prepare_draw(BOOL immediate) stats.draws++; draw_flush(); state_program(entry->program); + if (key.source_blend && entry->blend_constant >= 0) + { + float blend_constant[4]; + + color_to_vec4(D3D__RenderState[D3DRS_BLENDCOLOR], blend_constant); + if (memcmp(entry->blend_constant_value, blend_constant, sizeof(blend_constant))) + { + memcpy(entry->blend_constant_value, blend_constant, sizeof(blend_constant)); + glUniform4fv(entry->blend_constant, 1, blend_constant); + } + } #ifdef HALO_GLES if (key.count_samples) glBindBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, device.visibility_counters, diff --git a/port/linux/src/nv2a_psh.c b/port/linux/src/nv2a_psh.c index 8ee338be..ef675e33 100644 --- a/port/linux/src/nv2a_psh.c +++ b/port/linux/src/nv2a_psh.c @@ -620,6 +620,8 @@ char *nv2a_pixel_shader_to_glsl(const struct nv2a_pixel_shader_key *key) XGPU_PIXEL_UNIFORMS); for (stage = 0; stage < 4; stage++) xgpu_text_append(&text, "uniform %s tex%d;\n", sampler_declaration(key->sampler_type[stage]), stage); + if (key->source_blend) + xgpu_text_append(&text, "uniform vec4 blend_constant;\n"); if (key->per_pixel_lighting) model_lighting(&text, key->per_pixel_lighting == 2); xgpu_text_append(&text, @@ -745,6 +747,17 @@ char *nv2a_pixel_shader_to_glsl(const struct nv2a_pixel_shader_key *key) if (key->count_samples) xgpu_text_append(&text, "\tatomicCounterIncrement(visible_samples);\n"); #endif + if (key->source_blend) + { + static const char *const factors[] = + { + "blend_constant", "(1.0 - blend_constant)", "vec4(blend_constant.a)", "vec4(1.0 - blend_constant.a)" + }; + + xgpu_text_append(&text, "\tfragment_color = clamp(result, 0.0, 1.0) * %s;\n}\n", + factors[(key->source_blend - 1) & 3]); + return text.buffer; + } xgpu_text_append(&text, "\tfragment_color = clamp(result, 0.0, 1.0);\n}\n"); return text.buffer; } diff --git a/port/linux/src/xgpu.h b/port/linux/src/xgpu.h index a511c711..2246eca2 100644 --- a/port/linux/src/xgpu.h +++ b/port/linux/src/xgpu.h @@ -146,6 +146,11 @@ struct nv2a_pixel_shader_key proportion to how far alpha is past the reference, not all of the pixel or none of it, so that cut-out edges (foliage, grates) are smoothed too */ unsigned char alpha_test_samples; + /* the source's blend factor, applied here instead (1 the blend color, 2 + one less it, 3 its alpha, 4 one less that; 0 none): WebGL refuses a + blend of a constant color factor with a constant alpha one, as the + meters' (shader_transparent_meter) */ + unsigned char source_blend; }; char *nv2a_pixel_shader_to_glsl(const struct nv2a_pixel_shader_key *key); diff --git a/port/web/README.md b/port/web/README.md index 35423684..f4f16009 100644 --- a/port/web/README.md +++ b/port/web/README.md @@ -261,6 +261,10 @@ multiply-adds). `tools/web_build.py` writes the graph. each attribute (`d3d8_gl.c`). - No feedback loops: a draw that samples the target it draws into samples a copy of it. + - No blend of a constant color factor with a constant alpha one (the + meters, as the ammo counters on the weapons): the source's factor is + applied by the pixel shader instead (`nv2a_pixel_shader_key`'s + `source_blend`). - No `glCopyImageSubData`, `glDrawElementsBaseVertex` or `glMemoryBarrier`: the ES renderer's blits and rebased indices. An index array drawn twice the same is kept rebased in a buffer of its own