Struct to ubo support

This commit is contained in:
bloeys
2024-05-26 12:55:58 +04:00
parent cb20e8ba8b
commit ff7fe4e531
2 changed files with 263 additions and 26 deletions

View File

@ -1,6 +1,9 @@
package buffers package buffers
import ( import (
"math"
"reflect"
"github.com/bloeys/gglm/gglm" "github.com/bloeys/gglm/gglm"
"github.com/bloeys/nmage/assert" "github.com/bloeys/nmage/assert"
"github.com/bloeys/nmage/logging" "github.com/bloeys/nmage/logging"
@ -141,6 +144,217 @@ func (ub *UniformBuffer) SetMat4(fieldId uint16, val *gglm.Mat4) {
gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4*16, gl.Ptr(&val.Data[0][0])) gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4*16, gl.Ptr(&val.Data[0][0]))
} }
func (ub *UniformBuffer) SetStruct(inputStruct any) {
if inputStruct == nil {
logging.ErrLog.Panicf("UniformBuffer.SetStruct called with a value that is nil")
}
structVal := reflect.ValueOf(inputStruct)
if structVal.Kind() != reflect.Struct {
logging.ErrLog.Panicf("UniformBuffer.SetStruct called with a value that is not a struct. Val=%v\n", inputStruct)
}
if structVal.NumField() != len(ub.Fields) {
logging.ErrLog.Panicf("struct fields must match uniform buffer fields, but uniform buffer contains %d fields, while the passed struct has %d fields\n", len(ub.Fields), structVal.NumField())
}
writeIndex := 0
buf := make([]byte, ub.Size)
for i := 0; i < len(ub.Fields); i++ {
ubField := &ub.Fields[i]
valField := structVal.Field(i)
if valField.Kind() == reflect.Pointer {
valField = valField.Elem()
}
typeMatches := false
writeIndex = int(ubField.AlignedOffset)
switch ubField.Type {
case DataTypeUint32:
t := valField.Type()
typeMatches = t.Name() == "uint32"
if typeMatches {
Write32BitIntegerToByteBuf(buf, &writeIndex, uint32(valField.Uint()))
}
case DataTypeFloat32:
t := valField.Type()
typeMatches = t.Name() == "float32"
if typeMatches {
WriteF32ToByteBuf(buf, &writeIndex, float32(valField.Float()))
}
case DataTypeInt32:
t := valField.Type()
typeMatches = t.Name() == "int32"
if typeMatches {
Write32BitIntegerToByteBuf(buf, &writeIndex, uint32(valField.Int()))
}
case DataTypeVec2:
v2, ok := valField.Interface().(gglm.Vec2)
typeMatches = ok
if typeMatches {
WriteF32SliceToByteBuf(buf, &writeIndex, v2.Data[:])
}
case DataTypeVec3:
v3, ok := valField.Interface().(gglm.Vec3)
typeMatches = ok
if typeMatches {
WriteF32SliceToByteBuf(buf, &writeIndex, v3.Data[:])
}
case DataTypeVec4:
v4, ok := valField.Interface().(gglm.Vec4)
typeMatches = ok
if typeMatches {
WriteF32SliceToByteBuf(buf, &writeIndex, v4.Data[:])
}
case DataTypeMat2:
m2, ok := valField.Interface().(gglm.Mat2)
typeMatches = ok
if typeMatches {
WriteF32SliceToByteBuf(buf, &writeIndex, m2.Data[0][:])
WriteF32SliceToByteBuf(buf, &writeIndex, m2.Data[1][:])
}
case DataTypeMat3:
m3, ok := valField.Interface().(gglm.Mat3)
typeMatches = ok
if typeMatches {
WriteF32SliceToByteBuf(buf, &writeIndex, m3.Data[0][:])
WriteF32SliceToByteBuf(buf, &writeIndex, m3.Data[1][:])
WriteF32SliceToByteBuf(buf, &writeIndex, m3.Data[2][:])
}
case DataTypeMat4:
m4, ok := valField.Interface().(gglm.Mat4)
typeMatches = ok
if typeMatches {
WriteF32SliceToByteBuf(buf, &writeIndex, m4.Data[0][:])
WriteF32SliceToByteBuf(buf, &writeIndex, m4.Data[1][:])
WriteF32SliceToByteBuf(buf, &writeIndex, m4.Data[2][:])
WriteF32SliceToByteBuf(buf, &writeIndex, m4.Data[3][:])
}
default:
assert.T(false, "Unknown uniform buffer data type passed. DataType '%d'", ubField.Type)
}
if !typeMatches {
logging.ErrLog.Panicf("Struct field ordering and types must match uniform buffer fields, but at field index %d got UniformBufferField=%v but a struct field of %s\n", i, ubField, valField.String())
}
}
if writeIndex == 0 {
return
}
gl.BufferSubData(gl.UNIFORM_BUFFER, 0, writeIndex, gl.Ptr(&buf[0]))
}
func Write32BitIntegerToByteBuf[T uint32 | int32](buf []byte, startIndex *int, val T) {
assert.T(*startIndex+4 <= len(buf), "failed to write uint32/int32 to buffer because the buffer doesn't have enough space. Start index=%d, Buffer length=%d", *startIndex, len(buf))
buf[*startIndex] = byte(val)
buf[*startIndex+1] = byte(val >> 8)
buf[*startIndex+2] = byte(val >> 16)
buf[*startIndex+3] = byte(val >> 24)
*startIndex += 4
}
func WriteF32ToByteBuf(buf []byte, startIndex *int, val float32) {
assert.T(*startIndex+4 <= len(buf), "failed to write float32 to buffer because the buffer doesn't have enough space. Start index=%d, Buffer length=%d", *startIndex, len(buf))
bits := math.Float32bits(val)
buf[*startIndex] = byte(bits)
buf[*startIndex+1] = byte(bits >> 8)
buf[*startIndex+2] = byte(bits >> 16)
buf[*startIndex+3] = byte(bits >> 24)
*startIndex += 4
}
func WriteF32SliceToByteBuf(buf []byte, startIndex *int, vals []float32) {
assert.T(*startIndex+4 <= len(buf), "failed to write slice of float32 to buffer because the buffer doesn't have enough space. Start index=%d, Buffer length=%d, but needs %d bytes free", *startIndex, len(buf), len(vals)*4)
for i := 0; i < len(vals); i++ {
bits := math.Float32bits(vals[i])
buf[*startIndex] = byte(bits)
buf[*startIndex+1] = byte(bits >> 8)
buf[*startIndex+2] = byte(bits >> 16)
buf[*startIndex+3] = byte(bits >> 24)
*startIndex += 4
}
}
func ReflectValueMatchesUniformBufferField(v reflect.Value, ubField *UniformBufferField) bool {
if v.Kind() == reflect.Pointer {
v = v.Elem()
}
switch ubField.Type {
case DataTypeUint32:
t := v.Type()
return t.Name() == "uint32"
case DataTypeFloat32:
t := v.Type()
return t.Name() == "float32"
case DataTypeInt32:
t := v.Type()
return t.Name() == "int32"
case DataTypeVec2:
_, ok := v.Interface().(gglm.Vec2)
return ok
case DataTypeVec3:
_, ok := v.Interface().(gglm.Vec3)
return ok
case DataTypeVec4:
_, ok := v.Interface().(gglm.Vec4)
return ok
case DataTypeMat2:
_, ok := v.Interface().(gglm.Mat2)
return ok
case DataTypeMat3:
_, ok := v.Interface().(gglm.Mat3)
return ok
case DataTypeMat4:
_, ok := v.Interface().(gglm.Mat4)
return ok
default:
assert.T(false, "Unknown uniform buffer data type passed. DataType '%d'", ubField.Type)
return false
}
}
func NewUniformBuffer(fields []UniformBufferFieldInput) UniformBuffer { func NewUniformBuffer(fields []UniformBufferFieldInput) UniformBuffer {
ub := UniformBuffer{} ub := UniformBuffer{}

75
main.go
View File

@ -671,41 +671,64 @@ func (g *Game) Init() {
updateAllProjViewMats(cam.ProjMat, cam.ViewMat) updateAllProjViewMats(cam.ProjMat, cam.ViewMat)
// Ubos // Ubos
// testUbos() testUbos()
} }
// func testUbos() { func testUbos() {
// ubo := buffers.NewUniformBuffer([]buffers.UniformBufferFieldInput{ ubo := buffers.NewUniformBuffer([]buffers.UniformBufferFieldInput{
// {Id: 0, Type: buffers.DataTypeFloat32}, // 04 00 {Id: 0, Type: buffers.DataTypeFloat32}, // 04 00
// {Id: 1, Type: buffers.DataTypeVec3}, // 16 16 {Id: 1, Type: buffers.DataTypeVec3}, // 16 16
// {Id: 2, Type: buffers.DataTypeFloat32}, // 04 32 {Id: 2, Type: buffers.DataTypeFloat32}, // 04 32
// {Id: 3, Type: buffers.DataTypeMat2}, // 32 48 {Id: 3, Type: buffers.DataTypeMat2}, // 32 48
// }) // Total size: 48+32 = 80 }) // Total size: 48+32 = 80
// println("!!!!!!!!!!!!! Id:", ubo.Id, "; Size:", ubo.Size) println("!!!!!!!!!!!!! Id:", ubo.Id, "; Size:", ubo.Size)
// fmt.Printf("%+v\n", ubo.Fields) fmt.Printf("%+v\n", ubo.Fields)
// ubo.Bind() ubo.Bind()
// ubo.SetFloat32(0, 99) ubo.SetFloat32(0, 99)
// ubo.SetFloat32(2, 199) ubo.SetFloat32(2, 199)
// ubo.SetVec3(1, &gglm.Vec3{Data: [3]float32{33, 33, 33}}) ubo.SetVec3(1, &gglm.Vec3{Data: [3]float32{33, 33, 33}})
// ubo.SetMat2(3, &gglm.Mat2{Data: [2][2]float32{{1, 3}, {2, 4}}}) ubo.SetMat2(3, &gglm.Mat2{Data: [2][2]float32{{1, 3}, {2, 4}}})
// var v gglm.Vec3 var v gglm.Vec3
// var m2 gglm.Mat2 var m2 gglm.Mat2
// var x, x2 float32 var x, x2 float32
// gl.GetBufferSubData(gl.UNIFORM_BUFFER, 0, 4, gl.Ptr(&x)) gl.GetBufferSubData(gl.UNIFORM_BUFFER, 0, 4, gl.Ptr(&x))
// gl.GetBufferSubData(gl.UNIFORM_BUFFER, 32, 4, gl.Ptr(&x2)) gl.GetBufferSubData(gl.UNIFORM_BUFFER, 32, 4, gl.Ptr(&x2))
// gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 12, gl.Ptr(&v.Data[0])) gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 12, gl.Ptr(&v.Data[0]))
// gl.GetBufferSubData(gl.UNIFORM_BUFFER, 48, 16, gl.Ptr(&m2.Data[0][0])) gl.GetBufferSubData(gl.UNIFORM_BUFFER, 48, 16, gl.Ptr(&m2.Data[0][0]))
// fmt.Printf("x=%f; x2=%f; v3=%s; m2=%s\n", x, x2, v.String(), m2.String()) fmt.Printf("x=%f; x2=%f; v3=%s; m2=%s\n", x, x2, v.String(), m2.String())
// ubo.SetVec3(1, &gglm.Vec3{Data: [3]float32{-123, 33, 33}}) ubo.SetVec3(1, &gglm.Vec3{Data: [3]float32{-123, 33, 33}})
// gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 12, gl.Ptr(&v.Data[0])) gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 12, gl.Ptr(&v.Data[0]))
// }
type TestUBO struct {
FirstF32 float32
V3 gglm.Vec3
SecondF32 float32
M2 gglm.Mat2
}
s := TestUBO{
FirstF32: 1.5,
V3: gglm.Vec3{Data: [3]float32{11, 22, 33}},
SecondF32: 9.5,
M2: gglm.Mat2{Data: [2][2]float32{{6, 8}, {7, 9}}},
}
ubo.SetStruct(s)
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 0, 4, gl.Ptr(&x))
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 32, 4, gl.Ptr(&x2))
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 12, gl.Ptr(&v.Data[0]))
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 48, 16, gl.Ptr(&m2.Data[0][0]))
fmt.Printf("x=%f; x2=%f; v3=%s; m2=%s\n", x, x2, v.String(), m2.String())
}
func (g *Game) initFbos() { func (g *Game) initFbos() {