diff --git a/internal/graphicsdriver/directx/graphics_windows.go b/internal/graphicsdriver/directx/graphics_windows.go index aaf89ed4e..c157f0422 100644 --- a/internal/graphicsdriver/directx/graphics_windows.go +++ b/internal/graphicsdriver/directx/graphics_windows.go @@ -1682,24 +1682,37 @@ func (s *Shader) uniformsToFloat32s(uniforms [][]float32) []float32 { fs = append(fs, u...) case shaderir.Mat2: for j := 0; j < t.Length; j++ { - for k := 0; k < 2; k++ { - fs = append(fs, u[2*(2*j+k):2*(2*j+k+1)]...) - if j < t.Length-1 || k < 1 { - fs = append(fs, 0, 0) - } - } + u1 := u[4*j : 4*(j+1)] + fs = append(fs, + u1[0], u1[2], 0, 0, + u1[1], u1[3], 0, 0, + ) + } + if t.Length > 0 { + fs = fs[:len(fs)-2] } case shaderir.Mat3: for j := 0; j < t.Length; j++ { - for k := 0; k < 3; k++ { - fs = append(fs, u[3*(3*j+k):3*(3*j+k+1)]...) - if j < t.Length-1 || k < 2 { - fs = append(fs, 0) - } - } + u1 := u[9*j : 9*(j+1)] + fs = append(fs, + u1[0], u1[3], u1[6], 0, + u1[1], u1[4], u1[7], 0, + u1[2], u1[5], u1[8], 0, + ) + } + if t.Length > 0 { + fs = fs[:len(fs)-1] } case shaderir.Mat4: - fs = append(fs, u...) + for j := 0; j < t.Length; j++ { + u1 := u[16*j : 16*(j+1)] + fs = append(fs, + u1[0], u1[4], u1[8], u1[12], + u1[1], u1[5], u1[9], u1[13], + u1[2], u1[6], u1[10], u1[14], + u1[3], u1[7], u1[11], u1[15], + ) + } default: panic(fmt.Sprintf("directx: not implemented type for uniform variables: %s", t.String())) } diff --git a/internal/graphicsdriver/metal/graphics_darwin.go b/internal/graphicsdriver/metal/graphics_darwin.go index 34165c1be..b7c1a9cd9 100644 --- a/internal/graphicsdriver/metal/graphics_darwin.go +++ b/internal/graphicsdriver/metal/graphics_darwin.go @@ -998,14 +998,30 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. // Set the additional uniform variables. for i, v := range uniforms { const offset = graphics.PreservedUniformVariablesNum - switch g.shaders[shaderID].ir.Uniforms[offset+i].Main { + t := g.shaders[shaderID].ir.Uniforms[offset+i] + switch t.Main { case shaderir.Mat3: // float3x3 requires 16-byte alignment (#2036). - newV := make([]float32, 12) - copy(newV[0:3], v[0:3]) - copy(newV[4:7], v[3:6]) - copy(newV[8:11], v[6:9]) - uniformVars[offset+i] = newV + v1 := make([]float32, 12) + copy(v1[0:3], v[0:3]) + copy(v1[4:7], v[3:6]) + copy(v1[8:11], v[6:9]) + uniformVars[offset+i] = v1 + case shaderir.Array: + switch t.Sub[0].Main { + case shaderir.Mat3: + v1 := make([]float32, t.Length*12) + for j := 0; j < t.Length; j++ { + offset0 := j * 9 + offset1 := j * 12 + copy(v1[offset1:offset1+3], v[offset0:offset0+3]) + copy(v1[offset1+4:offset1+7], v[offset0+3:offset0+6]) + copy(v1[offset1+8:offset1+11], v[offset0+6:offset0+9]) + } + uniformVars[offset+i] = v1 + default: + uniformVars[offset+i] = v + } default: uniformVars[offset+i] = v } diff --git a/shader_test.go b/shader_test.go index 4f2c34951..e301356d6 100644 --- a/shader_test.go +++ b/shader_test.go @@ -763,6 +763,44 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 { } } +func TestShaderUniformMatrix2Array(t *testing.T) { + const w, h = 16, 16 + + dst := ebiten.NewImage(w, h) + s, err := ebiten.NewShader([]byte(`package main + +var Mat2 [2]mat2 + +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + return vec4(256 * Mat2[0] * Mat2[1] * vec2(1), 1, 1) +} +`)) + if err != nil { + t.Fatal(err) + } + + op := &ebiten.DrawRectShaderOptions{} + op.Uniforms = map[string]interface{}{ + "Mat2": []float32{ + 1.0 / 256.0, 2.0 / 256.0, + 3.0 / 256.0, 4.0 / 256.0, + 5.0 / 256.0, 6.0 / 256.0, + 7.0 / 256.0, 8.0 / 256.0, + }, + } + dst.DrawRectShader(w, h, s, op) + + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + got := dst.At(i, j).(color.RGBA) + want := color.RGBA{54, 80, 0xff, 0xff} + if !sameColors(got, want, 2) { + t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want) + } + } + } +} + func TestShaderUniformMatrix3(t *testing.T) { const w, h = 16, 16 @@ -800,6 +838,46 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 { } } +func TestShaderUniformMatrix3Array(t *testing.T) { + const w, h = 16, 16 + + dst := ebiten.NewImage(w, h) + s, err := ebiten.NewShader([]byte(`package main + +var Mat3 [2]mat3 + +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + return vec4(3 * Mat3[0] * Mat3[1] * vec3(1), 1) +} +`)) + if err != nil { + t.Fatal(err) + } + + op := &ebiten.DrawRectShaderOptions{} + op.Uniforms = map[string]interface{}{ + "Mat3": []float32{ + 1.0 / 256.0, 2.0 / 256.0, 3.0 / 256.0, + 4.0 / 256.0, 5.0 / 256.0, 6.0 / 256.0, + 7.0 / 256.0, 8.0 / 256.0, 9.0 / 256.0, + 10.0 / 256.0, 11.0 / 256.0, 12.0 / 256.0, + 13.0 / 256.0, 14.0 / 256.0, 15.0 / 256.0, + 16.0 / 256.0, 17.0 / 256.0, 18.0 / 256.0, + }, + } + dst.DrawRectShader(w, h, s, op) + + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + got := dst.At(i, j).(color.RGBA) + want := color.RGBA{6, 8, 9, 0xff} + if !sameColors(got, want, 2) { + t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want) + } + } + } +} + func TestShaderUniformMatrix4(t *testing.T) { const w, h = 16, 16 @@ -837,3 +915,45 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 { } } } + +func TestShaderUniformMatrix4Array(t *testing.T) { + const w, h = 16, 16 + + dst := ebiten.NewImage(w, h) + s, err := ebiten.NewShader([]byte(`package main + +var Mat4 [2]mat4 + +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + return Mat4[0] * Mat4[1] * vec4(1) +} +`)) + if err != nil { + t.Fatal(err) + } + + op := &ebiten.DrawRectShaderOptions{} + op.Uniforms = map[string]interface{}{ + "Mat4": []float32{ + 1.0 / 256.0, 2.0 / 256.0, 3.0 / 256.0, 4.0 / 256.0, + 5.0 / 256.0, 6.0 / 256.0, 7.0 / 256.0, 8.0 / 256.0, + 9.0 / 256.0, 10.0 / 256.0, 11.0 / 256.0, 12.0 / 256.0, + 13.0 / 256.0, 14.0 / 256.0, 15.0 / 256.0, 16.0 / 256.0, + 17.0 / 256.0, 18.0 / 256.0, 19.0 / 256.0, 20.0 / 256.0, + 21.0 / 256.0, 22.0 / 256.0, 23.0 / 256.0, 24.0 / 256.0, + 25.0 / 256.0, 26.0 / 256.0, 27.0 / 256.0, 28.0 / 256.0, + 29.0 / 256.0, 30.0 / 256.0, 31.0 / 256.0, 32.0 / 256.0, + }, + } + dst.DrawRectShader(w, h, s, op) + + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + got := dst.At(i, j).(color.RGBA) + want := color.RGBA{11, 13, 14, 16} + if !sameColors(got, want, 2) { + t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want) + } + } + } +}