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.
This commit is contained in:
MrBruh committed 2026-10-11 22:38:11 +13:00
1 parent a3277d94dc
commit 5cd65a0cb0
4 files changed
+64 -2

No files matched your search

+42 -2
View File
@@ -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,
+13
View File
@@ -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;
}
+5
View File
@@ -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);
+4
View File
@@ -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