diff --git a/internal/shaderir/glsl/glsl.go b/internal/shaderir/glsl/glsl.go index f603e0217..11b32d186 100644 --- a/internal/shaderir/glsl/glsl.go +++ b/internal/shaderir/glsl/glsl.go @@ -147,9 +147,45 @@ func Compile(p *shaderir.Program, version GLSLVersion) (vertexShader, fragmentSh } } + // Add a dummy function to just touch uniform array variable's elements (#1754). + // Without this, the first elements of a uniform array might not be initialized correctly on some environments. + var touchedUniforms []string + for i, t := range p.Uniforms { + if t.Main != shaderir.Array { + continue + } + if t.Length <= 1 { + continue + } + str := fmt.Sprintf("U%d[%d]", i, t.Length-1) + switch t.Sub[0].Main { + case shaderir.Vec2, shaderir.Vec3, shaderir.Vec4: + str += ".x" + case shaderir.Mat2, shaderir.Mat3, shaderir.Mat4: + str += "[0][0]" + } + str = "float(" + str + ")" + touchedUniforms = append(touchedUniforms, str) + } + + var touchUniformsFunc []string + if len(touchedUniforms) > 0 { + touchUniformsFunc = append(touchUniformsFunc, "float touchUniforms() {") + touchUniformsFunc = append(touchUniformsFunc, fmt.Sprintf("\treturn %s;", strings.Join(touchedUniforms, " + "))) + touchUniformsFunc = append(touchUniformsFunc, "}") + + } + if p.VertexFunc.Block != nil && len(p.VertexFunc.Block.Stmts) > 0 { + if len(touchUniformsFunc) > 0 { + vslines = append(vslines, "") + vslines = append(vslines, touchUniformsFunc...) + } vslines = append(vslines, "") vslines = append(vslines, "void main(void) {") + if len(touchUniformsFunc) > 0 { + vslines = append(vslines, "\ttouchUniforms();") + } vslines = append(vslines, c.glslBlock(p, p.VertexFunc.Block, p.VertexFunc.Block, 0)...) vslines = append(vslines, "}") } diff --git a/shader_test.go b/shader_test.go index 9b83c17ef..c2515c714 100644 --- a/shader_test.go +++ b/shader_test.go @@ -1111,3 +1111,72 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 { } } } + +// Issue #1754 +func TestShaderUniformFirstElement(t *testing.T) { + shaders := []struct { + Name string + Shader string + Uniforms map[string]interface{} + }{ + { + Name: "float array", + Shader: `package main + +var C [2]float + +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + return vec4(C[0], 1, 1, 1) +}`, + Uniforms: map[string]interface{}{ + "C": []float32{1, 1}, + }, + }, + { + Name: "float one-element array", + Shader: `package main + +var C [1]float + +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + return vec4(C[0], 1, 1, 1) +}`, + Uniforms: map[string]interface{}{ + "C": []float32{1}, + }, + }, + { + Name: "matrix array", + Shader: `package main + +var C [2]mat2 + +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + return vec4(C[0][0][0], 1, 1, 1) +}`, + Uniforms: map[string]interface{}{ + "C": []float32{1, 0, 0, 0, 0, 0, 0, 0}, + }, + }, + } + + for _, shader := range shaders { + shader := shader + t.Run(shader.Name, func(t *testing.T) { + const w, h = 1, 1 + + dst := NewImage(w, h) + s, err := NewShader([]byte(shader.Shader)) + if err != nil { + t.Fatal(err) + } + + op := &DrawRectShaderOptions{} + op.Uniforms = shader.Uniforms + dst.DrawRectShader(w, h, s, op) + if got, want := dst.At(0, 0), (color.RGBA{0xff, 0xff, 0xff, 0xff}); got != want { + t.Errorf("got: %v, want: %v", got, want) + } + }) + } +}