Quick answer: Cache the handle from shader_get_uniform(sh, "name") once. After shader_set(sh), call shader_set_uniform_f_array(handle, flat_array). Length must match the shader’s declared uniform size.

You upload 16 light positions to a shader. Lights render at the origin. The handle was wrong, or the call happened before shader_set, or the array length didn’t match.

The Symptom

Uniform array values default to 0 in the shader. show_debug_message of the array on the GML side shows correct values; the shader doesn’t see them.

The Fix

/// In Create event
sh_handle_lights = shader_get_uniform(sh_lighting, "u_lights");

/// In Draw event
shader_set(sh_lighting);

var data = array_create(16 * 4, 0);
for (var i = 0; i < 16; ++i) {
    data[i*4 + 0] = lights[i].x;
    data[i*4 + 1] = lights[i].y;
    data[i*4 + 2] = lights[i].radius;
    data[i*4 + 3] = lights[i].intensity;
}

shader_set_uniform_f_array(sh_handle_lights, data);

draw_self();
shader_reset();

Cache handle in Create. shader_set first, then upload, then draw, then reset.

Shader Declaration

// Shader
uniform vec4 u_lights[16];

void main() {
    vec4 light = u_lights[0];
    // ...
}

16 vec4s = 64 floats in the GML array. The size declared in the shader must match (or be larger).

For Big Data

If you need more than ~64 vec4s, encode the data into a surface and sample as a texture in the shader. Uniform array slots are limited.

Verifying

Add a debug visualization in the shader (output u_lights[0].x as red channel). If red is 0, upload failed. If non-zero, upload succeeded.

“Cache handle. shader_set first. Flat array of right length. Uniforms upload.”

Related Issues

For surface lost, see surface lost. For asset precache, see asset precache.

Cache. Set. Upload. Shader sees.