internal/ui: add a length check for uniform variables

This commit is contained in:
Hajime Hoshi 2023-07-29 18:57:24 +09:00
parent 5ddf1df423
commit d0e4023d88
3 changed files with 122 additions and 0 deletions

View File

@ -577,6 +577,8 @@ var _ [len(DrawTrianglesShaderOptions{}.Images) - graphics.ShaderImageCount]stru
// //
// When a specified image is non-nil and is disposed, DrawTrianglesShader panics. // When a specified image is non-nil and is disposed, DrawTrianglesShader panics.
// //
// If a specified uniform variable's length or type doesn't match with an expected one, DrawTrianglesShader panics.
//
// When the image i is disposed, DrawTrianglesShader does nothing. // When the image i is disposed, DrawTrianglesShader does nothing.
func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader *Shader, options *DrawTrianglesShaderOptions) { func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader *Shader, options *DrawTrianglesShaderOptions) {
i.copyCheck() i.copyCheck()
@ -724,6 +726,8 @@ var _ [len(DrawRectShaderOptions{}.Images)]struct{} = [graphics.ShaderImageCount
// //
// When one of the specified image is non-nil and is disposed, DrawRectShader panics. // When one of the specified image is non-nil and is disposed, DrawRectShader panics.
// //
// If a specified uniform variable's length or type doesn't match with an expected one, DrawRectShader panics.
//
// When the image i is disposed, DrawRectShader does nothing. // When the image i is disposed, DrawRectShader does nothing.
func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawRectShaderOptions) { func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawRectShaderOptions) {
i.copyCheck() i.copyCheck()

View File

@ -69,13 +69,25 @@ func (s *Shader) AppendUniforms(dst []uint32, uniforms map[string]any) []uint32
t := v.Type() t := v.Type()
switch t.Kind() { switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if typ.Uint32Count() != 1 {
panic(fmt.Sprintf("ui: unexpected uniform value for %s (%s)", name, typ.String()))
}
dst[idx] = uint32(v.Int()) dst[idx] = uint32(v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
if typ.Uint32Count() != 1 {
panic(fmt.Sprintf("ui: unexpected uniform value for %s (%s)", name, typ.String()))
}
dst[idx] = uint32(v.Uint()) dst[idx] = uint32(v.Uint())
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
if typ.Uint32Count() != 1 {
panic(fmt.Sprintf("ui: unexpected uniform value for %s (%s)", name, typ.String()))
}
dst[idx] = math.Float32bits(float32(v.Float())) dst[idx] = math.Float32bits(float32(v.Float()))
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
l := v.Len() l := v.Len()
if typ.Uint32Count() != l {
panic(fmt.Sprintf("ui: unexpected uniform value for %s (%s)", name, typ.String()))
}
switch t.Elem().Kind() { switch t.Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
for i := 0; i < l; i++ { for i := 0; i < l; i++ {

View File

@ -1854,3 +1854,109 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
} }
func TestShaderUniformSizes(t *testing.T) {
const w, h = 16, 16
dst := ebiten.NewImage(w, h)
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
package main
var U vec4
var V [3]float
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
return vec4(0)
}
`))
if err != nil {
t.Fatal(err)
}
tests := []struct {
uniforms map[string]any
err bool
}{
{
uniforms: nil,
err: false,
},
{
uniforms: map[string]any{
"U": 1,
},
err: true,
},
{
uniforms: map[string]any{
"U": "1",
},
err: true,
},
{
uniforms: map[string]any{
"U": []int32{1, 2, 3},
},
err: true,
},
{
uniforms: map[string]any{
"U": []int32{1, 2, 3, 4},
},
err: false,
},
{
uniforms: map[string]any{
"U": []int32{1, 2, 3, 4, 5},
},
err: true,
},
{
uniforms: map[string]any{
"V": 1,
},
err: true,
},
{
uniforms: map[string]any{
"V": "1",
},
err: true,
},
{
uniforms: map[string]any{
"V": []int32{1, 2},
},
err: true,
},
{
uniforms: map[string]any{
"V": []int32{1, 2, 3},
},
err: false,
},
{
uniforms: map[string]any{
"V": []int32{1, 2, 3, 4},
},
err: true,
},
}
for _, tc := range tests {
tc := tc
t.Run(fmt.Sprintf("%v", tc.uniforms), func(t *testing.T) {
defer func() {
r := recover()
if r != nil && !tc.err {
t.Errorf("DrawRectShader must not panic but did")
} else if r == nil && tc.err {
t.Errorf("DrawRectShader must panic but does not")
}
}()
op := &ebiten.DrawRectShaderOptions{}
op.Uniforms = tc.uniforms
dst.DrawRectShader(w, h, s, op)
})
}
}