all: allow integer uniform variables for Kage shaders

Closes #2305
Updates #2448
This commit is contained in:
Hajime Hoshi 2022-11-12 18:38:15 +09:00
parent 4ff9a12930
commit 1ecac8d834
20 changed files with 279 additions and 57 deletions

View File

@ -505,7 +505,7 @@ type DrawTrianglesShaderOptions struct {
// Uniforms is a set of uniform variables for the shader. // Uniforms is a set of uniform variables for the shader.
// The keys are the names of the uniform variables. // The keys are the names of the uniform variables.
// The values must be float or []float. // The values must be a numeric type or a slice of a numeric type.
// If the uniform variable type is an array, a vector or a matrix, // If the uniform variable type is an array, a vector or a matrix,
// you have to specify linearly flattened values as a slice. // you have to specify linearly flattened values as a slice.
// For example, if the uniform variable type is [4]vec4, the number of the slice values will be 16. // For example, if the uniform variable type is [4]vec4, the number of the slice values will be 16.
@ -665,7 +665,7 @@ type DrawRectShaderOptions struct {
// Uniforms is a set of uniform variables for the shader. // Uniforms is a set of uniform variables for the shader.
// The keys are the names of the uniform variables. // The keys are the names of the uniform variables.
// The values must be float or []float. // The values must be a numeric type or a slice of a numeric type.
// If the uniform variable type is an array, a vector or a matrix, // If the uniform variable type is an array, a vector or a matrix,
// you have to specify linearly flattened values as a slice. // you have to specify linearly flattened values as a slice.
// For example, if the uniform variable type is [4]vec4, the number of the slice values will be 16. // For example, if the uniform variable type is [4]vec4, the number of the slice values will be 16.

View File

@ -1732,6 +1732,12 @@ func (s *Shader) flattenUniforms(uniforms [][]uint32) []uint32 {
} else { } else {
fs = append(fs, 0) fs = append(fs, 0)
} }
case shaderir.Int:
if u != nil {
fs = append(fs, u...)
} else {
fs = append(fs, 0)
}
case shaderir.Vec2: case shaderir.Vec2:
if u != nil { if u != nil {
fs = append(fs, u...) fs = append(fs, u...)
@ -1806,6 +1812,17 @@ func (s *Shader) flattenUniforms(uniforms [][]uint32) []uint32 {
} else { } else {
fs = append(fs, make([]uint32, (t.Length-1)*4+1)...) fs = append(fs, make([]uint32, (t.Length-1)*4+1)...)
} }
case shaderir.Int:
if u != nil {
for j := 0; j < t.Length; j++ {
fs = append(fs, u[j])
if j < t.Length-1 {
fs = append(fs, 0, 0, 0)
}
}
} else {
fs = append(fs, make([]uint32, (t.Length-1)*4+1)...)
}
case shaderir.Vec2: case shaderir.Vec2:
if u != nil { if u != nil {
for j := 0; j < t.Length; j++ { for j := 0; j < t.Length; j++ {

View File

@ -375,7 +375,7 @@ func (c *context) uniformInt(p program, location string, v int) bool {
return true return true
} }
func (c *context) uniformFloats(p program, location string, v []float32, typ shaderir.Type) bool { func (c *context) uniforms(p program, location string, v []uint32, typ shaderir.Type) bool {
l := int32(c.locationCache.GetUniformLocation(c, p, location)) l := int32(c.locationCache.GetUniformLocation(c, p, location))
if l == invalidUniform { if l == invalidUniform {
return false return false
@ -391,6 +391,8 @@ func (c *context) uniformFloats(p program, location string, v []float32, typ sha
switch base { switch base {
case shaderir.Float: case shaderir.Float:
gl.Uniform1fv(l, len, (*float32)(gl.Ptr(v))) gl.Uniform1fv(l, len, (*float32)(gl.Ptr(v)))
case shaderir.Int:
gl.Uniform1iv(l, len, (*int32)(gl.Ptr(v)))
case shaderir.Vec2: case shaderir.Vec2:
gl.Uniform2fv(l, len, (*float32)(gl.Ptr(v))) gl.Uniform2fv(l, len, (*float32)(gl.Ptr(v)))
case shaderir.Vec3: case shaderir.Vec3:

View File

@ -344,7 +344,7 @@ func (c *context) uniformInt(p program, location string, v int) bool {
return true return true
} }
func (c *context) uniformFloats(p program, location string, v []float32, typ shaderir.Type) bool { func (c *context) uniforms(p program, location string, v []uint32, typ shaderir.Type) bool {
l := c.locationCache.GetUniformLocation(c, p, location) l := c.locationCache.GetUniformLocation(c, p, location)
if l == invalidUniform { if l == invalidUniform {
return false return false
@ -357,19 +357,21 @@ func (c *context) uniformFloats(p program, location string, v []float32, typ sha
switch base { switch base {
case shaderir.Float: case shaderir.Float:
c.ctx.Uniform1fv(int32(l), v) c.ctx.Uniform1fv(int32(l), uint32sToFloat32s(v))
case shaderir.Int:
c.ctx.Uniform1iv(int32(l), uint32sToInt32s(v))
case shaderir.Vec2: case shaderir.Vec2:
c.ctx.Uniform2fv(int32(l), v) c.ctx.Uniform2fv(int32(l), uint32sToFloat32s(v))
case shaderir.Vec3: case shaderir.Vec3:
c.ctx.Uniform3fv(int32(l), v) c.ctx.Uniform3fv(int32(l), uint32sToFloat32s(v))
case shaderir.Vec4: case shaderir.Vec4:
c.ctx.Uniform4fv(int32(l), v) c.ctx.Uniform4fv(int32(l), uint32sToFloat32s(v))
case shaderir.Mat2: case shaderir.Mat2:
c.ctx.UniformMatrix2fv(int32(l), false, v) c.ctx.UniformMatrix2fv(int32(l), false, uint32sToFloat32s(v))
case shaderir.Mat3: case shaderir.Mat3:
c.ctx.UniformMatrix3fv(int32(l), false, v) c.ctx.UniformMatrix3fv(int32(l), false, uint32sToFloat32s(v))
case shaderir.Mat4: case shaderir.Mat4:
c.ctx.UniformMatrix4fv(int32(l), false, v) c.ctx.UniformMatrix4fv(int32(l), false, uint32sToFloat32s(v))
default: default:
panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String())) panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String()))
} }

View File

@ -451,7 +451,7 @@ func (c *context) uniformInt(p program, location string, v int) bool {
return true return true
} }
func (c *context) uniformFloats(p program, location string, v []float32, typ shaderir.Type) bool { func (c *context) uniforms(p program, location string, v []uint32, typ shaderir.Type) bool {
gl := c.gl gl := c.gl
l := c.locationCache.GetUniformLocation(c, p, location) l := c.locationCache.GetUniformLocation(c, p, location)
if l.equal(invalidUniform) { if l.equal(invalidUniform) {
@ -463,46 +463,58 @@ func (c *context) uniformFloats(p program, location string, v []float32, typ sha
base = typ.Sub[0].Main base = typ.Sub[0].Main
} }
arr := jsutil.TemporaryFloat32Array(len(v), v)
switch base { switch base {
case shaderir.Float: case shaderir.Float:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() { if c.usesWebGL2() {
gl.uniform1fv.Invoke(js.Value(l), arr, 0, len(v)) gl.uniform1fv.Invoke(js.Value(l), arr, 0, len(v))
} else { } else {
gl.uniform1fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) gl.uniform1fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
} }
case shaderir.Int:
arr := jsutil.TemporaryInt32Array(len(v), uint32sToInt32s(v))
if c.usesWebGL2() {
gl.uniform1iv.Invoke(js.Value(l), arr, 0, len(v))
} else {
gl.uniform1iv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
}
case shaderir.Vec2: case shaderir.Vec2:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() { if c.usesWebGL2() {
gl.uniform2fv.Invoke(js.Value(l), arr, 0, len(v)) gl.uniform2fv.Invoke(js.Value(l), arr, 0, len(v))
} else { } else {
gl.uniform2fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) gl.uniform2fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
} }
case shaderir.Vec3: case shaderir.Vec3:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() { if c.usesWebGL2() {
gl.uniform3fv.Invoke(js.Value(l), arr, 0, len(v)) gl.uniform3fv.Invoke(js.Value(l), arr, 0, len(v))
} else { } else {
gl.uniform3fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) gl.uniform3fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
} }
case shaderir.Vec4: case shaderir.Vec4:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() { if c.usesWebGL2() {
gl.uniform4fv.Invoke(js.Value(l), arr, 0, len(v)) gl.uniform4fv.Invoke(js.Value(l), arr, 0, len(v))
} else { } else {
gl.uniform4fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) gl.uniform4fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
} }
case shaderir.Mat2: case shaderir.Mat2:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() { if c.usesWebGL2() {
gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr, 0, len(v)) gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr, 0, len(v))
} else { } else {
gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v))) gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v)))
} }
case shaderir.Mat3: case shaderir.Mat3:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() { if c.usesWebGL2() {
gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr, 0, len(v)) gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr, 0, len(v))
} else { } else {
gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v))) gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v)))
} }
case shaderir.Mat4: case shaderir.Mat4:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() { if c.usesWebGL2() {
gl.uniformMatrix4fv.Invoke(js.Value(l), false, arr, 0, len(v)) gl.uniformMatrix4fv.Invoke(js.Value(l), false, arr, 0, len(v))
} else { } else {

View File

@ -31,6 +31,8 @@ func Ptr(data any) unsafe.Pointer {
addr = unsafe.Pointer(&v[0]) addr = unsafe.Pointer(&v[0])
case []uint16: case []uint16:
addr = unsafe.Pointer(&v[0]) addr = unsafe.Pointer(&v[0])
case []uint32:
addr = unsafe.Pointer(&v[0])
case []float32: case []float32:
addr = unsafe.Pointer(&v[0]) addr = unsafe.Pointer(&v[0])
default: default:

View File

@ -163,8 +163,9 @@ package gl
// typedef void (APIENTRYP GPTEXIMAGE2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); // typedef void (APIENTRYP GPTEXIMAGE2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels);
// typedef void (APIENTRYP GPTEXPARAMETERI)(GLenum target, GLenum pname, GLint param); // typedef void (APIENTRYP GPTEXPARAMETERI)(GLenum target, GLenum pname, GLint param);
// typedef void (APIENTRYP GPTEXSUBIMAGE2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); // typedef void (APIENTRYP GPTEXSUBIMAGE2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels);
// typedef void (APIENTRYP GPUNIFORM1I)(GLint location, GLint v0);
// typedef void (APIENTRYP GPUNIFORM1FV)(GLint location, GLsizei count, const GLfloat * value); // typedef void (APIENTRYP GPUNIFORM1FV)(GLint location, GLsizei count, const GLfloat * value);
// typedef void (APIENTRYP GPUNIFORM1I)(GLint location, GLint v0);
// typedef void (APIENTRYP GPUNIFORM1IV)(GLint location, GLsizei count, const GLint * value);
// typedef void (APIENTRYP GPUNIFORM2FV)(GLint location, GLsizei count, const GLfloat * value); // typedef void (APIENTRYP GPUNIFORM2FV)(GLint location, GLsizei count, const GLfloat * value);
// typedef void (APIENTRYP GPUNIFORM3FV)(GLint location, GLsizei count, const GLfloat * value); // typedef void (APIENTRYP GPUNIFORM3FV)(GLint location, GLsizei count, const GLfloat * value);
// typedef void (APIENTRYP GPUNIFORM4FV)(GLint location, GLsizei count, const GLfloat * value); // typedef void (APIENTRYP GPUNIFORM4FV)(GLint location, GLsizei count, const GLfloat * value);
@ -382,10 +383,13 @@ package gl
// static void glowTexSubImage2D(GPTEXSUBIMAGE2D fnptr, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels) { // static void glowTexSubImage2D(GPTEXSUBIMAGE2D fnptr, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels) {
// (*fnptr)(target, level, xoffset, yoffset, width, height, format, type, pixels); // (*fnptr)(target, level, xoffset, yoffset, width, height, format, type, pixels);
// } // }
// static void glowUniform1fv(GPUNIFORM1FV fnptr, GLint location, GLsizei count, const GLfloat * value) {
// (*fnptr)(location, count, value);
// }
// static void glowUniform1i(GPUNIFORM1I fnptr, GLint location, GLint v0) { // static void glowUniform1i(GPUNIFORM1I fnptr, GLint location, GLint v0) {
// (*fnptr)(location, v0); // (*fnptr)(location, v0);
// } // }
// static void glowUniform1fv(GPUNIFORM1FV fnptr, GLint location, GLsizei count, const GLfloat * value) { // static void glowUniform1iv(GPUNIFORM1IV fnptr, GLint location, GLsizei count, const GLint * value) {
// (*fnptr)(location, count, value); // (*fnptr)(location, count, value);
// } // }
// static void glowUniform2fv(GPUNIFORM2FV fnptr, GLint location, GLsizei count, const GLfloat * value) { // static void glowUniform2fv(GPUNIFORM2FV fnptr, GLint location, GLsizei count, const GLfloat * value) {
@ -494,8 +498,9 @@ var (
gpTexImage2D C.GPTEXIMAGE2D gpTexImage2D C.GPTEXIMAGE2D
gpTexParameteri C.GPTEXPARAMETERI gpTexParameteri C.GPTEXPARAMETERI
gpTexSubImage2D C.GPTEXSUBIMAGE2D gpTexSubImage2D C.GPTEXSUBIMAGE2D
gpUniform1i C.GPUNIFORM1I
gpUniform1fv C.GPUNIFORM1FV gpUniform1fv C.GPUNIFORM1FV
gpUniform1i C.GPUNIFORM1I
gpUniform1iv C.GPUNIFORM1IV
gpUniform2fv C.GPUNIFORM2FV gpUniform2fv C.GPUNIFORM2FV
gpUniform3fv C.GPUNIFORM3FV gpUniform3fv C.GPUNIFORM3FV
gpUniform4fv C.GPUNIFORM4FV gpUniform4fv C.GPUNIFORM4FV
@ -791,12 +796,16 @@ func TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, wid
C.glowTexSubImage2D(gpTexSubImage2D, (C.GLenum)(target), (C.GLint)(level), (C.GLint)(xoffset), (C.GLint)(yoffset), (C.GLsizei)(width), (C.GLsizei)(height), (C.GLenum)(format), (C.GLenum)(xtype), pixels) C.glowTexSubImage2D(gpTexSubImage2D, (C.GLenum)(target), (C.GLint)(level), (C.GLint)(xoffset), (C.GLint)(yoffset), (C.GLsizei)(width), (C.GLsizei)(height), (C.GLenum)(format), (C.GLenum)(xtype), pixels)
} }
func Uniform1fv(location int32, count int32, value *float32) {
C.glowUniform1fv(gpUniform1fv, (C.GLint)(location), (C.GLsizei)(count), (*C.GLfloat)(unsafe.Pointer(value)))
}
func Uniform1i(location int32, v0 int32) { func Uniform1i(location int32, v0 int32) {
C.glowUniform1i(gpUniform1i, (C.GLint)(location), (C.GLint)(v0)) C.glowUniform1i(gpUniform1i, (C.GLint)(location), (C.GLint)(v0))
} }
func Uniform1fv(location int32, count int32, value *float32) { func Uniform1iv(location int32, count int32, value *int32) {
C.glowUniform1fv(gpUniform1fv, (C.GLint)(location), (C.GLsizei)(count), (*C.GLfloat)(unsafe.Pointer(value))) C.glowUniform1iv(gpUniform1iv, (C.GLint)(location), (C.GLsizei)(count), (*C.GLint)(unsafe.Pointer(value)))
} }
func Uniform2fv(location int32, count int32, value *float32) { func Uniform2fv(location int32, count int32, value *float32) {
@ -1044,13 +1053,17 @@ func InitWithProcAddrFunc(getProcAddr func(name string) unsafe.Pointer) error {
if gpTexSubImage2D == nil { if gpTexSubImage2D == nil {
return errors.New("gl: glTexSubImage2D is missing") return errors.New("gl: glTexSubImage2D is missing")
} }
gpUniform1fv = (C.GPUNIFORM1FV)(getProcAddr("glUniform1fv"))
if gpUniform1fv == nil {
return errors.New("gl: glUniform1fv is missing")
}
gpUniform1i = (C.GPUNIFORM1I)(getProcAddr("glUniform1i")) gpUniform1i = (C.GPUNIFORM1I)(getProcAddr("glUniform1i"))
if gpUniform1i == nil { if gpUniform1i == nil {
return errors.New("gl: glUniform1i is missing") return errors.New("gl: glUniform1i is missing")
} }
gpUniform1fv = (C.GPUNIFORM1FV)(getProcAddr("glUniform1fv")) gpUniform1iv = (C.GPUNIFORM1IV)(getProcAddr("glUniform1iv"))
if gpUniform1fv == nil { if gpUniform1iv == nil {
return errors.New("gl: glUniform1fv is missing") return errors.New("gl: glUniform1iv is missing")
} }
gpUniform2fv = (C.GPUNIFORM2FV)(getProcAddr("glUniform2fv")) gpUniform2fv = (C.GPUNIFORM2FV)(getProcAddr("glUniform2fv"))
if gpUniform2fv == nil { if gpUniform2fv == nil {

View File

@ -81,8 +81,9 @@ var (
gpTexImage2D uintptr gpTexImage2D uintptr
gpTexParameteri uintptr gpTexParameteri uintptr
gpTexSubImage2D uintptr gpTexSubImage2D uintptr
gpUniform1i uintptr
gpUniform1fv uintptr gpUniform1fv uintptr
gpUniform1i uintptr
gpUniform1iv uintptr
gpUniform2fv uintptr gpUniform2fv uintptr
gpUniform3fv uintptr gpUniform3fv uintptr
gpUniform4fv uintptr gpUniform4fv uintptr
@ -378,12 +379,16 @@ func TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, wid
purego.SyscallN(gpTexSubImage2D, uintptr(target), uintptr(level), uintptr(xoffset), uintptr(yoffset), uintptr(width), uintptr(height), uintptr(format), uintptr(xtype), uintptr(pixels)) purego.SyscallN(gpTexSubImage2D, uintptr(target), uintptr(level), uintptr(xoffset), uintptr(yoffset), uintptr(width), uintptr(height), uintptr(format), uintptr(xtype), uintptr(pixels))
} }
func Uniform1fv(location int32, count int32, value *float32) {
purego.SyscallN(gpUniform1fv, uintptr(location), uintptr(count), uintptr(unsafe.Pointer(value)))
}
func Uniform1i(location int32, v0 int32) { func Uniform1i(location int32, v0 int32) {
purego.SyscallN(gpUniform1i, uintptr(location), uintptr(v0)) purego.SyscallN(gpUniform1i, uintptr(location), uintptr(v0))
} }
func Uniform1fv(location int32, count int32, value *float32) { func Uniform1iv(location int32, count int32, value *int32) {
purego.SyscallN(gpUniform1fv, uintptr(location), uintptr(count), uintptr(unsafe.Pointer(value))) purego.SyscallN(gpUniform1iv, uintptr(location), uintptr(count), uintptr(unsafe.Pointer(value)))
} }
func Uniform2fv(location int32, count int32, value *float32) { func Uniform2fv(location int32, count int32, value *float32) {
@ -631,13 +636,17 @@ func InitWithProcAddrFunc(getProcAddr func(name string) uintptr) error {
if gpTexSubImage2D == 0 { if gpTexSubImage2D == 0 {
return errors.New("gl: glTexSubImage2D is missing") return errors.New("gl: glTexSubImage2D is missing")
} }
gpUniform1fv = getProcAddr("glUniform1fv")
if gpUniform1fv == 0 {
return errors.New("gl: glUniform1fv is missing")
}
gpUniform1i = getProcAddr("glUniform1i") gpUniform1i = getProcAddr("glUniform1i")
if gpUniform1i == 0 { if gpUniform1i == 0 {
return errors.New("gl: glUniform1i is missing") return errors.New("gl: glUniform1i is missing")
} }
gpUniform1fv = getProcAddr("glUniform1fv") gpUniform1iv = getProcAddr("glUniform1iv")
if gpUniform1fv == 0 { if gpUniform1iv == 0 {
return errors.New("gl: glUniform1fv is missing") return errors.New("gl: glUniform1iv is missing")
} }
gpUniform2fv = getProcAddr("glUniform2fv") gpUniform2fv = getProcAddr("glUniform2fv")
if gpUniform2fv == 0 { if gpUniform2fv == 0 {

View File

@ -80,10 +80,11 @@ type gl struct {
texSubImage2D js.Value texSubImage2D js.Value
texParameteri js.Value texParameteri js.Value
uniform1fv js.Value uniform1fv js.Value
uniform1i js.Value
uniform1iv js.Value
uniform2fv js.Value uniform2fv js.Value
uniform3fv js.Value uniform3fv js.Value
uniform4fv js.Value uniform4fv js.Value
uniform1i js.Value
uniformMatrix2fv js.Value uniformMatrix2fv js.Value
uniformMatrix3fv js.Value uniformMatrix3fv js.Value
uniformMatrix4fv js.Value uniformMatrix4fv js.Value
@ -156,10 +157,11 @@ func (c *context) newGL(v js.Value) *gl {
texSubImage2D: v.Get("texSubImage2D").Call("bind", v), texSubImage2D: v.Get("texSubImage2D").Call("bind", v),
texParameteri: v.Get("texParameteri").Call("bind", v), texParameteri: v.Get("texParameteri").Call("bind", v),
uniform1fv: v.Get("uniform1fv").Call("bind", v), uniform1fv: v.Get("uniform1fv").Call("bind", v),
uniform1i: v.Get("uniform1i").Call("bind", v),
uniform1iv: v.Get("uniform1iv").Call("bind", v),
uniform2fv: v.Get("uniform2fv").Call("bind", v), uniform2fv: v.Get("uniform2fv").Call("bind", v),
uniform3fv: v.Get("uniform3fv").Call("bind", v), uniform3fv: v.Get("uniform3fv").Call("bind", v),
uniform4fv: v.Get("uniform4fv").Call("bind", v), uniform4fv: v.Get("uniform4fv").Call("bind", v),
uniform1i: v.Get("uniform1i").Call("bind", v),
uniformMatrix2fv: v.Get("uniformMatrix2fv").Call("bind", v), uniformMatrix2fv: v.Get("uniformMatrix2fv").Call("bind", v),
uniformMatrix3fv: v.Get("uniformMatrix3fv").Call("bind", v), uniformMatrix3fv: v.Get("uniformMatrix3fv").Call("bind", v),
uniformMatrix4fv: v.Get("uniformMatrix4fv").Call("bind", v), uniformMatrix4fv: v.Get("uniformMatrix4fv").Call("bind", v),

View File

@ -332,6 +332,10 @@ func (DefaultContext) Uniform1i(location int32, v0 int32) {
C.glUniform1i(C.GLint(location), C.GLint(v0)) C.glUniform1i(C.GLint(location), C.GLint(v0))
} }
func (DefaultContext) Uniform1iv(location int32, value []int32) {
C.glUniform1iv(C.GLint(location), C.GLsizei(len(value)), (*C.GLint)(unsafe.Pointer(&value[0])))
}
func (DefaultContext) Uniform2fv(location int32, value []float32) { func (DefaultContext) Uniform2fv(location int32, value []float32) {
C.glUniform2fv(C.GLint(location), C.GLsizei(len(value)/2), (*C.GLfloat)(unsafe.Pointer(&value[0]))) C.glUniform2fv(C.GLint(location), C.GLsizei(len(value)/2), (*C.GLfloat)(unsafe.Pointer(&value[0])))
} }

View File

@ -306,6 +306,10 @@ func (g *GomobileContext) Uniform1i(location int32, v0 int32) {
g.ctx.Uniform1i(gl.Uniform{Value: location}, int(v0)) g.ctx.Uniform1i(gl.Uniform{Value: location}, int(v0))
} }
func (g *GomobileContext) Uniform1iv(location int32, value []int32) {
g.ctx.Uniform1iv(gl.Uniform{Value: location}, value)
}
func (g *GomobileContext) Uniform2fv(location int32, value []float32) { func (g *GomobileContext) Uniform2fv(location int32, value []float32) {
g.ctx.Uniform2fv(gl.Uniform{Value: location}, value) g.ctx.Uniform2fv(gl.Uniform{Value: location}, value)
} }

View File

@ -76,6 +76,7 @@ type Context interface {
TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels []byte)
Uniform1fv(location int32, value []float32) Uniform1fv(location int32, value []float32)
Uniform1i(location int32, v0 int32) Uniform1i(location int32, v0 int32)
Uniform1iv(location int32, value []int32)
Uniform2fv(location int32, value []float32) Uniform2fv(location int32, value []float32)
Uniform3fv(location int32, value []float32) Uniform3fv(location int32, value []float32)
Uniform4fv(location int32, value []float32) Uniform4fv(location int32, value []float32)

View File

@ -223,7 +223,7 @@ func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textu
if u.value == nil { if u.value == nil {
continue continue
} }
if got, expected := len(u.value), u.typ.FloatCount(); got != expected { if got, expected := len(u.value), u.typ.Uint32Count(); got != expected {
// Copy a shaderir.Type value once. Do not pass u.typ directly to fmt.Errorf arguments, or // Copy a shaderir.Type value once. Do not pass u.typ directly to fmt.Errorf arguments, or
// the value u would be allocated on heap. // the value u would be allocated on heap.
typ := u.typ typ := u.typ
@ -234,7 +234,7 @@ func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textu
if ok && areSameUint32Array(cached, u.value) { if ok && areSameUint32Array(cached, u.value) {
continue continue
} }
g.context.uniformFloats(program, u.name, uint32sToFloat32s(u.value), u.typ) g.context.uniforms(program, u.name, u.value, u.typ)
if g.state.lastUniforms == nil { if g.state.lastUniforms == nil {
g.state.lastUniforms = map[string][]uint32{} g.state.lastUniforms = map[string][]uint32{}
} }
@ -284,3 +284,7 @@ loop:
func uint32sToFloat32s(s []uint32) []float32 { func uint32sToFloat32s(s []uint32) []float32 {
return unsafe.Slice((*float32)(unsafe.Pointer(&s[0])), len(s)) return unsafe.Slice((*float32)(unsafe.Pointer(&s[0])), len(s))
} }
func uint32sToInt32s(s []uint32) []int32 {
return unsafe.Slice((*int32)(unsafe.Pointer(&s[0])), len(s))
}

View File

@ -23,6 +23,7 @@ var (
arrayBuffer = js.Global().Get("ArrayBuffer") arrayBuffer = js.Global().Get("ArrayBuffer")
uint8Array = js.Global().Get("Uint8Array") uint8Array = js.Global().Get("Uint8Array")
float32Array = js.Global().Get("Float32Array") float32Array = js.Global().Get("Float32Array")
int32Array = js.Global().Get("Int32Array")
) )
var ( var (
@ -38,6 +39,9 @@ var (
// temporaryFloat32Array is a Float32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer. // temporaryFloat32Array is a Float32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
temporaryFloat32Array = float32Array.New(temporaryArrayBuffer) temporaryFloat32Array = float32Array.New(temporaryArrayBuffer)
// temporaryInt32Array is a Float32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
temporaryInt32Array = int32Array.New(temporaryArrayBuffer)
) )
func ensureTemporaryArrayBufferSize(byteLength int) { func ensureTemporaryArrayBufferSize(byteLength int) {
@ -49,6 +53,7 @@ func ensureTemporaryArrayBufferSize(byteLength int) {
temporaryArrayBuffer = arrayBuffer.New(bufl) temporaryArrayBuffer = arrayBuffer.New(bufl)
temporaryUint8Array = uint8Array.New(temporaryArrayBuffer) temporaryUint8Array = uint8Array.New(temporaryArrayBuffer)
temporaryFloat32Array = float32Array.New(temporaryArrayBuffer) temporaryFloat32Array = float32Array.New(temporaryArrayBuffer)
temporaryInt32Array = int32Array.New(temporaryArrayBuffer)
} }
} }
@ -66,7 +71,7 @@ func TemporaryUint8ArrayFromUint8Slice(minLength int, data []uint8) js.Value {
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization. // data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func TemporaryUint8ArrayFromUint16Slice(minLength int, data []uint16) js.Value { func TemporaryUint8ArrayFromUint16Slice(minLength int, data []uint16) js.Value {
ensureTemporaryArrayBufferSize(minLength * 2) ensureTemporaryArrayBufferSize(minLength * 2)
copyUint16SliceToTemporaryArrayBuffer(data) copySliceToTemporaryArrayBuffer(data)
return temporaryUint8Array return temporaryUint8Array
} }
@ -75,7 +80,7 @@ func TemporaryUint8ArrayFromUint16Slice(minLength int, data []uint16) js.Value {
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization. // data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func TemporaryUint8ArrayFromFloat32Slice(minLength int, data []float32) js.Value { func TemporaryUint8ArrayFromFloat32Slice(minLength int, data []float32) js.Value {
ensureTemporaryArrayBufferSize(minLength * 4) ensureTemporaryArrayBufferSize(minLength * 4)
copyFloat32SliceToTemporaryArrayBuffer(data) copySliceToTemporaryArrayBuffer(data)
return temporaryUint8Array return temporaryUint8Array
} }
@ -84,6 +89,15 @@ func TemporaryUint8ArrayFromFloat32Slice(minLength int, data []float32) js.Value
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization. // data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func TemporaryFloat32Array(minLength int, data []float32) js.Value { func TemporaryFloat32Array(minLength int, data []float32) js.Value {
ensureTemporaryArrayBufferSize(minLength * 4) ensureTemporaryArrayBufferSize(minLength * 4)
copyFloat32SliceToTemporaryArrayBuffer(data) copySliceToTemporaryArrayBuffer(data)
return temporaryFloat32Array return temporaryFloat32Array
} }
// TemporaryInt32Array returns a Int32Array whose length is at least minLength.
// Be careful that the length can exceed the given minLength.
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func TemporaryInt32Array(minLength int, data []int32) js.Value {
ensureTemporaryArrayBufferSize(minLength * 4)
copySliceToTemporaryArrayBuffer(data)
return temporaryInt32Array
}

View File

@ -27,18 +27,14 @@ func copyUint8SliceToTemporaryArrayBuffer(src []uint8) {
js.CopyBytesToJS(temporaryUint8Array, src) js.CopyBytesToJS(temporaryUint8Array, src)
} }
func copyUint16SliceToTemporaryArrayBuffer(src []uint16) { type numeric interface {
if len(src) == 0 { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64
return
}
js.CopyBytesToJS(temporaryUint8Array, unsafe.Slice((*byte)(unsafe.Pointer(&src[0])), len(src)*2))
runtime.KeepAlive(src)
} }
func copyFloat32SliceToTemporaryArrayBuffer(src []float32) { func copySliceToTemporaryArrayBuffer[T numeric](src []T) {
if len(src) == 0 { if len(src) == 0 {
return return
} }
js.CopyBytesToJS(temporaryUint8Array, unsafe.Slice((*byte)(unsafe.Pointer(&src[0])), len(src)*4)) js.CopyBytesToJS(temporaryUint8Array, unsafe.Slice((*byte)(unsafe.Pointer(&src[0])), len(src)*int(unsafe.Sizeof(T(0)))))
runtime.KeepAlive(src) runtime.KeepAlive(src)
} }

View File

@ -59,6 +59,7 @@ func FragmentPrelude(version GLSLVersion) string {
} }
prelude := prefix + `#if defined(GL_ES) prelude := prefix + `#if defined(GL_ES)
precision highp float; precision highp float;
precision highp int;
#else #else
#define lowp #define lowp
#define mediump #define mediump

View File

@ -43,6 +43,9 @@ func calculateMemoryOffsets(uniforms []shaderir.Type) []int {
case shaderir.Float: case shaderir.Float:
offsets = append(offsets, head) offsets = append(offsets, head)
head += 4 head += 4
case shaderir.Int:
offsets = append(offsets, head)
head += 4
case shaderir.Vec2: case shaderir.Vec2:
if head%boundaryInBytes >= 4*3 { if head%boundaryInBytes >= 4*3 {
head = align(head) head = align(head)
@ -79,7 +82,7 @@ func calculateMemoryOffsets(uniforms []shaderir.Type) []int {
// TODO: What if the array has 2 or more dimensions? // TODO: What if the array has 2 or more dimensions?
head = align(head) head = align(head)
offsets = append(offsets, head) offsets = append(offsets, head)
n := u.Sub[0].FloatCount() n := u.Sub[0].Uint32Count()
switch u.Sub[0].Main { switch u.Sub[0].Main {
case shaderir.Mat2: case shaderir.Mat2:
n = 6 n = 6
@ -95,7 +98,7 @@ func calculateMemoryOffsets(uniforms []shaderir.Type) []int {
// TODO: Implement this // TODO: Implement this
panic("hlsl: offset for a struct is not implemented yet") panic("hlsl: offset for a struct is not implemented yet")
default: default:
panic(fmt.Sprintf("hlsl: unexpected type: %d", u.Main)) panic(fmt.Sprintf("hlsl: unexpected type: %s", u.String()))
} }
} }

View File

@ -66,7 +66,7 @@ func (t *Type) String() string {
case Mat4: case Mat4:
return "mat4" return "mat4"
case Array: case Array:
return fmt.Sprintf("%s[%d]", t.Sub[0].String(), t.Length) return fmt.Sprintf("[%d]%s", t.Length, t.Sub[0].String())
case Struct: case Struct:
str := "struct{" str := "struct{"
sub := make([]string, 0, len(t.Sub)) sub := make([]string, 0, len(t.Sub))
@ -81,8 +81,10 @@ func (t *Type) String() string {
} }
} }
func (t *Type) FloatCount() int { func (t *Type) Uint32Count() int {
switch t.Main { switch t.Main {
case Int:
return 1
case Float: case Float:
return 1 return 1
case Vec2: case Vec2:
@ -98,7 +100,7 @@ func (t *Type) FloatCount() int {
case Mat4: case Mat4:
return 16 return 16
case Array: case Array:
return t.Length * t.Sub[0].FloatCount() return t.Length * t.Sub[0].Uint32Count()
default: // TODO: Parse a struct correctly default: // TODO: Parse a struct correctly
return -1 return -1
} }

View File

@ -17,6 +17,7 @@ package ui
import ( import (
"fmt" "fmt"
"math" "math"
"reflect"
"strings" "strings"
"github.com/hajimehoshi/ebiten/v2/internal/mipmap" "github.com/hajimehoshi/ebiten/v2/internal/mipmap"
@ -48,17 +49,37 @@ func (s *Shader) MarkDisposed() {
func (s *Shader) ConvertUniforms(uniforms map[string]any) [][]uint32 { func (s *Shader) ConvertUniforms(uniforms map[string]any) [][]uint32 {
nameToU32s := map[string][]uint32{} nameToU32s := map[string][]uint32{}
for name, v := range uniforms { for name, v := range uniforms {
switch v := v.(type) { v := reflect.ValueOf(v)
case float32: t := v.Type()
nameToU32s[name] = []uint32{math.Float32bits(v)} switch t.Kind() {
case []float32: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
u32s := make([]uint32, len(v)) nameToU32s[name] = []uint32{uint32(v.Int())}
for i := range v { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
u32s[i] = math.Float32bits(v[i]) nameToU32s[name] = []uint32{uint32(v.Uint())}
case reflect.Float32, reflect.Float64:
nameToU32s[name] = []uint32{math.Float32bits(float32(v.Float()))}
case reflect.Slice:
// TODO: Allow reflect.Array (#2448)
u32s := make([]uint32, v.Len())
switch t.Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
for i := range u32s {
u32s[i] = uint32(v.Index(i).Int())
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
for i := range u32s {
u32s[i] = uint32(v.Index(i).Uint())
}
case reflect.Float32, reflect.Float64:
for i := range u32s {
u32s[i] = math.Float32bits(float32(v.Index(i).Float()))
}
default:
panic(fmt.Sprintf("ebiten: unexpected uniform value type: %s (%s)", name, v.Kind().String()))
} }
nameToU32s[name] = u32s nameToU32s[name] = u32s
default: default:
panic(fmt.Sprintf("ebiten: unexpected uniform value type: %s, %T", name, v)) panic(fmt.Sprintf("ebiten: unexpected uniform value type: %s (%s)", name, v.Kind().String()))
} }
} }
@ -84,7 +105,7 @@ func (s *Shader) ConvertUniforms(uniforms map[string]any) [][]uint32 {
continue continue
} }
t := s.uniformNameToType[name] t := s.uniformNameToType[name]
us[idx] = make([]uint32, t.FloatCount()) us[idx] = make([]uint32, t.Uint32Count())
} }
// TODO: Panic if uniforms include an invalid name // TODO: Panic if uniforms include an invalid name

View File

@ -548,10 +548,13 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
const w, h = 1, 1 const w, h = 1, 1
dst := ebiten.NewImage(w, h) dst := ebiten.NewImage(w, h)
defer dst.Dispose()
s, err := ebiten.NewShader([]byte(shader.Shader)) s, err := ebiten.NewShader([]byte(shader.Shader))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer s.Dispose()
op := &ebiten.DrawRectShaderOptions{} op := &ebiten.DrawRectShaderOptions{}
op.Uniforms = shader.Uniforms op.Uniforms = shader.Uniforms
@ -1269,3 +1272,113 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
} }
} }
} }
func TestShaderUniformInt(t *testing.T) {
const ints = `package main
var U0 int
var U1 int
var U2 int
var U3 int
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
return vec4(float(U0)/255.0, float(U1)/255.0, float(U2)/255.0, float(U3)/255.0)
}
`
const intArray = `package main
var U [4]int
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
return vec4(float(U[0])/255.0, float(U[1])/255.0, float(U[2])/255.0, float(U[3])/255.0)
}
`
testCases := []struct {
Name string
Uniforms map[string]any
Shader string
Want color.RGBA
}{
{
Name: "0xff",
Uniforms: map[string]any{
"U0": 0xff,
"U1": 0xff,
"U2": 0xff,
"U3": 0xff,
},
Shader: ints,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff},
},
{
Name: "int",
Uniforms: map[string]any{
"U0": int8(0x24),
"U1": int16(0x3f),
"U2": int32(0x6a),
"U3": int64(0x88),
},
Shader: ints,
Want: color.RGBA{0x24, 0x3f, 0x6a, 0x88},
},
{
Name: "uint",
Uniforms: map[string]any{
"U0": uint8(0x85),
"U1": uint16(0xa3),
"U2": uint32(0x08),
"U3": uint64(0xd3),
},
Shader: ints,
Want: color.RGBA{0x85, 0xa3, 0x08, 0xd3},
},
{
Name: "0xff,array",
Uniforms: map[string]any{
"U": []int{0xff, 0xff, 0xff, 0xff},
},
Shader: intArray,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff},
},
{
Name: "int,array",
Uniforms: map[string]any{
"U": []int16{0x24, 0x3f, 0x6a, 0x88},
},
Shader: intArray,
Want: color.RGBA{0x24, 0x3f, 0x6a, 0x88},
},
{
Name: "uint,array",
Uniforms: map[string]any{
"U": []uint8{0x85, 0xa3, 0x08, 0xd3},
},
Shader: intArray,
Want: color.RGBA{0x85, 0xa3, 0x08, 0xd3},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.Name, func(t *testing.T) {
const w, h = 1, 1
dst := ebiten.NewImage(w, h)
defer dst.Dispose()
s, err := ebiten.NewShader([]byte(tc.Shader))
if err != nil {
t.Fatal(err)
}
defer s.Dispose()
op := &ebiten.DrawRectShaderOptions{}
op.Uniforms = tc.Uniforms
dst.DrawRectShader(w, h, s, op)
if got, want := dst.At(0, 0).(color.RGBA), tc.Want; !sameColors(got, want, 1) {
t.Errorf("got: %v, want: %v", got, want)
}
})
}
}